feat: table组件替换

This commit is contained in:
rd
2025-09-04 18:05:16 +08:00
parent aaa8a320c8
commit 3f5249c731
42 changed files with 1816 additions and 1833 deletions

View File

@ -33,6 +33,7 @@ export function useTableSelectionWithPagination(options: UseTableSelectionWithPa
const select = selectedKeys.includes(rowKeyValue); const select = selectedKeys.includes(rowKeyValue);
selectedRowKeys.value = selectedKeys; selectedRowKeys.value = selectedKeys;
console.log('handleSelect', selectedKeys, rowKeyValue, record);
if (select) { if (select) {
if (!selectedRows.value.some((v) => v[rowKey] === record[rowKey])) { if (!selectedRows.value.some((v) => v[rowKey] === record[rowKey])) {
selectedRows.value.push(record); selectedRows.value.push(record);

View File

@ -1,7 +1,7 @@
<script lang="jsx"> <script lang="jsx">
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { Button, Checkbox, Input, Tooltip } from 'ant-design-vue'; import { Button, Checkbox, Input, Tooltip, Table, Pagination } from 'ant-design-vue';
import { Table, TableColumn, Pagination, Notification } from '@arco-design/web-vue'; import { Notification } from '@arco-design/web-vue';
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon'; import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
import NoData from '@/components/no-data'; import NoData from '@/components/no-data';
@ -15,6 +15,8 @@ import DeleteTaskModal from './delete-task-modal.vue';
import icon1 from '@/assets/img/media-account/icon-delete.png'; import icon1 from '@/assets/img/media-account/icon-delete.png';
import { showExportNotification, showFailExportNotification } from '@/utils/arcoD'; import { showExportNotification, showFailExportNotification } from '@/utils/arcoD';
export default { export default {
setup(props, { emit, expose }) { setup(props, { emit, expose }) {
const { const {
@ -77,10 +79,12 @@ export default {
getData(); getData();
}; };
const handleSorterChange = (column, order) => { const handleSorterChange = (pagination, filters, sorter) => {
query.value.sort_column = column; if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc'; query.value.sort_column = sorter.columnKey;
reload(); query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
reload();
}
}; };
const handleSearch = () => { const handleSearch = () => {
@ -273,110 +277,103 @@ export default {
{/* 表格 */} {/* 表格 */}
<Table <Table
ref="tableRef" ref="tableRef"
data={dataSource.value} dataSource={dataSource.value}
column-resizable rowKey="id"
row-key="id" rowSelection={{
row-selection={rowSelection.value} selectedRowKeys: selectedRowKeys.value,
selected-keys={selectedRowKeys.value} onChange: (keys, rows) => handleSelect(keys, rows),
onSelectAll: (selected, rows, changeRows) => handleSelectAll(selected, rows, changeRows)
}}
pagination={false} pagination={false}
scroll={{ x: '100%', y: '100%' }} scroll={{ x: '100%', y: '100%' }}
class="w-100% flex-1 overflow-hidden" class="w-100% flex-1 overflow-hidden"
bordered bordered
onSorterChange={handleSorterChange} showSorterTooltip={false}
onSelect={handleSelect} onChange={handleSorterChange}
onSelectAll={handleSelectAll}
v-slots={{ v-slots={{
empty: () => <NoData />, emptyText: () => <NoData />,
columns: () => (
<>
{TABLE_COLUMNS.map((column) => (
<TableColumn
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
v-slots={{
title: () => (
<div class="flex items-center">
<span class="cts mr-4px">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
),
cell: ({ record }) => {
if (column.dataIndex === 'status') {
return (
<div class={['status-box', `status-box-${record.status}`]}>
<span>{EXPORT_TASK_STATUS.find((v) => v.value === record.status)?.label}</span>
</div>
);
} else if (column.dataIndex === 'operator.name') {
return record.operator?.name || record.operator?.mobile;
} else if (column.dataIndex === 'created_at') {
return exactFormatTime(record.created_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
} else {
return formatTableField(column, record, true);
}
},
}}
/>
))}
<TableColumn
data-index="operation"
width={dataSource.value.some((record) => record.status !== enumTaskStatus.Exporting) ? 120 : 60}
fixed="right"
title="操作"
v-slots={{
cell: ({ record }) => (
<div class="flex items-center">
<img
src={icon1}
width="14"
height="14"
class="mr-8px cursor-pointer"
onClick={() => handleDelete(record)}
/>
{record.status !== enumTaskStatus.Exporting && (
<Button
type="primary"
ghost
size="small"
class="search-btn"
onClick={() => handleDownload(record)}
>
{record.status === enumTaskStatus.Failed ? '重新导出' : '下载'}
</Button>
)}
</div>
),
}}
/>
</>
),
}} }}
/> >
{TABLE_COLUMNS.map((column) => (
<Table.Column
key={column.dataIndex}
dataIndex={column.dataIndex}
fixed={column.fixed}
width={column.width}
minWidth={column.minWidth}
sorter={column.sortable}
align={column.align}
ellipsis
title={() => (
<div class="flex items-center">
<span class="cts mr-4px">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
)}
customRender={({ record }) => {
if (column.dataIndex === 'status') {
return (
<div class={['status-box', `status-box-${record.status}`]}>
<span>{EXPORT_TASK_STATUS.find((v) => v.value === record.status)?.label}</span>
</div>
);
} else if (column.dataIndex === 'operator.name') {
return record.operator?.name || record.operator?.mobile;
} else if (column.dataIndex === 'created_at') {
return exactFormatTime(record.created_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
} else {
return formatTableField(column, record, true);
}
}}
/>
))}
<Table.Column
dataIndex="operation"
width={dataSource.value.some((record) => record.status !== enumTaskStatus.Exporting) ? 120 : 60}
fixed="right"
title="操作"
customRender={({ record }) => (
<div class="flex items-center">
<img
src={icon1}
width="14"
height="14"
class="mr-8px cursor-pointer"
onClick={() => handleDelete(record)}
/>
{record.status !== enumTaskStatus.Exporting && (
<Button
type="primary"
ghost
size="small"
class="search-btn"
onClick={() => handleDownload(record)}
>
{record.status === enumTaskStatus.Failed ? '重新导出' : '下载'}
</Button>
)}
</div>
)}
/>
</Table>
{/* 分页 */} {/* 分页 */}
{pageInfo.value.total > 0 && ( {pageInfo.value.total > 0 && (
<div class="flex justify-end my-16px"> <div class="flex justify-end my-16px">
<Pagination <Pagination
total={pageInfo.value.total} total={pageInfo.value.total}
size="mini" size="small"
show-total showTotal={(total, range) => `${total} 条记录`}
show-jumper showQuickJumper
show-page-size showSizeChanger
current={pageInfo.value.page} current={pageInfo.value.page}
page-size={pageInfo.value.page_size} pageSize={pageInfo.value.page_size}
onChange={onPageChange} onChange={onPageChange}
onPageSizeChange={onPageSizeChange} onShowSizeChange={onPageSizeChange}
/> />
</div> </div>
)} )}

View File

@ -1,7 +1,6 @@
<script lang="jsx"> <script lang="jsx">
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { Button, Tooltip } from 'ant-design-vue'; import { Button, Tooltip, Table, Pagination } from 'ant-design-vue';
import { Table, TableColumn, Pagination, Notification } from '@arco-design/web-vue';
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon'; import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
import NoData from '@/components/no-data'; import NoData from '@/components/no-data';
import { getTask } from '@/api/all/common'; import { getTask } from '@/api/all/common';
@ -73,10 +72,12 @@ export default {
getData(); getData();
}; };
const handleSorterChange = (column, order) => { const handleSorterChange = (pagination, filters, sorter) => {
query.value.sort_column = column; if (sorter && sorter.columnKey) {
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc'; query.value.sort_column = sorter.columnKey;
reload(); query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
reload();
}
}; };
const handleSearch = () => { const handleSearch = () => {
@ -121,103 +122,97 @@ export default {
<div class="import-task-wrap"> <div class="import-task-wrap">
<Table <Table
ref="tableRef" ref="tableRef"
data={dataSource.value} dataSource={dataSource.value}
column-resizable rowKey="id"
row-key="id" rowSelection={{
selected-keys={selectedRowKeys.value} selectedRowKeys: selectedRowKeys.value,
onChange: (keys, rows) => handleSelect(keys, rows),
onSelectAll: (selected, rows, changeRows) => handleSelectAll(selected, rows, changeRows)
}}
pagination={false} pagination={false}
scroll={{ x: '100%', y: '100%' }} scroll={{ x: '100%', y: '100%' }}
class="w-100% flex-1 overflow-hidden" class="w-100% flex-1 overflow-hidden"
bordered bordered
onSorterChange={handleSorterChange} showSorterTooltip={false}
onSelect={handleSelect} onChange={handleSorterChange}
onSelectAll={handleSelectAll}
v-slots={{ v-slots={{
empty: () => <NoData />, empty: () => <NoData />,
columns: () => (
<>
{TABLE_COLUMNS.map((column) => (
<TableColumn
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
v-slots={{
title: () => (
<div class="flex items-center">
<span class="cts mr-4px">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
),
cell: ({ record }) => {
if (column.dataIndex === 'status') {
return (
<div class={['status-box', `status-box-${record.status}`]}>
<span>{IMPORT_TASK_STATUS.find((v) => v.value === record.status)?.label}</span>
</div>
);
} else if (column.dataIndex === 'operator.name') {
return <span>{record.operator?.name || record.operator?.mobile}</span>;
} else if (column.dataIndex === 'created_at') {
return exactFormatTime(record.created_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
} else {
return formatTableField(column, record, true);
}
},
}}
/>
))}
<TableColumn
data-index="operation"
width={dataSource.value.some((record) => record.status === enumTaskStatus.Failed) ? 180 : 60}
fixed="right"
title="操作"
v-slots={{
cell: ({ record }) => (
<div class="flex items-center">
<img
src={icon1}
width="14"
height="14"
class="mr-8px cursor-pointer"
onClick={() => handleDelete(record)}
/>
{record.status === enumTaskStatus.Failed && (
<Button type="primary" ghost size="small" class="search-btn" onClick={() => handleDownload(record)}>
下载问题表格
</Button>
)}
</div>
),
}}
/>
</>
),
}} }}
/> >
{TABLE_COLUMNS.map((column) => (
<Table.Column
key={column.dataIndex}
dataIndex={column.dataIndex}
fixed={column.fixed}
width={column.width}
minWidth={column.minWidth}
sorter={column.sortable}
align={column.align}
ellipsis
title={() => (
<div class="flex items-center">
<span class="cts mr-4px">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
)}
customRender={({ record }) => {
if (column.dataIndex === 'status') {
return (
<div class={['status-box', `status-box-${record.status}`]}>
<span>{IMPORT_TASK_STATUS.find((v) => v.value === record.status)?.label}</span>
</div>
);
} else if (column.dataIndex === 'operator.name') {
return <span>{record.operator?.name || record.operator?.mobile}</span>;
} else if (column.dataIndex === 'created_at') {
return exactFormatTime(record.created_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
} else {
return formatTableField(column, record, true);
}
}}
/>
))}
<Table.Column
dataIndex="operation"
width={dataSource.value.some((record) => record.status === enumTaskStatus.Failed) ? 180 : 60}
fixed="right"
title="操作"
customRender={({ record }) => (
<div class="flex items-center">
<img
src={icon1}
width="14"
height="14"
class="mr-8px cursor-pointer"
onClick={() => handleDelete(record)}
/>
{record.status === enumTaskStatus.Failed && (
<Button type="primary" ghost size="small" class="search-btn" onClick={() => handleDownload(record)}>
下载问题表格
</Button>
)}
</div>
)}
/>
</Table>
{/* 分页 */} {/* 分页 */}
{pageInfo.value.total > 0 && ( {pageInfo.value.total > 0 && (
<div class="flex justify-end my-16px"> <div class="flex justify-end my-16px">
<Pagination <Pagination
total={pageInfo.value.total} total={pageInfo.value.total}
size="mini" size="small"
show-total showTotal={(total, range) => `${total} 条记录`}
show-jumper showQuickJumper
show-page-size showSizeChanger
current={pageInfo.value.page} current={pageInfo.value.page}
page-size={pageInfo.value.page_size} pageSize={pageInfo.value.page_size}
onChange={onPageChange} onChange={onPageChange}
onPageSizeChange={onPageSizeChange} onShowSizeChange={onPageSizeChange}
/> />
</div> </div>
)} )}

View File

@ -13,39 +13,37 @@
.ant-table-container { .ant-table-container {
border: none !important; border: none !important;
height: 100%; height: 100%;
.ant-table-content { .ant-table-thead {
.ant-table-thead { > tr {
> tr { .ant-table-cell {
.ant-table-cell { padding: 13px 16px;
padding: 13px 16px; background: var(--BG-100, #f7f8fa);
background: var(--BG-100, #f7f8fa); border-start-start-radius: 0 !important;
border-start-start-radius: 0 !important; border-start-end-radius: 0 !important;
border-start-end-radius: 0 !important; @include table-cell-text;
@include table-cell-text; font-family: 'PingFangSC-Medium';
font-family: 'PingFangSC-Medium'; .ant-table-column-sorters {
.ant-table-column-sorters { justify-content: normal;
justify-content: normal; .ant-table-column-title {
.ant-table-column-title { flex: none;
flex: none; }
} .ant-table-column-sorter {
.ant-table-column-sorter { margin-left: 8px;
margin-left: 8px; color: #939499;
color: #939499; line-height: normal;
line-height: normal; .anticon.active {
.anticon.active { color: $color-primary;
color: $color-primary;
}
} }
} }
} }
} }
} }
.ant-table-tbody { }
.ant-table-row { .ant-table-tbody {
.ant-table-cell { .ant-table-row {
padding: 11px 16px; .ant-table-cell {
@include table-cell-text; padding: 11px 16px;
} @include table-cell-text;
} }
} }
} }

View File

@ -10,70 +10,77 @@
<icon-question-circle size="16" class="!color-#737478" /> <icon-question-circle size="16" class="!color-#737478" />
</Tooltip> </Tooltip>
</div> </div>
<a-table <Table
:columns="columns" :dataSource="dataList"
:data="dataList"
:filter-icon-align-left="alignLeft"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <Table.Column
v-for="column in columns"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
>
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template v-else-if="column.slotName === 'keywords'" #customRender="{ record }">
<a-tag
v-for="item in record.keywords"
:key="item"
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
>{{ item }}</a-tag
>
</template>
<template v-else-if="column.slotName === 'hot'" #customRender="{ record }">
<img
v-for="i in record.hot"
:key="i"
:src="starImages[i - 1]"
style="width: 16px; height: 16px"
class="mr-2px"
/>
</template>
<template v-else-if="column.slotName === 'sentiment'" #customRender="{ record }">
<img v-if="record.felling == '2'" src="@/assets/img/hottranslation/good.png" class="w-24px h-24px" />
<img v-else-if="record.felling == '1'" src="@/assets/img/hottranslation/normal.png" class="w-24px h-24px" />
<img v-else-if="record.felling == '0'" src="@/assets/img/hottranslation/poor.png" class="w-24px h-24px" />
</template>
<template v-else-if="column.slotName === 'optional'" #customRender="{ record }">
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
</template>
<template v-else-if="column.titleSlotName === 'hotTitle'" #title>
<div class="flex items-center">
<span class="mr-8px">热度指数</span>
<Tooltip>
<template #title>综合话题出现频次互动数据如点赞收藏评论加权计算的热度得分</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</div>
</template>
<template v-else-if="column.titleSlotName === 'sentimentTitle'" #title>
<div class="flex items-center">
<span class="mr-8px">情感倾向</span>
<Tooltip>
<template #title
>统计该行业下全部内容的情绪分布选取占比最高的情绪类型作为该话题的整体情感倾向</template
>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</div>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #hotTitle> </Table>
<div class="flex items-center">
<span class="mr-8px">热度指数</span>
<Tooltip>
<template #title>综合话题出现频次互动数据如点赞收藏评论加权计算的热度得分</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</div>
</template>
<template #sentimentTitle>
<div class="flex items-center">
<span class="mr-8px">情感倾向</span>
<Tooltip>
<template #title
>统计该行业下全部内容的情绪分布选取占比最高的情绪类型作为该话题的整体情感倾向</template
>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</div>
</template>
<template #rank="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template #keywords="{ record }">
<a-tag
v-for="item in record.keywords"
:key="item"
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
>{{ item }}</a-tag
>
</template>
<template #hot="{ record }">
<img
v-for="i in record.hot"
:key="i"
:src="starImages[i - 1]"
style="width: 16px; height: 16px"
class="mr-2px"
/>
</template>
<template #sentiment="{ record }">
<img v-if="record.felling == '2'" src="@/assets/img/hottranslation/good.png" class="w-24px h-24px" />
<img v-else-if="record.felling == '1'" src="@/assets/img/hottranslation/normal.png" class="w-24px h-24px" />
<img v-else-if="record.felling == '0'" src="@/assets/img/hottranslation/poor.png" class="w-24px h-24px" />
</template>
<template #optional="{ record }">
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
</template>
</a-table>
</div> </div>
<Modal <Modal
v-model:open="visible" v-model:open="visible"
@ -146,7 +153,7 @@
<script setup> <script setup>
import topHeader from './topHeader.vue'; import topHeader from './topHeader.vue';
import { Modal, Button, Tooltip,Space } from 'ant-design-vue'; import { Modal, Button, Tooltip, Space, Table } from 'ant-design-vue';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index'; import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
import star1 from '@/assets/img/hottranslation/star-fill1.png'; import star1 from '@/assets/img/hottranslation/star-fill1.png';
@ -256,7 +263,7 @@ onMounted(() => {
const getIndustriesTree = async () => { const getIndustriesTree = async () => {
const res = await fetchIndustriesTree(); const res = await fetchIndustriesTree();
industriesTree.value = res; industriesTree.value = res;
selectedIndustry.value = res[0].id; selectedIndustry.value = res[0]?.id;
getIndustryTopics(); getIndustryTopics();
}; };

View File

@ -16,62 +16,72 @@
<icon-question-circle size="16" class="!color-#737478" /> <icon-question-circle size="16" class="!color-#737478" />
</Tooltip> </Tooltip>
</div> </div>
<a-table <Table
:columns="columns" :dataSource="dataList"
:data="dataList"
:filter-icon-align-left="alignLeft"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <Table.Column
v-for="column in columns"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
>
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template v-else-if="column.slotName === 'hot'" #customRender="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
<template v-else-if="column.slotName === 'trend'" #customRender="{ record }">
<div class="flex items-center" :class="record.trend > 0 ? 'color-#F64B31' : 'color-#25C883'">
<icon-arrow-up v-if="record.trend > 0" size="16" />
<icon-arrow-down v-else size="16" />
{{ `${(record.trend * 100).toFixed(2)}%` }}
</div>
</template>
<template v-else-if="column.slotName === 'volumeRate'" #customRender="{ record }">
<a-statistic :value="record.volume_rate * 100" />%
</template>
<template v-else-if="column.titleSlotName === 'hotTitle'" #title>
<Space>
<span>热度指数</span>
<Tooltip>
<template #title>综合话题出现频次互动数据如点赞收藏评论加权计算的热度得分</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
<template v-else-if="column.titleSlotName === 'trendTitle'" #title>
<Space>
<span>变化幅度</span>
<Tooltip>
<template #title>仅基于品牌出现频次</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
<template v-else-if="column.titleSlotName === 'volume_rateTitle'" #title>
<Space>
<span>占总声量比例</span>
<Tooltip>
<template #title>该品牌在当前周期内被提及的内容量占整个行业内容总量的比例</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #hotTitle> </Table>
<Space>
<span>热度指数</span>
<Tooltip>
<template #title>综合话题出现频次互动数据如点赞收藏评论加权计算的热度得分</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
<template #trendTitle>
<Space>
<span>变化幅度</span>
<Tooltip>
<template #title>仅基于品牌出现频次</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
<template #volume_rateTitle>
<Space>
<span>占总声量比例</span>
<Tooltip>
<template #title>该品牌在当前周期内被提及的内容量占整个行业内容总量的比例</template>
<icon-question-circle size="14" class="!color-#737478" />
</Tooltip>
</Space>
</template>
<template #rank="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template #hot="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
<template #trend="{ record }">
<div class="flex items-center" :class="record.trend > 0 ? 'color-#F64B31' : 'color-#25C883'">
<icon-arrow-up v-if="record.trend > 0" size="16" />
<icon-arrow-down v-else size="16" />
{{ `${(record.trend * 100).toFixed(2)}%` }}
</div>
</template>
<template #volumeRate="{ record }"> <a-statistic :value="record.volume_rate * 100" />% </template>
</a-table>
</Space> </Space>
<!-- 舆情 & 敏感动态--> <!-- 舆情 & 敏感动态-->
<Space <Space
@ -88,18 +98,27 @@
</Tooltip> </Tooltip>
</div> </div>
<a-table :data="otherList" :columns="columns2" :pagination="false" :scroll="true" style="font-size: 12px"> <Table :dataSource="otherList" :pagination="false" :showSorterTooltip="false" style="font-size: 12px">
<template #empty> <Table.Column
v-for="column in columns2"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
/>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
</a-table> </Table>
</Space> </Space>
</view> </view>
</template> </template>
<script setup> <script setup>
import topHeader from './topHeader.vue'; import topHeader from './topHeader.vue';
import { Tooltip, Space } from 'ant-design-vue'; import { Tooltip, Space, Table } from 'ant-design-vue';
import { fetchFocusBrandsList, fetchEventDynamicsList } from '@/api/all/index'; import { fetchFocusBrandsList, fetchEventDynamicsList } from '@/api/all/index';
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import star1 from '@/assets/img/hottranslation/star-fill1.png'; import star1 from '@/assets/img/hottranslation/star-fill1.png';

View File

@ -15,15 +15,15 @@
</Tooltip> </Tooltip>
</div> </div>
<a-table <Table
:columns="columns" :columns="columns"
:data="dataList" :dataSource="dataList"
:filter-icon-align-left="alignLeft" :scroll="{ x: true }"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #heatLevel> <template #heatLevel>
@ -69,7 +69,7 @@
{{ `${(record.trend * 100).toFixed(2)}%` }} {{ `${(record.trend * 100).toFixed(2)}%` }}
</div> </div>
</template> </template>
</a-table> </Table>
</Space> </Space>
<!-- 行业情绪 --> <!-- 行业情绪 -->
<Space <Space
@ -103,17 +103,16 @@
</div> </div>
</div> </div>
</div> </div>
<a-table <Table
class="flex-1" class="flex-1"
:columns="columns2" :columns="columns2"
:data="sortedRowData" :dataSource="sortedRowData"
:span-method="spanMethod" :scroll="{ x: true }"
:filter-icon-align-left="alignLeft"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #felling="{ record }"> <template #felling="{ record }">
@ -122,7 +121,7 @@
<span>{{ fellingStatus[record.felling].label }}</span> <span>{{ fellingStatus[record.felling].label }}</span>
</div> </div>
</template> </template>
</a-table> </Table>
</div> </div>
</Space> </Space>
<!-- 新兴关键词 --> <!-- 新兴关键词 -->
@ -140,15 +139,15 @@
</Tooltip> </Tooltip>
</div> </div>
<a-table <Table
:columns="columns3" :columns="columns3"
:data="keywordList" :dataSource="keywordList"
:filter-icon-align-left="alignLeft" :scroll="{ x: true }"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #rank="{ record }"> <template #rank="{ record }">
@ -209,7 +208,7 @@
<template #optional="{ record }"> <template #optional="{ record }">
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button> <Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
</template> </template>
</a-table> </Table>
</Space> </Space>
<!-- modal --> <!-- modal -->
<Modal <Modal

View File

@ -17,44 +17,51 @@
<icon-question-circle size="16" class="!color-#737478" /> <icon-question-circle size="16" class="!color-#737478" />
</Tooltip> </Tooltip>
</div> </div>
<a-table <Table
:columns="columns" :dataSource="dataList"
:data="dataList"
:filter-icon-align-left="alignLeft"
:scroll="true"
:pagination="false" :pagination="false"
:showSorterTooltip="false"
@change="handleChange" @change="handleChange"
> >
<template #empty> <Table.Column
v-for="column in columns"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
>
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template v-else-if="column.slotName === 'keywords'" #customRender="{ record }">
<a-tag
v-for="item in record.keywords"
:key="item"
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
>{{ item }}</a-tag
>
</template>
<template v-else-if="column.slotName === 'frequency'" #customRender="{ record }">
<a-tag
:class="`!rounded-2px !px-8px !py-1px !bg-${frequencyStatus[record.frequency].bgColor} !h-22px !color-${
frequencyStatus[record.frequency].color
}`"
>{{ frequencyStatus[record.frequency].label }}</a-tag
>
</template>
<template v-else-if="column.slotName === 'optional'" #customRender="{ record }">
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #rank="{ record }"> </Table>
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
<template #keywords="{ record }">
<a-tag
v-for="item in record.keywords"
:key="item"
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
>{{ item }}</a-tag
>
</template>
<template #frequency="{ record }">
<a-tag
:class="`!rounded-2px !px-8px !py-1px !bg-${frequencyStatus[record.frequency].bgColor} !h-22px !color-${
frequencyStatus[record.frequency].color
}`"
>{{ frequencyStatus[record.frequency].label }}</a-tag
>
</template>
<template #optional="{ record }">
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
</template>
</a-table>
</Space> </Space>
<Modal <Modal
@ -120,7 +127,7 @@
<script setup> <script setup>
import topHeader from './topHeader.vue'; import topHeader from './topHeader.vue';
import { Modal, Button, Tooltip, Space } from 'ant-design-vue'; import { Modal, Button, Tooltip, Space, Table } from 'ant-design-vue';
import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index'; import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index';
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import top1 from '@/assets/img/captcha/top1.svg'; import top1 from '@/assets/img/captcha/top1.svg';

View File

@ -92,36 +92,30 @@
<div class="flex flex-col h-486px"> <div class="flex flex-col h-486px">
<Tabs defaultActiveKey="1" class="h-100%" @change="tabChange" size="large"> <Tabs defaultActiveKey="1" class="h-100%" @change="tabChange" size="large">
<TabPane key="1" tab="省份"> <TabPane key="1" tab="省份">
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }"> <Table :dataSource="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }" :showSorterTooltip="false">
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> <Column title="排名" dataIndex="rank" />
<a-table-column title="排名" data-index="rank" /> <Column title="省份" dataIndex="geo" />
<a-table-column title="省份" data-index="geo" /> <Column title="分布占比" dataIndex="rate" />
<a-table-column title="分布占比" data-index="rate" /> <Column title="TGI指数" dataIndex="tgi" />
</Table>
<a-table-column title="TGI指数" data-index="tgi" />
</template>
</a-table>
</TabPane> </TabPane>
<TabPane key="2" tab="城市"> <TabPane key="2" tab="城市">
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }"> <Table :dataSource="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }" :showSorterTooltip="false">
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> <Column title="排名" dataIndex="rank" />
<a-table-column title="排名" data-index="rank" /> <Column title="城市" dataIndex="geo" />
<a-table-column title="城市" data-index="geo" /> <Column title="分布占比" dataIndex="rate">
<a-table-column title="分布占比" data-index="rate"> <template #customRender="{ record }">
<template #cell="{ record }"> <span class="cts">{{ (record.rate * 100).toFixed(2) }}%</span>
<span class="cts">{{ (record.rate * 100).toFixed(2) }}%</span> </template>
</template> </Column>
</a-table-column> <Column title="TGI指数" dataIndex="tgi" />
</Table>
<a-table-column title="TGI指数" data-index="tgi" />
</template>
</a-table>
</TabPane> </TabPane>
</Tabs> </Tabs>
</div> </div>
@ -138,8 +132,9 @@ import * as echarts from 'echarts';
import chinaJson from '@/assets/maps/china.json'; import chinaJson from '@/assets/maps/china.json';
echarts.registerMap('china', chinaJson); echarts.registerMap('china', chinaJson);
import { Tabs, Tooltip, Space } from 'ant-design-vue'; import { Tabs, Tooltip, Space, Table } from 'ant-design-vue';
const { TabPane } = Tabs; const { TabPane } = Tabs;
const { Column } = Table;
const scope = ref(1); // 地域范围1-省2-市 const scope = ref(1); // 地域范围1-省2-市
const chartInstance = (ref < echarts.ECharts) | (null > null); const chartInstance = (ref < echarts.ECharts) | (null > null);

View File

@ -4,38 +4,41 @@
<span class="title">账号管理</span> <span class="title">账号管理</span>
<Button type="primary" ghost class="add-account-button" @click="handleAddAccount">添加子账号</Button> <Button type="primary" ghost class="add-account-button" @click="handleAddAccount">添加子账号</Button>
</div> </div>
<a-table <Table
:columns="columns" :dataSource="dataSource"
:data="dataSource"
:pagination="pagination" :pagination="pagination"
:showSorterTooltip="false"
class="mt-8px" class="mt-8px"
@page-change="handlePageChange" @change="handleTableChange"
@page-size-change="handlePageSizeChange"
> >
<template #empty> <Table.Column title="手机号" dataIndex="mobile">
<template #customRender="{ record }">
<div class="flex item-center pt-13px pb-13px">
<span class="mr-4px">{{ record.mobile }}</span>
<a-tag v-if="record.type === 0" class="primary-account">主账号</a-tag>
<a-tag v-else class="sub-account">子账号</a-tag>
</div>
</template>
</Table.Column>
<Table.Column title="操作" dataIndex="action" width="120">
<template #customRender="{ record }">
<Button
v-if="record.type !== 0"
class="delete-button"
size="small"
type="primary"
ghost
danger
@click="openDeleteModal(record)"
>
删除
</Button>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #mobile="{ record }"> </Table>
<div class="flex item-center pt-13px pb-13px">
<span class="mr-4px">{{ record.mobile }}</span>
<a-tag v-if="record.type === 0" class="primary-account">主账号</a-tag>
<a-tag v-else class="sub-account">子账号</a-tag>
</div>
</template>
<template #action="{ record }">
<Button
v-if="record.type !== 0"
class="delete-button"
size="small"
type="primary"
ghost
danger
@click="openDeleteModal(record)"
>
删除
</Button>
</template>
</a-table>
<Modal v-model:open="addAccountVisible" centered width="480px" title="添加子账号" :okText="okText" @ok="handleOk" > <Modal v-model:open="addAccountVisible" centered width="480px" title="添加子账号" :okText="okText" @ok="handleOk" >
<div v-if="canAddAccount" class="add-account-container"> <div v-if="canAddAccount" class="add-account-container">
<h2 class="add-account-title">生成企业专属链接成员通过访问即可注册并加入企业账号</h2> <h2 class="add-account-title">生成企业专属链接成员通过访问即可注册并加入企业账号</h2>
@ -59,9 +62,8 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Container from '@/components/container.vue';
import { ref, onMounted, reactive, computed } from 'vue'; import { ref, onMounted, reactive, computed } from 'vue';
import { Button } from 'ant-design-vue'; import { Button, Table } from 'ant-design-vue';
import { fetchSubAccountPage, removeEnterpriseAccount, getEnterpriseInviteCode } from '@/api/all'; import { fetchSubAccountPage, removeEnterpriseAccount, getEnterpriseInviteCode } from '@/api/all';
import Modal from '@/components/modal.vue'; import Modal from '@/components/modal.vue';
@ -84,10 +86,10 @@ const columns = [
const dataSource = ref([]); const dataSource = ref([]);
const pagination = reactive({ const pagination = reactive({
total: 0, total: 0,
showPageSize: true, showSizeChanger: true,
showTotal: true, showTotal: (total: number, range: [number, number]) => `${total} 条记录`,
defaultCurrent: 1, current: 1,
defaultPageSize: 10, pageSize: 10,
}); });
const params = reactive({ const params = reactive({
@ -122,13 +124,12 @@ const currentSelectAccount = ref();
const { copy, copied, isSupported } = useClipboard({ source: inviteUrl }); const { copy, copied, isSupported } = useClipboard({ source: inviteUrl });
function handlePageChange(current: number) { function handleTableChange(paginationInfo: any, filters: any, sorter: any) {
params.page = current; params.page = paginationInfo.current;
getSubAccount(); params.page_size = paginationInfo.pageSize;
} // 更新分页状态
pagination.current = paginationInfo.current;
function handlePageSizeChange(pageSize: number) { pagination.pageSize = paginationInfo.pageSize;
params.page_size = pageSize;
getSubAccount(); getSubAccount();
} }

View File

@ -3,17 +3,21 @@
<div class="title-row"> <div class="title-row">
<span class="title">企业信息</span> <span class="title">企业信息</span>
</div> </div>
<a-table :columns="columns" :data="dataSource" :pagination="false" class="mt-8px"> <Table :dataSource="dataSource" :pagination="false" :showSorterTooltip="false" class="mt-8px">
<template #empty> <Table.Column title="企业信息" dataIndex="info">
<template #customRender="{ record }">
{{ record.name }}
</template>
</Table.Column>
<Table.Column title="操作" dataIndex="action" width="120">
<template #customRender>
<Button class="edit-button" size="small" type="primary" ghost @click="handleUpdate">修改</Button>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #info="{ record }"> </Table>
{{ record.name }}
</template>
<template #action>
<Button class="edit-button" size="small" type="primary" ghost @click="handleUpdate">修改</Button>
</template>
</a-table>
<Modal <Modal
v-model:open="infoVisible" v-model:open="infoVisible"
width="480px" width="480px"
@ -45,7 +49,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Button, Form, FormItem, Input } from 'ant-design-vue'; import { Button, Form, FormItem, Input, Table } from 'ant-design-vue';
import Container from '@/components/container.vue'; import Container from '@/components/container.vue';
import Modal from '@/components/modal.vue'; import Modal from '@/components/modal.vue';
import { ref, reactive, computed } from 'vue'; import { ref, reactive, computed } from 'vue';

View File

@ -3,22 +3,26 @@
<div class="title-row"> <div class="title-row">
<span class="title">个人信息</span> <span class="title">个人信息</span>
</div> </div>
<a-table :columns="columns" :data="dataSource" :pagination="false" class="mt-8px"> <Table :dataSource="dataSource" :pagination="false" :showSorterTooltip="false" class="mt-8px">
<template #empty> <Table.Column title="用户信息" dataIndex="info">
<template #customRender="{ record }">
<div class="pt-3px pb-3px">
<a-avatar :image-url="record.head_image" :size="32" />
{{ record.name || '-' }}
<icon-edit size="13" class="ml-8px" @click="openEditInfoModal" />
</div>
</template>
</Table.Column>
<Table.Column title="手机号" dataIndex="mobile">
<template #customRender="{ record }">
{{ record.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}
<icon-edit size="13" class="ml-8px" @click="openEditMobileModal" />
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #info="{ record }"> </Table>
<div class="pt-3px pb-3px">
<a-avatar :image-url="record.head_image" :size="32" />
{{ record.name || '-' }}
<icon-edit size="13" class="ml-8px" @click="openEditInfoModal" />
</div>
</template>
<template #mobile="{ record }">
{{ record.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}
<icon-edit size="13" class="ml-8px" @click="openEditMobileModal" />
</template>
</a-table>
<Modal v-model:open="infoVisible" centered title="修改用户信息" @ok="handleSubmitUserInfo"> <Modal v-model:open="infoVisible" centered title="修改用户信息" @ok="handleSubmitUserInfo">
<Form <Form
class="form" class="form"
@ -81,7 +85,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Button, Form, FormItem, Input } from 'ant-design-vue'; import { Button, Form, FormItem, Input, Table } from 'ant-design-vue';
import Container from '@/components/container.vue'; import Container from '@/components/container.vue';
import Modal from '@/components/modal.vue'; import Modal from '@/components/modal.vue';
import PuzzleVerification from '@/views/components/login/components/PuzzleVerification.vue'; import PuzzleVerification from '@/views/components/login/components/PuzzleVerification.vue';

View File

@ -152,7 +152,7 @@ const props = defineProps({
}, },
rowSelection: { rowSelection: {
type: Array, type: Array,
default: () => [], default: () => {},
}, },
selectedRowKeys: { selectedRowKeys: {
type: Array, type: Array,
@ -175,6 +175,7 @@ const handleTableChange = (pagination, filters, sorter) => {
const rowSelection = { const rowSelection = {
selectedRowKeys: computed(() => props.selectedRowKeys), selectedRowKeys: computed(() => props.selectedRowKeys),
onSelect: (record, selected, selectedRows, nativeEvent) => { onSelect: (record, selected, selectedRows, nativeEvent) => {
console.log(selectedRows, record);
emits( emits(
'select', 'select',
selectedRows.map((row) => row.id), selectedRows.map((row) => row.id),

View File

@ -1,5 +1,5 @@
export const INITIAL_FORM = { export const INITIAL_FORM = {
audit_status: '', audit_status: undefined,
sort_column: undefined, sort_column: undefined,
sort_order: undefined, sort_order: undefined,
}; };

View File

@ -1,6 +1,5 @@
<script lang="jsx"> <script lang="jsx">
import { Button, Modal, Tooltip } from 'ant-design-vue'; import { Button, Modal, Tooltip, Table, Pagination } from 'ant-design-vue';
import { Table, TableColumn, Pagination } from '@arco-design/web-vue';
import CommonSelect from '@/components/common-select'; import CommonSelect from '@/components/common-select';
import TextOverTips from '@/components/text-over-tips'; import TextOverTips from '@/components/text-over-tips';
import ShareModal from '@/views/material-center/components/finished-products/manuscript/components/share-manuscript-modal/share-modal'; import ShareModal from '@/views/material-center/components/finished-products/manuscript/components/share-manuscript-modal/share-modal';
@ -112,10 +111,12 @@ export default {
const onShare = () => { const onShare = () => {
shareModalRef.value?.open(selectedRowKeys.value); shareModalRef.value?.open(selectedRowKeys.value);
}; };
const handleSorterChange = (column, order) => { const handleSorterChange = (pagination, filters, sorter) => {
query.value.sort_column = column; if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc'; query.value.sort_column = sorter.columnKey;
reload(); query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
reload();
}
}; };
const getStatusInfo = (audit_status) => { const getStatusInfo = (audit_status) => {
return CHECK_STATUS.find((v) => v.id === audit_status) ?? {}; return CHECK_STATUS.find((v) => v.id === audit_status) ?? {};
@ -163,109 +164,100 @@ export default {
</div> </div>
<Table <Table
ref={tableRef} ref={tableRef}
data={dataSource.value} dataSource={dataSource.value}
row-key="id" rowKey="id"
column-resizable rowSelection={{
row-selection={rowSelection.value} selectedRowKeys: selectedRowKeys.value,
selected-keys={selectedRowKeys.value} onChange: (keys, rows) => handleSelect(keys, rows),
onSelectAll: (selected, rows, changeRows) => handleSelectAll(selected, rows, changeRows),
}}
pagination={false} pagination={false}
scroll={{ x: '100%', y: '100%' }} scroll={{ x: '100%', y: '100%' }}
class="overflow-hidden" class="overflow-hidden"
bordered bordered
onSorterChange={handleSorterChange} showSorterTooltip={false}
onSelect={handleSelect} onChange={handleSorterChange}
onSelectAll={handleSelectAll}
v-slots={{ v-slots={{
empty: () => <NoData />, emptyText: () => <NoData />,
columns: () => (
<>
{TABLE_COLUMNS.map((column) => (
<TableColumn
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
v-slots={{
title: () => (
<div class="flex items-center">
<span class="cts mr-4px bold color-#211F24">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
),
cell: ({ record }) => {
if (column.dataIndex === 'audit_status') {
return (
<div
class="flex items-center w-fit h-24px px-8px rounded-2px"
style={{ backgroundColor: getStatusInfo(record.audit_status).backgroundColor }}
>
<span class="cts s1 bold" style={{ color: getStatusInfo(record.audit_status).color }}>
{getStatusInfo(record.audit_status).name}
</span>
</div>
);
} else if (column.dataIndex === 'title') {
return <TextOverTips context={record.title} />;
} else if (column.dataIndex === 'type') {
return (
<div class="flex items-center">
<img
src={record.type === EnumManuscriptType.Image ? icon2 : icon3}
width="16"
height="16"
class="mr-4px"
/>
<span
class={`cts !text-14px !lh-22px ${
record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'
}`}
>
{record.type === EnumManuscriptType.Image ? '图文' : '视频'}
</span>
</div>
);
} else if (column.dataIndex === 'last_modified_at') {
return (
<span class="cts num">
{exactFormatTime(
record.last_modified_at,
'YYYY-MM-DD HH:mm:ss',
'YYYY-MM-DD HH:mm:ss',
)}
</span>
);
} else {
return formatTableField(column, record, true);
}
},
}}
/>
))}
</>
),
}} }}
/> >
{TABLE_COLUMNS.map((column) => (
<Table.Column
key={column.dataIndex}
dataIndex={column.dataIndex}
fixed={column.fixed}
width={column.width}
minWidth={column.minWidth}
sorter={column.sortable}
align={column.align}
ellipsis
title={() => (
<div class="flex items-center">
<span class="cts mr-4px bold color-#211F24">{column.title}</span>
{column.tooltip && (
<Tooltip title={column.tooltip} placement="top">
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
</Tooltip>
)}
</div>
)}
customRender={({ record }) => {
if (column.dataIndex === 'audit_status') {
return (
<div
class="flex items-center w-fit h-24px px-8px rounded-2px"
style={{ backgroundColor: getStatusInfo(record.audit_status).backgroundColor }}
>
<span class="cts s1 bold" style={{ color: getStatusInfo(record.audit_status).color }}>
{getStatusInfo(record.audit_status).name}
</span>
</div>
);
} else if (column.dataIndex === 'title') {
return <TextOverTips context={record.title} class="!text-12px" />;
} else if (column.dataIndex === 'type') {
return (
<div class="flex items-center">
<img
src={record.type === EnumManuscriptType.Image ? icon2 : icon3}
width="16"
height="16"
class="mr-4px"
/>
<span
class={`cts !text-14px !lh-22px ${
record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'
}`}
>
{record.type === EnumManuscriptType.Image ? '图文' : '视频'}
</span>
</div>
);
} else if (column.dataIndex === 'last_modified_at') {
return (
<span class="cts num">
{exactFormatTime(record.last_modified_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss')}
</span>
);
} else {
return formatTableField(column, record, true);
}
}}
/>
))}
</Table>
{pageInfo.value.total > 0 && ( {pageInfo.value.total > 0 && (
<div class="flex justify-end mt-16px"> <div class="flex justify-end mt-16px">
<Pagination <Pagination
total={pageInfo.value.total} total={pageInfo.value.total}
size="mini" size="small"
show-total showTotal={(total, range) => `${total} 条记录`}
show-jumper showQuickJumper
show-page-size showSizeChanger
current={pageInfo.value.page} current={pageInfo.value.page}
page-size={pageInfo.value.page_size} pageSize={pageInfo.value.page_size}
onChange={onPageChange} onChange={onPageChange}
onPageSizeChange={onPageSizeChange} onShowSizeChange={onPageSizeChange}
/> />
</div> </div>
)} )}

View File

@ -23,44 +23,31 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
.arco-scrollbar-track { .ant-scrollbar-track {
display: none !important; display: none !important;
} }
.arco-table { .ant-table {
.arco-table-container { .ant-table-thead {
.arco-table-element { .ant-table-cell {
thead { padding: 10px 16px !important;
.arco-table-tr { }
.arco-table-th { }
.arco-table-cell { .ant-table-body {
padding: 10px 16px !important; .ant-table-cell {
} padding: 6px 16px;
} .ant-table-cell-content {
} font-size: 12px;
} line-height: 20px;
tbody {
.arco-table-tr {
.arco-table-td {
.arco-table-cell {
padding: 6px 16px;
.arco-table-cell-content,
.arco-table-td-content {
font-size: 12px;
line-height: 20px;
}
}
}
}
} }
} }
} }
} }
.arco-pagination { .ant-pagination {
.arco-pagination-total, .ant-pagination-total-text,
.arco-pagination-jumper-prepend { .ant-pagination-options-quick-jumper {
font-size: 14px; font-size: 14px;
} }
.arco-pagination-jumper-prepend { .ant-pagination-options-quick-jumper {
font-family: $font-family-regular; font-family: $font-family-regular;
} }
} }

View File

@ -2,7 +2,7 @@ export const INITIAL_QUERY = {
title: '', title: '',
// project_ids: [], // project_ids: [],
uid: '', uid: '',
audit_status: '', audit_status: undefined,
created_at: [], created_at: [],
sort_column: undefined, sort_column: undefined,
sort_order: undefined, sort_order: undefined,
@ -11,13 +11,13 @@ export const INITIAL_QUERY = {
export enum EnumCheckStatus { export enum EnumCheckStatus {
All = '', All = '',
Wait = 1, Wait = 1,
Checking = 2, Checking = 2,
Passed = 3, Passed = 3,
} }
export enum EnumManuscriptType { export enum EnumManuscriptType {
All = '', All = '',
Image = 0, Image = 0,
Video = 1, Video = 1,
} }
export const CHECK_STATUS = [ export const CHECK_STATUS = [
@ -25,19 +25,19 @@ export const CHECK_STATUS = [
name: '待审核', name: '待审核',
id: EnumCheckStatus.Wait, id: EnumCheckStatus.Wait,
backgroundColor: '#F2F3F5', backgroundColor: '#F2F3F5',
color: '#3C4043' color: '#3C4043',
}, },
{ {
name: '审核中', name: '审核中',
id: EnumCheckStatus.Checking, id: EnumCheckStatus.Checking,
backgroundColor: '#FFF7E5', backgroundColor: '#FFF7E5',
color: '#FFAE00' color: '#FFAE00',
}, },
{ {
name: '已通过', name: '已通过',
id: EnumCheckStatus.Passed, id: EnumCheckStatus.Passed,
backgroundColor: '#EBF7F2', backgroundColor: '#EBF7F2',
color: '#25C883' color: '#25C883',
}, },
]; ];
export const MANUSCRIPT_TYPE = [ export const MANUSCRIPT_TYPE = [
@ -53,4 +53,4 @@ export const MANUSCRIPT_TYPE = [
label: '视频', label: '视频',
value: EnumManuscriptType.Video, value: EnumManuscriptType.Video,
}, },
] ];

View File

@ -27,7 +27,7 @@
</template> </template>
<script lang="jsx" setup> <script lang="jsx" setup>
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Button } from '@arco-design/web-vue'; import { Button, Pagination } from '@arco-design/web-vue';
import FilterBlock from './components/filter-block'; import FilterBlock from './components/filter-block';
import ManuscriptTable from './components/manuscript-table'; import ManuscriptTable from './components/manuscript-table';
import DeleteManuscriptModal from './components/manuscript-table/delete-manuscript-modal.vue'; import DeleteManuscriptModal from './components/manuscript-table/delete-manuscript-modal.vue';

View File

@ -1,90 +1,85 @@
<template> <template>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="flex-1 manuscript-table w-100%" class="flex-1 manuscript-table w-100%"
bordered bordered
:row-selection="rowSelection" :rowSelection="rowSelection"
:selected-row-keys="selectedRowKeys" :showSorterTooltip="false"
@sorter-change="handleSorterChange" @change="handleTableChange"
@select="(selectedKeys, rowKeyValue, record) => emits('select', selectedKeys, rowKeyValue, record)"
@select-all="(check) => emits('selectAll', check)"
> >
<template #empty> <template #emptyText>
<NoData text="暂无文件" /> <NoData text="暂无文件" />
</template> </template>
<template #columns> <Column
<a-table-column v-for="column in tableColumns"
v-for="column in tableColumns" :key="column.dataIndex"
:key="column.dataIndex" :dataIndex="column.dataIndex"
:data-index="column.dataIndex" :fixed="column.fixed"
:fixed="column.fixed" :width="column.width"
:width="column.width" :minWidth="column.minWidth"
:min-width="column.minWidth" :sorter="column.sortable"
:sortable="column.sortable" :align="column.align"
:align="column.align" :ellipsis="true"
ellipsis >
tooltip <template #title>
> <div class="flex items-center">
<template #title> <span class="cts mr-4px">{{ column.title }}</span>
<div class="flex items-center"> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<span class="cts mr-4px">{{ column.title }}</span> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top"> </Tooltip>
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> </div>
</Tooltip> </template>
<template v-if="column.dataIndex === 'name'" #customRender="{ record }">
<div class="flex items-center">
<HoverImagePreview :src="record.cover">
<Image :width="64" :height="64" :src="record.cover" class="!rounded-8px mr-16px" :preview="false">
<template #error>
<img :src="icon4" class="w-full h-full" />
</template>
</Image>
</HoverImagePreview>
<div class="flex-1 flex flex-col overflow-hidden">
<TextOverTips :context="record.name" :line="1" class="cts mb-4px regular" />
<TextOverTips :context="`序号:${record.uid}`" :line="1" class="cts !color-#737478 regular" />
</div> </div>
</template> </div>
<template v-if="column.dataIndex === 'name'" #cell="{ record }"> </template>
<div class="flex items-center"> <template v-else-if="column.dataIndex === 'type'" #customRender="{ record }">
<HoverImagePreview :src="record.cover"> {{ TABS_LIST.find((item) => item.value === record.type)?.label ?? '-' }}
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-8px mr-16px" fit="cover"> </template>
<template #error> <template v-else-if="column.dataIndex === 'size'" #customRender="{ record }">
<img :src="icon4" class="w-full h-full" /> <span class="cts num">{{ formatFileSize(record.size) }}</span>
</template> </template>
</a-image> <template v-else-if="column.dataIndex === 'origin'" #customRender="{ record }">
</HoverImagePreview> {{ ORIGIN_LIST.find((item) => item.value === record.origin)?.label ?? '-' }}
<div class="flex-1 flex flex-col overflow-hidden"> </template>
<TextOverTips :context="record.name" :line="1" class="cts mb-4px regular" /> <template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #customRender="{ record }">
<TextOverTips :context="`序号:${record.uid}`" :line="1" class="cts !color-#737478 regular" /> {{ record[column.dataIndex].name || record[column.dataIndex].mobile }}
</div> </template>
</div> <template #customRender="{ record }" v-else-if="['created_at'].includes(column.dataIndex)">
</template> <span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span>
<template v-else-if="column.dataIndex === 'type'" #cell="{ record }"> </template>
{{ TABS_LIST.find((item) => item.value === record.type)?.label ?? '-' }} <template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
</template> <div class="flex items-center">
<template v-else-if="column.dataIndex === 'size'" #cell="{ record }"> <img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
<span class="cts num">{{ formatFileSize(record.size) }}</span> <Button type="primary" ghost size="small" @click="onDownload(record)">下载</Button>
</template> </div>
<template v-else-if="column.dataIndex === 'origin'" #cell="{ record }"> </template>
{{ ORIGIN_LIST.find((item) => item.value === record.origin)?.label ?? '-' }} <template v-else #customRender="{ record }">
</template> {{ formatTableField(column, record, true) }}
<template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #cell="{ record }"> </template>
{{ record[column.dataIndex].name || record[column.dataIndex].mobile }} </Column>
</template> </Table>
<template #cell="{ record }" v-else-if="['created_at'].includes(column.dataIndex)">
<span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span>
</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)" />
<Button type="primary" ghost size="small" @click="onDownload(record)">下载</Button>
</div>
</template>
<template v-else #cell="{ record }">
{{ formatTableField(column, record, true) }}
</template>
</a-table-column>
</template>
</a-table>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref, computed } from 'vue';
import { Button, Tooltip } from 'ant-design-vue'; import { Button, Tooltip, Table, Image } from 'ant-design-vue';
const { Column } = Table;
import { formatTableField, exactFormatTime, formatFileSize, downloadByUrl } from '@/utils/tools'; import { formatTableField, exactFormatTime, formatFileSize, downloadByUrl } from '@/utils/tools';
import { slsWithCatch } from '@/utils/stroage.ts'; import { slsWithCatch } from '@/utils/stroage.ts';
import { TABS_LIST, ORIGIN_LIST } from '../../constants'; import { TABS_LIST, ORIGIN_LIST } from '../../constants';
@ -122,8 +117,20 @@ const props = defineProps({
const tableRef = ref(null); const tableRef = ref(null);
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc'); if (sorter && sorter.field) {
emits('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
}
};
const rowSelection = {
selectedRowKeys: computed(() => props.selectedRowKeys),
onSelect: (record, selected, selectedRows, nativeEvent) => {
emits('select', selectedRows.map(row => row.id), record.id, record);
},
onSelectAll: (selected, selectedRows, changeRows) => {
emits('selectAll', selected);
},
}; };
const onDelete = (item) => { const onDelete = (item) => {
emits('delete', item); emits('delete', item);

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="brand-wrap"> <div class="brand-wrap">
<div class="filter-wrap bg-#fff rounded-8px "> <div class="filter-wrap bg-#fff rounded-8px">
<div class="top flex h-64px px-24px py-10px justify-between items-center"> <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> <p class="text-18px font-400 lh-26px color-#211F24 title">品牌物料</p>
<div class="flex items-center"> <div class="flex items-center">
@ -23,13 +23,13 @@
<div class="filter-row flex"> <div class="filter-row flex">
<Button type="outline" ghost class="mr-12px" @click="handleSearch"> <Button type="outline" ghost class="mr-12px" @click="handleSearch">
<template #icon> <template #icon>
<icon-search class="mr-8px"/> <icon-search class="mr-8px" />
</template> </template>
<template #default>搜索</template> <template #default>搜索</template>
</Button> </Button>
<Button @click="handleReset"> <Button @click="handleReset">
<template #icon> <template #icon>
<icon-refresh class="mr-8px"/> <icon-refresh class="mr-8px" />
</template> </template>
<template #default>重置</template> <template #default>重置</template>
</Button> </Button>
@ -46,7 +46,15 @@
<template #title> <template #title>
<span class="modal-title">{{ form.id > 0 ? '编辑品牌' : '添加品牌' }}</span> <span class="modal-title">{{ form.id > 0 ? '编辑品牌' : '添加品牌' }}</span>
</template> </template>
<Form :model="form" :rules="formRule" ref="formRef" layout="horizontal" labelAlign="right" :labelCol="{ span: 3 }" :wrapperCol="{ span: 21 }"> <Form
:model="form"
:rules="formRule"
ref="formRef"
layout="horizontal"
labelAlign="right"
:labelCol="{ span: 4 }"
:wrapperCol="{ span: 20 }"
>
<FormItem name="name" label="品牌名称"> <FormItem name="name" label="品牌名称">
<Input v-model:value="form.name" class="h-36px" placeholder="请输入..." /> <Input v-model:value="form.name" class="h-36px" placeholder="请输入..." />
</FormItem> </FormItem>
@ -72,40 +80,32 @@
</Modal> </Modal>
</div> </div>
</div> </div>
<div <div class="table-wrap bg-#fff rounded-8px px-24px py-24px flex flex-col">
class="table-wrap bg-#fff rounded-8px px-24px py-24px flex flex-col" <Table :dataSource="tableData" ref="tableRef" :pagination="false" :showSorterTooltip="false">
> <Table.Column title="品牌名称" dataIndex="name" />
<a-table :data="tableData" ref="tableRef" :pagination="false"> <Table.Column title="品牌logo" dataIndex="logo">
<template #columns> <template #customRender="{ record }">
<a-table-column title="品牌名称" data-index="name" /> <img :src="record.logo" style="width: 50px; height: 50px" />
<a-table-column title="品牌logo" data-index="logo"> </template>
<template #cell="{ record }"> </Table.Column>
<img :src="record.logo" style="width: 50px; height: 50px" /> <Table.Column title="Slogan" dataIndex="slogan" />
</template> <Table.Column :width="80" title="操作" dataIndex="optional">
</a-table-column> <template #customRender="{ record }">
<a-table-column title="Slogan" data-index="slogan" /> <Space>
<a-table-column width="150" min-widht="150" title="操作" data-index="optional"> <a-popconfirm
<template #cell="{ record }"> content="确定删除吗?"
<Space size="medium"> type="warning"
<Space> ok-text="确认删除"
<a-popconfirm cancel-text="取消"
content="确定删除吗?" @ok="deleteBrand(record.id)"
type="warning" >
ok-text="确认删除" <icon-delete></icon-delete>
cancel-text="取消" </a-popconfirm>
@ok="deleteBrand(record.id)" <Button type="outline" class="edit-btn" size="small" @click="handleEdit(record.id)">编辑</Button>
> </Space>
<icon-delete></icon-delete> </template>
</a-popconfirm> </Table.Column>
</Space> </Table>
<Space>
<Button type="outline" class="edit-btn" size="small" @click="handleEdit(record.id)">编辑</Button>
</Space>
</Space>
</template>
</a-table-column>
</template>
</a-table>
<div class="pagination-row"> <div class="pagination-row">
<a-pagination <a-pagination
@ -128,7 +128,7 @@
import { ref, computed, reactive, onMounted } from 'vue'; import { ref, computed, reactive, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { IconDelete } from '@arco-design/web-vue/es/icon'; import { IconDelete } from '@arco-design/web-vue/es/icon';
import { Button, Modal, Space, Form, FormItem, Input } from 'ant-design-vue'; import { Button, Modal, Space, Form, FormItem, Input, Table } from 'ant-design-vue';
const { TextArea } = Input; const { TextArea } = Input;
import { import {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="business-wrap"> <div class="business-wrap">
<div class="filter-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid"> <div class="filter-wrap bg-#fff rounded-8px">
<div class="top flex h-64px px-24px py-10px justify-between items-center"> <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> <p class="text-18px font-400 lh-26px color-#211F24 title">业务洞察报告</p>
</div> </div>
@ -40,9 +40,18 @@
</div> </div>
</div> </div>
<div <div
class="table-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid px-24px py-24px flex-1 flex flex-col" class="table-wrap bg-#fff rounded-8px px-24px py-24px flex-1 flex flex-col"
> >
<a-table :columns="columns" :data="tableData" @change="handleChange" :pagination="false"> </a-table> <Table :dataSource="tableData" :pagination="false" :showSorterTooltip="false" @change="handleChange">
<Table.Column
v-for="column in columns"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:minWidth="column.minWidth"
/>
</Table>
<div class="pagination-row"> <div class="pagination-row">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -62,7 +71,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { Button, Input, Space } from 'ant-design-vue'; import { Button, Input, Space, Table } from 'ant-design-vue';
const pageInfo = reactive({ const pageInfo = reactive({
page: 1, page: 1,
@ -86,8 +95,8 @@ const columns = [
title: '服务/产品', title: '服务/产品',
dataIndex: 'service_name', dataIndex: 'service_name',
slotName: 'rank', slotName: 'rank',
width: 60, width: 100,
minWidth: 60, minWidth: 100,
}, },
{ {
title: '生成日期', title: '生成日期',

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="competitive-wrap"> <div class="competitive-wrap">
<div class="filter-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid"> <div class="filter-wrap bg-#fff rounded-8px ">
<div class="top flex h-64px px-24px py-10px justify-between items-center"> <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> <p class="text-18px font-400 lh-26px color-#211F24 title">竞品对比报告</p>
</div> </div>
@ -9,8 +9,8 @@
<div class="filter-row flex mb-20px"> <div class="filter-row flex mb-20px">
<div class="filter-row-item flex items-center"> <div class="filter-row-item flex items-center">
<span class="label">服务/产品</span> <span class="label">服务/产品</span>
<Space size="medium"> <Space>
<Input v-model:value="query.name" class="w-240px" placeholder="请搜索..." size="medium" allowClear> <Input v-model:value="query.name" class="w-240px" placeholder="请搜索..." allowClear>
<template #prefix> <template #prefix>
<icon-search /> <icon-search />
</template> </template>
@ -42,9 +42,46 @@
</div> </div>
</div> </div>
<div <div
class="table-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid px-24px py-24px flex-1 flex flex-col" class="table-wrap bg-#fff rounded-8px px-24px py-24px flex-1 flex flex-col"
> >
<a-table :columns="columns" :data="tableData" @change="handleChange" :pagination="false"> </a-table> <Table :dataSource="tableData" :pagination="false" :showSorterTooltip="false" @change="handleChange">
<Table.Column
title="服务/产品"
dataIndex="service_name"
:width="60"
:minWidth="60"
/>
<Table.Column
title="生成日期"
dataIndex="create_time"
:width="120"
:minWidth="120"
/>
<Table.Column
:width="180"
:minWidth="180"
title="竞争对手"
dataIndex="customer"
/>
<Table.Column
title="最后更新日期"
dataIndex="volumeRate"
:width="180"
:minWidth="180"
/>
<Table.Column
title="操作人"
dataIndex="operator"
:width="120"
:minWidth="120"
/>
<Table.Column
title="操作"
dataIndex="operator"
:width="120"
:minWidth="120"
/>
</Table>
<div class="pagination-row"> <div class="pagination-row">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -63,7 +100,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Button, Input, Space } from 'ant-design-vue'; import { Button, Input, Space, Table } from 'ant-design-vue';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
const pageInfo = reactive({ const pageInfo = reactive({
@ -80,50 +117,7 @@ const onPageChange = () => {};
const onPageSizeChange = () => {}; const onPageSizeChange = () => {};
const tableData = ref([]); const tableData = ref([]);
const columns = [
{
title: '服务/产品',
dataIndex: 'service_name',
slotName: 'rank',
width: 60,
minWidth: 60,
},
{
title: '生成日期',
dataIndex: 'create_time',
width: 120,
minWidth: 120,
},
{
titleSlotName: 'customer',
width: 180,
minWidth: 180,
title: '竞争对手',
dataIndex: 'customer',
slotName: 'hot',
},
{
titleSlotName: 'lasterUpdateTime',
title: '最后更新日期',
dataIndex: 'volumeRate',
width: 180,
minWidth: 180,
slotName: 'lasterUpdateTime',
},
{
title: '操作人',
dataIndex: 'operator',
width: 120,
minWidth: 120,
},
{
title: '操作',
dataIndex: 'operator',
width: 120,
minWidth: 120,
},
];
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -28,127 +28,122 @@
</div> </div>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable :rowSelection="{
:row-selection="{
type: 'checkbox', type: 'checkbox',
showCheckedAll: true, selectedRowKeys: selectedItems,
width: 48, onSelect: handleSelect,
onSelectAll: handleSelectAll,
}" }"
:selected-keys="selectedItems"
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="account-table w-100% flex-1" class="account-table w-100% flex-1"
bordered bordered
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@select="handleSelect" @change="handleTableChange"
@select-all="handleSelectAll"
> >
<template #empty> <template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> <Column
<a-table-column v-for="column in tableColumns"
v-for="column in tableColumns" :key="column.dataIndex"
:key="column.dataIndex" :dataIndex="column.dataIndex"
:data-index="column.dataIndex" :fixed="column.fixed"
:fixed="column.fixed" :width="column.width"
:width="column.width" :minWidth="column.minWidth"
:min-width="column.minWidth" :sorter="column.sortable"
:sortable="column.sortable" :align="column.align"
:align="column.align" :ellipsis="true"
ellipsis >
tooltip <template #title>
> <div class="flex items-center">
<template #title> <img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" />
<div class="flex items-center"> <span class="cts mr-4px">{{ column.title }}</span>
<img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" /> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<span class="cts mr-4px">{{ column.title }}</span> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<Tooltip v-if="column.tooltip" :title="column.tooltip" position="top"> </Tooltip>
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> </div>
</Tooltip> </template>
</div>
</template>
<template v-if="column.dataIndex === 'platform'" #cell="{ record }"> <template v-if="column.dataIndex === 'platform'" #customRender="{ record }">
<img :src="getMediaAccountPlatformLogo(record.platform)" width="20" height="20" class="rounded-4px" /> <img :src="getMediaAccountPlatformLogo(record.platform)" width="20" height="20" class="rounded-4px" />
</template> </template>
<template v-else-if="column.dataIndex === 'status'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'status'" #customRender="{ record }">
<StatusBox :item="record" class="w-fit h-28px" /> <StatusBox :item="record" class="w-fit h-28px" />
</template> </template>
<template v-else-if="column.dataIndex === 'ai_evaluate'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'ai_evaluate'" #customRender="{ record }">
<div class="ai-evaluation-row flex"> <div class="ai-evaluation-row flex">
<template v-if="record.ai_evaluate"> <template v-if="record.ai_evaluate">
<img <img
width="16" width="16"
height="16" height="16"
:src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4" :src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4"
class="mr-8px icon" class="mr-8px icon"
/> />
<div> <div>
<p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p> <p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p>
<p class="cts text-12px lh-20px !color-#939499"> <p class="cts text-12px lh-20px !color-#939499">
{{ {{
`观看: ${record[`${getPropPrefix(dateType)}view_rate`]}% 点赞: ${ `观看: ${record[`${getPropPrefix(dateType)}view_rate`]}% 点赞: ${
record[`${getPropPrefix(dateType)}like_rate`] record[`${getPropPrefix(dateType)}like_rate`]
}%` }%`
}} }}
</p> </p>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<p class="cts">-</p> <p class="cts">-</p>
</template> </template>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'like_collect_number'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'like_collect_number'" #customRender="{ record }">
{{ {{
formatNumberShow({ formatNumberShow({
value: `${record[`${getPropPrefix(dateType)}like_number`] ?? 0} + ${ value: `${record[`${getPropPrefix(dateType)}like_number`] ?? 0} + ${
record[`${getPropPrefix(dateType)}collect_number`] ?? 0 record[`${getPropPrefix(dateType)}collect_number`] ?? 0
}`, }`,
showExactValue: true, showExactValue: true,
}) })
}} }}
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
<Button type="outline" size="small" @click="handleDetail(record)">详情</Button> <Button type="outline" size="small" @click="handleDetail(record)">详情</Button>
</template> </template>
<template v-else-if="column.isRateField" #cell="{ record }"> <template v-else-if="column.isRateField" #customRender="{ record }">
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'"> <div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'">
<icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" /> <icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" /> <icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }} {{ formatTableField(column, record) }}
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'newest_work_title'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'newest_work_title'" #customRender="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFE">{{ record.newest_work_title }}</p> <p class="cts cursor-pointer hover:!color-#6D4CFE">{{ record.newest_work_title }}</p>
<p class="cts text-12px lh-20px !color-#939499"> <p class="cts text-12px lh-20px !color-#939499">
{{ exactFormatTime(record.newest_work_published_at) }} {{ exactFormatTime(record.newest_work_published_at) }}
</p> </p>
</template> </template>
<template v-else-if="column.dataIndex === 'second_new_work_title'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'second_new_work_title'" #customRender="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFE">{{ record.second_new_work_title }}</p> <p class="cts cursor-pointer hover:!color-#6D4CFE">{{ record.second_new_work_title }}</p>
<p class="cts text-12px lh-20px !color-#939499"> <p class="cts text-12px lh-20px !color-#939499">
{{ exactFormatTime(record.second_new_work_published_at) }} {{ exactFormatTime(record.second_new_work_published_at) }}
</p> </p>
</template> </template>
<template v-else #cell="{ record }"> <template v-else #customRender="{ record }">
{{ formatTableField(column, record, true) }} {{ formatTableField(column, record, true) }}
</template> </template>
</a-table-column> </Column>
<a-table-column data-index="operation" fixed="right" width="100" title="操作"> <Column dataIndex="operation" fixed="right" :width="100" title="操作">
<template #cell="{ record }"> <template #customRender="{ record }">
<Button type="outline" size="small" @click="handleDetail(record)">详情</Button> <Button type="outline" size="small" @click="handleDetail(record)">详情</Button>
</template> </template>
</a-table-column> </Column>
</template> </Table>
</a-table>
<CustomTableColumnModal <CustomTableColumnModal
ref="customTableColumnModalRef" ref="customTableColumnModalRef"
@ -161,7 +156,8 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Checkbox, Button, Tooltip } from 'ant-design-vue'; import { Checkbox, Button, Tooltip, Table } from 'ant-design-vue';
const { Column } = Table;
import { getCustomColumns } from '@/api/all/common'; import { getCustomColumns } from '@/api/all/common';
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx'; import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
@ -220,30 +216,34 @@ const tableColumns = computed(() => {
return _result; return _result;
}); });
const handleSelectAll = (checked) => {
if (checked) {
selectedItems.value = props.dataSource.map((item) => item.id);
} else {
selectedItems.value = [];
}
emit('selectionChange', checked ? selectedItems.value : []);
};
const handleDetail = (record) => { const handleDetail = (record) => {
router.push(`/media-account/detail/${record.id}?type=${dateType.value}`); router.push(`/media-account/detail/${record.id}?type=${dateType.value}`);
}; };
// 处理排序变化 // 处理表格变化
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
console.log(column, order); if (sorter && sorter.field) {
emit('sorterChange', column, order === 'ascend' ? 'asc' : 'desc'); emit('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
}
}; };
const handleSelect = (selectedRowKeys, selectedRows) => { const handleSelect = (record, selected, selectedRows, nativeEvent) => {
selectedItems.value = selectedRowKeys; selectedItems.value = selectedRows.map(row => row.id);
emit('selectionChange', selectedRows); emit('selectionChange', selectedRows);
}; };
const handleSelectAll = (selected, selectedRows, changeRows) => {
if (selected) {
selectedItems.value = props.dataSource.map((item) => item.id);
emit('selectionChange', props.dataSource);
} else {
selectedItems.value = [];
emit('selectionChange', []);
}
};
const handleExport = () => { const handleExport = () => {
emit('export'); emit('export');
}; };

View File

@ -45,54 +45,50 @@
<template #default>重置</template> <template #default>重置</template>
</Button> </Button>
</div> </div>
<a-table <Table
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="w-100%" class="w-100%"
bordered bordered
column-resizable :showSorterTooltip="false"
> >
<template #empty> <Table.Column
v-for="column in TABLE_COLUMNS"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #customRender="{ record }">
<template v-if="column.dataIndex === 'published_at'">
{{ exactFormatTime(record.published_at) }}
</template>
<template v-else-if="column.dataIndex === 'exposure_number'">
{{ formatNumberShow({ value: record.view_number * 10, showExactValue: true }) }}
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> </Table>
<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>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #cell="{ record }">
<template v-if="column.dataIndex === 'published_at'">
{{ exactFormatTime(record.published_at) }}
</template>
<template v-else-if="column.dataIndex === 'exposure_number'">
{{ formatNumberShow({ value: record.view_number * 10, showExactValue: true }) }}
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</a-table-column>
</template>
</a-table>
<div v-if="pageInfo.total > 0" class="pagination-row mb-24px"> <div v-if="pageInfo.total > 0" class="pagination-row mb-24px">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -110,7 +106,7 @@
</template> </template>
<script setup> <script setup>
import { Button, Input, Tooltip } from 'ant-design-vue'; import { Button, Input, Tooltip, Table } from 'ant-design-vue';
import { TABLE_COLUMNS, INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants'; import { TABLE_COLUMNS, INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { formatTableField, exactFormatTime, formatNumberShow } from '@/utils/tools'; import { formatTableField, exactFormatTime, formatNumberShow } from '@/utils/tools';

View File

@ -53,7 +53,7 @@
</template> </template>
<template v-else-if="column.dataIndex === 'group_id'"> <template v-else-if="column.dataIndex === 'group_id'">
<div class="flex items-center w-100%"> <div class="flex items-center w-100%">
<CommonSelect v-model="record.group_id" :options="groupOptions" :multiple="false" /> <CommonSelect v-model="record.group_id" :options="groupOptions" :multiple="false" class="w-full" />
</div> </div>
</template> </template>
</template> </template>

View File

@ -47,31 +47,29 @@
<!-- 分别编辑 --> <!-- 分别编辑 -->
<template v-if="editType === 'each'"> <template v-if="editType === 'each'">
<a-table :data="accountTagList" :pagination="false" row-key="id" class="w-100%" column-resizable> <Table :dataSource="accountTagList" :pagination="false" rowKey="id" class="w-100%">
<template #columns> <Table.Column title="账号名称" dataIndex="name" :width="200">
<a-table-column title="账号名称" data-index="name" width="200"> <template #customRender="{ record }">
<template #cell="{ record }"> <span>{{ record.name || '-' }}</span>
<span>{{ record.name || '-' }}</span> </template>
</template> </Table.Column>
</a-table-column> <Table.Column title="选择标签" dataIndex="tags">
<a-table-column title="选择标签" data-index="tags"> <template #customRender="{ record, index }">
<template #cell="{ record, rowIndex }"> <div class="flex items-center w-100%">
<div class="flex items-center w-100%"> <Select
<Select v-model:value="record.tags"
v-model:value="record.tags" :options="tagOptions"
:options="tagOptions" mode="tags"
mode="tags" placeholder="请输入标签,回车键可直接添加..."
placeholder="请输入标签,回车键可直接添加..." :max-tag-count="5"
:max-tag-count="5" class="!w-full"
style="width: 90%" @search="(val) => handleCreateTag(val, index)"
@search="(val) => handleCreateTag(val, rowIndex)" />
/> <span class="tag-count ml-8px">{{ record.tags.length }}/5</span>
<span class="tag-count ml-8px">{{ record.tags.length }}/5</span> </div>
</div> </template>
</template> </Table.Column>
</a-table-column> </Table>
</template>
</a-table>
</template> </template>
</Form> </Form>
<template #footer> <template #footer>
@ -83,7 +81,7 @@
<script setup> <script setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { Button, Modal, Form, FormItem,Tooltip, RadioGroup, Radio, Select } from 'ant-design-vue'; import { Button, Form, FormItem, Modal, Radio, RadioGroup, Select, Table, Tooltip } from 'ant-design-vue';
import { fetchAccountTags, batchPutTag } from '@/api/all/propertyMarketing'; import { fetchAccountTags, batchPutTag } from '@/api/all/propertyMarketing';
import icon1 from '@/assets/img/icon-question.png'; import icon1 from '@/assets/img/icon-question.png';

View File

@ -32,18 +32,46 @@
</Button> </Button>
</div> </div>
<a-table <Table
column-resizable :dataSource="list"
:columns="columns" rowKey="id"
:data="list"
row-key="id"
:loading="loading" :loading="loading"
:scroll="{ y: 500 }" :scroll="{ y: 500 }"
class="h-500px" class="h-500px"
:pagination="false" :pagination="false"
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@change="(pagination, filters, sorter) => {
if (sorter && sorter.columnKey) {
handleSorterChange(sorter.columnKey, sorter.order);
}
}"
> >
<template #empty> <Table.Column title="分组名称" dataIndex="name" />
<Table.Column title="创建人" dataIndex="creator">
<template #customRender="{ record }">
{{ record.creator?.name || '-' }}
</template>
</Table.Column>
<Table.Column
title="创建日期"
dataIndex="created_at"
:width="160"
key="created_at"
:sorter="true"
>
<template #customRender="{ record }">
{{ exactFormatTime(record.created_at) }}
</template>
</Table.Column>
<Table.Column title="操作" :align="'center'" :width="120">
<template #customRender="{ record }">
<div class="flex items-center">
<img :src="icon1" width="16" height="16" class="mr-8px cursor-pointer" @click="openDelete(record)" />
<Button type="primary" size="small" @click="openEdit(record)">编辑</Button>
</div>
</template>
</Table.Column>
<template #emptyText>
<NoData> <NoData>
<span class="s1 mb-16px">暂无分组</span> <span class="s1 mb-16px">暂无分组</span>
<Button type="primary" class="mb-16px" size="middle" @click="openAdd" <Button type="primary" class="mb-16px" size="middle" @click="openAdd"
@ -54,13 +82,7 @@
</Button> </Button>
</NoData> </NoData>
</template> </template>
<template #action="{ record }"> </Table>
<div class="flex items-center">
<img :src="icon1" width="16" height="16" class="mr-8px cursor-pointer" @click="openDelete(record)" />
<Button type="primary" size="small" @click="openEdit(record)">编辑</Button>
</div>
</template>
</a-table>
<div v-if="pageInfo.total > 0" class="pagination-row flex justify-end"> <div v-if="pageInfo.total > 0" class="pagination-row flex justify-end">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -82,7 +104,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { Button, Modal, Input, Space } from 'ant-design-vue'; import { Button, Input, Modal, Space, Table } from 'ant-design-vue';
import { getAccountGroup } from '@/api/all/propertyMarketing'; import { getAccountGroup } from '@/api/all/propertyMarketing';
import { exactFormatTime } from '@/utils/tools'; import { exactFormatTime } from '@/utils/tools';
@ -117,24 +139,7 @@ const loading = ref(false);
const query = ref(cloneDeep(INITIAL_QUERY)); const query = ref(cloneDeep(INITIAL_QUERY));
const pageInfo = ref(cloneDeep(INITIAL_PAGE_INFO)); const pageInfo = ref(cloneDeep(INITIAL_PAGE_INFO));
const columns = [
{ title: '分组名称', dataIndex: 'name' },
{
title: '创建人',
dataIndex: 'creator',
render: ({ record }) => record.creator?.name || '-',
},
{
title: '创建日期',
dataIndex: 'created_at',
width: 160,
render: ({ record }) => exactFormatTime(record.created_at),
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{ title: '操作', slotName: 'action', align: 'center', width: 120 },
];
function open() { function open() {
visible.value = true; visible.value = true;

View File

@ -40,56 +40,51 @@
</Option> </Option>
</Select> </Select>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
column-resizable rowKey="id"
row-key="id" :rowSelection="rowSelection"
:row-selection="rowSelection"
:pagination="false" :pagination="false"
:scroll="{ x: '100%', y: '100%' }" :scroll="{ x: '100%', y: '100%' }"
class="flex-1 overflow-hidden" class="flex-1 overflow-hidden"
:selected-keys="selectedRowKeys"
bordered bordered
@select="handleSelect" :showSorterTooltip="false"
@select-all="handleSelectAll" @change="(pagination, filters, sorter) => {}"
> >
<template #empty> <Table.Column
v-for="column in TABLE_COLUMNS"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #customRender="{ record }">
<template v-if="column.dataIndex === 'created_at'">
{{ exactFormatTime(record.created_at) }}
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</Table.Column>
<template #emptyText>
<NoData text="暂无账户" /> <NoData text="暂无账户" />
</template> </template>
<template #columns> </Table>
<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>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #cell="{ record }">
<template v-if="column.dataIndex === 'created_at'">
{{ exactFormatTime(record.created_at) }}
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</a-table-column>
</template>
</a-table>
</div> </div>
<div class="right w-320px px-12px flex flex-col"> <div class="right w-320px px-12px flex flex-col">
<div class="flex justify-between"> <div class="flex justify-between">
@ -113,7 +108,7 @@
<script setup> <script setup>
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { Button, Input, Select, Tooltip } from 'ant-design-vue'; import { Button, Input, Select, Table, Tooltip } from 'ant-design-vue';
const { Option } = Select; const { Option } = Select;
import { getPlacementAccountOperators, getWorksList } from '@/api/all/propertyMarketing'; import { getPlacementAccountOperators, getWorksList } from '@/api/all/propertyMarketing';

View File

@ -43,56 +43,51 @@
</Option> </Option>
</Select> </Select>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
column-resizable rowKey="id"
row-key="id" :rowSelection="rowSelection"
:row-selection="rowSelection"
:pagination="false" :pagination="false"
:scroll="{ x: '100%', y: '100%' }" :scroll="{ x: '100%', y: '100%' }"
class="flex-1 overflow-hidden" class="flex-1 overflow-hidden"
:selected-keys="selectedRowKeys"
bordered bordered
@select="handleSelect" :showSorterTooltip="false"
@select-all="handleSelectAll" @change="(pagination, filters, sorter) => {}"
> >
<template #empty> <Table.Column
v-for="column in TABLE_COLUMNS"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #customRender="{ record }">
<template v-if="column.dataIndex === 'platform'">
<img :src="getPutAccountPlatformLogo(record.platform)" width="19" height="19" />
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</Table.Column>
<template #emptyText>
<NoData text="暂无账户"/> <NoData text="暂无账户"/>
</template> </template>
<template #columns> </Table>
<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>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #cell="{ record }">
<template v-if="column.dataIndex === 'platform'">
<img :src="getPutAccountPlatformLogo(record.platform)" width="19" height="19" />
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</a-table-column>
</template>
</a-table>
</div> </div>
<div class="right w-320px px-12px flex flex-col"> <div class="right w-320px px-12px flex flex-col">
<div class="flex justify-between"> <div class="flex justify-between">
@ -117,7 +112,7 @@
<script setup> <script setup>
import { PLATFORM_LIST, getPutAccountPlatformLogo } from '@/utils/platform'; import { PLATFORM_LIST, getPutAccountPlatformLogo } from '@/utils/platform';
import { formatTableField } from '@/utils/tools'; import { formatTableField } from '@/utils/tools';
import { Button, Input, Select, Tooltip } from 'ant-design-vue'; import { Button, Input, Select, Table, Tooltip } from 'ant-design-vue';
const { Option } = Select; const { Option } = Select;
import { getPlacementAccountOperators, getPlacementAccountsList } from '@/api/all/propertyMarketing'; import { getPlacementAccountOperators, getPlacementAccountsList } from '@/api/all/propertyMarketing';

View File

@ -43,56 +43,54 @@
</Option> </Option>
</Select> </Select>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
column-resizable rowKey="id"
row-key="id" :rowSelection="{
:row-selection="rowSelection" type: 'checkbox',
selectedRowKeys: selectedRowKeys,
onChange: handleSelect,
onSelectAll: handleSelectAll,
}"
:pagination="false" :pagination="false"
:scroll="{ x: '100%', y: '100%' }" :scroll="{ x: '100%', y: '100%' }"
class="flex-1 overflow-hidden" class="flex-1 overflow-hidden"
:selected-keys="selectedRowKeys"
bordered bordered
@select="handleSelect" :showSorterTooltip="false"
@select-all="handleSelectAll"
> >
<template #empty> <Table.Column
v-for="column in TABLE_COLUMNS"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #customRender="{ record }">
<template v-if="column.dataIndex === 'platform'">
<img :src="getMediaAccountPlatformLogo(record.platform)" width="19" height="19" />
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</Table.Column>
<template #emptyText>
<NoData text="暂无账号"/> <NoData text="暂无账号"/>
</template> </template>
<template #columns> </Table>
<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>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #cell="{ record }">
<template v-if="column.dataIndex === 'platform'">
<img :src="getMediaAccountPlatformLogo(record.platform)" width="19" height="19" />
</template>
<template v-else>
{{ formatTableField(column, record, true) }}
</template>
</template>
</a-table-column>
</template>
</a-table>
</div> </div>
<div class="right w-320px px-12px flex flex-col"> <div class="right w-320px px-12px flex flex-col">
<div class="flex justify-between"> <div class="flex justify-between">
@ -115,7 +113,7 @@
<script setup> <script setup>
import { MEDIA_ACCOUNT_PLATFORMS, getMediaAccountPlatformLogo } from '@/utils/platform'; import { MEDIA_ACCOUNT_PLATFORMS, getMediaAccountPlatformLogo } from '@/utils/platform';
import { formatTableField } from '@/utils/tools'; import { formatTableField } from '@/utils/tools';
import { Button, Input, Select, Tooltip } from 'ant-design-vue'; import { Button, Input, Select, Tooltip, Table } from 'ant-design-vue';
const { Option } = Select; const { Option } = Select;
import { fetchAccountOperators, getMediaAccountList } from '@/api/all/propertyMarketing'; import { fetchAccountOperators, getMediaAccountList } from '@/api/all/propertyMarketing';

View File

@ -1,61 +1,59 @@
<template> <template>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="flex-1 project-table w-100%" class="flex-1 project-table w-100%"
bordered bordered
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@change="handleTableChange"
> >
<template #empty> <template #emptyText>
<NoData text="暂无项目"/> <NoData text="暂无项目"/>
</template> </template>
<template #columns> <Column
<a-table-column v-for="column in TABLE_COLUMNS"
v-for="column in TABLE_COLUMNS" :key="column.dataIndex"
:key="column.dataIndex" :dataIndex="column.dataIndex"
:data-index="column.dataIndex" :fixed="column.fixed"
:fixed="column.fixed" :width="column.width"
:width="column.width" :minWidth="column.minWidth"
:min-width="column.minWidth" :sorter="column.sortable"
:sortable="column.sortable" :align="column.align"
:align="column.align" :ellipsis="true"
ellipsis >
tooltip <template #title>
> <div class="flex items-center">
<template #title> <span class="cts mr-4px">{{ column.title }}</span>
<div class="flex items-center"> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<span class="cts mr-4px">{{ column.title }}</span> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top"> </Tooltip>
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> </div>
</Tooltip> </template>
</div>
</template>
<template v-if="column.dataIndex === 'create_at'" #cell="{ record }"> <template v-if="column.dataIndex === 'create_at'" #customRender="{ record }">
{{ exactFormatTime(record.create_at) }} {{ exactFormatTime(record.create_at) }}
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
<div class="flex items-center"> <div class="flex items-center">
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" /> <img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
<Button type="primary" ghost size="small" @click="onEdit(record)">编辑</Button> <Button type="primary" ghost size="small" @click="onEdit(record)">编辑</Button>
</div> </div>
</template> </template>
<template v-else #cell="{ record }"> <template v-else #customRender="{ record }">
{{ formatTableField(column, record, true) }} {{ formatTableField(column, record, true) }}
</template> </template>
</a-table-column> </Column>
</template> </Table>
</a-table>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { Button, Tooltip } from 'ant-design-vue'; import { Button, Tooltip, Table } from 'ant-design-vue';
const { Column } = Table;
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants'; import { TABLE_COLUMNS } from './constants';
@ -72,9 +70,11 @@ const props = defineProps({
const tableRef = ref(null); const tableRef = ref(null);
// 处理排序变化 // 处理表格变化
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc'); if (sorter && sorter.field) {
emits('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
}
}; };
const onDelete = (item) => { const onDelete = (item) => {
emits('delete', item); emits('delete', item);

View File

@ -29,112 +29,110 @@
</div> </div>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable :rowSelection="{
:row-selection="{
type: 'checkbox', type: 'checkbox',
showCheckedAll: true, selectedRowKeys: selectedItems,
width: 48, onChange: handleSelect,
}" }"
:selected-keys="selectedItems"
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="account-table w-100%" class="account-table w-100%"
bordered bordered
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@select="handleSelect" @change="(pagination, filters, sorter) => {
@select-all="handleSelectAll" if (sorter && sorter.columnKey) {
handleSorterChange(sorter.columnKey, sorter.order === 'ascend' ? 'asc' : 'desc');
}
}"
> >
<template #empty> <Table.Column
v-for="column in tableColumns"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sorter="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" />
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template v-if="column.dataIndex === 'platform'" #customRender="{ record }">
{{ record.platform === 0 ? '抖音' : record.platform === 1 ? '小红书' : '-' }}
</template>
<template v-else-if="column.dataIndex === 'status'" #customRender="{ record }">
<div class="status-tag" :class="`status-tag-${record.status}`">
<span class="cts status-tag-text">{{
STATUS_LIST.find((item) => item.value === record.status)?.label
}}</span>
</div>
</template>
<template v-else-if="column.dataIndex === 'ai_evaluate'" #customRender="{ record }">
<div class="ai-evaluation-row flex">
<template v-if="record.ai_evaluate">
<img
width="16"
height="16"
:src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4"
class="mr-8px icon"
/>
<div>
<p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p>
<p class="cts text-12px lh-20px !color-#939499">
{{ `ROI: ${record.roi}% CVR: ${record.conversion_rate}%` }}
</p>
</div>
</template>
<template v-else>
<p class="cts">-</p>
</template>
</div>
</template>
<template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
<Button type="primary" ghost size="small" @click="handleDetail(record)">详情</Button>
</template>
<template v-else-if="column.isRateField" #customRender="{ record }">
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'">
<icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }}
</div>
</template>
<template v-else-if="column.dataIndex === 'newest_work_title'" #customRender="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFe">{{ record.newest_work_title }}</p>
<p class="cts text-12px lh-20px !color-#939499">
{{ exactFormatTime(record.newest_project_published_at) }}
</p>
</template>
<template v-else #customRender="{ record }">
{{ formatTableField(column, record, true) }}
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> </Table>
<a-table-column
v-for="column in tableColumns"
: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">
<img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" />
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template v-if="column.dataIndex === 'platform'" #cell="{ record }">
{{ record.platform === 0 ? '抖音' : record.platform === 1 ? '小红书' : '-' }}
</template>
<template v-else-if="column.dataIndex === 'status'" #cell="{ record }">
<div class="status-tag" :class="`status-tag-${record.status}`">
<span class="cts status-tag-text">{{
STATUS_LIST.find((item) => item.value === record.status)?.label
}}</span>
</div>
</template>
<template v-else-if="column.dataIndex === 'ai_evaluate'" #cell="{ record }">
<div class="ai-evaluation-row flex">
<template v-if="record.ai_evaluate">
<img
width="16"
height="16"
:src="record.ai_evaluate?.status === 0 ? icon2 : record.ai_evaluate?.status === 1 ? icon3 : icon4"
class="mr-8px icon"
/>
<div>
<p class="cts">{{ `${record.ai_evaluate?.level} | ${record.ai_evaluate?.advise}` }}</p>
<p class="cts text-12px lh-20px !color-#939499">
{{ `ROI: ${record.roi}% CVR: ${record.conversion_rate}%` }}
</p>
</div>
</template>
<template v-else>
<p class="cts">-</p>
</template>
</div>
</template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
<Button type="primary" ghost size="small" @click="handleDetail(record)">详情</Button>
</template>
<template v-else-if="column.isRateField" #cell="{ record }">
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'">
<icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }}
</div>
</template>
<template v-else-if="column.dataIndex === 'newest_work_title'" #cell="{ record }">
<p class="cts cursor-pointer hover:!color-#6D4CFe">{{ record.newest_work_title }}</p>
<p class="cts text-12px lh-20px !color-#939499">
{{ exactFormatTime(record.newest_project_published_at) }}
</p>
</template>
<template v-else #cell="{ record }">
{{ formatTableField(column, record, true) }}
</template>
</a-table-column>
</template>
</a-table>
<CustomTableColumnModal ref="modalRef" :type="CUSTOM_COLUMN_TYPE" @success="onCustomColumnSuccess" /> <CustomTableColumnModal ref="modalRef" :type="CUSTOM_COLUMN_TYPE" @success="onCustomColumnSuccess" />
</template> </template>
<script setup> <script setup>
import { Checkbox, Button } from 'ant-design-vue'; import { Button, Checkbox, Table, Tooltip } from 'ant-design-vue';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants'; import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants';
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';

View File

@ -31,18 +31,39 @@
<template #default>添加新分组</template> <template #default>添加新分组</template>
</Button> </Button>
</div> </div>
<a-table <Table
column-resizable :dataSource="list"
:columns="columns" rowKey="id"
:data="list"
row-key="id"
:loading="loading" :loading="loading"
:scroll="{ y: 500 }" :scroll="{ y: 500 }"
class="h-500px" class="h-500px"
:pagination="false" :pagination="false"
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@change="handleTableChange"
> >
<template #empty> <Column
v-for="column in columns"
:key="column.dataIndex"
:title="column.title"
:dataIndex="column.dataIndex"
:width="column.width"
:align="column.align"
:sortable="column.sortable"
>
<template v-if="column.slotName === 'action'" #customRender="{ record }">
<div class="flex items-center">
<img :src="icon1" width="16" height="16" class="mr-8px cursor-pointer" @click="openDelete(record)" />
<Button type="primary" size="small" @click="openEdit(record)">编辑</Button>
</div>
</template>
<template v-else-if="column.dataIndex === 'creator'" #customRender="{ record }">
{{ record.creator?.name || '-' }}
</template>
<template v-else-if="column.dataIndex === 'created_at'" #customRender="{ record }">
{{ exactFormatTime(record.created_at) }}
</template>
</Column>
<template #emptyText>
<NoData> <NoData>
<span class="s1 mb-16px">暂无分组</span> <span class="s1 mb-16px">暂无分组</span>
<Button type="primary" class="mb-16px" size="medium" @click="openAdd" <Button type="primary" class="mb-16px" size="medium" @click="openAdd"
@ -53,13 +74,7 @@
</Button> </Button>
</NoData> </NoData>
</template> </template>
<template #action="{ record }"> </Table>
<div class="flex items-center">
<img :src="icon1" width="16" height="16" class="mr-8px cursor-pointer" @click="openDelete(record)" />
<Button type="primary" @click="openEdit(record)">编辑</Button>
</div>
</template>
</a-table>
<div v-if="pageInfo.total > 0" class="pagination-row flex justify-end"> <div v-if="pageInfo.total > 0" class="pagination-row flex justify-end">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -81,9 +96,10 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { Button, Modal, Input, Space } from 'ant-design-vue'; import { Button, Modal, Input, Space, Table } from 'ant-design-vue';
import { getPlacementAccountProjectGroups } from '@/api/all/propertyMarketing'; import { getPlacementAccountProjectGroups } from '@/api/all/propertyMarketing';
import { exactFormatTime } from '@/utils/tools'; import { exactFormatTime } from '@/utils/tools';
const { Column } = Table;
import AddGroup from './add-group.vue'; import AddGroup from './add-group.vue';
import DeleteGroup from './delete-group.vue'; import DeleteGroup from './delete-group.vue';
@ -159,9 +175,9 @@ function openDelete(record) {
deleteGroupRef.value.open(record); deleteGroupRef.value.open(record);
} }
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
query.value.sort_column = column; query.value.sort_column = sorter.field;
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc'; query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
getData(); getData();
}; };
async function getData() { async function getData() {

View File

@ -29,62 +29,55 @@
</div> </div>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
column-resizable :dataSource="dataSource"
:data="dataSource" rowKey="id"
row-key="id" :rowSelection="{
:row-selection="{
type: 'checkbox', type: 'checkbox',
showCheckedAll: true, selectedRowKeys: selectedItems,
width: 48, onChange: handleSelect,
onSelectAll: handleSelectAll,
}" }"
:selected-keys="selectedItems"
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="plan-table w-100%" class="plan-table w-100%"
bordered bordered
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@select="handleSelect" @change="handleTableChange"
@select-all="handleSelectAll"
> >
<template #empty> <Table.Column
<NoData /> v-for="column in tableColumns"
</template> :key="column.dataIndex"
<template #columns> :dataIndex="column.dataIndex"
<a-table-column :fixed="column.fixed"
v-for="column in tableColumns" :width="column.width"
:key="column.dataIndex" :minWidth="column.minWidth"
:data-index="column.dataIndex" :sortable="column.sortable"
:fixed="column.fixed" :align="column.align"
:width="column.width" :ellipsis="true"
:min-width="column.minWidth" >
:sortable="column.sortable" <template #title>
:align="column.align" <div class="flex items-center">
ellipsis <img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" />
tooltip <span class="cts mr-4px">{{ column.title }}</span>
> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<template #title> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<div class="flex items-center"> </Tooltip>
<img v-if="column.dataIndex === 'ai_evaluate'" width="16" height="16" :src="icon5" class="mr-4px" /> </div>
<span class="cts mr-4px">{{ column.title }}</span> </template>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top"> <template #customRender="{ record }">
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> <template v-if="column.dataIndex === 'platform'">
</Tooltip>
</div>
</template>
<template v-if="column.dataIndex === 'platform'" #cell="{ record }">
{{ record.platform === 0 ? '抖音' : record.platform === 1 ? '小红书' : '-' }} {{ record.platform === 0 ? '抖音' : record.platform === 1 ? '小红书' : '-' }}
</template> </template>
<template v-else-if="column.dataIndex === 'status'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'status'">
<div class="status-tag" :class="`status-tag-${record.status}`"> <div class="status-tag" :class="`status-tag-${record.status}`">
<span class="cts status-tag-text">{{ <span class="cts status-tag-text">{{
STATUS_LIST.find((item) => item.value === record.status)?.label STATUS_LIST.find((item) => item.value === record.status)?.label
}}</span> }}</span>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'ai_evaluate'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'ai_evaluate'">
<div class="ai-evaluation-row flex"> <div class="ai-evaluation-row flex">
<template v-if="record.ai_evaluate"> <template v-if="record.ai_evaluate">
<img <img
@ -105,41 +98,40 @@
</template> </template>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'">
<Button type="primary" ghost size="small" @click="handleDetail(record)">详情</Button> <Button type="primary" ghost size="small" @click="handleDetail(record)">详情</Button>
</template> </template>
<template v-else-if="column.isRateField">
<template v-else-if="column.isRateField" #cell="{ record }">
<div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'"> <div class="flex items-center rate-row justify-end" :class="record[column.dataIndex] > 0 ? 'up' : 'down'">
<icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" /> <icon-arrow-up v-if="record[column.dataIndex] > 0" size="16" />
<icon-arrow-down v-else size="16" /> <icon-arrow-down v-else size="16" />
{{ formatTableField(column, record) }} {{ formatTableField(column, record) }}
</div> </div>
</template> </template>
<!-- <template v-else-if="['like_chain1', 'like_chain4'].includes(column.dataIndex)" #cell="{ record }"> <template v-else>
<p class="cts cursor-pointer hover:!color-#6D4CFE">打工人的环游世界旅行计划国内版</p>
<p class="cts text-12px lh-20px !color-#939499">2025-06-18</p>
</template> -->
<template v-else #cell="{ record }">
{{ formatTableField(column, record, true) }} {{ formatTableField(column, record, true) }}
</template> </template>
</a-table-column> </template>
</Table.Column>
<template #emptyText>
<NoData />
</template> </template>
</a-table> </Table>
<CustomTableColumnModal ref="modalRef" :type="CUSTOM_COLUMN_TYPE" @success="onCustomColumnSuccess" /> <CustomTableColumnModal ref="modalRef" :type="CUSTOM_COLUMN_TYPE" @success="onCustomColumnSuccess" />
</template> </template>
<script setup> <script setup>
import { Checkbox, Button } from 'ant-design-vue'; import { Checkbox, Button, Table } from 'ant-design-vue';
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { cloneDeep, union } from 'lodash-es';
import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants'; import { STATUS_LIST } from '@/views/property-marketing/put-account/components/status-select/constants';
import { formatTableField } from '@/utils/tools'; import { formatTableField } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants'; import { TABLE_COLUMNS } from './constants';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import CustomTableColumnModal from '@/components/custom-table-column-modal'; import CustomTableColumnModal from '@/components/custom-table-column-modal';
import { getCustomColumns } from '@/api/all/common'; import { getCustomColumns } from '@/api/all/common';
const { Column } = Table;
import icon1 from '@/assets/img/media-account/icon-custom.png'; import icon1 from '@/assets/img/media-account/icon-custom.png';
import icon2 from '@/assets/img/media-account/icon-warn.png'; import icon2 from '@/assets/img/media-account/icon-warn.png';
@ -172,22 +164,11 @@ const indeterminate = computed(
() => selectedItems.value.length > 0 && selectedItems.value.length < props.dataSource.length, () => selectedItems.value.length > 0 && selectedItems.value.length < props.dataSource.length,
); );
const handleSelectAll = (checked) => { // 处理表格变化(排序、分页等)
if (checked) { const handleTableChange = (pagination, filters, sorter) => {
selectedItems.value = props.dataSource.map((item) => item.id); if (sorter && sorter.field) {
} else { emit('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
selectedItems.value = [];
} }
emit('selectionChange', checked ? selectedItems.value : []);
};
const handleDetail = (record) => {
router.push(`/media-account/detail/${record.id}`);
};
// 处理排序变化
const handleSorterChange = (column, order) => {
emit('sorterChange', column, order === 'ascend' ? 'asc' : 'desc');
}; };
const handleSelect = (selectedRowKeys, selectedRows) => { const handleSelect = (selectedRowKeys, selectedRows) => {
@ -195,6 +176,19 @@ const handleSelect = (selectedRowKeys, selectedRows) => {
emit('selectionChange', selectedRows); emit('selectionChange', selectedRows);
}; };
const handleSelectAll = (selected, selectedRows, changeRows) => {
if (selected) {
selectedItems.value = props.dataSource.map((item) => item.id);
} else {
selectedItems.value = [];
}
emit('selectionChange', selected ? selectedRows : []);
};
const handleDetail = (record) => {
router.push(`/media-account/detail/${record.id}`);
};
const handleExport = () => { const handleExport = () => {
emit('export'); emit('export');
}; };

View File

@ -7,17 +7,15 @@
<div class="filter-wrap bg-#fff rounded-8px mb-16px"> <div class="filter-wrap bg-#fff rounded-8px mb-16px">
<Tabs v-model:activeKey="activeTab" @change="handleTabClick" size="large"> <Tabs v-model:activeKey="activeTab" @change="handleTabClick" size="large">
<TabPane key="1" tab="账户"></TabPane> <TabPane key="1" tab="账户"></TabPane>
<TabPane key="2" tab="计划"> <TabPane key="2" tab="计划"> </TabPane>
<template v-if="!isAccountTab" #tab> <template v-if="!isAccountTab" #rightExtra>
计划 <Button type="primary" ghost class="mr-12px flex items-center" @click="handleOpenGroupModal">
<Button type="primary" ghost class="mr-12px flex items-center" @click="handleOpenGroupModal"> <template #icon>
<template #icon> <img :src="icon2" width="16" height="16" class="mr-8px" />
<img :src="icon2" width="16" height="16" class="mr-8px" /> </template>
</template> <template #default>分组管理</template>
<template #default>分组管理</template> </Button>
</Button> </template>
</template>
</TabPane>
</Tabs> </Tabs>
<FilterBlock <FilterBlock
ref="filterBlockRef" ref="filterBlockRef"

View File

@ -42,51 +42,46 @@
</div> </div>
</div> </div>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
column-resizable :rowSelection="rowSelection"
:row-selection="rowSelection" :rowKey="ROW_KEY"
:row-key="ROW_KEY"
:pagination="false" :pagination="false"
:scroll="{ x: '100%', y: '100%' }" :scroll="{ x: '100%', y: '100%' }"
class="w-100% flex-1 overflow-hidden" class="w-100% flex-1 overflow-hidden"
:selected-keys="selectedRowKeys"
bordered bordered
@select="handleSelect" :showSorterTooltip="false"
@select-all="handleSelectAll" @change="(pagination, filters, sorter) => {}"
> >
<template #empty> <Table.Column
v-for="column in TABLE_COLUMNS"
:key="column.dataIndex"
:dataIndex="column.dataIndex"
:fixed="column.fixed"
:width="column.width"
:minWidth="column.minWidth"
:sortable="column.sortable"
:align="column.align"
:ellipsis="true"
>
<template #title>
<div class="flex items-center">
<span class="cts mr-4px">{{ column.title }}</span>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</Tooltip>
</div>
</template>
<template #customRender="{ record }">
{{ formatTableField(column, record, true) }}
</template>
</Table.Column>
<template #emptyText>
<NoData /> <NoData />
</template> </template>
<template #columns> </Table>
<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>
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
</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"> <div v-if="pageInfo.total > 0" class="flex justify-end mt-16px">
<a-pagination <a-pagination
:total="pageInfo.total" :total="pageInfo.total"
@ -115,12 +110,11 @@
</template> </template>
<script setup> <script setup>
import { Button, Input, Tooltip } from 'ant-design-vue'; import { Button, Input, Modal, Table, Tooltip } from 'ant-design-vue';
import { INITIAL_FORM, INITIAL_PAGE_INFO, TABLE_COLUMNS } from './constants'; import { INITIAL_FORM, INITIAL_PAGE_INFO, TABLE_COLUMNS } from './constants';
import { formatTableField } from '@/utils/tools'; import { formatTableField } from '@/utils/tools';
import { postSubAccount, postAddSubAccount } from '@/api/all/propertyMarketing'; import { postSubAccount, postAddSubAccount } from '@/api/all/propertyMarketing';
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination'; import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
import { Modal } from 'ant-design-vue';
const emits = defineEmits('confirm'); const emits = defineEmits('confirm');
const update = inject('update'); const update = inject('update');

View File

@ -1,73 +1,74 @@
<template> <template>
<view> <view>
<a-table class="account-table" :columns="columns" :data="listData" :pagination="false"> <Table class="account-table" :dataSource="listData" :pagination="false" :showSorterTooltip="false" @change="(pagination, filters, sorter) => {}">
<template #platform="{ record }"> <Table.Column
<span class="mr-8px" v-if="record.platform.length > 0" v-for="(item, index) in record.platform"> title="生成日期"
<img :src="PLATFORM_LIST?.[item]?.icon" width="15" height="15" /> dataIndex="created_at"
<span class="label ml-5px">{{ PLATFORM_LIST?.[item]?.label }}</span> :width="60"
</span> :minWidth="60"
</template> />
<template #operation="{ record }"> <Table.Column
<Space size="medium"> title="账号"
dataIndex="account"
:width="120"
:minWidth="120"
/>
<Table.Column
:width="180"
:minWidth="180"
title="计划"
dataIndex="plan"
/>
<Table.Column
title="平台"
:width="120"
:minWidth="120"
>
<template #customRender="{ record }">
<template v-if="record.platform.length > 0">
<span class="mr-8px" v-for="(item, index) in record.platform" :key="index">
<img :src="PLATFORM_LIST?.[item]?.icon" width="15" height="15" />
<span class="label ml-5px">{{ PLATFORM_LIST?.[item]?.label }}</span>
</span>
</template>
</template>
</Table.Column>
<Table.Column
title="操作"
dataIndex="operation"
:width="60"
:minWidth="60"
>
<template #customRender="{ record }">
<Space> <Space>
<a-popconfirm <Space>
content="确定删除吗?" <a-popconfirm
type="warning" content="确定删除吗?"
ok-text="确认删除" type="warning"
cancel-text="取消" ok-text="确认删除"
@ok="deleteData(record.id)" cancel-text="取消"
> @ok="deleteData(record.id)"
<icon-delete></icon-delete> >
</a-popconfirm> <icon-delete></icon-delete>
</a-popconfirm>
</Space>
<Space>
<Button type="primary" ghost @click="downLoad(record.file_url)" class="operation-btn">下载</Button>
<Button type="primary" ghost @click="goDetail(record.id)" class="operation-btn">详情</Button>
</Space>
</Space> </Space>
<Space> </template>
<Button type="primary" ghost @click="downLoad(record.file_url)" class="operation-btn">下载</Button> </Table.Column>
<Button type="primary" ghost @click="goDetail(record.id)" class="operation-btn">详情</Button> </Table>
</Space>
</Space>
</template>
</a-table>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Button, Space } from 'ant-design-vue'; import { Button, Space, Table } from 'ant-design-vue';
import { IconDelete } from '@arco-design/web-vue/es/icon'; import { IconDelete } from '@arco-design/web-vue/es/icon';
import { PLATFORM_LIST } from '@/utils/platform'; import { PLATFORM_LIST } from '@/utils/platform';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
const columns = [
{
title: '生成日期',
dataIndex: 'created_at',
width: 60,
minWidth: 60,
},
{
title: '账号',
dataIndex: 'account',
width: 120,
minWidth: 120,
},
{
width: 180,
minWidth: 180,
title: '计划',
dataIndex: 'plan',
},
{
title: '平台',
width: 120,
minWidth: 120,
slotName: 'platform',
},
{
title: '操作',
dataIndex: 'operation',
slotName: 'operation',
width: 60,
minWidth: 60,
},
];
// hotTranslation.vue // hotTranslation.vue
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { deleteHistorylog } from '@/api/all/propertyMarketing'; import { deleteHistorylog } from '@/api/all/propertyMarketing';

View File

@ -1,98 +1,144 @@
<template> <template>
<view class="table-data"> <view class="table-data">
<a-table <Table
class="account-table" class="account-table"
:columns="columns" :dataSource="listData"
:data="listData"
:pagination="false" :pagination="false"
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@change="(pagination, filters, sorter) => {
if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
handleSorterChange(sorter.columnKey, sorter.order);
}
}"
> >
<template #total_use_amount_label> <Table.Column
<Space> title="账户名称"
<span>本周总消耗</span> dataIndex="account_name"
<a-popover position="tl"> :width="60"
<icon-question-circle /> :minWidth="60"
<template #content> />
<p>当前周内所有投流账户的累计广告花费反映整体投放规模</p> <Table.Column
:width="180"
:minWidth="180"
title="计划名称"
dataIndex="name"
/>
<Table.Column
title="平台"
dataIndex="platform"
:width="120"
:minWidth="120"
>
<template #customRender="{ record }">
<div class="field-row">
<img :src="PLATFORM_LIST.find((v) => v.value === record.platform)?.icon" width="15" height="15" />
<span class="label ml-5px">{{ PLATFORM_LIST.find((v) => v.value === record.platform)?.label }}</span>
</div>
</template>
</Table.Column>
<Table.Column
:width="120"
:minWidth="120"
key="total_use_amount"
:sorter="true"
dataIndex="total_use_amount"
>
<template #title>
<Space>
<span>本周总消耗</span>
<a-popover position="tl">
<icon-question-circle />
<template #content>
<p>当前周内所有投流账户的累计广告花费反映整体投放规模</p>
</template>
</a-popover>
</Space>
</template>
<template #customRender="{ record }">
<Space>{{ record.total_use_amount }}</Space>
</template>
</Table.Column>
<Table.Column
:width="120"
:minWidth="120"
key="pre_total_use_amount_chain"
:sorter="true"
dataIndex="pre_total_use_amount_chain"
>
<template #title>
<Space>
<span>本周总消耗环比</span>
<a-popover position="tl">
<icon-question-circle />
<template #content>
<p>本周消耗金额与上周对比的变化百分比用于衡量预算投放趋势</p>
</template>
</a-popover>
</Space>
</template>
<template #customRender="{ record }">
<a-statistic
:value="record.pre_total_use_amount_chain * 100"
:value-style="{
color: record.pre_total_use_amount_chain > 0 ? '#F64B31' : '#25C883',
fontStyle: 'normal',
fontSize: '14px',
}"
>
<template #prefix>
<icon-arrow-rise v-if="record.pre_total_use_amount_chain > 0" />
<icon-arrow-down v-else />
</template> </template>
</a-popover> <template #suffix>%</template>
</Space> </a-statistic>
</template> </template>
<template #pre_total_use_amount_chain_title> </Table.Column>
<Space> <Table.Column
<span>本周总消耗环比</span> dataIndex="roi"
<a-popover position="tl"> :width="120"
<icon-question-circle /> :minWidth="120"
<template #content> key="roi"
<p>本周消耗金额与上周对比的变化百分比用于衡量预算投放趋势</p> :sorter="true"
</template> >
</a-popover> <template #title>
</Space> <Space>
</template> <span>ROI</span>
<template #roi> <a-popover position="tl">
<Space> <icon-question-circle />
<span>ROI</span> <template #content>
<a-popover position="tl"> <p>投资回报率ROI= 收益 ÷ 投入成本,反映整体投流账户的收益效率。</p>
<icon-question-circle /> </template>
<template #content> </a-popover>
<p>投资回报率ROI= 收益 ÷ 投入成本反映整体投流账户的收益效率</p> </Space>
</template> </template>
</a-popover> </Table.Column>
</Space> <Table.Column
</template> dataIndex="click_rate"
<template #ctr> :width="120"
<Space> :minWidth="120"
<span>CTR</span> key="click_rate"
<a-popover position="tl"> :sorter="true"
<icon-question-circle /> >
<template #content> <template #title>
<p>点击率CTR= 点击量 ÷ 展示量是衡量广告素材吸引力的关键指标</p> <Space>
</template> <span>CTR</span>
</a-popover> <a-popover position="tl">
</Space> <icon-question-circle />
</template> <template #content>
<p>点击率CTR= 点击量 ÷ 展示量,是衡量广告素材吸引力的关键指标。</p>
<template #platform="{ record }"> </template>
<div class="field-row"> </a-popover>
<img :src="PLATFORM_LIST.find((v) => v.value === record.platform)?.icon" width="15" height="15" /> </Space>
<span class="label ml-5px">{{ PLATFORM_LIST.find((v) => v.value === record.platform)?.label }}</span> </template>
</div> <template #customRender="{ record }">
</template> <span>{{ `${record.click_rate}%` }}</span>
<template #total_use_amount="{ record }"> </template>
<Space>{{ record.total_use_amount }}</Space> </Table.Column>
</template> </Table>
<template #total_use_amoun2="{ record }">
<div class="field-row">
<img :src="PLATFORM_LIST.find((v) => v.value === record.platform)?.icon" width="15" height="15" />
<span class="label ml-5px">{{ PLATFORM_LIST.find((v) => v.value === record.platform)?.label }}</span>
</div>
</template>
<template #pre_total_use_amount_chain="{ record }">
<a-statistic
:value="record.pre_total_use_amount_chain * 100"
:value-style="{
color: record.pre_total_use_amount_chain > 0 ? '#F64B31' : '#25C883',
fontStyle: 'normal',
fontSize: '14px',
}"
>
<template #prefix>
<icon-arrow-rise v-if="record.pre_total_use_amount_chain > 0" />
<icon-arrow-down v-else />
</template>
<template #suffix>%</template>
</a-statistic>
</template>
<template #clickRate="{ record }">
<span>{{ `${record.click_rate}%` }}</span>
</template>
</a-table>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Space } from "ant-design-vue" import { Space, Table } from "ant-design-vue"
import { PLATFORM_LIST } from '@/utils/platform'; import { PLATFORM_LIST } from '@/utils/platform';
const props = defineProps({ const props = defineProps({
@ -109,69 +155,6 @@ const emit = defineEmits(['updateQuery']);
const handleSorterChange = (column, order) => { const handleSorterChange = (column, order) => {
emit('updateQuery', { column, order }); emit('updateQuery', { column, order });
}; };
const columns = [
{
title: '账户名称',
dataIndex: 'account_name',
width: 60,
minWidth: 60,
},
{
width: 180,
minWidth: 180,
title: '计划名称',
dataIndex: 'name',
},
{
title: '平台',
dataIndex: 'platform',
width: 120,
minWidth: 120,
slotName: 'platform',
},
{
title: '本周总消耗',
titleSlotName: 'total_use_amount_label',
dataIndex: 'total_use_amount',
slotName: 'total_use_amount',
width: 120,
minWidth: 120,
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '本周总消耗环比',
titleSlotName: 'pre_total_use_amount_chain_title',
dataIndex: 'pre_total_use_amount_chain',
width: 120,
minWidth: 120,
slotName: 'pre_total_use_amount_chain',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
titleSlotName: 'roi',
dataIndex: 'roi',
width: 120,
minWidth: 120,
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
titleSlotName: 'ctr',
dataIndex: 'click_rate',
width: 120,
minWidth: 120,
slotName: 'clickRate',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
];
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>

View File

@ -1,135 +1,130 @@
<template> <template>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="flex-1 manuscript-table w-100%" class="flex-1 manuscript-table w-100%"
bordered bordered
:row-selection="rowSelection" :rowSelection="rowSelection"
:selected-row-keys="selectedRowKeys" :showSorterTooltip="false"
@sorter-change="handleSorterChange" @change="handleTableChange"
@select="(selectedKeys, rowKeyValue, record) => emits('select', selectedKeys, rowKeyValue, record)"
@select-all="(check) => emits('selectAll', check)"
> >
<template #empty> <template #emptyText>
<NoData text="暂无稿件" /> <NoData text="暂无稿件" />
</template> </template>
<template #columns> <Column
<a-table-column v-for="column in tableColumns"
v-for="column in tableColumns" :key="column.dataIndex"
:key="column.dataIndex" :dataIndex="column.dataIndex"
:data-index="column.dataIndex" :fixed="column.fixed"
:fixed="column.fixed" :width="column.width"
:width="column.width" :minWidth="column.minWidth"
:min-width="column.minWidth" :sorter="column.sortable"
:sortable="column.sortable" :align="column.align"
:align="column.align" :ellipsis="true"
ellipsis >
tooltip <template #title>
> <div class="flex items-center">
<template #title> <span class="cts mr-4px">{{ column.title }}</span>
<div class="flex items-center"> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<span class="cts mr-4px">{{ column.title }}</span> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top"> </Tooltip>
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> </div>
</Tooltip> </template>
</div>
</template>
<template v-if="column.dataIndex === 'customer_opinion'" #cell="{ record }"> <template v-if="column.dataIndex === 'customer_opinion'" #customRender="{ record }">
<p <p
class="h-28px px-8px flex items-center rounded-2px w-fit" class="h-28px px-8px flex items-center rounded-2px w-fit"
:style="{ background: getCustomerOpinionInfo(record.customer_opinion)?.bg }" :style="{ background: getCustomerOpinionInfo(record.customer_opinion)?.bg }"
>
<span class="cts" :class="getCustomerOpinionInfo(record.customer_opinion)?.color">{{
getCustomerOpinionInfo(record.customer_opinion)?.label ?? '-'
}}</span>
</p>
</template>
<template v-else-if="column.dataIndex === 'platform'" #cell="{ record }">
<template v-if="!PLATFORMS.find((item) => item.value === record.platform)"> - </template>
<img
v-else
width="24"
height="24"
class="rounded-4px"
:src="PLATFORMS.find((item) => item.value === record.platform)?.icon"
/>
</template>
<template v-else-if="column.dataIndex === 'compliance_level'" #cell="{ record }">
<span class="cts num !color-#6D4CFE">{{
record.ai_review?.compliance_level ? `${record.ai_review?.compliance_level}%` : '-'
}}</span>
</template>
<template v-else-if="column.dataIndex === 'title'" #cell="{ record }">
<TextOverTips :context="record.title" :line="3" class="title" @click="onDetail(record)" />
</template>
<template v-else-if="column.dataIndex === 'type'" #cell="{ record }">
<div class="flex items-center">
<img
:src="record.type === EnumManuscriptType.Image ? icon2 : icon3"
width="16"
height="16"
class="mr-4px"
/>
<span class="cts" :class="record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'">{{
record.type === EnumManuscriptType.Image ? '图文' : '视频'
}}</span>
</div>
</template>
<template
#cell="{ record }"
v-else-if="
['created_at', 'last_modified_at', 'audit_started_at', 'audit_passed_at'].includes(column.dataIndex)
"
> >
<span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span> <span class="cts" :class="getCustomerOpinionInfo(record.customer_opinion)?.color">{{
</template> getCustomerOpinionInfo(record.customer_opinion)?.label ?? '-'
<template v-else-if="column.dataIndex === 'cover'" #cell="{ record }"> }}</span>
<HoverImagePreview :src="record.cover"> </p>
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px" fit="cover"> </template>
<template #error> <template v-else-if="column.dataIndex === 'platform'" #customRender="{ record }">
<img :src="icon4" class="w-full h-full" /> <template v-if="!PLATFORMS.find((item) => item.value === record.platform)"> - </template>
</template> <img
</a-image> v-else
</HoverImagePreview> width="24"
</template> height="24"
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> class="rounded-4px"
<div class="flex items-center"> :src="PLATFORMS.find((item) => item.value === record.platform)?.icon"
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" /> />
<Button </template>
type="primary" <template v-else-if="column.dataIndex === 'compliance_level'" #customRender="{ record }">
ghost <span class="cts num !color-#6D4CFE">{{
size="small" record.ai_review?.compliance_level ? `${record.ai_review?.compliance_level}%` : '-'
@click="onCheck(record)" }}</span>
v-if="audit_status === AuditStatus.Pending" </template>
>审核</Button <template v-else-if="column.dataIndex === 'title'" #customRender="{ record }">
> <TextOverTips :context="record.title" :line="3" class="title" @click="onDetail(record)" />
<Button </template>
type="primary" <template v-else-if="column.dataIndex === 'type'" #customRender="{ record }">
ghost <div class="flex items-center">
size="small" <img
@click="onScan(record)" :src="record.type === EnumManuscriptType.Image ? icon2 : icon3"
v-else-if="audit_status === AuditStatus.Auditing" width="16"
>查看</Button height="16"
> class="mr-4px"
<Button type="primary" ghost size="small" @click="onDetail(record)" v-else>详情</Button> />
</div> <span class="cts" :class="record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'">{{
</template> record.type === EnumManuscriptType.Image ? '图文' : '视频'
<template v-else #cell="{ record }"> }}</span>
{{ formatTableField(column, record, true) }} </div>
</template> </template>
</a-table-column> <template
</template> #customRender="{ record }"
</a-table> v-else-if="
['created_at', 'last_modified_at', 'audit_started_at', 'audit_passed_at'].includes(column.dataIndex)
"
>
<span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span>
</template>
<template v-else-if="column.dataIndex === 'cover'" #customRender="{ record }">
<HoverImagePreview :src="record.cover">
<Image :width="64" :height="64" :src="record.cover" class="!rounded-6px" :preview="false">
<template #error>
<img :src="icon4" class="w-full h-full" />
</template>
</Image>
</HoverImagePreview>
</template>
<template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
<div class="flex items-center">
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
<Button
type="primary"
ghost
size="small"
@click="onCheck(record)"
v-if="audit_status === AuditStatus.Pending"
>审核</Button
>
<Button
type="primary"
ghost
size="small"
@click="onScan(record)"
v-else-if="audit_status === AuditStatus.Auditing"
>查看</Button
>
<Button type="primary" ghost size="small" @click="onDetail(record)" v-else>详情</Button>
</div>
</template>
<template v-else #customRender="{ record }">
{{ formatTableField(column, record, true) }}
</template>
</Column>
</Table>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref, computed } from 'vue';
import { Button } from 'ant-design-vue'; import { Button, Tooltip, Table, Image } from 'ant-design-vue';
const { Column } = Table;
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { EnumManuscriptType } from '@/views/writer-material-center/components/finished-products/manuscript/list/constants'; import { EnumManuscriptType } from '@/views/writer-material-center/components/finished-products/manuscript/list/constants';
import { patchWorkAuditsAuditWriter } from '@/api/all/generationWorkshop-writer.ts'; import { patchWorkAuditsAuditWriter } from '@/api/all/generationWorkshop-writer.ts';
@ -179,8 +174,20 @@ const tableRef = ref(null);
const writerCode = computed(() => route.params.writerCode); const writerCode = computed(() => route.params.writerCode);
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc'); if (sorter && sorter.field) {
emits('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
}
};
const rowSelection = {
selectedRowKeys: computed(() => props.selectedRowKeys),
onSelect: (record, selected, selectedRows, nativeEvent) => {
emits('select', selectedRows.map(row => row.id), record.id, record);
},
onSelectAll: (selected, selectedRows, changeRows) => {
emits('selectAll', selected);
},
}; };
const onDelete = (item) => { const onDelete = (item) => {
emits('delete', item); emits('delete', item);

View File

@ -334,6 +334,7 @@ export default {
maskClosable={false} maskClosable={false}
unmount-on-close unmount-on-close
onCancel={onClose} onCancel={onClose}
centered
footer={isDefault.value && isLocal.value ? null : renderFooterButtons()} footer={isDefault.value && isLocal.value ? null : renderFooterButtons()}
> >
<Form ref={formRef} model={form.value} layout="horizontal" auto-label-width> <Form ref={formRef} model={form.value} layout="horizontal" auto-label-width>

View File

@ -52,16 +52,3 @@ async function onDelete() {
defineExpose({ open }); defineExpose({ open });
</script> </script>
<template>
<Modal v-model:open="visible" title="删除稿件" width="480px" @cancel="onClose" centered>
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>确认删除 {{ projectName }} 这个稿件吗</span>
</div>
<template #footer>
<Button size="large" @click="onClose">取消</Button>
<Button type="primary" class="ml-16px" danger size="large" @click="onDelete">确认删除</Button>
</template>
</Modal>
</template>

View File

@ -1,102 +1,100 @@
<template> <template>
<a-table <Table
ref="tableRef" ref="tableRef"
:data="dataSource" :dataSource="dataSource"
row-key="id" rowKey="id"
column-resizable
:pagination="false" :pagination="false"
:scroll="{ x: '100%' }" :scroll="{ x: '100%' }"
class="manuscript-table w-100%" class="manuscript-table w-100%"
bordered bordered
@sorter-change="handleSorterChange" :showSorterTooltip="false"
@change="handleTableChange"
> >
<template #empty> <template #emptyText>
<NoData text="暂无稿件" /> <NoData text="暂无稿件" />
</template> </template>
<template #columns> <Column
<a-table-column v-for="column in TABLE_COLUMNS"
v-for="column in TABLE_COLUMNS" :key="column.dataIndex"
:key="column.dataIndex" :dataIndex="column.dataIndex"
:data-index="column.dataIndex" :fixed="column.fixed"
:fixed="column.fixed" :width="column.width"
:width="column.width" :minWidth="column.minWidth"
:min-width="column.minWidth" :sorter="column.sortable"
:sortable="column.sortable" :align="column.align"
:align="column.align" :ellipsis="true"
ellipsis >
tooltip <template #title>
> <div class="flex items-center">
<template #title> <span class="cts mr-4px">{{ column.title }}</span>
<div class="flex items-center"> <Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
<span class="cts mr-4px">{{ column.title }}</span> <icon-question-circle class="tooltip-icon color-#737478" size="16" />
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top"> </Tooltip>
<icon-question-circle class="tooltip-icon color-#737478" size="16" /> </div>
</Tooltip> </template>
</div>
</template>
<template v-if="column.dataIndex === 'create_at'" #cell="{ record }"> <template v-if="column.dataIndex === 'create_at'" #customRender="{ record }">
{{ exactFormatTime(record.create_at) }} {{ exactFormatTime(record.create_at) }}
</template> </template>
<template v-else-if="column.dataIndex === 'title'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'title'" #customRender="{ record }">
<TextOverTips :context="record.title" :line="3" class="title" @click="onDetail(record)" /> <TextOverTips :context="record.title" :line="3" class="title" @click="onDetail(record)" />
</template> </template>
<template v-else-if="column.dataIndex === 'audit_status'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'audit_status'" #customRender="{ record }">
<div <div
class="flex items-center w-fit h-28px px-8px rounded-2px" class="flex items-center w-fit h-28px px-8px rounded-2px"
:style="{ backgroundColor: getStatusInfo(record.audit_status).backgroundColor }" :style="{ backgroundColor: getStatusInfo(record.audit_status).backgroundColor }"
> >
<span class="cts s1" :style="{ color: getStatusInfo(record.audit_status).color }">{{ <span class="cts s1" :style="{ color: getStatusInfo(record.audit_status).color }">{{
getStatusInfo(record.audit_status).name getStatusInfo(record.audit_status).name
}}</span> }}</span>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'type'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'type'" #customRender="{ record }">
<div class="flex items-center"> <div class="flex items-center">
<img <img
:src="record.type === EnumManuscriptType.Image ? icon2 : icon3" :src="record.type === EnumManuscriptType.Image ? icon2 : icon3"
width="16" width="16"
height="16" height="16"
class="mr-4px" class="mr-4px"
/> />
<span class="cts" :class="record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'">{{ <span class="cts" :class="record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'">{{
record.type === EnumManuscriptType.Image ? '图文' : '视频' record.type === EnumManuscriptType.Image ? '图文' : '视频'
}}</span> }}</span>
</div> </div>
</template> </template>
<template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #cell="{ record }"> <template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #customRender="{ record }">
{{ record[column.dataIndex].name || record[column.dataIndex].mobile }} {{ record[column.dataIndex].name || record[column.dataIndex].mobile }}
</template> </template>
<template v-else-if="['created_at', 'last_modified_at'].includes(column.dataIndex)" #cell="{ record }"> <template v-else-if="['created_at', 'last_modified_at'].includes(column.dataIndex)" #customRender="{ record }">
{{ exactFormatTime(record[column.dataIndex]) }} {{ exactFormatTime(record[column.dataIndex]) }}
</template> </template>
<template v-else-if="column.dataIndex === 'cover'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'cover'" #customRender="{ record }">
<HoverImagePreview :src="record.cover"> <HoverImagePreview :src="record.cover">
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px" fit="cover"> <Image :width="64" :height="64" :src="record.cover" class="!rounded-6px" :preview="false">
<template #error> <template #error>
<img :src="icon4" class="w-full h-full" /> <img :src="icon4" class="w-full h-full" />
</template> </template>
</a-image> </Image>
</HoverImagePreview> </HoverImagePreview>
</template> </template>
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
<div class="flex items-center"> <div class="flex items-center">
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" /> <img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
<Button type="primary" ghost size="small" class="mr-8px" @click="onEdit(record)">编辑</Button> <Button type="primary" ghost size="small" class="mr-8px" @click="onEdit(record)">编辑</Button>
<Button type="primary" ghost size="small" @click="onDetail(record)">详情</Button> <Button type="primary" ghost size="small" @click="onDetail(record)">详情</Button>
</div> </div>
</template> </template>
<template v-else #cell="{ record }"> <template v-else #customRender="{ record }">
{{ formatTableField(column, record, true) }} {{ formatTableField(column, record, true) }}
</template> </template>
</a-table-column> </Column>
</template> </Table>
</a-table>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { Button, Tooltip } from 'ant-design-vue'; import { Button, Tooltip, Table, Image } from 'ant-design-vue';
const { Column } = Table;
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants'; import { TABLE_COLUMNS } from './constants';
import { import {
@ -125,8 +123,10 @@ const props = defineProps({
const tableRef = ref(null); const tableRef = ref(null);
const route = useRoute(); const route = useRoute();
const handleSorterChange = (column, order) => { const handleTableChange = (pagination, filters, sorter) => {
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc'); if (sorter && sorter.field) {
emits('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
}
}; };
const onDelete = (item) => { const onDelete = (item) => {
emits('delete', item); emits('delete', item);