1.1.40 预算添加编号查询,项目大类bug修复

dev
yinq 4 weeks ago
parent 16bc122e92
commit 845bdec99d

@ -367,6 +367,11 @@ export interface budgetInfoForm extends BaseEntity {
}
export interface budgetInfoQuery extends PageQuery {
/**
*
*/
budgetCode?: string;
/**
* ID
*/

@ -17,8 +17,14 @@
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="项目号">
<el-input v-model="searchForm.projectCode" placeholder="请选择项目" readonly @click="showProjectSelectDialog" suffix-icon="Search">
</el-input>
<el-input
v-model="searchForm.projectCode"
:placeholder="isTopFormReadonly ? '' : '请选择项目'"
:disabled="isTopFormReadonly"
:readonly="!isTopFormReadonly"
:suffix-icon="isTopFormReadonly ? undefined : 'Search'"
@click="onProjectCodeInputClick"
/>
</el-form-item>
</el-col>
<el-col :span="6">
@ -35,7 +41,7 @@
</el-col>
<el-col :span="6">
<el-form-item label="申请说明">
<el-input v-model="searchForm.remark" type="textarea" placeholder="请输入申请说明" />
<el-input v-model="searchForm.remark" type="textarea" placeholder="请输入申请说明" :disabled="isTopFormReadonly" />
</el-form-item>
</el-col>
@ -51,7 +57,7 @@
<!-- 中间标签页区域 -->
<!-- 研发项目预算 -->
<el-card class="mb-6" v-if="searchForm.projectCategory === PROJECT_CATEGORY.RD || searchForm.projectCategory === PROJECT_CATEGORY.PRE_PRODUCTION">
<el-card class="mb-6" v-if="isRdBudgetCategory(searchForm.projectCategory)">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="预算编制标准说明" name="budgetDefinition">
<RdBudgetDefinition ref="rdBudgetDefinitionRef" :projectId="searchForm.projectId" />
@ -90,7 +96,7 @@
<el-card
class="mb-6"
shadow="never"
v-if="searchForm.projectCategory === PROJECT_CATEGORY.MARKET || searchForm.projectCategory === PROJECT_CATEGORY.MARKET_PART"
v-if="isMarketBudgetCategory(searchForm.projectCategory)"
>
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="预算表" name="budget">
@ -186,7 +192,7 @@
</template>
<script setup lang="ts" name="BudgetInfoEdit">
import { ref, reactive } from 'vue';
import { ref, reactive, computed } from 'vue';
import { ElMessage } from 'element-plus';
import {
listProjectInfo,
@ -201,6 +207,7 @@ import { getUserList } from '@/api/system/user';
import { deepEqualArrays } from '@/utils/objHandle';
import { cloneDeep } from 'lodash-es';
import { ProjectCategoryEnum } from '@/enums/OAEnum';
const router = useRouter();
@ -278,12 +285,21 @@ const oriRdBudgetExpertMeetingCostList = ref<rdBudgetTechCostVO[]>([]); //专家
const oriRdBudgetExpertCommCostList = ref<rdBudgetTechCostVO[]>([]); //
const oriRdBudgetOtherCostList = ref<rdBudgetOtherCostVO[]>([]); //
const PROJECT_CATEGORY = reactive({
MARKET: '1', //
MARKET_PART: '2', //
RD: '3', //
PRE_PRODUCTION: '4' //
});
/** 研发 / 预投(与字典 project_category、OAEnum 一致) */
function isRdBudgetCategory(category: string | number | undefined | null): boolean {
const c = category === undefined || category === null ? '' : String(category);
return c === ProjectCategoryEnum.RD || c === ProjectCategoryEnum.PRE_INVEST;
}
/** 销售实施、物流、备件(与列表页 index 及 OAEnum 一致;含类别 0避免选「实施」项目后无表单 */
function isMarketBudgetCategory(category: string | number | undefined | null): boolean {
const c = category === undefined || category === null ? '' : String(category);
return (
c === ProjectCategoryEnum.SALE_IMPLEMENT ||
c === ProjectCategoryEnum.SALE_LOGISTICS ||
c === ProjectCategoryEnum.SALE_SPARE
);
}
const BUSINESS_STATUS = reactive({
DRAFT: '1', //
@ -322,19 +338,22 @@ const searchForm = reactive({
watch(
() => searchForm.projectCategory,
(newCategory) => {
if (newCategory === PROJECT_CATEGORY.RD || newCategory === PROJECT_CATEGORY.PRE_PRODUCTION) {
if (isRdBudgetCategory(newCategory as string)) {
activeTab.value = 'budgetTable';
} else if (newCategory === PROJECT_CATEGORY.MARKET || newCategory === PROJECT_CATEGORY.MARKET_PART) {
} else if (isMarketBudgetCategory(newCategory as string)) {
activeTab.value = 'budget';
}
}
);
//
const projectInfo = reactive<ProjectInfoVO>({
const projectInfo = reactive<Partial<ProjectInfoVO>>({
projectId: undefined,
projectName: '',
projectCode: '',
projectCategory: ''
projectCategory: '',
managerId: undefined,
amount: undefined
});
//
@ -412,7 +431,6 @@ const resetQuery = () => {
const getProjectList = async () => {
projectLoading.value = true;
const res = await listProjectInfo(queryParams.value);
console.log(searchForm);
projectList.value = res.rows;
projectTotal.value = res.total;
projectLoading.value = false;
@ -422,9 +440,9 @@ const getProjectList = async () => {
const handleTabClick = (tab: any) => {
activeTab.value = tab.paneName;
if ((budgetId.value && routeParams.value.type === 'update') || !budgetId.value) {
if (searchForm.projectCategory === PROJECT_CATEGORY.RD || searchForm.projectCategory === PROJECT_CATEGORY.PRE_PRODUCTION) {
if (isRdBudgetCategory(searchForm.projectCategory)) {
updateRdBudgetTable();
} else if (searchForm.projectCategory === PROJECT_CATEGORY.MARKET || searchForm.projectCategory === PROJECT_CATEGORY.MARKET_PART) {
} else if (isMarketBudgetCategory(searchForm.projectCategory)) {
updateBudgetTable();
}
}
@ -459,6 +477,8 @@ const confirmProjectSelection = () => {
projectInfo.projectName = selectedProject.value.projectName;
projectInfo.projectCode = selectedProject.value.projectCode;
projectInfo.projectCategory = selectedProject.value.projectCategory;
projectInfo.managerId = selectedProject.value.managerId;
projectInfo.amount = selectedProject.value.amount;
projectSelectDialogVisible.value = false;
// ElMessage.success('');
@ -536,8 +556,8 @@ const handleSave = async (status: string, mode: boolean) => {
try {
buttonLoading.value = true;
const isRdProject = [PROJECT_CATEGORY.RD, PROJECT_CATEGORY.PRE_PRODUCTION].includes(searchForm.projectCategory);
const isMarketProject = [PROJECT_CATEGORY.MARKET, PROJECT_CATEGORY.MARKET_PART].includes(searchForm.projectCategory);
const isRdProject = isRdBudgetCategory(searchForm.projectCategory);
const isMarketProject = isMarketBudgetCategory(searchForm.projectCategory);
let budgetForm: budgetInfoForm = {};
@ -767,6 +787,17 @@ const handleClose = () => {
const route = useRoute();
const budgetId = ref();
/** 查看、审批:顶部基础信息只读(与 route.query.type 一致) */
const isTopFormReadonly = computed(() => {
const t = route.query.type as string | undefined;
return t === 'view' || t === 'approval';
});
const onProjectCodeInputClick = () => {
if (isTopFormReadonly.value) return;
showProjectSelectDialog();
};
//
//
const PRIMARY_KEY_MAP = {
@ -817,9 +848,10 @@ async function loadBudgetData() {
}
async function handleAddMode() {
searchForm.projectCategory = routeParams.value.projectCategory;
queryParams.value.projectCategory = searchForm.projectCategory;
if (isRdOrPreProductionCategory()) {
const raw = routeParams.value.projectCategory;
searchForm.projectCategory = raw === undefined || raw === null || raw === '' ? '' : String(raw);
queryParams.value.projectCategory = searchForm.projectCategory || undefined;
if (isRdBudgetCategory(searchForm.projectCategory)) {
const rdBudgetDefinitionListData = await getRdBudgetDefinitionList();
if (rdBudgetDefinitionRef.value.budgetDefinitionData) {
rdBudgetDefinitionRef.value.budgetDefinitionData = rdBudgetDefinitionListData.data;
@ -838,9 +870,9 @@ async function handleLoadMode() {
searchForm.budgetVersion = (searchForm.budgetVersion || 0) + 1;
}
if (isRdOrPreProductionCategory()) {
if (isRdBudgetCategory(searchForm.projectCategory)) {
await handleRdOrPreProductionData(res.data);
} else if (isMarketCategory()) {
} else if (isMarketBudgetCategory(searchForm.projectCategory)) {
await handleMarketCategoryData(res.data);
}
} finally {
@ -848,14 +880,6 @@ async function handleLoadMode() {
}
}
function isRdOrPreProductionCategory(): boolean {
return searchForm.projectCategory === PROJECT_CATEGORY.RD || searchForm.projectCategory === PROJECT_CATEGORY.PRE_PRODUCTION;
}
function isMarketCategory(): boolean {
return searchForm.projectCategory === PROJECT_CATEGORY.MARKET || searchForm.projectCategory === PROJECT_CATEGORY.MARKET_PART;
}
async function handleRdOrPreProductionData(data: any) {
const isChangeType = routeParams.value.changeFlag === '1';
@ -1146,6 +1170,7 @@ import { rdBudgetOtherCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetOtherCos
import { UserVO } from '@/api/system/user/types';
//
const routeParams = ref<Record<string, any>>({});
//
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();

@ -7,6 +7,9 @@
<!-- <el-form-item label="版本,新版本+1" prop="budgetVersion">-->
<!-- <el-input v-model="queryParams.budgetVersion" placeholder="请输入版本,新版本+1" clearable @keyup.enter="handleQuery" />-->
<!-- </el-form-item>-->
<el-form-item label="预算编号" prop="budgetCode">
<el-input v-model="queryParams.budgetCode" placeholder="请输入预算编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="项目类别" prop="projectCategory">
<el-select v-model="queryParams.projectCategory" placeholder="请选择项目类别">
<el-option v-for="dict in project_category" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
@ -112,49 +115,52 @@
<dict-tag :options="wf_business_status" :value="scope.row.flowStatus" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="130" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" fixed="right" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top" v-if="scope.row.budgetStatus !== BUDGET_STATUS.DRAFT">
<el-button type="primary" size="small" icon="View" @click="handleUpdate(scope.row,'view')" v-hasPermi="['oa:erp/budgetInfo:view']">&nbsp;</el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button
link
type="info"
icon="DocumentChecked"
@click="handleUpdate(scope.row, 'view')"
v-hasPermi="['oa:erp/budgetInfo:view']"
></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="scope.row.budgetStatus === BUDGET_STATUS.DRAFT">
<el-button
link
type="primary"
icon="Edit"
size="small"
@click="handleUpdate(scope.row, 'update')"
v-if="scope.row.budgetStatus === BUDGET_STATUS.DRAFT"
v-hasPermi="['oa:erp/budgetInfo:edit']"
>修改&nbsp;</el-button>
></el-button>
</el-tooltip>
<el-tooltip content="导出" placement="top">
<el-button
type="success"
link
type="warning"
icon="Download"
size="small"
@click="handleExport(scope.row)"
v-hasPermi="['oa:erp/budgetInfo:export']"
>导出&nbsp;</el-button>
></el-button>
</el-tooltip>
<el-tooltip content="变更" placement="top">
<el-tooltip content="变更" placement="top" v-if="scope.row.budgetStatus === BUDGET_STATUS.AVAILABLE">
<el-button
size="small"
type="warning"
icon="Delete"
link
type="primary"
icon="RefreshRight"
@click="handleChange(scope.row)"
v-if="scope.row.budgetStatus === BUDGET_STATUS.AVAILABLE"
v-hasPermi="['oa:erp/budgetInfo:change']"
>变更&nbsp;</el-button>
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-tooltip content="删除" placement="top" v-if="scope.row.budgetStatus === BUDGET_STATUS.DRAFT">
<el-button
size="small"
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-if="scope.row.budgetStatus === BUDGET_STATUS.DRAFT"
v-hasPermi="['oa:erp/budgetInfo:remove']"
>删除&nbsp;</el-button>
></el-button>
</el-tooltip>
</template>
</el-table-column>
@ -172,6 +178,7 @@ import router from '@/router';
import { getUserList } from '@/api/system/user';
import { UserVO } from '@/api/system/user/types';
import { reactive } from 'vue';
import { ProjectCategoryEnum } from '@/enums/OAEnum';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -272,6 +279,7 @@ const data = reactive<PageData<budgetInfoForm, budgetInfoQuery>>({
queryParams: {
pageNum: 1,
pageSize: 10,
budgetCode: undefined,
projectId: undefined,
approvedFlag: undefined,
budgetVersion: undefined,
@ -311,12 +319,32 @@ const data = reactive<PageData<budgetInfoForm, budgetInfoQuery>>({
const { queryParams, form, rules } = toRefs(data);
const PROJECT_CATEGORY = reactive({
MARKET: '1', //
MARKET_PART: '2', //
RD: '3', //
PRE_PRODUCTION: '4' //
});
/** 销售类项目(实施 / 物流 / 备件),与字典 project_category 及 OAEnum 一致 */
const MARKET_BUDGET_CATEGORIES: readonly string[] = [
ProjectCategoryEnum.SALE_IMPLEMENT,
ProjectCategoryEnum.SALE_LOGISTICS,
ProjectCategoryEnum.SALE_SPARE
];
function isMarketBudgetCategory(category: string | number | undefined | null): boolean {
const c = category === undefined || category === null ? '' : String(category);
return MARKET_BUDGET_CATEGORIES.includes(c);
}
/** 列表「新增市场项目预算」进入编辑页的默认类别(选项目后仍以项目档案为准) */
const DEFAULT_ADD_MARKET_PROJECT_CATEGORY = ProjectCategoryEnum.SALE_LOGISTICS;
/** 关闭当前列表 Tab 并跳转预算相关路由,统一附带列表页码 */
function navigateToBudget(path: string, query: Record<string, string | number | undefined>) {
proxy.$tab.closePage(route);
router.push({
path,
query: {
pageNum: queryParams.value.pageNum,
...query
} as Record<string, any>
});
}
const BUDGET_STATUS = reactive({
DRAFT: '1', //稿
@ -378,69 +406,40 @@ const handleSelectionChange = (selection: budgetInfoVO[]) => {
/** 新增市场项目预算按钮操作 */
const handleAdd = () => {
proxy.$tab.closePage(route);
router.push({
path: '/budget/budgetInfo/edit',
query: {
navigateToBudget('/budget/budgetInfo/edit', {
type: 'add',
projectCategory: PROJECT_CATEGORY.MARKET,
pageNum: queryParams.value.pageNum
}
projectCategory: DEFAULT_ADD_MARKET_PROJECT_CATEGORY
});
// const params = {pageNum: queryParams.value.pageNum,type:'add'};
// proxy.$tab.openPage('/oa/erp/budgetInfo/edit',"", params);
// router.push({ path: '/budget/budget-add/index', query: { pageNum: queryParams.value.pageNum,type:'add' } });
};
/** 新增研发项目按钮操作 */
const handleAddRd = () => {
proxy.$tab.closePage(route);
router.push({
path: '/budget/budgetInfo/edit',
query: {
navigateToBudget('/budget/budgetInfo/edit', {
type: 'add',
pageNum: queryParams.value.pageNum,
projectCategory: PROJECT_CATEGORY.RD
}
projectCategory: ProjectCategoryEnum.RD
});
};
/** 查询、修改按钮操作 */
const handleUpdate = async (row?: budgetInfoVO, type?: string, changeFlag?: string) => {
proxy.$tab.closePage(route);
router.push({
path: '/budget/budgetInfo/edit',
query: {
type: type,
if (!row?.budgetId) return;
navigateToBudget('/budget/budgetInfo/edit', {
type: type ?? 'view',
id: row.budgetId,
changeFlag: changeFlag,
pageNum: queryParams.value.pageNum,
...(changeFlag !== undefined && changeFlag !== '' ? { changeFlag } : {}),
projectCategory: row.projectCategory
}
});
// const params = { pageNum: queryParams.value.pageNum, type: 'update', id: row.budgetId };//router
// proxy.$tab.openPage('/oa/erp/budgetInfo/edit', '', params);
};
/** 变更按钮操作 */
const handleChange = async (row?: budgetInfoVO) => {
proxy.$tab.closePage(route);
router.push({
path: '/budget/budgetInfo/change',
query: {
if (!row?.budgetId) return;
navigateToBudget('/budget/budgetInfo/change', {
type: 'update',
id: row.budgetId,
changeFlag: '1',
pageNum: queryParams.value.pageNum,
projectCategory: row.projectCategory
}
});
// const params = { pageNum: queryParams.value.pageNum, type: 'update', id: row.budgetId };//router
// proxy.$tab.openPage('/oa/erp/budgetInfo/edit', '', params);
};
/** 删除按钮操作 */
@ -455,18 +454,18 @@ const handleDelete = async (row?: budgetInfoVO) => {
/** 导出按钮操作 */
const handleExport = (row?: budgetInfoVO) => {
if (!row?.budgetId) return;
const budgetId = row.budgetId;
const projectName = row.projectName;
const projectCode = row.projectCode;
const projectCategory = row.projectCategory;
const fileNamePrefix =
projectCategory === PROJECT_CATEGORY.MARKET || projectCategory === PROJECT_CATEGORY.MARKET_PART ? '市场项目经费预算表-' : '研发项目经费预算表-';
const fileNamePrefix = isMarketBudgetCategory(projectCategory) ? '市场项目经费预算表-' : '研发项目经费预算表-';
proxy?.download(
'oa/erp/budgetInfo/exportBudgetInfo',
{
budgetId: budgetId
},
projectCode + '_' + projectName + `_预算表.xlsx`
`${fileNamePrefix}${projectCode}_${projectName}_预算表.xlsx`
);
};

@ -127,6 +127,7 @@
import { ref, reactive, computed, watch } from 'vue';
import { UserVO } from '@/api/system/user/types';
import { getUserList } from '@/api/system/user';
import type { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
//
const budgetForm = reactive({
@ -239,6 +240,24 @@ const format2TenThousandNumber = (value: number) => {
return (parseFloat(value) / 10000).toFixed(2);
};
const computedAmountAndRate = async () => {
if (budgetForm.netContractAmount && budgetForm.budgetCost && budgetForm.netContractAmount > 0 && budgetForm.budgetCost > 0) {
budgetForm.budgetRate = (((budgetForm.netContractAmount - budgetForm.budgetCost) * 100) / budgetForm.netContractAmount).toFixed(2);
}
if (budgetForm.netContractAmount && budgetForm.reduceBudgetCost && budgetForm.netContractAmount > 0 && budgetForm.reduceBudgetCost > 0) {
budgetForm.reduceBudgetRate = (((budgetForm.netContractAmount - budgetForm.reduceBudgetCost) * 100) / budgetForm.netContractAmount).toFixed(2);
}
};
const contractAmountChange = async () => {
const ca = budgetForm.contractAmount;
if (ca !== undefined && ca !== null && ca !== '' && !Number.isNaN(Number(ca))) {
budgetForm.netContractAmount = (Number(ca) / 1.13).toFixed(2);
} else {
budgetForm.netContractAmount = undefined;
}
};
//
const getSummaries = (param: any) => {
const { columns, data } = param;
@ -306,30 +325,29 @@ watch(
() => props.projectInfo,
(newProjectInfo) => {
if (newProjectInfo) {
budgetForm.projectId = newProjectInfo.projectId || '';
budgetForm.projectName = newProjectInfo.projectName || '';
budgetForm.projectCode = newProjectInfo.projectCode || '';
budgetForm.projectCategory = newProjectInfo.projectCategory || '';
const pi = newProjectInfo as Partial<ProjectInfoVO>;
budgetForm.projectId = pi.projectId || '';
budgetForm.projectName = pi.projectName || '';
budgetForm.projectCode = pi.projectCode || '';
budgetForm.projectCategory = pi.projectCategory || '';
const mid = pi.managerId;
budgetForm.managerId =
mid !== undefined && mid !== null && String(mid).trim() !== '' ? mid : undefined;
const amt = pi.amount;
if (amt !== undefined && amt !== null && String(amt).trim() !== '' && !Number.isNaN(Number(amt))) {
budgetForm.contractAmount = Number(amt);
contractAmountChange();
} else {
budgetForm.contractAmount = undefined;
budgetForm.netContractAmount = undefined;
}
computedAmountAndRate();
}
},
{ deep: true, immediate: true }
);
const computedAmountAndRate = async () => {
if (budgetForm.netContractAmount && budgetForm.budgetCost && budgetForm.netContractAmount > 0 && budgetForm.budgetCost > 0) {
budgetForm.budgetRate = (((budgetForm.netContractAmount - budgetForm.budgetCost) * 100) / budgetForm.netContractAmount).toFixed(2);
}
if (budgetForm.netContractAmount && budgetForm.reduceBudgetCost && budgetForm.netContractAmount > 0 && budgetForm.reduceBudgetCost > 0) {
budgetForm.reduceBudgetRate = (((budgetForm.netContractAmount - budgetForm.reduceBudgetCost) * 100) / budgetForm.netContractAmount).toFixed(2);
}
};
const contractAmountChange = async () => {
if (budgetForm.contractAmount) {
budgetForm.netContractAmount = (budgetForm.contractAmount / 1.13).toFixed(2);
}
};
//

@ -96,6 +96,7 @@
<script setup lang="ts">
import { ref, reactive, onMounted, computed, watch } from 'vue';
import { budgetInfoVO } from '@/api/oa/erp/budgetInfo/types';
import type { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
//
//
@ -216,11 +217,28 @@ watch(
() => props.projectInfo,
(newProjectInfo) => {
if (newProjectInfo) {
rdBudgetInfoForm.projectId = newProjectInfo.projectId || '';
rdBudgetInfoForm.projectName = newProjectInfo.projectName || '';
rdBudgetInfoForm.projectCode = newProjectInfo.projectCode || '';
const pi = newProjectInfo as Partial<ProjectInfoVO>;
rdBudgetInfoForm.projectId = pi.projectId || '';
rdBudgetInfoForm.projectName = pi.projectName || '';
rdBudgetInfoForm.projectCode = pi.projectCode || '';
rdBudgetInfoForm.unitName = '万元';
rdBudgetInfoForm.projectCategory = newProjectInfo.projectCategory || '';
rdBudgetInfoForm.projectCategory = pi.projectCategory || '';
const mid = pi.managerId;
rdBudgetInfoForm.managerId =
mid !== undefined && mid !== null && String(mid).trim() !== '' ? mid : undefined;
const amt = pi.amount;
if (amt !== undefined && amt !== null && String(amt).trim() !== '' && !Number.isNaN(Number(amt))) {
rdBudgetInfoForm.contractAmount = Number(amt);
const ca = rdBudgetInfoForm.contractAmount;
rdBudgetInfoForm.netContractAmount =
ca !== undefined && ca !== null && ca !== '' && !Number.isNaN(Number(ca))
? (Number(ca) / 1.13).toFixed(2)
: undefined;
} else {
rdBudgetInfoForm.contractAmount = undefined;
rdBudgetInfoForm.netContractAmount = undefined;
}
}
},
{ deep: true, immediate: true }

Loading…
Cancel
Save