关卡管理等

V0.1
yujialong 1 year ago
parent ecec0c7dd6
commit 26d3172827
  1. 2
      .env
  2. 5
      src/api/config.ts
  3. 8
      src/api/model.ts
  4. 5
      src/components/Panel/index.vue
  5. 7
      src/layout/components/AppHeader.vue
  6. 24
      src/layout/components/AppSidebar/Menu.vue
  7. 12
      src/layout/index.vue
  8. 5
      src/router/index.ts
  9. 5
      src/settings.ts
  10. 23
      src/styles/index.scss
  11. 13
      src/views/Home.vue
  12. 261
      src/views/config/level/Index.vue
  13. 2
      src/views/config/param/Buyer.vue
  14. 2
      src/views/config/param/Financial.vue
  15. 0
      src/views/config/param/Index.vue
  16. 26
      src/views/product/bank/List.vue
  17. 26
      src/views/product/insurance/List.vue
  18. 2
      src/views/product/strategy/CardList.vue

@ -2,7 +2,7 @@ VITE_APP_TITLE=金融产品设计及数字化营销沙盘
VITE_PORT=9520 VITE_PORT=9520
VITE_PROXY=http://192.168.31.125:8080 VITE_PROXY=http://192.168.31.125:8080
VITE_PUBLIC_PATH=./ VITE_PUBLIC_PATH=./
VITE_BASE_API=http://192.168.31.51:9000 VITE_BASE_API=http://192.168.31.217:9000
# VITE_BASE_API=http://121.37.12.51 # VITE_BASE_API=http://121.37.12.51
VITE_I18N_LOCALE=zh-cn VITE_I18N_LOCALE=zh-cn
VITE_I18N_FALLBACK_LOCALE=zh-cn VITE_I18N_FALLBACK_LOCALE=zh-cn

@ -8,3 +8,8 @@ export const accountType = async (): Promise<any> => (await axios.post('/product
export const buyerType = async (): Promise<any> => (await axios.post('/product/buyerType/list')).data; export const buyerType = async (): Promise<any> => (await axios.post('/product/buyerType/list')).data;
export const financialMarketFind = async (): Promise<any> => (await axios.post('/product/financialMarket/details')).data; export const financialMarketFind = async (): Promise<any> => (await axios.post('/product/financialMarket/details')).data;
export const financialMarketSave = async (data: Record<string, any>): Promise<any> => (await axios.post('/product/financialMarket/saveOrUpdate', data)).data; export const financialMarketSave = async (data: Record<string, any>): Promise<any> => (await axios.post('/product/financialMarket/saveOrUpdate', data)).data;
export const delPass = async (data: number[]): Promise<any> => (await axios.post('/nakadai/nakadai/customsPass/batchDeletion', data)).data;
export const listPass = async (data: Record<string, any>): Promise<any> => (await axios.post('/nakadai/nakadai/customsPass/checkpointList', data)).data;
export const savePass = async (data: Record<string, any>): Promise<any> => (await axios.post('/nakadai/nakadai/customsPass/save', data)).data;
export const updatePass = async (data: Record<string, any>[]): Promise<any> => (await axios.post(`/nakadai/nakadai/customsPass/update`), data).data;

@ -45,13 +45,13 @@ export const detailRick = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/riskDegreeStrategy/details?checkpointId=${data.checkpointId}&projectId=${data.projectId}&type=${data.type}`)).data; (await axios.post(`/product/riskDegreeStrategy/details?checkpointId=${data.checkpointId}&projectId=${data.projectId}&type=${data.type}`)).data;
export const saveRick = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/riskDegreeStrategy/saveOrUpdate`, data)).data; export const saveRick = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/riskDegreeStrategy/saveOrUpdate`, data)).data;
export const detailInterestRateBusiness = async (data: Record<string, any>): Promise<any> => export const businessInterestRateDetails = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/businessInterestRateDetails?checkpointId=${data.checkpointId}&projectId=${data.projectId}`)).data; (await axios.post(`/product/interestRateModel/businessInterestRateDetails?checkpointId=${data.checkpointId}&projectId=${data.projectId}`)).data;
export const saveInterestRateBusiness = async (data: Record<string, any>): Promise<any> => export const businessInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/businessInterestRateSaveOrUpdate`, data)).data; (await axios.post(`/product/interestRateModel/businessInterestRateSaveOrUpdate`, data)).data;
export const detailInterestRatePerson = async (data: Record<string, any>): Promise<any> => export const personalInterestRateDetails = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/personalInterestRateDetails?checkpointId=${data.checkpointId}&projectId=${data.projectId}`)).data; (await axios.post(`/product/interestRateModel/personalInterestRateDetails?checkpointId=${data.checkpointId}&projectId=${data.projectId}`)).data;
export const saveInterestRatePerson = async (data: Record<string, any>): Promise<any> => export const personalInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/personalInterestRateSaveOrUpdate`, data)).data; (await axios.post(`/product/interestRateModel/personalInterestRateSaveOrUpdate`, data)).data;
export const businessQuotaModelDetails = async (data: Record<string, any>): Promise<any> => export const businessQuotaModelDetails = async (data: Record<string, any>): Promise<any> =>

@ -1,5 +1,6 @@
<template> <template>
<div :class="['panel', { active: visible }]" <div v-if="!hidePanel"
:class="['panel', { active: visible }]"
id="panel"> id="panel">
<el-container class="scrollbar" <el-container class="scrollbar"
id="container" id="container"
@ -214,6 +215,7 @@ import { ref, reactive, onMounted, toRefs, computed, watch } from 'vue';
import { submitOpe } from '@/api/bank'; import { submitOpe } from '@/api/bank';
import { deleteCache } from '@/api/judgment'; import { deleteCache } from '@/api/judgment';
import { pageStuAssessment, getProjectBySystemId, getProjectDetail, getDetailById, getCompetition } from '@/api/system'; import { pageStuAssessment, getProjectBySystemId, getProjectDetail, getDetailById, getCompetition } from '@/api/system';
import Settings from '@/settings';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import type { Action } from 'element-plus'; import type { Action } from 'element-plus';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
@ -227,6 +229,7 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const param = reactive<Record<string, any>>(route.query); const param = reactive<Record<string, any>>(route.query);
const hidePanel = computed(() => Settings.hidePanelPath.includes(route.path));
const curSystemId = ref<number>(1); const curSystemId = ref<number>(1);
const per = ref<number>(0); // (0 1 2) const per = ref<number>(0); // (0 1 2)
const isSubmit = ref<boolean>(false); // const isSubmit = ref<boolean>(false); //

@ -10,6 +10,7 @@
alt="" alt=""
@click="logout" /> @click="logout" />
</el-tooltip> </el-tooltip>
<template v-if="!hidePanel">
<el-tooltip effect="light" <el-tooltip effect="light"
content="返回关卡" content="返回关卡"
placement="bottom"> placement="bottom">
@ -26,18 +27,22 @@
alt="" alt=""
@click="toRole" /> @click="toRole" />
</el-tooltip> </el-tooltip>
</template>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Settings from '@/settings';
import { currentUser, perm, logout } from '@/store/useCurrentUser'; import { currentUser, perm, logout } from '@/store/useCurrentUser';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Logo from './Logo.vue'; import Logo from './Logo.vue';
const router = useRouter(); const router = useRouter();
const route = useRoute();
const hidePanel = computed(() => Settings.hidePanelPath.includes(route.path));
onMounted(() => {}); onMounted(() => {});

@ -80,14 +80,30 @@
alt="" /> alt="" />
<p class="text">保险产品</p> <p class="text">保险产品</p>
</li> </li>
<!-- 参数配置 --> <!-- 配置 -->
<li v-else <template v-else>
class="active"> <li :class="{ active: route.path === '/config/index' }"
@click="toPage('/config/index')">
<img class="icon"
src="@/assets/images/icon4.png"
alt="" />
<img class="icon-1" <img class="icon-1"
src="@/assets/images/icon8-1.png" src="@/assets/images/icon4-1.png"
alt="" /> alt="" />
<p class="text">参数配置</p> <p class="text">参数配置</p>
</li> </li>
<li :class="{ active: route.path === '/config/level' }"
@click="toPage('/config/level')">
<img class="icon"
src="@/assets/images/icon1.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon1-1.png"
alt="" />
<p class="text">关卡配置</p>
</li>
</template>
</ul> </ul>
</template> </template>

@ -4,7 +4,8 @@
<app-sidebar v-if="!hideNav" <app-sidebar v-if="!hideNav"
class="sidebar fixed w-sidebar h-full px-5 overflow-hidden transition-width duration-300 z-40" /> class="sidebar fixed w-sidebar h-full px-5 overflow-hidden transition-width duration-300 z-40" />
<div class="main h-[calc(100vh-95px)] transition-margin duration-300 overflow-auto" <div class="main h-[calc(100vh-95px)] transition-margin duration-300 overflow-auto"
:class="{ 'md:ml-sidebar': !hideNav }"> :class="{ 'md:ml-sidebar': !hideNav }"
id="appMain">
<app-main /> <app-main />
</div> </div>
<Panel /> <Panel />
@ -14,7 +15,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import Setting from '@/settings'; import Settings from '@/settings';
import { AppSidebar, AppHeader, AppMain } from './components'; import { AppSidebar, AppHeader, AppMain } from './components';
import useResizeHandler from './composables/useResizeHandler'; import useResizeHandler from './composables/useResizeHandler';
import Panel from '@/components/Panel/index.vue'; import Panel from '@/components/Panel/index.vue';
@ -26,12 +27,17 @@ export default defineComponent({
const route = useRoute(); const route = useRoute();
// //
const hideNav = computed(() => { const hideNav = computed(() => {
return Setting.hideNavPath.includes(route.path); return Settings.hideNavPath.includes(route.path);
});
//
const hidePanel = computed(() => {
return Settings.hidePanelPath.includes(route.path);
}); });
useResizeHandler(); useResizeHandler();
return { return {
hideNav, hideNav,
hidePanel,
}; };
}, },
}); });

@ -56,7 +56,10 @@ export const routes: Array<RouteRecordRaw> = [
path: '/config', path: '/config',
redirect: '/config/index', redirect: '/config/index',
component: Layout, component: Layout,
children: [{ path: 'index', component: () => import('@/views/config/Index.vue'), meta: { title: '参数配置' } }], children: [
{ path: 'index', component: () => import('@/views/config/param/Index.vue'), meta: { title: '参数配置' } },
{ path: 'level', component: () => import('@/views/config/level/Index.vue'), meta: { title: '关卡配置' } },
],
}, },
// 404 page must be placed at the end !!! // 404 page must be placed at the end !!!
{ {

@ -7,4 +7,9 @@ export default {
* @description * @description
*/ */
hideNavPath: ['/finance/publish', '/finance/account', '/finance/order', '/finance/armory'], hideNavPath: ['/finance/publish', '/finance/account', '/finance/order', '/finance/armory'],
/**
* @type {array}
* @description
*/
hidePanelPath: ['/config/index', '/config/level'],
}; };

@ -80,3 +80,26 @@ body {
@apply text-[#006BFF]; @apply text-[#006BFF];
} }
} }
.filter {
@apply inline-flex items-center;
.select {
@apply relative mr-[12px];
.icon {
@apply absolute top-[12px] left-5;
}
.el-select {
@apply w-[170px];
.el-input__inner {
@apply pl-[41px] rounded-[18px] border-[#dfe9f8];
}
}
}
.add-btn {
@apply inline-flex items-center h-[36px] px-[24px] text-sm text-white rounded-[18px] cursor-pointer;
background: linear-gradient(-36deg, #006bff, #2ab1ff);
border: 1px solid #ffffff;
.icon {
@apply mr-[8px];
}
}
}

@ -30,12 +30,12 @@
<div class="relative mt-5"> <div class="relative mt-5">
<div v-for="(item, i) in levels" <div v-for="(item, i) in levels"
:key="i" :key="i"
:class="['item', { active: curLevel === item.id }]" :class="['item', { active: curLevel === item.checkpointId }]"
@click="selecLevel(item)"> @click="selecLevel(item)">
<span class="num">LV.{{ i + 1 }}</span> <span class="num">LV.{{ i + 1 }}</span>
<div class="texts"> <div class="texts">
<h6>{{ numToChinese(item.name.split(' ')[0].replace('关卡', '')) }}</h6> <h6>{{ numToChinese(item.customsPassName.split(' ')[0].replace('关卡', '')) }}</h6>
<p class="des mul-ellipsis2">{{ item.name.split(' ')[1] }}</p> <p class="des mul-ellipsis2">{{ item.customsPassName.split(' ')[1] }}</p>
<img v-if="item.collect" <img v-if="item.collect"
class="icon" class="icon"
src="@/assets/images/level/star2.png" src="@/assets/images/level/star2.png"
@ -68,6 +68,7 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { checkPointList } from '@/api/judgment'; import { checkPointList } from '@/api/judgment';
import { cancelCollection, collect } from '@/api/bank'; import { cancelCollection, collect } from '@/api/bank';
import { listPass } from '@/api/config';
import Panel from '@/components/Panel/index.vue'; import Panel from '@/components/Panel/index.vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
@ -85,12 +86,14 @@ const step = 150;
// //
const getLevel = async (only: any = '') => { const getLevel = async (only: any = '') => {
if (only) collected.value = !collected.value; if (only) collected.value = !collected.value;
const { data } = await checkPointList(+projectId, collected.value ? 1 : ''); const { data } = await listPass({
platformId: 1,
});
levels.value = data; levels.value = data;
}; };
// //
const selecLevel = (item: Record<string, any>) => { const selecLevel = (item: Record<string, any>) => {
curLevel.value = item.id; curLevel.value = item.checkpointId;
}; };
// //
const collectItem = async (item: Record<string, any>) => { const collectItem = async (item: Record<string, any>) => {

@ -0,0 +1,261 @@
<template>
<div class="block">
<div class="flex justify-between items-center mb-5">
<search v-model="params.customsPassName"
@change="getList"></search>
<div class="filter">
<div class="select">
<el-select v-model="params.isEnable"
placeholder="启用状态"
size="large"
clearable>
<el-option v-for="item in enables"
:key="item.id"
:label="item.name"
:value="item.id" />
</el-select>
<img src="@/assets/images/7.png"
alt=""
class="icon" />
</div>
<el-popconfirm title="您确定删除吗?"
@confirm.stop="delAll">
<template #reference>
<div class="add-btn mr-2">
<el-icon :size="24"
color="#fff">
<Delete />
</el-icon>
删除关卡
</div>
</template>
</el-popconfirm>
<div class="add-btn"
@click="toAdd">
<img src="@/assets/images/plus.png"
alt=""
class="icon" />
新增关卡
</div>
</div>
</div>
<div class="h-[calc(100vh-270px)] overflow-auto"
id="tableWrap">
<el-table ref="tableRef"
v-loading="loading"
:data="list"
@selection-change="handleSelectionChange">
<el-table-column label="移动"
width="100">
<template #default="{ row }">
<el-dropdown @command="command => move(command,row)">
<el-icon class="rotate-90 cursor-pointer"
:size="16"
color="#877c7c">
<MoreFilled />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="up">上移</el-dropdown-item>
<el-dropdown-item command="down">下移</el-dropdown-item>
<el-dropdown-item command="top">置顶</el-dropdown-item>
<el-dropdown-item command="bottom">置底</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
<el-table-column type="selection"
width="55" />
<el-table-column label="序号"
width="80"
align="center">
<template #default="{ row, $index }">
{{ $index + 1 }}
</template>
</el-table-column>
<el-table-column prop="customsPassName"
label="关卡名称">
<template #default="{ row }">
<div class="flex items-center">
<el-input v-if="row.editing"
class="w-[250px]"
placeholder="请输入关卡名称"
v-model="row.customsPassName"></el-input>
<span v-else>{{ row.customsPassName }}</span>
<el-icon class="ml-2 cursor-pointer"
:size="16"
color="#877c7c"
@click="edit(row)">
<Check v-if="row.editing" />
<Edit v-else />
</el-icon>
</div>
</template>
</el-table-column>
<el-table-column prop="creator"
label="创建人"></el-table-column>
<el-table-column prop="createTime"
label="创建时间"
sortable="custom"></el-table-column>
<el-table-column label="启用关卡"
width="140"
align="center">
<template #default="{ row }">
<el-switch v-if="row.checkpointId"
v-model="row.isEnable"
:active-value="1"
:inactive-value="0"
@change="switchEnable(row)" />
</template>
</el-table-column>
<el-table-column label="操作"
width="140">
<template #default="{ row }">
<el-popconfirm v-if="row.checkpointId"
title="您确定删除吗?"
@confirm.stop="handleDelete([row.checkpointId])">
<template #reference>
<el-button type="text"
size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="filter flex justify-end mt-3">
<div class="add-btn"
@click="save">
保存
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, reactive, watch, nextTick } from 'vue';
import { ElMessage, ElTable } from 'element-plus';
import { Delete, Edit, Check, MoreFilled } from '@element-plus/icons-vue';
import { listPass, savePass, updatePass } from '@/api/config';
import Search from '@/components/Search.vue';
import { useRouter, useRoute } from 'vue-router';
import Cookies from 'js-cookie';
const router = useRouter();
const route = useRoute();
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const height = window.innerHeight - 270;
const tableRef = ref<InstanceType<typeof ElTable>>();
const params = reactive({
platformId: 3,
customsPassName: '',
isEnable: '',
});
const table = ref<any>();
const enables = ref<Array<any>>([
{
id: 0,
name: '禁用',
},
{
id: 1,
name: '启用',
},
]);
const list = ref<Array<any>>([]);
const loading = ref<boolean>(false);
const multipleSelection = ref<Record<string, any>[]>([]);
//
const getList = async () => {
loading.value = true;
try {
const { data } = await listPass(params);
list.value = data;
} finally {
loading.value = false;
}
};
onMounted(() => {
getList();
});
watch([params, () => route.query], getList);
//
const save = () => {};
//
const move = async (command: string | number | object, row: Record<string, any>) => {
const type = command === 'up' ? 1 : command === 'down' ? 2 : command === 'top' ? 3 : 4;
await updatePass({
checkpointId: row.checkpointId,
type,
});
getList();
};
//
const handleSelectionChange = (val: Record<string, any>[]) => {
multipleSelection.value = val;
};
//
const toAdd = () => {
console.log(22, window.innerHeight);
list.value.push({
customsPassName: '',
editing: true,
});
nextTick(() => {
// tableRef.value.scrollTo({
// row: list.value.length,
// });
document.querySelector('#tableWrap').scrollTo({
top: document.querySelector('#tableWrap')?.scrollHeight + 200,
behavior: 'smooth',
});
});
};
//
const edit = async (row: Record<string, any>) => {
if (row.editing) {
const param = {
checkpointId: row.checkpointId || '',
customsPassName: row.customsPassName,
};
if (row.checkpointId) {
await updatePass(param);
} else {
await savePass(param);
}
getList();
}
row.editing = !row.editing;
};
//
const switchEnable = async (row: Record<string, any>) => {
if (row.checkpointId) {
await updatePass([
{
checkpointId: row.checkpointId,
isEnable: row.isEnable,
},
]);
getList();
}
};
//
const handleDelete = async (id: number[]) => {
id.map((e) => (e.isDel = 1));
await updatePass(id);
getList();
ElMessage.success('删除成功!');
};
//
const delAll = async () => {
handleDelete(multipleSelection.value.map((e) => e.checkpointId));
};
</script>

@ -414,7 +414,7 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import url(../../styles/form.scss); @import url(../../../styles/form.scss);
.title { .title {
@apply flex items-center mb-5 text-base text-[#222D42] font-semibold; @apply flex items-center mb-5 text-base text-[#222D42] font-semibold;
&:before { &:before {

@ -126,7 +126,7 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import url(../../styles/form.scss); @import url(../../../styles/form.scss);
.title { .title {
@apply flex items-center mb-5 text-base text-[#222D42] font-semibold; @apply flex items-center mb-5 text-base text-[#222D42] font-semibold;
&:before { &:before {

@ -228,29 +228,3 @@ const handleDelete = async (id: number) => {
ElMessage.success(t('success')); ElMessage.success(t('success'));
}; };
</script> </script>
<style lang="scss" scoped>
.filter {
@apply inline-flex items-center;
.select {
@apply relative mr-[12px];
.icon {
@apply absolute top-[12px] left-5;
}
:deep(.el-select) {
@apply w-[170px];
.el-input__inner {
@apply pl-[41px] rounded-[18px] border-[#dfe9f8];
}
}
}
.add-btn {
@apply inline-flex items-center h-[36px] px-[24px] text-sm text-white rounded-[18px] cursor-pointer;
background: linear-gradient(-36deg, #006bff, #2ab1ff);
border: 1px solid #ffffff;
.icon {
@apply mr-[8px];
}
}
}
</style>

@ -193,29 +193,3 @@ const handleDelete = async (id: number) => {
ElMessage.success(t('success')); ElMessage.success(t('success'));
}; };
</script> </script>
<style lang="scss" scoped>
.filter {
@apply inline-flex items-center;
.select {
@apply relative mr-[12px];
.icon {
@apply absolute top-[12px] left-5;
}
:deep(.el-select) {
@apply w-[170px];
.el-input__inner {
@apply pl-[41px] rounded-[18px] border-[#dfe9f8];
}
}
}
.add-btn {
@apply inline-flex items-center h-[36px] px-[24px] text-sm text-white rounded-[18px] cursor-pointer;
background: linear-gradient(-36deg, #006bff, #2ab1ff);
border: 1px solid #ffffff;
.icon {
@apply mr-[8px];
}
}
}
</style>

@ -114,7 +114,7 @@ const router = useRouter();
const route = useRoute(); const route = useRoute();
const projectId = +Cookies.get('sand-projectId'); const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level'); const levelId = +Cookies.get('sand-level');
const curTab = ref<string>('tab3'); const curTab = ref<string>('tab1');
const list = ref<Array<Record<string, any>>>([]); const list = ref<Array<Record<string, any>>>([]);
const list1 = ref<Array<Record<string, any>>>([]); const list1 = ref<Array<Record<string, any>>>([]);
const list2 = ref<Array<Record<string, any>>>([]); const list2 = ref<Array<Record<string, any>>>([]);

Loading…
Cancel
Save