feat: 初始化项目管理列表
This commit is contained in:
@ -0,0 +1,70 @@
|
||||
<!-- eslint-disable vue/no-mutating-props -->
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<div class="container px-24px pt-12px pb-24px">
|
||||
<div class="filter-row flex">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">项目名称</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
v-model="query.search"
|
||||
class="w-240px"
|
||||
placeholder="请搜索..."
|
||||
size="medium"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<a-button class="w-84px search-btn mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button class="w-84px reset-btn" size="medium" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
query: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits('onSearch', 'onReset', 'update:query');
|
||||
|
||||
const handleSearch = () => {
|
||||
emits('update:query', props.query);
|
||||
nextTick(() => {
|
||||
emits('onSearch');
|
||||
});
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emits('onReset');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,23 @@
|
||||
.container {
|
||||
.filter-row {
|
||||
.filter-row-item {
|
||||
&:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: $font-family-regular;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
:deep(.arco-space-item) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-28 10:33:06
|
||||
*/
|
||||
export const TABLE_COLUMNS = [
|
||||
{
|
||||
title: '项目名称',
|
||||
dataIndex: 'name',
|
||||
width: 240,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '项目预算',
|
||||
dataIndex: 'key1',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '关联平台账号',
|
||||
dataIndex: 'key2',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '关联渠道账户',
|
||||
dataIndex: 'key3',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '关联内容稿件',
|
||||
dataIndex: 'key4',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'create_at',
|
||||
width: 180,
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<a-table
|
||||
ref="tableRef"
|
||||
:data="dataSource"
|
||||
row-key="id"
|
||||
column-resizable
|
||||
:pagination="false"
|
||||
:scroll="{ x: '100%' }"
|
||||
class="flex-1 project-table w-100%"
|
||||
bordered
|
||||
@sorter-change="handleSorterChange"
|
||||
>
|
||||
<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 v-if="column.dataIndex === 'create_at'" #cell="{ record }">
|
||||
{{ exactFormatTime(record.create_at) }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
|
||||
<div class="flex items-center">
|
||||
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
|
||||
<a-button type="outline" size="mini" class="search-btn" @click="onEdit(record)">编辑</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else #cell="{ record }">
|
||||
{{ formatTableField(column, record, true) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||
import { TABLE_COLUMNS } from './constants';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||
|
||||
const emits = defineEmits(['edit', 'sorterChange', 'delete']);
|
||||
|
||||
const props = defineProps({
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const tableRef = ref(null);
|
||||
|
||||
// 处理排序变化
|
||||
const handleSorterChange = (column, order) => {
|
||||
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc');
|
||||
};
|
||||
const onDelete = (item) => {
|
||||
emits('delete', item);
|
||||
};
|
||||
const onEdit = (item) => {
|
||||
emits('edit', item);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,10 @@
|
||||
.project-table {
|
||||
.cts {
|
||||
color: var(--Text-1, #211f24);
|
||||
font-family: $font-family-medium;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
export const INITIAL_QUERY = {
|
||||
search: '',
|
||||
column: '',
|
||||
order: '',
|
||||
};
|
||||
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="project-list-wrap">
|
||||
<div class="filter-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid mb-16px">
|
||||
<div class="top flex h-64px px-24px py-10px justify-between items-center">
|
||||
<p class="text-18px font-400 lh-26px color-#211F24 title">项目列表</p>
|
||||
<div class="flex items-center">
|
||||
<a-button type="primary" class="w-112px search-btn" size="medium" @click="handleOpenAddProjectModal">
|
||||
<template #icon>
|
||||
<img :src="icon1" width="16" height="16" />
|
||||
</template>
|
||||
<template #default>添加项目</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<FilterBlock v-model:query="query" @onSearch="handleSearch" @onReset="handleReset" />
|
||||
</div>
|
||||
<div
|
||||
class="table-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid px-24px py-24px flex-1 flex flex-col"
|
||||
>
|
||||
<ProjectTable
|
||||
:dataSource="dataSource"
|
||||
@sorterChange="handleSorterChange"
|
||||
@delete="handleDelete"
|
||||
@edit="handleEdit"
|
||||
/>
|
||||
<div v-if="pageInfo.total > 0" class="pagination-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { INITIAL_QUERY } from './constants';
|
||||
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||
|
||||
import FilterBlock from './components/filter-block';
|
||||
import ProjectTable from './components/project-table';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-add.png';
|
||||
|
||||
const { dataSource, pageInfo, onPageChange, onPageSizeChange, resetPageInfo } = useTableSelectionWithPagination({
|
||||
onPageChange: () => {
|
||||
getData();
|
||||
},
|
||||
onPageSizeChange: () => {
|
||||
getData();
|
||||
},
|
||||
});
|
||||
|
||||
const addProjectModalRef = ref(null);
|
||||
|
||||
const query = ref(cloneDeep(INITIAL_QUERY));
|
||||
|
||||
const getData = async () => {
|
||||
dataSource.value = [
|
||||
{
|
||||
id: 1,
|
||||
name: '闲鱼用户增长投放规划',
|
||||
key1: 500131,
|
||||
key2: 4141,
|
||||
key3: 55,
|
||||
key4: 12,
|
||||
create_at: 1753069077,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '闲鱼用户增长投放规划',
|
||||
key1: 500131,
|
||||
key2: 4141,
|
||||
key3: 55,
|
||||
key4: 12,
|
||||
create_at: 1753069077,
|
||||
},
|
||||
];
|
||||
pageInfo.value.total = 2;
|
||||
// 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 handleSearch = () => {
|
||||
reload();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
resetPageInfo();
|
||||
query.value = cloneDeep(INITIAL_QUERY);
|
||||
reload();
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
pageInfo.value.page = 1;
|
||||
getData();
|
||||
};
|
||||
|
||||
const handleOpenAddProjectModal = () => {
|
||||
addProjectModalRef.value?.open();
|
||||
};
|
||||
const handleSorterChange = (column, order) => {
|
||||
query.value.column = column;
|
||||
query.value.order = order;
|
||||
reload();
|
||||
};
|
||||
|
||||
const handleDelete = (item) => {
|
||||
console.log('handleDelete', item);
|
||||
};
|
||||
const handleEdit = (item) => {
|
||||
console.log('handleDelete', item);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
provide('update', getData);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,39 @@
|
||||
.project-list-wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
:deep(.search-btn) {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--Brand-Brand-6, #6d4cfe);
|
||||
color: #6d4cfe;
|
||||
}
|
||||
:deep(.reset-btn) {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--BG-500, #b1b2b5);
|
||||
background: var(--BG-white, #fff);
|
||||
}
|
||||
.filter-wrap {
|
||||
.top {
|
||||
.title {
|
||||
font-family: $font-family-medium;
|
||||
font-style: normal;
|
||||
}
|
||||
:deep(.arco-btn) {
|
||||
.arco-btn-icon {
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.table-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.pagination-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 16px 24px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,7 @@
|
||||
show-page-size
|
||||
:page-size-options="[8, 16, 20, 32, 64]"
|
||||
:current="pageInfo.page"
|
||||
:page-size="pageInfo.pageSize"
|
||||
:page-size="pageInfo.page_size"
|
||||
@change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
/>
|
||||
@ -114,7 +114,7 @@ const deleteAccountRef = ref(null);
|
||||
|
||||
const pageInfo = ref({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
page_size: 20,
|
||||
total: 0,
|
||||
});
|
||||
const query = ref(cloneDeep(INITIAL_QUERY));
|
||||
@ -174,10 +174,10 @@ const getHealthData = async () => {
|
||||
}
|
||||
};
|
||||
const getAccountData = async () => {
|
||||
const { page, pageSize } = pageInfo.value;
|
||||
const { page, page_size } = pageInfo.value;
|
||||
const { code, data, total } = await getPlacementAccounts({
|
||||
page,
|
||||
page_size: pageSize,
|
||||
page_size,
|
||||
...query.value,
|
||||
});
|
||||
if (code === 200) {
|
||||
@ -190,11 +190,11 @@ const reload = () => {
|
||||
getData();
|
||||
};
|
||||
const handleSearch = () => {
|
||||
getData();
|
||||
reload();
|
||||
};
|
||||
const handleReset = () => {
|
||||
pageInfo.value.page = 1;
|
||||
pageInfo.value.pageSize = 20;
|
||||
pageInfo.value.page_size = 20;
|
||||
pageInfo.value.total = 0;
|
||||
selectedItems.value = [];
|
||||
query.value = cloneDeep(INITIAL_QUERY);
|
||||
@ -206,7 +206,7 @@ const onPageChange = (current) => {
|
||||
getData();
|
||||
};
|
||||
const onPageSizeChange = (pageSize) => {
|
||||
pageInfo.value.pageSize = pageSize;
|
||||
pageInfo.value.page_size = pageSize;
|
||||
reload();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user