chore: 框架构建流程调整
This commit is contained in:
@@ -1,50 +1,3 @@
|
||||
<template>
|
||||
<a-modal @ok="handleOk">
|
||||
<a-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
>
|
||||
<a-form-item label="字段标题" name="field_title">
|
||||
<a-input
|
||||
placeholder="请输入字段标题"
|
||||
v-model:value="formData.field_title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段名称" name="field_name">
|
||||
<a-input placeholder="请输入字段名称" v-model="formData.field_name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="搜索状态" name="is_search">
|
||||
<a-switch
|
||||
v-model:checked="formData.is_search"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段类型" name="field_type_id">
|
||||
<a-select
|
||||
placeholder="请选择字段类型"
|
||||
v-model:value="formData.field_type_id"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所属表名称" name="belong_to_table">
|
||||
<a-input
|
||||
placeholder="请输入所属表名称"
|
||||
v-model="formData.belong_to_table"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="sql数据源" name="original_sql">
|
||||
<a-input
|
||||
placeholder="请输入sql数据源"
|
||||
v-model="formData.original_sql"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
@@ -108,3 +61,50 @@ const handleOk = () => {
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal @ok="handleOk">
|
||||
<a-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
>
|
||||
<a-form-item label="字段标题" name="field_title">
|
||||
<a-input
|
||||
placeholder="请输入字段标题"
|
||||
v-model:value="formData.field_title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段名称" name="field_name">
|
||||
<a-input placeholder="请输入字段名称" v-model="formData.field_name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="搜索状态" name="is_search">
|
||||
<a-switch
|
||||
v-model:checked="formData.is_search"
|
||||
:checked-value="1"
|
||||
:un-checked-value="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段类型" name="field_type_id">
|
||||
<a-select
|
||||
placeholder="请选择字段类型"
|
||||
v-model:value="formData.field_type_id"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所属表名称" name="belong_to_table">
|
||||
<a-input
|
||||
placeholder="请输入所属表名称"
|
||||
v-model="formData.belong_to_table"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="sql数据源" name="original_sql">
|
||||
<a-input
|
||||
placeholder="请输入sql数据源"
|
||||
v-model="formData.original_sql"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
@@ -1,84 +1,16 @@
|
||||
<template>
|
||||
<div class="normal-container view-cfg-box">
|
||||
<div class="header-box">
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="fieldName"
|
||||
placeholder="请输入字段名称"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
@change="search"
|
||||
/>
|
||||
<a-select
|
||||
placeholder="请选择数据表id"
|
||||
v-model:value="modularId"
|
||||
allow-clear
|
||||
style="width: 160px"
|
||||
@change="search"
|
||||
></a-select>
|
||||
<a-button type="primary" @click="openCreateModal">新建</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<a-table
|
||||
:columns="viewCfgCols"
|
||||
:data-source="dataList"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
@click="toGetDetail(record.field_id)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="toDelete(record.field_id)"
|
||||
>
|
||||
<a-button type="link" size="small">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="pageState.page"
|
||||
:total="pageState.total"
|
||||
:page-size="pageState.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="toGetFieldList"
|
||||
/>
|
||||
</div>
|
||||
<CreateModal
|
||||
:width="700"
|
||||
:open="modalState.visible"
|
||||
:title="modalState.title"
|
||||
:type="modalState.type"
|
||||
:data="modalState.data"
|
||||
@cancel="modalState.visible = false"
|
||||
@ok="toSave"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, reactive } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import CreateModal from './components/create-modal.vue';
|
||||
import { viewCfgCols } from './config';
|
||||
import {
|
||||
getFieldList,
|
||||
deleteField,
|
||||
saveField,
|
||||
getFieldDetail,
|
||||
getFieldList,
|
||||
saveField,
|
||||
} from './service';
|
||||
import CreateModal from './components/create-modal.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const listLoading = ref(false);
|
||||
const saveLoading = ref(false);
|
||||
@@ -166,6 +98,77 @@ const search = () => {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="normal-container view-cfg-box">
|
||||
<div class="header-box">
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="fieldName"
|
||||
placeholder="请输入字段名称"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
@change="search"
|
||||
/>
|
||||
<a-select
|
||||
placeholder="请选择数据表id"
|
||||
v-model:value="modularId"
|
||||
allow-clear
|
||||
style="width: 160px"
|
||||
@change="search"
|
||||
/>
|
||||
<a-button type="primary" @click="openCreateModal">新建</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<a-table
|
||||
:columns="viewCfgCols"
|
||||
:data-source="dataList"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
@click="toGetDetail(record.field_id)"
|
||||
>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="toDelete(record.field_id)"
|
||||
>
|
||||
<a-button type="link" size="small">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="pageState.page"
|
||||
:total="pageState.total"
|
||||
:page-size="pageState.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="toGetFieldList"
|
||||
/>
|
||||
</div>
|
||||
<CreateModal
|
||||
:width="700"
|
||||
:open="modalState.visible"
|
||||
:title="modalState.title"
|
||||
:type="modalState.type"
|
||||
:data="modalState.data"
|
||||
@cancel="modalState.visible = false"
|
||||
@ok="toSave"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.header-box {
|
||||
margin-bottom: 10px;
|
||||
|
||||
@@ -1,131 +1,19 @@
|
||||
<template>
|
||||
<div class="page-view-wrapp">
|
||||
<div v-if="!isInQiankun" class="project">
|
||||
<span>项目: </span>
|
||||
<a-select
|
||||
style="min-width: 160px"
|
||||
placeholder="请选择项目"
|
||||
v-model:value="projectVal"
|
||||
:options="projectOptions"
|
||||
@change="handleProjectChange"
|
||||
></a-select>
|
||||
</div>
|
||||
<div>
|
||||
<grid-layout
|
||||
v-if="isDraggable"
|
||||
:layout.sync="layoutList"
|
||||
:col-num="2"
|
||||
:is-draggable="true"
|
||||
:is-resizable="false"
|
||||
:is-mirrored="false"
|
||||
:vertical-compact="true"
|
||||
:use-css-transforms="true"
|
||||
>
|
||||
<grid-item
|
||||
v-for="(item, index) in layoutList"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
drag-allow-from=".vue-draggable-handle"
|
||||
drag-ignore-from=".no-drag"
|
||||
>
|
||||
<div class="view-box view-draggable">
|
||||
<div class="vue-draggable-handle"><BarsOutlined /></div>
|
||||
<div class="content no-drag">
|
||||
<a-spin :spinning="ids[index].loading">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@toFilt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-table>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chartCfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@toFilt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-chart>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</grid-item>
|
||||
</grid-layout>
|
||||
|
||||
<a-row v-else :gutter="[16, 16]">
|
||||
<a-col v-for="(item, index) in layoutList" :span="24">
|
||||
<a-spin :spinning="item.loading">
|
||||
<div>
|
||||
<div class="view-box">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@toFilt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-table>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chartCfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@toFilt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef, computed, onMounted, watch } from 'vue';
|
||||
import type { SelectProps } from 'ant-design-vue';
|
||||
|
||||
import { computed, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { getProjectDrop } from '@/api/common';
|
||||
// api
|
||||
import { searchInfo } from '@/api/preview/index';
|
||||
import { BarsOutlined } from '@ant-design/icons-vue';
|
||||
// utils
|
||||
import PLimit from 'p-limit';
|
||||
// api
|
||||
import { searchInfo } from '@/api/preview/index';
|
||||
import { getProjectDrop } from '@/api/common';
|
||||
import { getPageInfo } from './service';
|
||||
import type { SelectProps } from 'ant-design-vue';
|
||||
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
|
||||
|
||||
import { getPageInfo } from './service';
|
||||
|
||||
interface ItemDetail {
|
||||
id: number | string;
|
||||
data: any;
|
||||
@@ -204,7 +92,7 @@ const handleSingle = (info: ItemDetail, otherParams?: object) => {
|
||||
getSinglePreview({ info, otherParams, type: SEARCH_TYPE.SEARCH });
|
||||
};
|
||||
|
||||
const handleProjectChange = (value: string | number, option: Option) => {
|
||||
const handleProjectChange = (value: number | string, option: Option) => {
|
||||
projectTag.value = option.mark;
|
||||
router.replace({
|
||||
path: route.path,
|
||||
@@ -263,27 +151,25 @@ const getPageInfoData = () => {
|
||||
getPageInfo({ mark: projectTag.value, page_id: pageId.value ?? '-1' })
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (route.query.viewId) {
|
||||
ids.value = res.data
|
||||
?.filter((item: any) => {
|
||||
return item.preview_id === Number(route.query.viewId);
|
||||
})
|
||||
.map((item: any) => {
|
||||
ids.value = route.query.viewId
|
||||
? res.data
|
||||
?.filter((item: any) => {
|
||||
return item.preview_id === Number(route.query.viewId);
|
||||
})
|
||||
.map((item: any) => {
|
||||
return {
|
||||
id: item.preview_id,
|
||||
data: item,
|
||||
loading: false,
|
||||
};
|
||||
})
|
||||
: res.data?.map((item: any) => {
|
||||
return {
|
||||
id: item.preview_id,
|
||||
data: item,
|
||||
loading: false,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
ids.value = res.data?.map((item: any) => {
|
||||
return {
|
||||
id: item.preview_id,
|
||||
data: item,
|
||||
loading: false,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getAllCardsData();
|
||||
}
|
||||
@@ -301,14 +187,129 @@ const fetchFn = (delay: number, info: ItemDetail) => {
|
||||
};
|
||||
|
||||
const getAllCardsData = async () => {
|
||||
let listDB = [];
|
||||
for (let i in ids.value) {
|
||||
const listDB = [];
|
||||
for (const i in ids.value) {
|
||||
listDB.push(pLimit(() => fetchFn(i === '0' ? 200 : 1000, ids.value[i])));
|
||||
}
|
||||
await Promise.all(listDB);
|
||||
//此处的listDB就是最后整合的数据
|
||||
// 此处的listDB就是最后整合的数据
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="page-view-wrapp">
|
||||
<div v-if="!isInQiankun" class="project">
|
||||
<span>项目: </span>
|
||||
<a-select
|
||||
style="min-width: 160px"
|
||||
placeholder="请选择项目"
|
||||
v-model:value="projectVal"
|
||||
:options="projectOptions"
|
||||
@change="handleProjectChange"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<grid-layout
|
||||
v-if="isDraggable"
|
||||
v-model:layout="layoutList"
|
||||
:col-num="2"
|
||||
:is-draggable="true"
|
||||
:is-resizable="false"
|
||||
:is-mirrored="false"
|
||||
:vertical-compact="true"
|
||||
:use-css-transforms="true"
|
||||
>
|
||||
<grid-item
|
||||
v-for="(item, index) in layoutList"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
drag-allow-from=".vue-draggable-handle"
|
||||
drag-ignore-from=".no-drag"
|
||||
>
|
||||
<div class="view-box view-draggable">
|
||||
<div class="vue-draggable-handle"><BarsOutlined /></div>
|
||||
<div class="content no-drag">
|
||||
<a-spin :spinning="ids[index].loading">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@to-filt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chart-cfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@to-filt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</grid-item>
|
||||
</grid-layout>
|
||||
|
||||
<a-row v-else :gutter="[16, 16]">
|
||||
<a-col v-for="(item, index) in layoutList" :span="24">
|
||||
<a-spin :spinning="item.loading">
|
||||
<div>
|
||||
<div class="view-box">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@to-filt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chart-cfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@to-filt="
|
||||
(params?: object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.view-box {
|
||||
height: 100%;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { get, post } from '@/utils/request';
|
||||
import { get } from '@/utils/request';
|
||||
|
||||
interface PageInfoParams {
|
||||
mark: string;
|
||||
page_id: number | string;
|
||||
|
||||
@@ -1,235 +1,18 @@
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-create-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="项目"
|
||||
><a-select
|
||||
placeholder="请选择项目"
|
||||
:options="projectSel"
|
||||
v-model:value="projectId"
|
||||
@change="onProjectChange"
|
||||
></a-select
|
||||
></a-form-item>
|
||||
<a-form-item label="数据来源">
|
||||
<a-select
|
||||
placeholder="请选择"
|
||||
:options="modularSel"
|
||||
v-model:value="modularId"
|
||||
@change="onModularChange"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="展示类型">
|
||||
<a-select
|
||||
placeholder="请选择展示类型"
|
||||
:options="showTypeSel"
|
||||
v-model:value="showTypeId"
|
||||
@change="onShowTypeChange"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否导出" v-if="showTypeId === 1">
|
||||
<a-switch
|
||||
v-model:checked="isExport"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段" v-if="fieldList.length">
|
||||
<a-checkbox-group v-model:value="fieldIds">
|
||||
<a-checkbox
|
||||
v-for="(item, index) in fieldList"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-checkbox
|
||||
>
|
||||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="x轴" v-if="xDataList.length">
|
||||
<a-radio-group
|
||||
:options="xDataList"
|
||||
v-model:value="xDataId"
|
||||
></a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="y轴" v-if="yDataList.length">
|
||||
<a-checkbox-group
|
||||
:options="yDataList"
|
||||
v-model:value="yDataId"
|
||||
></a-checkbox-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="footer">
|
||||
<a-button
|
||||
class="preview-btn"
|
||||
:loading="previewLoading"
|
||||
@click="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
>预览</a-button
|
||||
>
|
||||
<a-button type="primary" @click="addViewName">点击保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<div class="y-table-container" v-if="previewData.type === 'table'">
|
||||
<div class="y-table-name">
|
||||
<div class="title">{{ previewData.preview_name }}</div>
|
||||
</div>
|
||||
<div class="y-table-filter">
|
||||
<div
|
||||
v-for="item in previewData.filterConfig"
|
||||
:key="item.name"
|
||||
class="filter-item"
|
||||
>
|
||||
<span>{{ item.label }}:</span>
|
||||
<a-select
|
||||
v-if="item.type === 'select'"
|
||||
class="input-item"
|
||||
:options="item.options"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
@change="toFilt"
|
||||
></a-select>
|
||||
<a-input
|
||||
v-if="item.type === 'text'"
|
||||
class="input-item"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-if="item.type === 'time'"
|
||||
class="date-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'date_time'"
|
||||
class="date-time-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 数值区间 -->
|
||||
<div
|
||||
v-else-if="
|
||||
item.type === 'number_range' &&
|
||||
previewData.filterData[item.name]
|
||||
"
|
||||
class="number_range_box"
|
||||
>
|
||||
<a-input-number
|
||||
placeholder="最小值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].min"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<span class="divider"> - </span>
|
||||
<a-input-number
|
||||
placeholder="最大值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].max"
|
||||
@change="toFilt"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="previewData.isExport" class="filter-item">
|
||||
<a-button type="primary">导出</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="y-table-content">
|
||||
<a-table
|
||||
:columns="previewData.columnConfig"
|
||||
:data-source="previewData.dataList"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 1000, y: `calc(100vh - 260px)` }"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<a-image
|
||||
v-if="column.show_type === 'img'"
|
||||
:src="record[column.dataIndex]"
|
||||
:width="160"
|
||||
/>
|
||||
<a
|
||||
v-else-if="column.show_type === 'link'"
|
||||
target="_blank"
|
||||
:href="record[column.dataIndex]"
|
||||
>{{ record[column.dataIndex] }}</a
|
||||
>
|
||||
<div
|
||||
v-else-if="column.show_type === 'richText'"
|
||||
v-html="record[column.dataIndex]"
|
||||
></div>
|
||||
<template v-else>{{ record[column.dataIndex] }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="previewData.page"
|
||||
:total="previewData.total"
|
||||
:page-size="previewData.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<y-chart
|
||||
v-else-if="previewData.type === 'chart'"
|
||||
:chart-cfg="previewData.chartCfg"
|
||||
:filter-config="previewData.filter"
|
||||
@toFilt="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
></y-chart>
|
||||
<div class="preview-area" v-else>
|
||||
<div><BarChartOutlined /></div>
|
||||
<div>预览区</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal
|
||||
:open="nameVisible"
|
||||
title="保存视图"
|
||||
:bodyStyle="{ paddingTop: '20px' }"
|
||||
@cancel="nameVisible = false"
|
||||
@ok="toSaveView"
|
||||
>
|
||||
<a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="视图名称">
|
||||
<a-input placeholder="请输入视图名称" v-model:value="previewName" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import yChart from '@/components/common/y-chart.vue';
|
||||
import { BarChartOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import {
|
||||
getFieldOpts,
|
||||
getProModularField,
|
||||
getShowTypeSelect,
|
||||
preview,
|
||||
saveView,
|
||||
getShowTypeSelect,
|
||||
getFieldOpts,
|
||||
} from './service';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { BarChartOutlined } from '@ant-design/icons-vue';
|
||||
import yChart from '@/components/common/y-chart.vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const projectSel = ref([]); // 项目下拉
|
||||
const modularSel = ref([]); // 数据来源下拉
|
||||
@@ -289,7 +72,7 @@ const toGetFieldOpts = () => {
|
||||
fieldList.value = res.data.list ? tranformList(res.data.list) : [];
|
||||
xDataList.value = res.data.x_data ? tranformList(res.data.x_data) : [];
|
||||
yDataList.value = res.data.y_data ? tranformList(res.data.y_data) : [];
|
||||
if (!fieldList.value.length) {
|
||||
if (fieldList.value.length === 0) {
|
||||
fieldIds.value = [];
|
||||
} else {
|
||||
xDataId.value = undefined;
|
||||
@@ -354,7 +137,9 @@ const resetPreviewData = () => {
|
||||
const toPreview = ({ filter }) => {
|
||||
previewLoading.value = true;
|
||||
let filterData;
|
||||
if (!filter) {
|
||||
if (filter) {
|
||||
filterData = filter;
|
||||
} else {
|
||||
const cloneFilter = cloneDeep(previewData.filterConfig);
|
||||
filterData = cloneFilter
|
||||
.filter((item) => {
|
||||
@@ -381,12 +166,12 @@ const toPreview = ({ filter }) => {
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time:
|
||||
previewData.filterData[item.name][0].format('YYYY-MM-DD HH:mm') +
|
||||
':00',
|
||||
end_time:
|
||||
previewData.filterData[item.name][1].format('YYYY-MM-DD HH:mm') +
|
||||
':59',
|
||||
start_time: `${previewData.filterData[item.name][0].format(
|
||||
'YYYY-MM-DD HH:mm',
|
||||
)}:00`,
|
||||
end_time: `${previewData.filterData[item.name][1].format(
|
||||
'YYYY-MM-DD HH:mm',
|
||||
)}:59`,
|
||||
};
|
||||
} else if (item.type === 'number_range') {
|
||||
// 数值区间
|
||||
@@ -408,8 +193,6 @@ const toPreview = ({ filter }) => {
|
||||
};
|
||||
}
|
||||
});
|
||||
} else {
|
||||
filterData = filter;
|
||||
}
|
||||
|
||||
preview({
|
||||
@@ -489,6 +272,219 @@ const toFilt = () => {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-create-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="项目">
|
||||
<a-select
|
||||
placeholder="请选择项目"
|
||||
:options="projectSel"
|
||||
v-model:value="projectId"
|
||||
@change="onProjectChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据来源">
|
||||
<a-select
|
||||
placeholder="请选择"
|
||||
:options="modularSel"
|
||||
v-model:value="modularId"
|
||||
@change="onModularChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="展示类型">
|
||||
<a-select
|
||||
placeholder="请选择展示类型"
|
||||
:options="showTypeSel"
|
||||
v-model:value="showTypeId"
|
||||
@change="onShowTypeChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否导出" v-if="showTypeId === 1">
|
||||
<a-switch
|
||||
v-model:checked="isExport"
|
||||
:checked-value="1"
|
||||
:un-checked-value="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段" v-if="fieldList.length > 0">
|
||||
<a-checkbox-group v-model:value="fieldIds">
|
||||
<a-checkbox
|
||||
v-for="(item, index) in fieldList"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="x轴" v-if="xDataList.length > 0">
|
||||
<a-radio-group :options="xDataList" v-model:value="xDataId" />
|
||||
</a-form-item>
|
||||
<a-form-item label="y轴" v-if="yDataList.length > 0">
|
||||
<a-checkbox-group :options="yDataList" v-model:value="yDataId" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="footer">
|
||||
<a-button
|
||||
class="preview-btn"
|
||||
:loading="previewLoading"
|
||||
@click="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
>
|
||||
预览
|
||||
</a-button>
|
||||
<a-button type="primary" @click="addViewName">点击保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<div class="y-table-container" v-if="previewData.type === 'table'">
|
||||
<div class="y-table-name">
|
||||
<div class="title">{{ previewData.preview_name }}</div>
|
||||
</div>
|
||||
<div class="y-table-filter">
|
||||
<div
|
||||
v-for="item in previewData.filterConfig"
|
||||
:key="item.name"
|
||||
class="filter-item"
|
||||
>
|
||||
<span>{{ item.label }}:</span>
|
||||
<a-select
|
||||
v-if="item.type === 'select'"
|
||||
class="input-item"
|
||||
:options="item.options"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-input
|
||||
v-if="item.type === 'text'"
|
||||
class="input-item"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-if="item.type === 'time'"
|
||||
class="date-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'date_time'"
|
||||
class="date-time-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 数值区间 -->
|
||||
<div
|
||||
v-else-if="
|
||||
item.type === 'number_range' &&
|
||||
previewData.filterData[item.name]
|
||||
"
|
||||
class="number_range_box"
|
||||
>
|
||||
<a-input-number
|
||||
placeholder="最小值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].min"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<span class="divider"> - </span>
|
||||
<a-input-number
|
||||
placeholder="最大值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].max"
|
||||
@change="toFilt"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="previewData.isExport" class="filter-item">
|
||||
<a-button type="primary">导出</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="y-table-content">
|
||||
<a-table
|
||||
:columns="previewData.columnConfig"
|
||||
:data-source="previewData.dataList"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 1000, y: `calc(100vh - 260px)` }"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<a-image
|
||||
v-if="column.show_type === 'img'"
|
||||
:src="record[column.dataIndex]"
|
||||
:width="160"
|
||||
/>
|
||||
<a
|
||||
v-else-if="column.show_type === 'link'"
|
||||
target="_blank"
|
||||
:href="record[column.dataIndex]"
|
||||
>{{ record[column.dataIndex] }}</a>
|
||||
<div
|
||||
v-else-if="column.show_type === 'richText'"
|
||||
v-html="record[column.dataIndex]"
|
||||
></div>
|
||||
<template v-else>{{ record[column.dataIndex] }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="previewData.page"
|
||||
:total="previewData.total"
|
||||
:page-size="previewData.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<y-chart
|
||||
v-else-if="previewData.type === 'chart'"
|
||||
:chart-cfg="previewData.chartCfg"
|
||||
:filter-config="previewData.filter"
|
||||
@to-filt="
|
||||
() => {
|
||||
toPreview({});
|
||||
}
|
||||
"
|
||||
/>
|
||||
<div class="preview-area" v-else>
|
||||
<div><BarChartOutlined /></div>
|
||||
<div>预览区</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal
|
||||
:open="nameVisible"
|
||||
title="保存视图"
|
||||
:body-style="{ paddingTop: '20px' }"
|
||||
@cancel="nameVisible = false"
|
||||
@ok="toSaveView"
|
||||
>
|
||||
<a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="视图名称">
|
||||
<a-input placeholder="请输入视图名称" v-model:value="previewName" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.normal-container {
|
||||
height: calc(100vh - 120px);
|
||||
|
||||
@@ -1,114 +1,12 @@
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-list-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="项目">
|
||||
<a-select
|
||||
:options="projectSel"
|
||||
v-model:value="projectId"
|
||||
placeholder="请选择项目"
|
||||
@change="onProjectChange"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据来源">
|
||||
<a-select
|
||||
:options="modularSel"
|
||||
v-model:value="modularId"
|
||||
placeholder="请先选好项目再选择"
|
||||
@change="onModularChange"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-table
|
||||
:data-source="dataList"
|
||||
:columns="viewListCols"
|
||||
:pagination="false"
|
||||
:row-class-name="
|
||||
(record) =>
|
||||
record.preview_id === selectedRowId ? 'selected-row' : ''
|
||||
"
|
||||
:custom-row="
|
||||
(record, index) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
selectedRowId = record.preview_id;
|
||||
toGetViewInfo();
|
||||
},
|
||||
};
|
||||
}
|
||||
"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="toDelete(record.preview_id)"
|
||||
>
|
||||
<a-button
|
||||
type="link"
|
||||
@click="
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
}
|
||||
"
|
||||
>删除</a-button
|
||||
>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="pageState.page"
|
||||
:total="pageState.total"
|
||||
:page-size="pageState.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="toGetList"
|
||||
/>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<y-table
|
||||
v-if="selectViewInfo.type === 'table'"
|
||||
:previewId="selectedRowId"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
:data-list="selectViewInfo.data"
|
||||
:column-config="selectViewInfo.header"
|
||||
:total="selectViewInfo.count"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:is-export="selectViewInfo.is_export"
|
||||
@toFilt="
|
||||
(params) => {
|
||||
toGetViewInfo(params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<y-chart
|
||||
v-else-if="selectViewInfo.type === 'chart'"
|
||||
:chartCfg="selectViewInfo.config"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
@toFilt="toGetViewInfo"
|
||||
/>
|
||||
<div class="preview-area" v-else>
|
||||
<div><BarChartOutlined /></div>
|
||||
<div>展示区</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, reactive } from 'vue';
|
||||
import { getProModular, getViewList, getViewInfo, deleteView } from './service';
|
||||
import { viewListCols } from './config';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import yTable from '@/components/common/y-table.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { BarChartOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { viewListCols } from './config';
|
||||
import { deleteView, getProModular, getViewInfo, getViewList } from './service';
|
||||
|
||||
const projectSel = ref([]);
|
||||
const modularSel = ref([]);
|
||||
@@ -139,7 +37,7 @@ onMounted(() => {
|
||||
const toGetProModular = () => {
|
||||
getProModular().then((res) => {
|
||||
projectSel.value = res.data;
|
||||
if (res.data.length) {
|
||||
if (res.data.length > 0) {
|
||||
projectId.value = res.data[0].value;
|
||||
onProjectChange(projectId.value);
|
||||
}
|
||||
@@ -184,6 +82,111 @@ const toDelete = (previewId) => {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-list-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="项目">
|
||||
<a-select
|
||||
:options="projectSel"
|
||||
v-model:value="projectId"
|
||||
placeholder="请选择项目"
|
||||
@change="onProjectChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据来源">
|
||||
<a-select
|
||||
:options="modularSel"
|
||||
v-model:value="modularId"
|
||||
placeholder="请先选好项目再选择"
|
||||
@change="onModularChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-table
|
||||
:data-source="dataList"
|
||||
:columns="viewListCols"
|
||||
:pagination="false"
|
||||
:row-class-name="
|
||||
(record) =>
|
||||
record.preview_id === selectedRowId ? 'selected-row' : ''
|
||||
"
|
||||
:custom-row="
|
||||
(record, index) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
selectedRowId = record.preview_id;
|
||||
toGetViewInfo();
|
||||
},
|
||||
};
|
||||
}
|
||||
"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="toDelete(record.preview_id)"
|
||||
>
|
||||
<a-button
|
||||
type="link"
|
||||
@click="
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
}
|
||||
"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="pageState.page"
|
||||
:total="pageState.total"
|
||||
:page-size="pageState.perPage"
|
||||
:hide-on-single-page="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
@change="toGetList"
|
||||
/>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<y-table
|
||||
v-if="selectViewInfo.type === 'table'"
|
||||
:preview-id="selectedRowId"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
:data-list="selectViewInfo.data"
|
||||
:column-config="selectViewInfo.header"
|
||||
:total="selectViewInfo.count"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:is-export="selectViewInfo.is_export"
|
||||
@to-filt="
|
||||
(params) => {
|
||||
toGetViewInfo(params);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<y-chart
|
||||
v-else-if="selectViewInfo.type === 'chart'"
|
||||
:chart-cfg="selectViewInfo.config"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
@to-filt="toGetViewInfo"
|
||||
/>
|
||||
<div class="preview-area" v-else>
|
||||
<div><BarChartOutlined /></div>
|
||||
<div>展示区</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.normal-container {
|
||||
height: calc(100vh - 120px);
|
||||
|
||||
@@ -14,7 +14,7 @@ export function getViewList({ modularId, page = 1, perPage = 20 }) {
|
||||
params: {
|
||||
modular_id: modularId,
|
||||
page,
|
||||
perPage: perPage,
|
||||
perPage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user