Merge remote-tracking branch 'origin/main' into feature/0714_同步媒体账号数据_rxd
# Conflicts: # src/api/all/propertyMarketing.ts
This commit is contained in:
@ -21,7 +21,7 @@ export function configAutoImport() {
|
|||||||
'@vueuse/core',
|
'@vueuse/core',
|
||||||
{
|
{
|
||||||
dayjs: [['default', 'dayjs']],
|
dayjs: [['default', 'dayjs']],
|
||||||
'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty'],
|
'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty', 'merge'],
|
||||||
'@/hooks': ['useModal'],
|
'@/hooks': ['useModal'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -332,6 +332,16 @@ export const postPlacementAccountsSync = (id: string) => {
|
|||||||
return Http.post(`/v1/placement-accounts/${id}/sync-data`);
|
return Http.post(`/v1/placement-accounts/${id}/sync-data`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 投放账号-子账号分页
|
||||||
|
export const postSubAccount = (params = {}) => {
|
||||||
|
return Http.post('/v1/placement-accounts/get-subaccount', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 投放账号-添加子账号
|
||||||
|
export const postAddSubAccount = (params = {}) => {
|
||||||
|
return Http.post('/v1/placement-accounts/subaccount', params);
|
||||||
|
};
|
||||||
|
|
||||||
// 媒体账号-同步数据
|
// 媒体账号-同步数据
|
||||||
export const postSyncMediaAccountData = (id: string) => {
|
export const postSyncMediaAccountData = (id: string) => {
|
||||||
return Http.post(`/v1/media-accounts/${id}/sync-data`);
|
return Http.post(`/v1/media-accounts/${id}/sync-data`);
|
||||||
@ -350,4 +360,4 @@ export const getMediaAccountSyncStatus = (params = {}) => {
|
|||||||
// 媒体账号-移除同步状态
|
// 媒体账号-移除同步状态
|
||||||
export const deleteSyncStatus = (id: string) => {
|
export const deleteSyncStatus = (id: string) => {
|
||||||
return Http.delete(`/v1/media-accounts/${id}/sync-status`);
|
return Http.delete(`/v1/media-accounts/${id}/sync-status`);
|
||||||
};
|
};
|
||||||
87
src/hooks/useTableSelectionWithPagination.ts
Normal file
87
src/hooks/useTableSelectionWithPagination.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
interface UseTableSelectionWithPaginationOptions {
|
||||||
|
rowKey?: string; // 主键字段名,默认 'id'
|
||||||
|
pageInfo?: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
total?: number;
|
||||||
|
};
|
||||||
|
onPageChange?: (page: number) => void;
|
||||||
|
onPageSizeChange?: (size: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_PAGE_INFO = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20,
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useTableSelectionWithPagination(options: UseTableSelectionWithPaginationOptions = {}) {
|
||||||
|
const rowKey = options.rowKey || 'id';
|
||||||
|
|
||||||
|
const selectedRowKeys = ref<Array<string | number>>([]);
|
||||||
|
const selectedRows = ref<any[]>([]);
|
||||||
|
|
||||||
|
const pageInfo = ref(merge({}, DEFAULT_PAGE_INFO, options.pageInfo));
|
||||||
|
|
||||||
|
const dataSource = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 单行选择
|
||||||
|
const handleSelect = (selectedKeys: (string | number)[], rowKeyValue: string | number, record: any) => {
|
||||||
|
const select = selectedKeys.includes(rowKeyValue);
|
||||||
|
selectedRowKeys.value = selectedKeys;
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
if (!selectedRows.value.some((v) => v[rowKey] === record[rowKey])) {
|
||||||
|
selectedRows.value.push(record);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedRows.value = selectedRows.value.filter((v) => v[rowKey] !== record[rowKey]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全选/取消全选
|
||||||
|
const handleSelectAll = (checked: boolean) => {
|
||||||
|
const currentPageRows = dataSource.value;
|
||||||
|
const currentPageKeys = currentPageRows.map((v) => v[rowKey]);
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...currentPageKeys]));
|
||||||
|
const allRows = [...selectedRows.value, ...currentPageRows];
|
||||||
|
const map = new Map();
|
||||||
|
allRows.forEach((row) => map.set(row[rowKey], row));
|
||||||
|
selectedRows.value = Array.from(map.values());
|
||||||
|
} else {
|
||||||
|
selectedRowKeys.value = selectedRowKeys.value.filter((key) => !currentPageKeys.includes(key));
|
||||||
|
selectedRows.value = selectedRows.value.filter((row) => !currentPageKeys.includes(row[rowKey]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPageChange = (page: number) => {
|
||||||
|
pageInfo.value.page = page;
|
||||||
|
options.onPageChange?.(page);
|
||||||
|
};
|
||||||
|
const onPageSizeChange = (size: number) => {
|
||||||
|
pageInfo.value.page_size = size;
|
||||||
|
pageInfo.value.page = 1;
|
||||||
|
options.onPageSizeChange?.(size);
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowSelection = computed(() => ({
|
||||||
|
type: 'checkbox',
|
||||||
|
showCheckedAll: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
dataSource,
|
||||||
|
pageInfo,
|
||||||
|
onPageChange,
|
||||||
|
onPageSizeChange,
|
||||||
|
rowSelection,
|
||||||
|
handleSelect,
|
||||||
|
handleSelectAll,
|
||||||
|
};
|
||||||
|
}
|
||||||
15
src/styles/components/date-picker.scss
Normal file
15
src/styles/components/date-picker.scss
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.arco-picker {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
border-color: #d7d7d9 !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
&:focus-within,
|
||||||
|
&.arco-input-focus,
|
||||||
|
&.arco-textarea-focus {
|
||||||
|
background-color: var(--color-bg-2) !important;
|
||||||
|
border-color: rgb(var(--primary-6)) !important;
|
||||||
|
box-shadow: 0 0 0 0 var(--color-primary-light-2) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,4 +3,7 @@
|
|||||||
@import './checkbox.scss';
|
@import './checkbox.scss';
|
||||||
@import './pagination.scss';
|
@import './pagination.scss';
|
||||||
@import './tabs.scss';
|
@import './tabs.scss';
|
||||||
@import './modal.scss';
|
@import './modal.scss';
|
||||||
|
@import "./textarea.scss";
|
||||||
|
@import "./select.scss";
|
||||||
|
@import "./date-picker.scss"
|
||||||
@ -1,5 +1,15 @@
|
|||||||
.arco-input-wrapper {
|
.arco-input-wrapper {
|
||||||
border-radius: 4px;
|
border-radius: 4px !important;
|
||||||
border-color: #d7d7d9;
|
border-color: #d7d7d9 !important;
|
||||||
background-color: #fff;
|
background-color: #fff !important;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
&:focus-within,
|
||||||
|
&.arco-input-focus,
|
||||||
|
&.arco-textarea-focus {
|
||||||
|
background-color: var(--color-bg-2) !important;
|
||||||
|
border-color: rgb(var(--primary-6)) !important;
|
||||||
|
box-shadow: 0 0 0 0 var(--color-primary-light-2) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,11 @@
|
|||||||
height: 56px;
|
height: 56px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
.arco-modal-title {
|
.arco-modal-title {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
color: #211f24;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/styles/components/select.scss
Normal file
15
src/styles/components/select.scss
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.arco-select {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
border-color: #d7d7d9 !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
&:focus-within,
|
||||||
|
&.arco-input-focus,
|
||||||
|
&.arco-textarea-focus {
|
||||||
|
background-color: var(--color-bg-2) !important;
|
||||||
|
border-color: rgb(var(--primary-6)) !important;
|
||||||
|
box-shadow: 0 0 0 0 var(--color-primary-light-2) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/styles/components/textarea.scss
Normal file
15
src/styles/components/textarea.scss
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.arco-textarea-wrapper {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
border-color: #d7d7d9 !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
&:focus-within,
|
||||||
|
&.arco-input-focus,
|
||||||
|
&.arco-textarea-focus {
|
||||||
|
background-color: var(--color-bg-2) !important;
|
||||||
|
border-color: rgb(var(--primary-6)) !important;
|
||||||
|
box-shadow: 0 0 0 0 var(--color-primary-light-2) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,3 @@
|
|||||||
.arco-input-wrapper,
|
|
||||||
.arco-select-view-single,
|
|
||||||
.arco-textarea-wrapper,
|
|
||||||
.arco-picker,
|
|
||||||
.arco-select-view-multiple {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus,
|
|
||||||
&.arco-textarea-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.arco-modal {
|
.arco-modal {
|
||||||
.cancel-btn {
|
.cancel-btn {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|||||||
@ -1,17 +1,4 @@
|
|||||||
.container {
|
.container {
|
||||||
:deep(.arco-input-wrapper),
|
|
||||||
:deep(.arco-select-view-single),
|
|
||||||
:deep(.arco-select-view-multiple) {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
.filter-row-item {
|
.filter-row-item {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -1,18 +1,4 @@
|
|||||||
.note-table-wrap {
|
.note-table-wrap {
|
||||||
:deep(.arco-input-wrapper),
|
|
||||||
:deep(.arco-select-view-single),
|
|
||||||
:deep(.arco-select-view-multiple),
|
|
||||||
:deep(.arco-picker) {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
.filter-row-item {
|
.filter-row-item {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -1,17 +1,4 @@
|
|||||||
.container {
|
.container {
|
||||||
:deep(.arco-input-wrapper),
|
|
||||||
:deep(.arco-select-view-single),
|
|
||||||
:deep(.arco-select-view-multiple) {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
.filter-row-item {
|
.filter-row-item {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
size="medium"
|
size="medium"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
allow-clear
|
allow-clear
|
||||||
|
:max-tag-count="3"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
>
|
>
|
||||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
size="medium"
|
size="medium"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
allow-clear
|
allow-clear
|
||||||
|
:max-tag-count="3"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
>
|
>
|
||||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||||
|
|||||||
@ -1,18 +1,4 @@
|
|||||||
.container {
|
.container {
|
||||||
:deep(.arco-input-wrapper),
|
|
||||||
:deep(.arco-select-view-single),
|
|
||||||
:deep(.arco-select-view-multiple),
|
|
||||||
:deep(.arco-picker) {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
.filter-row-item {
|
.filter-row-item {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
size="medium"
|
size="medium"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
allow-clear
|
allow-clear
|
||||||
|
:max-tag-count="3"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
>
|
>
|
||||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||||
|
|||||||
@ -69,8 +69,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PauseAccountPatchModal ref="pauseAccountPatchModalRef" @update="emits('update')" />
|
<PauseAccountPatchModal ref="pauseAccountPatchModalRef" />
|
||||||
<AuthorizedAccountModal ref="authorizedAccountModalRef" @update="emits('update')" />
|
<AuthorizedAccountModal ref="authorizedAccountModalRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete']);
|
const emits = defineEmits(['openEdit', 'selectionChange', 'delete']);
|
||||||
|
|
||||||
const pauseAccountPatchModalRef = ref(null);
|
const pauseAccountPatchModalRef = ref(null);
|
||||||
const authorizedAccountModalRef = ref(null);
|
const authorizedAccountModalRef = ref(null);
|
||||||
@ -127,7 +127,7 @@ const openDelete = (item) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReauthorize = (item) => {
|
const handleReauthorize = (item) => {
|
||||||
authorizedAccountModalRef.value?.open(item.id, item.last_synced_at);
|
authorizedAccountModalRef.value?.open({ accountId: item.id, last_synced_at: item.last_synced_at });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePause = (item) => {
|
const handlePause = (item) => {
|
||||||
|
|||||||
@ -20,8 +20,8 @@ import { ref } from 'vue';
|
|||||||
import { pausePatchPlacementAccount } from '@/api/all/propertyMarketing';
|
import { pausePatchPlacementAccount } from '@/api/all/propertyMarketing';
|
||||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
|
|
||||||
const emits = defineEmits(['update', 'close']);
|
const emits = defineEmits(['close']);
|
||||||
|
const update = inject('update');
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const accountId = ref(null);
|
const accountId = ref(null);
|
||||||
const accountName = ref('');
|
const accountName = ref('');
|
||||||
@ -45,7 +45,7 @@ async function onConfirm() {
|
|||||||
const { code } = await pausePatchPlacementAccount(accountId.value);
|
const { code } = await pausePatchPlacementAccount(accountId.value);
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
AMessage.success('暂停成功');
|
AMessage.success('暂停成功');
|
||||||
emits('update');
|
update();
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,7 +137,7 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<AuthorizedAccountModal ref="authorizedAccountModalRef" @update="emits('update')" />
|
<AuthorizedAccountModal ref="authorizedAccountModalRef" />
|
||||||
<!-- <ImportPromptModal ref="importPromptModalRef" /> -->
|
<!-- <ImportPromptModal ref="importPromptModalRef" /> -->
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
@ -148,7 +148,7 @@ import { ref, defineEmits } from 'vue';
|
|||||||
import AuthorizedAccountModal from '../authorized-account-modal';
|
import AuthorizedAccountModal from '../authorized-account-modal';
|
||||||
// import ImportPromptModal from '../import-prompt-modal';
|
// import ImportPromptModal from '../import-prompt-modal';
|
||||||
import StatusBox from '../status-box';
|
import StatusBox from '../status-box';
|
||||||
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
postPlacementAccounts,
|
postPlacementAccounts,
|
||||||
@ -161,7 +161,7 @@ import {
|
|||||||
import icon1 from '@/assets/img/media-account/icon-download.png';
|
import icon1 from '@/assets/img/media-account/icon-download.png';
|
||||||
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
|
||||||
const emits = defineEmits(['update']);
|
const update = inject('update');
|
||||||
|
|
||||||
const UploadStatus = {
|
const UploadStatus = {
|
||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
@ -278,7 +278,7 @@ const handleBatchImport = async () => {
|
|||||||
});
|
});
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
AMessage.success('导入成功');
|
AMessage.success('导入成功');
|
||||||
emits('update');
|
update();
|
||||||
onClose();
|
onClose();
|
||||||
} else {
|
} else {
|
||||||
uploadStatus.value = UploadStatus.ERROR;
|
uploadStatus.value = UploadStatus.ERROR;
|
||||||
@ -290,6 +290,27 @@ const handleBatchImport = async () => {
|
|||||||
// importPromptModalRef.value.open();
|
// importPromptModalRef.value.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAdd = async () => {
|
||||||
|
// 聚光无子账号
|
||||||
|
if (form.value.platform === ENUM_PLATFORM.jg) {
|
||||||
|
const { code, data } = await postPlacementAccounts(form.value);
|
||||||
|
if (code === 200) {
|
||||||
|
update();
|
||||||
|
authorizedAccountModalRef.value.open({ accountId: data?.id });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
authorizedAccountModalRef.value.open({ form: form.value, needSelectSubAccount: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleEdit = async () => {
|
||||||
|
const { code } = await putPlacementAccounts({ id: id.value, ...form.value });
|
||||||
|
if (code === 200) {
|
||||||
|
isEdit.value && AMessage.success('修改成功');
|
||||||
|
update();
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
if (isBatchImport.value) {
|
if (isBatchImport.value) {
|
||||||
handleBatchImport();
|
handleBatchImport();
|
||||||
@ -298,27 +319,11 @@ async function onSubmit() {
|
|||||||
|
|
||||||
formRef.value.validate(async (errors) => {
|
formRef.value.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
const _fn = id.value ? putPlacementAccounts : postPlacementAccounts;
|
isEdit.value ? handleEdit() : handleAdd();
|
||||||
const _params = id.value ? { id: id.value, ...form.value } : form.value;
|
|
||||||
const { code, data } = await _fn(_params);
|
|
||||||
if (code === 200) {
|
|
||||||
isEdit.value && AMessage.success('修改成功');
|
|
||||||
emits('update');
|
|
||||||
|
|
||||||
if (isEdit.value) {
|
|
||||||
onClose();
|
|
||||||
} else {
|
|
||||||
handleSuccess(data?.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSuccess = (id) => {
|
|
||||||
authorizedAccountModalRef.value.open(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDownloadTemplate = async () => {
|
const handleDownloadTemplate = async () => {
|
||||||
const { code, data } = await getPlacementAccountsTemplateUrl();
|
const { code, data } = await getPlacementAccountsTemplateUrl();
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
@ -326,6 +331,7 @@ const handleDownloadTemplate = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide('closeAddAccountModal', onClose);
|
||||||
// 对外暴露打开弹窗方法
|
// 对外暴露打开弹窗方法
|
||||||
defineExpose({ open });
|
defineExpose({ open });
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -59,23 +59,28 @@
|
|||||||
<a-button type="primary" size="large" @click="handleOk">{{ confirmBtnText }} </a-button>
|
<a-button type="primary" size="large" @click="handleOk">{{ confirmBtnText }} </a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
<SelectSubAccountModal ref="selectSubAccountModalRef" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { defineExpose, ref, computed, defineEmits } from 'vue';
|
import { defineExpose, ref, computed, defineEmits } from 'vue';
|
||||||
import { exactFormatTime } from '@/utils/tools';
|
import { exactFormatTime } from '@/utils/tools';
|
||||||
|
import { ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants';
|
||||||
import {
|
import {
|
||||||
putPlacementAccountsAuthorized,
|
putPlacementAccountsAuthorized,
|
||||||
getPlacementAccountsAuthorizedStatus,
|
getPlacementAccountsAuthorizedStatus,
|
||||||
postPlacementAccountsSync,
|
postPlacementAccountsSync,
|
||||||
} from '@/api/all/propertyMarketing';
|
} from '@/api/all/propertyMarketing';
|
||||||
|
|
||||||
|
import SelectSubAccountModal from '../select-sub-account-modal';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
|
import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
|
||||||
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||||
|
|
||||||
const emits = defineEmits(['update']);
|
const update = inject('update');
|
||||||
const INITIAL_SYNC_TYPE = 'sync';
|
const INITIAL_SYNC_TYPE = 'sync';
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
@ -86,9 +91,12 @@ const failReason = ref('');
|
|||||||
const progress = ref(0);
|
const progress = ref(0);
|
||||||
const id = ref('');
|
const id = ref('');
|
||||||
|
|
||||||
|
const selectSubAccountModalRef = ref(null);
|
||||||
const lastSyncedAt = ref(null); // 上次同步时间戳
|
const lastSyncedAt = ref(null); // 上次同步时间戳
|
||||||
const showSyncTip = ref(false);
|
const showSyncTip = ref(false);
|
||||||
|
const shouldSelectSubAccount = ref(false);
|
||||||
const syncType = ref(INITIAL_SYNC_TYPE); // sync | no_sync
|
const syncType = ref(INITIAL_SYNC_TYPE); // sync | no_sync
|
||||||
|
const addAccountFormData = ref(null); // 添加账户表单数据
|
||||||
|
|
||||||
const INITIAL_FORM = {
|
const INITIAL_FORM = {
|
||||||
account: '',
|
account: '',
|
||||||
@ -125,13 +133,17 @@ const getDaysDiffText = (lastSyncedAt) => {
|
|||||||
return `${daysDiff}天`;
|
return `${daysDiff}天`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const open = (accountId, last_synced_at = null) => {
|
const open = ({ accountId, last_synced_at = null, form = null, needSelectSubAccount = false }) => {
|
||||||
|
reset();
|
||||||
|
|
||||||
id.value = accountId;
|
id.value = accountId;
|
||||||
lastSyncedAt.value = last_synced_at;
|
lastSyncedAt.value = last_synced_at;
|
||||||
|
addAccountFormData.value = form;
|
||||||
|
shouldSelectSubAccount.value = needSelectSubAccount;
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const close = () => {
|
const reset = () => {
|
||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
formRef.value?.clearValidate();
|
formRef.value?.clearValidate();
|
||||||
|
|
||||||
@ -145,8 +157,13 @@ const close = () => {
|
|||||||
lastSyncedAt.value = null;
|
lastSyncedAt.value = null;
|
||||||
syncType.value = INITIAL_SYNC_TYPE;
|
syncType.value = INITIAL_SYNC_TYPE;
|
||||||
showSyncTip.value = false;
|
showSyncTip.value = false;
|
||||||
|
shouldSelectSubAccount.value = false;
|
||||||
|
addAccountFormData.value = null;
|
||||||
clearFakeProgressTimer();
|
clearFakeProgressTimer();
|
||||||
clearStatusPollingTimer();
|
clearStatusPollingTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -242,27 +259,39 @@ const handleSyncData = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
|
// n天未同步更新
|
||||||
if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
|
if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
|
||||||
handleSyncData();
|
handleSyncData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已完成
|
||||||
if (isCompleted.value) {
|
if (isCompleted.value) {
|
||||||
if (isSuccess.value) {
|
if (isSuccess.value) {
|
||||||
emits('update');
|
update();
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
startLoading();
|
startLoading();
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
formRef.value.validate(async (errors) => {
|
|
||||||
if (!errors) {
|
|
||||||
startLoading();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 未完成,校验表单
|
||||||
|
formRef.value.validate(async (errors) => {
|
||||||
|
if (errors) return;
|
||||||
|
if (shouldSelectSubAccount.value) {
|
||||||
|
visible.value = false;
|
||||||
|
selectSubAccountModalRef.value.open({
|
||||||
|
...addAccountFormData.value,
|
||||||
|
...form.value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
startLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide('closeAuthorizeAccountModal', close);
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open,
|
open,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,17 +1,4 @@
|
|||||||
.container {
|
.container {
|
||||||
:deep(.arco-input-wrapper),
|
|
||||||
:deep(.arco-select-view-single),
|
|
||||||
:deep(.arco-select-view-multiple) {
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #d7d7d9;
|
|
||||||
background-color: #fff;
|
|
||||||
&:focus-within,
|
|
||||||
&.arco-input-focus {
|
|
||||||
background-color: var(--color-bg-2);
|
|
||||||
border-color: rgb(var(--primary-6));
|
|
||||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter-row {
|
.filter-row {
|
||||||
.filter-row-item {
|
.filter-row-item {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
export const INITIAL_FORM = {
|
||||||
|
platform: '',
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
account_name: '',
|
||||||
|
account_id: '',
|
||||||
|
};
|
||||||
|
export const INITIAL_PAGE_INFO = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20,
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TABLE_COLUMNS = [
|
||||||
|
{
|
||||||
|
title: '账户名称',
|
||||||
|
dataIndex: 'account_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '账户ID',
|
||||||
|
dataIndex: 'account_id',
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,213 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="选择子账户"
|
||||||
|
modal-class="select-sub-account-modal"
|
||||||
|
width="720px"
|
||||||
|
:mask-closable="false"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="filter-row flex mb-16px">
|
||||||
|
<div class="filter-row-item flex items-center">
|
||||||
|
<span class="label">账户名称</span>
|
||||||
|
<a-input
|
||||||
|
v-model="query.account_name"
|
||||||
|
class="w-240px"
|
||||||
|
placeholder="请搜索..."
|
||||||
|
size="medium"
|
||||||
|
allow-clear
|
||||||
|
@change="reload"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</div>
|
||||||
|
<div class="filter-row-item flex items-center">
|
||||||
|
<span class="label">账户ID</span>
|
||||||
|
<a-input
|
||||||
|
v-model="query.account_id"
|
||||||
|
class="w-240px"
|
||||||
|
placeholder="请搜索..."
|
||||||
|
size="medium"
|
||||||
|
allow-clear
|
||||||
|
@change="reload"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="dataSource"
|
||||||
|
column-resizable
|
||||||
|
:row-selection="rowSelection"
|
||||||
|
:row-key="ROW_KEY"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ x: '100%', y: '100%' }"
|
||||||
|
class="w-100% flex-1 overflow-hidden"
|
||||||
|
:selected-keys="selectedRowKeys"
|
||||||
|
bordered
|
||||||
|
@select="handleSelect"
|
||||||
|
@select-all="handleSelectAll"
|
||||||
|
>
|
||||||
|
<template #empty>
|
||||||
|
<NoData />
|
||||||
|
</template>
|
||||||
|
<template #columns>
|
||||||
|
<a-table-column
|
||||||
|
v-for="column in TABLE_COLUMNS"
|
||||||
|
:key="column.dataIndex"
|
||||||
|
:data-index="column.dataIndex"
|
||||||
|
:fixed="column.fixed"
|
||||||
|
:width="column.width"
|
||||||
|
:min-width="column.minWidth"
|
||||||
|
:sortable="column.sortable"
|
||||||
|
:align="column.align"
|
||||||
|
ellipsis
|
||||||
|
tooltip
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="cts mr-4px">{{ column.title }}</span>
|
||||||
|
<a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top">
|
||||||
|
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cell="{ record }">
|
||||||
|
{{ formatTableField(column, record, true) }}
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div v-if="pageInfo.total > 0" class="flex justify-end mt-16px">
|
||||||
|
<a-pagination
|
||||||
|
:total="pageInfo.total"
|
||||||
|
size="mini"
|
||||||
|
show-total
|
||||||
|
show-jumper
|
||||||
|
show-page-size
|
||||||
|
:current="pageInfo.page"
|
||||||
|
:page-size="pageInfo.page_size"
|
||||||
|
@change="onPageChange"
|
||||||
|
@page-size-change="onPageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="cts color-#3C4043 s1"
|
||||||
|
>已选<span class="color-#6D4CFE num mx-3px">{{ selectedAccounts.length }}个</span>账户</span
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a-button class="cancel-btn" size="large" @click="onClose">取消</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
class="ml-16px"
|
||||||
|
status="danger"
|
||||||
|
size="large"
|
||||||
|
:disabled="!selectedAccounts.length"
|
||||||
|
@click="onConfirm"
|
||||||
|
>添加已选账户</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { INITIAL_FORM, INITIAL_PAGE_INFO, TABLE_COLUMNS } from './constants';
|
||||||
|
import { formatTableField } from '@/utils/tools';
|
||||||
|
import { postSubAccount, postAddSubAccount } from '@/api/all/propertyMarketing';
|
||||||
|
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||||
|
|
||||||
|
const emits = defineEmits('confirm');
|
||||||
|
const update = inject('update');
|
||||||
|
const closeAddAccountModal = inject('closeAddAccountModal');
|
||||||
|
const closeAuthorizeAccountModal = inject('closeAuthorizeAccountModal');
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const query = ref(cloneDeep(INITIAL_FORM));
|
||||||
|
const form = ref(null);
|
||||||
|
|
||||||
|
const ROW_KEY = 'account_id';
|
||||||
|
const {
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows: selectedAccounts,
|
||||||
|
dataSource,
|
||||||
|
pageInfo,
|
||||||
|
onPageChange,
|
||||||
|
onPageSizeChange,
|
||||||
|
rowSelection,
|
||||||
|
handleSelect,
|
||||||
|
handleSelectAll,
|
||||||
|
} = useTableSelectionWithPagination({
|
||||||
|
rowKey: ROW_KEY,
|
||||||
|
onPageChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
onPageSizeChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const open = (formData) => {
|
||||||
|
const { platform, account, password } = formData;
|
||||||
|
form.value = formData;
|
||||||
|
query.value = {
|
||||||
|
...query.value,
|
||||||
|
platform,
|
||||||
|
account,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
|
||||||
|
getData();
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
form.value = null;
|
||||||
|
selectedAccounts.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
query.value = cloneDeep(INITIAL_FORM);
|
||||||
|
pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = async () => {
|
||||||
|
const { code } = await postAddSubAccount({ ...form.value, subaccounts: selectedAccounts.value });
|
||||||
|
if (code === 200) {
|
||||||
|
visible.value = false;
|
||||||
|
update();
|
||||||
|
closeAuthorizeAccountModal();
|
||||||
|
closeAddAccountModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const { page, page_size } = pageInfo.value;
|
||||||
|
const { code, data } = await postSubAccount({
|
||||||
|
...query.value,
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
});
|
||||||
|
if (code === 200) {
|
||||||
|
dataSource.value = data?.data ?? [];
|
||||||
|
pageInfo.value.total = data.total;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
pageInfo.value.page = 1;
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
.select-sub-account-modal {
|
||||||
|
.cts {
|
||||||
|
color: var(--Text-1, #211f24);
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
.arco-modal-header {
|
||||||
|
}
|
||||||
|
.arco-modal-body {
|
||||||
|
height: 536px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.filter-row {
|
||||||
|
.filter-row-item {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #211f24;
|
||||||
|
font-family: 'PuHuiTi-Regular';
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.arco-modal-footer {
|
||||||
|
justify-content: space-between;
|
||||||
|
.s1 {
|
||||||
|
font-family: $font-family-regular;
|
||||||
|
.num {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -67,7 +67,6 @@
|
|||||||
@selectionChange="handleSelectionChange"
|
@selectionChange="handleSelectionChange"
|
||||||
@delete="handleDelete"
|
@delete="handleDelete"
|
||||||
@openEdit="handleOpenEdit"
|
@openEdit="handleOpenEdit"
|
||||||
@update="getData"
|
|
||||||
/>
|
/>
|
||||||
<NoData v-else />
|
<NoData v-else />
|
||||||
|
|
||||||
@ -87,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AddAccountModal ref="addAccountModalRef" @update="getData" />
|
<AddAccountModal ref="addAccountModalRef" />
|
||||||
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
|
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -257,6 +256,8 @@ const handleOpenAbnormalAccount = () => {
|
|||||||
query.value.status = 2;
|
query.value.status = 2;
|
||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide('update', getData);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -2,6 +2,12 @@ import icon1 from '@/assets/img/media-account/icon-jl.png';
|
|||||||
import icon2 from '@/assets/img/media-account/icon-jg.png';
|
import icon2 from '@/assets/img/media-account/icon-jg.png';
|
||||||
import icon3 from '@/assets/img/media-account/icon-bili.png';
|
import icon3 from '@/assets/img/media-account/icon-bili.png';
|
||||||
|
|
||||||
|
export const ENUM_PLATFORM = {
|
||||||
|
jl: 0,
|
||||||
|
jg: 1,
|
||||||
|
bili: 2,
|
||||||
|
};
|
||||||
|
|
||||||
export const PLATFORM_LIST = [
|
export const PLATFORM_LIST = [
|
||||||
{
|
{
|
||||||
label: '巨量',
|
label: '巨量',
|
||||||
|
|||||||
Reference in New Issue
Block a user