Merge remote-tracking branch 'origin/main' into feature/0905_登录注册流程重构

# Conflicts:
#	src/App.vue
#	src/views/components/login/index.vue
#	src/views/components/management/person/index.vue
#	src/views/login/style.scss
This commit is contained in:
rd
2025-09-10 16:16:34 +08:00
233 changed files with 5564 additions and 6102 deletions

View File

@ -55,7 +55,7 @@ const checkHasInviteCode = () => {
<template>
<Layout :class="['layout-wrap', { mobile: appStore.hideMenu }]" class="h-full flex flex-col w-full">
<JoinModal v-model:visible="joinEnterpriseVisible" ref="joinModalRef" />
<JoinModal v-model:open="joinEnterpriseVisible" centered ref="joinModalRef" />
<Layout.Header class="layout-header-wrap">
<Navbar />
</Layout.Header>

View File

@ -1,4 +1,7 @@
<script setup lang="ts">
import { Button, Result } from 'ant-design-vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const back = () => {
router.replace('/');
@ -7,9 +10,9 @@ const back = () => {
<template>
<div class="content">
<a-result class="result" status="404" subtitle="页面跑路了" />
<div class="operation-row">
<a-button key="back" type="primary" @click="back">返回</a-button>
<Result class="result" status="404" sub-title="页面跑路了" />
<div class="operation-row flex justify-center">
<Button key="back" type="primary" @click="back">返回</Button>
</div>
</div>
</template>
@ -19,8 +22,10 @@ const back = () => {
position: absolute;
top: 50%;
left: 50%;
margin-left: -95px;
margin-top: -121px;
text-align: center;
transform: translate(-50%, -50%);
padding: 32px 32px 24px;
// margin-left: -95px;
// margin-top: -121px;
// text-align: center;
}
</style>

View File

@ -3,13 +3,14 @@
* @Date: 2025-06-26 17:23:52
-->
<template>
<a-modal
v-model:visible="visible"
<Modal
v-model:open="visible"
width="400px"
modal-class="exit-account-modal"
wrapClassName="exit-account-modal"
show-close="false"
:footer="false"
@close="onClose"
:footer="null"
@cancel="onClose"
centered
>
<div class="flex items-center mb-16px">
<img :src="icon1" width="20" height="20" class="mr-12px" />
@ -17,15 +18,14 @@
</div>
<p class="m-0 p-0 mb-24px s2 ml-32px">退出登录后你将无法收到该账号的通知</p>
<div class="flex items-center justify-end">
<a-button class="!rounded-4px" size="medium" @click="onClose">返回</a-button>
<a-button type="primary" class="ml-16px danger-btn" status="danger" size="medium" @click="onLogout"
>退出登录</a-button
>
<Button @click="onClose">返回</Button>
<Button danger type="primary" @click="onLogout" class="ml-8px">退出登录</Button>
</div>
</a-modal>
</Modal>
</template>
<script setup>
import { Modal, Button, message } from 'ant-design-vue';
import { ref } from 'vue';
import { fetchLogOut } from '@/api/all/login';
import { handleUserLogout } from '@/utils/user';
@ -46,7 +46,7 @@ async function onLogout() {
const { code } = await fetchLogOut();
if (code === 200) {
handleUserLogout();
AMessage.success('退出登录成功');
message.success('退出登录成功');
onClose();
}
}
@ -56,14 +56,14 @@ defineExpose({ open });
<style lang="scss">
.exit-account-modal {
border-radius: 8px;
border: 1px solid var(--BG-300, #e6e6e8) !important;
background-color: var(--BG-white, #fff) !important;
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
.arco-modal-header {
// border-radius: 8px;
// border: 1px solid var(--BG-300, #e6e6e8) !important;
// background-color: var(--BG-white, #fff) !important;
// box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
.ant-modal-header {
display: none;
}
.arco-modal-body {
.ant-modal-body {
padding: 24px;
.s1 {
color: var(--Text-1, #211f24);
@ -81,15 +81,6 @@ defineExpose({ open });
font-weight: 400;
line-height: 20px; /* 166.667% */
}
.cancel-btn {
border-radius: 4px;
}
.danger-btn {
background: var(--Functional-Danger-6, #f64b31) !important;
&:hover {
background: var(--Functional-Danger-6, #f64b31) !important;
}
}
}
}
</style>

View File

@ -10,33 +10,31 @@
<div class="agent-entry mx-16px" :class="isAgentRoute ? 'agent' : ''" @click="handleAgentClick"></div>
<!-- 头像设置 -->
<a-dropdown trigger="click" class="layout-avatar-dropdown">
<a-avatar class="cursor-pointer" :size="32">
<img alt="avatar" src="@/assets/avatar.svg" />
</a-avatar>
<template #content>
<div>
<a-doption>
<a-space class="flex justify-between w-100%" @click="setServerMenu">
<Dropdown trigger="click" overlayClassName="layout-avatar-dropdown">
<img alt="avatar" src="@/assets/avatar.svg" class="cursor-pointer w-32px h-32px rounded-50%" />
<template #overlay>
<Menu>
<MenuItem>
<div class="h-full flex justify-between items-center w-100%" @click="setServerMenu">
<div class="flex items-center">
<img :src="icon1" class="w-16px h-16px mr-8px" />
<span>管理中心</span>
</div>
<icon-right size="12" />
</a-space>
</a-doption>
<a-dsubmenu value="option-1" position="lt" trigger="hover" class="enterprises-dsubmenu">
<a-doption class="enterprises-doption">
<a-space class="flex justify-between w-100%">
<div class="flex items-center">
<img :src="icon3" class="w-16px h-16px mr-8px" />
<span>切换企业账号</span>
</div>
</MenuItem>
<MenuItem>
<SubMenu value="option-1" position="lt" trigger="hover" popupClassName="enterprises-dsubmenu">
<template #title>
<div class="flex justify-between w-100% h-full items-center">
<div class="flex items-center">
<img :src="icon3" class="w-16px h-16px mr-8px" />
<span>切换企业账号</span>
</div>
<icon-right size="12" />
</div>
<icon-right size="12" />
</a-space>
</a-doption>
<template #content>
<a-doption
</template>
<MenuItem
v-for="(item, index) in enterprises"
:key="index"
class="rounded-8px hover:bg-#F2F3F5"
@ -49,21 +47,21 @@
<span>{{ item.name }}</span>
<icon-check v-if="enterpriseInfo?.id === item.id" size="16" />
</div>
</a-doption>
</template>
</a-dsubmenu>
<a-doption>
<a-space class="flex justify-between w-100%" @click="clickExit">
</MenuItem>
</SubMenu>
</MenuItem>
<MenuItem>
<div class="flex justify-between w-100% h-full items-center" @click="clickExit">
<div class="flex items-center">
<img :src="icon2" class="w-16px h-16px mr-8px" />
<span>退出登录</span>
</div>
<icon-right size="12" />
</a-space>
</a-doption>
</div>
</div>
</MenuItem>
</Menu>
</template>
</a-dropdown>
</Dropdown>
<ExitAccountModal ref="exitAccountModalRef" />
<DownloadCenterModal ref="downloadCenterModalRef" />
@ -71,6 +69,7 @@
</template>
<script setup>
import { Dropdown, Menu, MenuItem, SubMenu } from 'ant-design-vue';
import router from '@/router';
import { useEnterpriseStore } from '@/stores/modules/enterprise';
import { useSidebarStore } from '@/stores/modules/side-bar';
@ -137,35 +136,51 @@ const handleAgentClick = () => {
<style lang="scss">
.layout-avatar-dropdown,
.enterprises-dsubmenu {
.arco-dropdown {
.ant-dropdown-menu {
border-radius: 8px;
border: 1px solid var(--BG-300, #e6e6e8);
background: var(--BG-white, #fff);
padding: 12px 0px;
.arco-dropdown-option {
.ant-dropdown-menu-item {
padding: 0 12px;
margin-bottom: 4px;
&-content {
.ant-dropdown-menu-title-content {
display: flex;
height: 40px;
width: 100%;
padding: 10px 24px;
align-items: center;
.menu-item-text {
color: var(--Text-2, #3c4043);
font-family: $font-family-regular;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 22px;
.ant-dropdown-menu-submenu {
width: 100%;
.ant-dropdown-menu-submenu-title {
padding: 0;
&:hover {
background: none;
}
.ant-dropdown-menu-title-content {
padding: 0 !important;
}
}
.ant-dropdown-menu-submenu-arrow {
display: none;
}
}
}
.arco-dropdown-option-content {
.menu-item-text {
color: var(--Text-2, #3c4043);
font-family: $font-family-regular;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.ant-dropdown-menu-title-content {
border-radius: 8px !important;
}
&:not(.arco-dropdown-option-disabled):hover {
&:not(.ant-dropdown-menu-item):hover {
background-color: transparent;
.arco-dropdown-option-content {
.ant-dropdown-menu-title-content {
background: var(--BG-200, #f2f3f5);
}
}
@ -175,29 +190,29 @@ const handleAgentClick = () => {
.layout-avatar-dropdown,
.enterprises-dsubmenu {
width: 200px;
.arco-dropdown {
.ant-dropdown-menu {
padding: 12px 4px;
.arco-dropdown-option {
.ant-dropdown-menu-item {
padding: 0 !important;
&-content {
.ant-dropdown-menu-title-content {
padding: 0 12px !important;
}
}
}
.arco-dropdown-option-suffix {
.ant-dropdown-option-suffix {
display: none;
}
.enterprises-doption {
.arco-dropdown-option-content {
padding: 0 !important;
border-radius: 8px;
}
&:not(.arco-dropdown-option-disabled):hover {
background-color: transparent;
.arco-dropdown-option-content {
background: var(--BG-200, #f2f3f5);
}
}
}
// .enterprises-doption {
// .ant-dropdown-menu-title-content {
// padding: 0 !important;
// border-radius: 8px;
// }
// &:not(.ant-dropdown-option-disabled):hover {
// background-color: transparent;
// .ant-dropdown-menu-title-content {
// background: var(--BG-200, #f2f3f5);
// }
// }
// }
}
</style>

View File

@ -1,24 +1,24 @@
<template>
<a-modal
v-model:visible="visible"
<Modal
v-model:open="visible"
:title="isBatch ? '批量删除下载记录' : '删除下载记录'"
width="400px"
@close="onClose"
@cancel="onClose"
centered
>
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>确认删除 {{ accountName }} 这条记录吗</span>
</div>
<template #footer>
<a-button size="large" @click="onClose">取消</a-button>
<a-button type="primary" class="ml-16px !bg-#f64b31 !border-none" status="danger" size="large" @click="onDelete"
>确定</a-button
>
<Button @click="onClose">取消</Button>
<Button type="primary" danger @click="onDelete">确定</Button>
</template>
</a-modal>
</Modal>
</template>
<script setup>
import { Modal, Button, message } from 'ant-design-vue';
import { ref } from 'vue';
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
@ -52,7 +52,7 @@ async function onDelete() {
const { code } = await _fn(_params);
if (code === 200) {
AMessage.success('删除成功');
message.success('删除成功');
isBatch.value ? emits('batchUpdate') : emits('update');
onClose();
}

View File

@ -1,7 +1,8 @@
<script lang="jsx">
import { ref, computed } from 'vue';
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue';
import { Button, Checkbox, Input, Tooltip, Table, Pagination, message, notification } from 'ant-design-vue';
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
import NoData from '@/components/no-data';
import { getTask, postRedoTask, postBatchDownload, batchQueryTaskStatus } from '@/api/all/common';
import { INITIAL_FORM, TABLE_COLUMNS } from './constants';
@ -21,8 +22,6 @@ export default {
dataSource,
pageInfo,
onPageChange,
onPageSizeChange,
rowSelection,
handleSelect,
handleSelectAll,
DEFAULT_PAGE_INFO,
@ -30,9 +29,6 @@ export default {
onPageChange: () => {
getData();
},
onPageSizeChange: () => {
getData();
},
});
let queryTaskTimer = null;
@ -75,10 +71,12 @@ export default {
getData();
};
const handleSorterChange = (column, order) => {
query.value.sort_column = column;
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
reload();
const handleSorterChange = (pagination, filters, sorter) => {
if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
query.value.sort_column = sorter.columnKey;
query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
reload();
}
};
const handleSearch = () => {
@ -137,10 +135,10 @@ export default {
completeTaskNum++;
const notificationId = downloadTaskInfos.value.find((v) => v.id === id)?.randomId;
notificationId && Notification.remove(notificationId);
notificationId && notification.close(notificationId);
if (status === 1) {
AMessage.success('批量下载已完成,正在下载文件...');
message.success('批量下载已完成,正在下载文件...');
downloadByUrl(file);
} else if (status === 2) {
const onReDownload = () => {
@ -208,11 +206,11 @@ export default {
<div class="filter-row-item flex items-center">
<span class="label">操作人员</span>
<Input
v-model={query.value.operator_name}
v-model:value={query.value.operator_name}
class="w-240px"
placeholder="请输入操作人员"
size="medium"
allow-clear
size="middle"
allowClear
onChange={handleSearch}
v-slots={{
prefix: () => <IconSearch />,
@ -222,11 +220,11 @@ export default {
<div class="filter-row-item flex items-center">
<span class="label">所属模块</span>
<Input
v-model={query.value.module}
v-model:value={query.value.module}
class="w-240px"
placeholder="请输入所属模块"
size="medium"
allow-clear
size="middle"
allowClear
onChange={handleSearch}
v-slots={{
prefix: () => <IconSearch />,
@ -239,17 +237,17 @@ export default {
{dataSource.value.length > 0 && selectedRows.value.length > 0 && (
<div
class={[
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-42px',
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-48px items-center',
selectedRows.value.length > 0 ? ' selected' : '',
].join('')}
>
<div class="flex items-center">
<div class="flex items-center">
<Checkbox
modelValue={checkedAll.value}
checked={checkedAll.value}
indeterminate={indeterminate.value}
class="mr-8px"
onChange={handleSelectAll}
onChange={(e) => handleSelectAll(e.target.checked)}
/>
<span class="label mr-24px">
已选
@ -271,104 +269,96 @@ export default {
{/* 表格 */}
<Table
ref="tableRef"
data={dataSource.value}
column-resizable
row-key="id"
row-selection={rowSelection.value}
selected-keys={selectedRowKeys.value}
dataSource={dataSource.value}
rowKey="id"
rowSelection={{
selectedRowKeys: selectedRowKeys.value,
onSelect: handleSelect,
onSelectAll: handleSelectAll,
}}
pagination={false}
scroll={{ x: '100%', y: '100%' }}
class="w-100% flex-1 overflow-hidden"
bordered
onSorterChange={handleSorterChange}
onSelect={handleSelect}
onSelectAll={handleSelectAll}
showSorterTooltip={false}
onChange={handleSorterChange}
v-slots={{
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 content={column.tooltip} position="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="outline" size="mini" class="search-btn" onClick={() => handleDownload(record)}>
{record.status === enumTaskStatus.Failed ? '重新导出' : '下载'}
</Button>
)}
</div>
),
}}
/>
</>
),
emptyText: () => <NoData />,
}}
/>
>
{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={() => (
<>
<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>
)}
</>
)}
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 && (
<div class="flex justify-end my-16px">
<Pagination
total={pageInfo.value.total}
size="mini"
show-total
show-jumper
show-page-size
size="small"
showTotal={(total, range) => `${total} 条记录`}
showQuickJumper
showSizeChanger
current={pageInfo.value.page}
page-size={pageInfo.value.page_size}
pageSize={pageInfo.value.page_size}
onChange={onPageChange}
onPageSizeChange={onPageSizeChange}
/>
</div>
)}

View File

@ -1,25 +1,25 @@
<template>
<a-modal
v-model:visible="visible"
<Modal
v-model:open="visible"
:title="isBatch ? '批量删除导入记录' : '删除导入记录'"
width="400px"
@close="onClose"
centered
@cancel="onClose"
>
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>确认删除 {{ accountName }} 这条记录吗</span>
</div>
<template #footer>
<a-button size="large" @click="onClose">取消</a-button>
<a-button type="primary" class="ml-16px !bg-#f64b31 !border-none" status="danger" size="large" @click="onDelete"
>确定</a-button
>
<Button @click="onClose">取消</Button>
<Button type="primary" danger @click="onDelete">确定</Button>
</template>
</a-modal>
</Modal>
</template>
<script setup>
import { ref } from 'vue';
import { Modal, Button, message } from 'ant-design-vue';
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
@ -51,7 +51,7 @@ async function onDelete() {
const _params = isBatch.value ? { ids: taskId.value } : taskId.value;
const { code } = await _fn(_params);
if (code === 200) {
AMessage.success('删除成功');
message.success('删除成功');
isBatch.value ? emits('batchUpdate') : emits('update');
onClose();
}

View File

@ -1,6 +1,6 @@
<script lang="jsx">
import { ref, computed } from 'vue';
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue';
import { Button, Tooltip, Table, Pagination } from 'ant-design-vue';
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
import NoData from '@/components/no-data';
import { getTask } from '@/api/all/common';
@ -21,8 +21,6 @@ export default {
dataSource,
pageInfo,
onPageChange,
onPageSizeChange,
rowSelection,
handleSelect,
handleSelectAll,
DEFAULT_PAGE_INFO,
@ -30,9 +28,6 @@ export default {
onPageChange: () => {
getData();
},
onPageSizeChange: () => {
getData();
},
});
const query = ref({ ...INITIAL_FORM });
@ -72,10 +67,12 @@ export default {
getData();
};
const handleSorterChange = (column, order) => {
query.value.sort_column = column;
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
reload();
const handleSorterChange = (pagination, filters, sorter) => {
if (sorter && sorter.columnKey) {
query.value.sort_column = sorter.columnKey;
query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
reload();
}
};
const handleSearch = () => {
@ -118,171 +115,98 @@ export default {
return () => (
<div class="import-task-wrap">
{/* 筛选行 */}
{/* <div class="filter-row flex mb-16px">
<div class="filter-row-item flex items-center">
<span class="label">操作人员</span>
<Input
v-model={query.value.operator_name}
class="w-240px"
placeholder="请输入操作人员"
size="medium"
allow-clear
onChange={handleSearch}
v-slots={{
prefix: () => <IconSearch />,
}}
/>
</div>
<div class="filter-row-item flex items-center">
<span class="label">所属模块</span>
<Input
v-model={query.value.module}
class="w-240px"
placeholder="请输入所属模块"
size="medium"
allow-clear
onChange={handleSearch}
v-slots={{
prefix: () => <IconSearch />,
}}
/>
</div>
</div> */}
{/* 已选提示行 */}
{/* {dataSource.value.length > 0 && selectedRows.value.length > 0 && (
<div
class={[
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-42px',
selectedRows.value.length > 0 ? ' selected' : '',
].join('')}
>
<div class="flex items-center">
<div class="flex items-center">
<Checkbox
modelValue={checkedAll.value}
indeterminate={indeterminate.value}
class="mr-8px"
onChange={handleSelectAll}
/>
<span class="label mr-24px">
已选
<span class="color-#6D4CFE">{selectedRows.value.length}</span>
个文件
</span>
<span class="operation-btn" onClick={handleBatchDownload}>
批量下载
</span>
<span class="operation-btn red" onClick={handleBatchDelete}>
批量删除
</span>
</div>
</div>
<IconClose size={16} class="cursor-pointer color-#737478" onClick={handleCloseTip} />
</div>
)} */}
{/* 表格 */}
<Table
ref="tableRef"
data={dataSource.value}
column-resizable
row-key="id"
selected-keys={selectedRowKeys.value}
dataSource={dataSource.value}
rowKey="id"
rowSelection={{
selectedRowKeys: selectedRowKeys.value,
onSelect: handleSelect,
onSelectAll: handleSelectAll,
}}
pagination={false}
scroll={{ x: '100%', y: '100%' }}
class="w-100% flex-1 overflow-hidden"
bordered
onSorterChange={handleSorterChange}
onSelect={handleSelect}
onSelectAll={handleSelectAll}
showSorterTooltip={false}
onChange={handleSorterChange}
v-slots={{
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 content={column.tooltip} position="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="outline" size="mini" 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={() => (
<>
<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>
)}
</>
)}
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 && (
<div class="flex justify-end my-16px">
<Pagination
total={pageInfo.value.total}
size="mini"
show-total
show-jumper
show-page-size
size="small"
showTotal={(total, range) => `${total} 条记录`}
showQuickJumper
showSizeChanger
current={pageInfo.value.page}
page-size={pageInfo.value.page_size}
pageSize={pageInfo.value.page_size}
onChange={onPageChange}
onPageSizeChange={onPageSizeChange}
/>
</div>
)}

View File

@ -1,37 +1,39 @@
<template>
<a-modal
v-model:visible="visible"
<Modal
v-model:open="visible"
title="任务中心"
modal-class="task-center-modal"
wrapClassName="task-center-modal"
width="860px"
:mask-closable="false"
:footer="false"
@close="onClose"
:footer="null"
@cancel="onClose"
centered
>
<a-tabs :active-key="activeTab" @tab-click="handleTabClick">
<a-tab-pane key="0" title="导入"> </a-tab-pane>
<a-tab-pane key="1" title="导出"> </a-tab-pane>
</a-tabs>
<Tabs v-model:activeKey="activeTab" @change="handleTabClick">
<TabPane key="1" tab="导入"> </TabPane>
<TabPane key="2" tab="导出"> </TabPane>
</Tabs>
<div class="content">
<component :is="activeTab === '0' ? ImportTask : ExportTask" ref="componentRef" />
<component :is="activeTab === '1' ? ImportTask : ExportTask" ref="componentRef" />
</div>
</a-modal>
</Modal>
</template>
<script setup>
import { Notification } from '@arco-design/web-vue';
import { Checkbox, Modal, Button, Tabs, notification } from 'ant-design-vue';
const { TabPane } = Tabs;
import ExportTask from './components/export-task';
import ImportTask from './components/import-task';
const visible = ref(false);
const componentRef = ref(null);
const activeTab = ref('0');
const activeTab = ref('1');
let timer = null;
const handleTabClick = (key) => {
activeTab.value = key;
// activeTab.value = key;
nextTick(() => {
getData();
});
@ -42,20 +44,21 @@ const getData = () => {
};
const open = () => {
getData();
visible.value = true;
nextTick(() => {
getData();
});
timer = setInterval(() => {
getData();
}, 10000);
visible.value = true;
};
const onClose = () => {
activeTab.value = '0';
clearTimer();
componentRef.value?.unloadComp?.();
Notification.clear();
notification.destroy();
visible.value = false;
};
const clearTimer = () => {

View File

@ -1,7 +1,7 @@
.task-center-modal {
.arco-modal-header {
.ant-modal-header {
border-bottom: none !important;
.arco-modal-title {
.ant-modal-title {
color: var(--Text-1, #211f24);
font-size: 16px;
font-style: normal;
@ -10,7 +10,7 @@
font-family: $font-family-medium;
}
}
.arco-modal-body {
.ant-modal-body {
padding: 0 !important;
display: flex;
flex-direction: column;