Merge pull request 'feature/0710_下载中心_rxd' (#12) from feature/0710_下载中心_rxd into main
Reviewed-on: ai-team/lingji-work-fe#12
@ -1,5 +1,5 @@
|
|||||||
import { configUnocss } from './plugins';
|
import { configUnocss } from './plugins';
|
||||||
import { configAutoImport, configComponents, configIcons } from './unplugin';
|
import { configAutoImport, configComponents,configIcons } from './unplugin';
|
||||||
import viteCompression from 'vite-plugin-compression';
|
import viteCompression from 'vite-plugin-compression';
|
||||||
import progress from 'vite-plugin-progress';
|
import progress from 'vite-plugin-progress';
|
||||||
import defineOptions from 'unplugin-vue-define-options/vite';
|
import defineOptions from 'unplugin-vue-define-options/vite';
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
/*
|
|
||||||
* @Author: RenXiaoDong
|
|
||||||
* @Date: 2025-06-24 16:29:10
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* 自动引入API
|
* 自动引入API
|
||||||
* */
|
* */
|
||||||
import AutoImport from 'unplugin-auto-import/vite';
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
|
|
||||||
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
|
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
|
||||||
import IconsResolver from 'unplugin-icons/resolver';
|
|
||||||
|
|
||||||
import { layoutsResolver } from '../utils';
|
import { layoutsResolver } from '../utils';
|
||||||
|
|
||||||
@ -21,7 +16,7 @@ export function configAutoImport() {
|
|||||||
'@vueuse/core',
|
'@vueuse/core',
|
||||||
{
|
{
|
||||||
dayjs: [['default', 'dayjs']],
|
dayjs: [['default', 'dayjs']],
|
||||||
'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty', 'merge'],
|
'lodash-es': ['cloneDeep', 'omit', 'pick', 'union', 'uniq', 'isNumber', 'uniqBy', 'isEmpty', 'merge', 'debounce'],
|
||||||
'@/hooks': ['useModal'],
|
'@/hooks': ['useModal'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -31,9 +26,6 @@ export function configAutoImport() {
|
|||||||
enable: true,
|
enable: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
IconsResolver({
|
|
||||||
enabledCollections: [],
|
|
||||||
}),
|
|
||||||
layoutsResolver(),
|
layoutsResolver(),
|
||||||
],
|
],
|
||||||
eslintrc: {
|
eslintrc: {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { kebabCase } from 'unplugin-vue-components';
|
|||||||
import Components from 'unplugin-vue-components/vite';
|
import Components from 'unplugin-vue-components/vite';
|
||||||
|
|
||||||
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
|
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
|
||||||
import IconsResolver from 'unplugin-icons/resolver';
|
|
||||||
|
|
||||||
import { getSep, getPath, setResolve, layoutsResolver } from '../utils';
|
import { getSep, getPath, setResolve, layoutsResolver } from '../utils';
|
||||||
|
|
||||||
@ -20,11 +19,6 @@ export function configComponents() {
|
|||||||
},
|
},
|
||||||
sideEffect: true,
|
sideEffect: true,
|
||||||
}),
|
}),
|
||||||
IconsResolver({
|
|
||||||
prefix: false,
|
|
||||||
customCollections: ['i'],
|
|
||||||
enabledCollections: [],
|
|
||||||
}),
|
|
||||||
layoutsResolver(),
|
layoutsResolver(),
|
||||||
{
|
{
|
||||||
type: 'component',
|
type: 'component',
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
/**
|
// /**
|
||||||
* 自动引入 svg 图标
|
// * 自动引入 svg 图标
|
||||||
* */
|
// * */
|
||||||
import Icons from 'unplugin-icons/vite';
|
import { resolve } from '../utils';
|
||||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
|
||||||
|
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
|
||||||
|
|
||||||
export function configIcons() {
|
export function configIcons() {
|
||||||
return Icons({
|
return createSvgIconsPlugin({
|
||||||
compiler: 'vue3',
|
iconDirs: [resolve( "src/assets/svg")],
|
||||||
customCollections: {
|
symbolId: "icon-[dir]-[name]",
|
||||||
i: FileSystemIconLoader('./src/assets', (svg) => svg.replace(/^<svg /, '<svg class="eo-i" ')),
|
})
|
||||||
},
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
"vite": "^4.0.4",
|
"vite": "^4.0.4",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-progress": "^0.0.6",
|
"vite-plugin-progress": "^0.0.6",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-eslint-parser": "^9.1.0",
|
"vue-eslint-parser": "^9.1.0",
|
||||||
"vue-tsc": "^1.0.24"
|
"vue-tsc": "^1.0.24"
|
||||||
},
|
},
|
||||||
|
|||||||
12
src/App.vue
@ -5,14 +5,16 @@
|
|||||||
</a-config-provider>
|
</a-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
|
|
||||||
import { getUserEnterpriseInfo } from '@/utils/user';
|
import { getUserEnterpriseInfo } from '@/utils/user';
|
||||||
|
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||||
|
|
||||||
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const sidebarStore = useSidebarStore();
|
||||||
|
|
||||||
const redTheme = {
|
const redTheme = {
|
||||||
token: {
|
token: {
|
||||||
@ -20,7 +22,6 @@ const redTheme = {
|
|||||||
colorLink: '#f5222d', // 链接色
|
colorLink: '#f5222d', // 链接色
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// 初始化企业信息
|
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
const { isLogin, getUserInfo } = userStore;
|
const { isLogin, getUserInfo } = userStore;
|
||||||
@ -28,6 +29,10 @@ const init = async () => {
|
|||||||
if (isLogin) {
|
if (isLogin) {
|
||||||
await getUserInfo(); // 初始化用户信息
|
await getUserInfo(); // 初始化用户信息
|
||||||
await getUserEnterpriseInfo();
|
await getUserEnterpriseInfo();
|
||||||
|
|
||||||
|
sidebarStore.startUnreadInfoPolling();
|
||||||
|
} else {
|
||||||
|
sidebarStore.stopUnreadInfoPolling();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +45,9 @@ onMounted(() => {
|
|||||||
console.error(`发现catch报错:${event.reason}`);
|
console.error(`发现catch报错:${event.reason}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
sideBarStore.stopUnreadInfoPolling();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -13,3 +13,48 @@ export const getCustomColumns = (params = {}) => {
|
|||||||
export const updateCustomColumns = (params = {}) => {
|
export const updateCustomColumns = (params = {}) => {
|
||||||
return Http.put('/v1/custom-columns', params);
|
return Http.put('/v1/custom-columns', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 任务中心-分页
|
||||||
|
export const getTask = (params = {}) => {
|
||||||
|
return Http.get('/v1/tasks', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-批量删除
|
||||||
|
export const deleteBatchTasks = (params = {}) => {
|
||||||
|
return Http.delete('/v1/tasks', { data: params });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-删除
|
||||||
|
export const deleteTask = (id: string) => {
|
||||||
|
return Http.delete(`/v1/tasks/${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-查询任务状态
|
||||||
|
export const getTaskStatus = (id: string) => {
|
||||||
|
return Http.get(`/v1/tasks/${id}/status`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-获取未读任务
|
||||||
|
export const getTaskUnread = () => {
|
||||||
|
return Http.get(`/v1/tasks/unread`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-已读
|
||||||
|
export const patchTaskRead = (params = {}) => {
|
||||||
|
return Http.patch('/v1/tasks/read', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-重做任务
|
||||||
|
export const postRedoTask = (id: string) => {
|
||||||
|
return Http.post(`/v1/tasks/${id}/redo`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-批量下载
|
||||||
|
export const postBatchDownload = (params = {}) => {
|
||||||
|
return Http.post(`/v1/tasks/batch-download`, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务中心-批量查询任务状态
|
||||||
|
export const batchQueryTaskStatus = (params = {}) => {
|
||||||
|
return Http.get(`/v1/tasks/batch-query-status`, params);
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 571 B |
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 984 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
3
src/assets/svg/svg-taskCenter.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<path d="M9.2041 2.32617C11.6897 2.32617 13.7595 4.10156 14.2178 6.45312C15.2902 7.15513 16 8.36571 16 9.74316C16 11.9142 14.2393 13.6738 12.0684 13.6738H3.93066C1.75983 13.6736 3.83345e-05 11.9141 0 9.74316C0 9.63068 0.00538693 9.51927 0.0146484 9.40918C0.00563418 9.29576 7.49779e-06 9.18117 0 9.06543C0 6.69938 1.91817 4.78127 4.28418 4.78125C4.46476 4.78126 4.64268 4.79275 4.81738 4.81445C5.70979 3.32391 7.34052 2.32633 9.2041 2.32617ZM9.2041 3.52539C7.77983 3.52555 6.53145 4.28786 5.84668 5.43164L5.44531 6.10156L4.66992 6.00586C4.53994 5.9897 4.41129 5.98048 4.28418 5.98047C2.6345 5.98048 1.28692 7.27669 1.2041 8.90625L1.2002 9.06543C1.2002 9.14467 1.20305 9.22752 1.20996 9.31445L1.21777 9.41211L1.20996 9.50977L1.2002 9.74316C1.20023 11.2514 2.42259 12.4744 3.93066 12.4746H12.0684C13.5297 12.4746 14.7226 11.3265 14.7959 9.88379L14.7998 9.74316C14.7998 8.78847 14.3094 7.94732 13.5605 7.45703L13.1377 7.17969L13.04 6.68262C12.6894 4.8834 11.104 3.52539 9.2041 3.52539ZM6.79688 6.74121C6.93478 6.61179 7.13694 6.57633 7.31055 6.65137C7.48407 6.72651 7.59654 6.89783 7.59668 7.08691V10.749C7.59642 11.0109 7.38396 11.2235 7.12207 11.2236C6.86013 11.2236 6.64772 11.0109 6.64746 10.749V8.18457L6.05859 8.73828C5.86754 8.91775 5.5662 8.90785 5.38672 8.7168C5.20757 8.52576 5.21736 8.2253 5.4082 8.0459L6.79688 6.74121ZM8.87793 6.6123C9.14003 6.61235 9.35254 6.82578 9.35254 7.08789V9.65137L9.94141 9.09863C10.1325 8.91917 10.4338 8.92809 10.6133 9.11914C10.7925 9.31017 10.7827 9.61061 10.5918 9.79004L9.20312 11.0947C9.0652 11.2242 8.86308 11.2596 8.68945 11.1846C8.51588 11.1094 8.40339 10.9382 8.40332 10.749V7.08789C8.40332 6.82582 8.61588 6.61241 8.87793 6.6123Z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
83
src/components/_base/navbar/components/navbar-menu/index.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div class="navbar-menu h-100%">
|
||||||
|
<a-menu mode="horizontal" :selected-keys="selectedKey">
|
||||||
|
<a-menu-item v-for="item in menuList" :key="String(item.id)">
|
||||||
|
<template v-if="item.children">
|
||||||
|
<a-dropdown :popup-max-height="false" class="layout-menu-item-dropdown">
|
||||||
|
<a-button>
|
||||||
|
<span class="menu-item-text mr-2px"> {{ item.name }}</span>
|
||||||
|
<icon-caret-down size="16" class="arco-icon-down !mr-0" />
|
||||||
|
</a-button>
|
||||||
|
<template #content>
|
||||||
|
<a-doption v-for="(child, ind) in item.children" :key="ind" @click="handleDropdownClick(child)">
|
||||||
|
<span class="menu-item-text"> {{ child.name }}</span>
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-menu-item :key="String(item.id)" @click="handleDropdownClick(item)">
|
||||||
|
<span class="menu-item-text"> {{ item.name }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</template>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||||
|
import router from '@/router';
|
||||||
|
|
||||||
|
const sidebarStore = useSidebarStore();
|
||||||
|
|
||||||
|
const selectedKey = computed(() => {
|
||||||
|
return [String(sidebarStore.activeMenuId)];
|
||||||
|
});
|
||||||
|
const menuList = computed(() => {
|
||||||
|
return sidebarStore.menuList;
|
||||||
|
});
|
||||||
|
const handleDropdownClick = (item) => {
|
||||||
|
router.push({ name: item.routeName });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.layout-menu-item-dropdown {
|
||||||
|
.arco-dropdown {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--BG-300, #e6e6e8);
|
||||||
|
background: var(--BG-white, #fff);
|
||||||
|
padding: 12px 0px;
|
||||||
|
.arco-dropdown-option {
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 24px;
|
||||||
|
align-items: center;
|
||||||
|
.menu-item-text {
|
||||||
|
color: var(--Text-2, #3c4043);
|
||||||
|
font-family: 'PuHuiTi-Regular';
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px; /* 137.5% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(.arco-dropdown-option-disabled):hover {
|
||||||
|
background-color: transparent;
|
||||||
|
.arco-dropdown-option-content {
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--BG-200, #f2f3f5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
.navbar-menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 40px;
|
||||||
|
.menu-item-text {
|
||||||
|
color: var(--Text-2, #3c4043);
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
:deep(.arco-menu) {
|
||||||
|
height: 100%;
|
||||||
|
.arco-menu-inner {
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
.arco-menu-item {
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
&.arco-menu-selected {
|
||||||
|
.menu-item-text,
|
||||||
|
.arco-menu-selected-label {
|
||||||
|
color: #6d4cfe;
|
||||||
|
}
|
||||||
|
.arco-menu-selected-label {
|
||||||
|
background: var(--Brand-Brand-6, #6d4cfe);
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 50%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-icon-down {
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
.arco-dropdown-open .arco-icon-down {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
196
src/components/_base/navbar/components/right-side/index.vue
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div class="right-wrap">
|
||||||
|
<div class="relative mr-12px" @click="setUnread">
|
||||||
|
<SvgIcon
|
||||||
|
name="svg-taskCenter"
|
||||||
|
size="16"
|
||||||
|
class="cursor-pointer color-#737478 hover:color-#6D4CFE"
|
||||||
|
@click="openDownloadCenter"
|
||||||
|
/>
|
||||||
|
<div class="w-4px h-4px rounded-50% bg-#F64B31 absolute top-1px right-1px" v-if="hasUnreadInfo"></div>
|
||||||
|
</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">
|
||||||
|
<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>
|
||||||
|
<icon-right size="12" />
|
||||||
|
</a-space>
|
||||||
|
</a-doption>
|
||||||
|
<template #content>
|
||||||
|
<a-doption
|
||||||
|
v-for="(item, index) in enterprises"
|
||||||
|
:key="index"
|
||||||
|
class="rounded-8px hover:bg-#F2F3F5"
|
||||||
|
@click="onEnterpriseItemClick(item)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex items-center w-100% justify-between"
|
||||||
|
:class="enterpriseInfo?.id === item.id ? '!color-#6D4CFE' : ''"
|
||||||
|
>
|
||||||
|
<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">
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
|
||||||
|
<ExitAccountModal ref="exitAccountModalRef" />
|
||||||
|
<DownloadCenterModal ref="downloadCenterModalRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import router from '@/router';
|
||||||
|
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||||
|
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||||
|
import { useUserStore } from '@/stores';
|
||||||
|
|
||||||
|
import ExitAccountModal from '@/components/_base/exit-account-modal';
|
||||||
|
import DownloadCenterModal from '../task-center-modal';
|
||||||
|
|
||||||
|
import icon1 from '@/assets/option.svg';
|
||||||
|
import icon2 from '@/assets/exit.svg';
|
||||||
|
import icon3 from '@/assets/change.svg';
|
||||||
|
|
||||||
|
const enterpriseStore = useEnterpriseStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const sideBarStore = useSidebarStore();
|
||||||
|
|
||||||
|
const hasUnreadInfo = computed(() => sideBarStore.unreadInfo.length);
|
||||||
|
|
||||||
|
const exitAccountModalRef = ref(null);
|
||||||
|
const downloadCenterModalRef = ref(null);
|
||||||
|
|
||||||
|
const enterprises = computed(() => {
|
||||||
|
return userStore.userInfo?.enterprises ?? [];
|
||||||
|
});
|
||||||
|
const enterpriseInfo = computed(() => {
|
||||||
|
return enterpriseStore?.enterpriseInfo ?? {};
|
||||||
|
});
|
||||||
|
|
||||||
|
const openDownloadCenter = () => {
|
||||||
|
downloadCenterModalRef.value.open();
|
||||||
|
};
|
||||||
|
const onEnterpriseItemClick = async (item) => {
|
||||||
|
enterpriseStore.setEnterpriseInfo(item);
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
const clickExit = async () => {
|
||||||
|
exitAccountModalRef.value?.open();
|
||||||
|
};
|
||||||
|
const setServerMenu = () => {
|
||||||
|
router.push('/management/person');
|
||||||
|
};
|
||||||
|
const setUnread = () => {
|
||||||
|
if (sideBarStore.unreadInfo.length) {
|
||||||
|
sideBarStore.removeTaskUnreadInfo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.right-wrap {
|
||||||
|
display: flex;
|
||||||
|
padding-right: 20px;
|
||||||
|
list-style: none;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.layout-avatar-dropdown,
|
||||||
|
.enterprises-dsubmenu {
|
||||||
|
.arco-dropdown {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--BG-300, #e6e6e8);
|
||||||
|
background: var(--BG-white, #fff);
|
||||||
|
padding: 12px 0px;
|
||||||
|
.arco-dropdown-option {
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
&-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-dropdown-option-content {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
&:not(.arco-dropdown-option-disabled):hover {
|
||||||
|
background-color: transparent;
|
||||||
|
.arco-dropdown-option-content {
|
||||||
|
background: var(--BG-200, #f2f3f5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layout-avatar-dropdown,
|
||||||
|
.enterprises-dsubmenu {
|
||||||
|
width: 200px;
|
||||||
|
.arco-dropdown {
|
||||||
|
padding: 12px 4px;
|
||||||
|
.arco-dropdown-option {
|
||||||
|
padding: 0 !important;
|
||||||
|
&-content {
|
||||||
|
padding: 0 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
export const INITIAL_FORM = {
|
||||||
|
type: 1,
|
||||||
|
operator_name: '',
|
||||||
|
module: '',
|
||||||
|
sort_column: undefined,
|
||||||
|
sort_order: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TABLE_COLUMNS = [
|
||||||
|
{
|
||||||
|
title: '文件名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属模块',
|
||||||
|
dataIndex: 'module',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
width: 180,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人员',
|
||||||
|
dataIndex: 'operator.name',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:title="isBatch ? '批量删除下载记录' : '删除下载记录'"
|
||||||
|
width="400px"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||||
|
<span>确认删除 {{ accountName }} 这条记录吗?</span>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<a-button class="cancel-btn" 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
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
|
|
||||||
|
const emits = defineEmits(['update', 'close', 'batchUpdate']);
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const taskId = ref(null);
|
||||||
|
const accountName = ref('');
|
||||||
|
|
||||||
|
const isBatch = computed(() => Array.isArray(taskId.value));
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
visible.value = false;
|
||||||
|
taskId.value = null;
|
||||||
|
accountName.value = '';
|
||||||
|
emits('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (record) => {
|
||||||
|
const { id = null, name = '' } = record;
|
||||||
|
taskId.value = id;
|
||||||
|
accountName.value = name;
|
||||||
|
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
const _fn = isBatch.value ? deleteBatchTasks : deleteTask;
|
||||||
|
const _params = isBatch.value ? { ids: taskId.value } : taskId.value;
|
||||||
|
|
||||||
|
const { code } = await _fn(_params);
|
||||||
|
if (code === 200) {
|
||||||
|
AMessage.success('删除成功');
|
||||||
|
isBatch.value ? emits('batchUpdate') : emits('update');
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
@ -0,0 +1,386 @@
|
|||||||
|
<script lang="jsx">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-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';
|
||||||
|
import { EXPORT_TASK_STATUS, enumTaskStatus } from '../../constants';
|
||||||
|
import { formatTableField, exactFormatTime, genRandomId } from '@/utils/tools';
|
||||||
|
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||||
|
import { downloadByUrl } from '@/utils/tools';
|
||||||
|
import DeleteTaskModal from './delete-task-modal.vue';
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
import { showExportNotification, showFailExportNotification } from '@/utils/arcoD';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup(props, { emit, expose }) {
|
||||||
|
const {
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
dataSource,
|
||||||
|
pageInfo,
|
||||||
|
onPageChange,
|
||||||
|
onPageSizeChange,
|
||||||
|
rowSelection,
|
||||||
|
handleSelect,
|
||||||
|
handleSelectAll,
|
||||||
|
DEFAULT_PAGE_INFO,
|
||||||
|
} = useTableSelectionWithPagination({
|
||||||
|
onPageChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
onPageSizeChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let queryTaskTimer = null;
|
||||||
|
|
||||||
|
const query = ref(cloneDeep(INITIAL_FORM));
|
||||||
|
const deleteTaskModalRef = ref(null);
|
||||||
|
const downloadTaskInfos = ref([]);
|
||||||
|
|
||||||
|
const checkedAll = computed(() => selectedRows.value.length === dataSource.value.length);
|
||||||
|
const indeterminate = computed(
|
||||||
|
() => selectedRows.value.length > 0 && selectedRows.value.length < dataSource.value.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
query.value = cloneDeep(INITIAL_FORM);
|
||||||
|
pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO);
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
selectedRows.value = [];
|
||||||
|
dataSource.value = [];
|
||||||
|
downloadTaskInfos.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const { page, page_size } = pageInfo.value;
|
||||||
|
const { code, data } = await getTask({
|
||||||
|
...query.value,
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
});
|
||||||
|
if (code === 200) {
|
||||||
|
dataSource.value = data?.data ?? [];
|
||||||
|
pageInfo.value.total = data?.total;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const reload = () => {
|
||||||
|
pageInfo.value.page = 1;
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSorterChange = (column, order) => {
|
||||||
|
query.value.sort_column = column;
|
||||||
|
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSelectedRows = () => {
|
||||||
|
selectedRows.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownload = async (record) => {
|
||||||
|
if (record.status === enumTaskStatus.Failed) {
|
||||||
|
const { code } = await postRedoTask(record.id);
|
||||||
|
if (code === 200) {
|
||||||
|
showExportNotification(`正在下载“${record.name}”,请稍后...`);
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
record.file && downloadByUrl(record.file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量下载
|
||||||
|
const handleBatchDownload = debounce(async () => {
|
||||||
|
const { code, data } = await postBatchDownload({ ids: selectedRowKeys.value });
|
||||||
|
if (code === 200) {
|
||||||
|
startBatchDownload(data.id);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
const startBatchDownload = (id) => {
|
||||||
|
const randomId = genRandomId();
|
||||||
|
showExportNotification(
|
||||||
|
`正在批量下载“${selectedRows.value[0]?.name}”等${selectedRows.value.length}个文件,请稍后...`,
|
||||||
|
{
|
||||||
|
duration: 0,
|
||||||
|
id: randomId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
downloadTaskInfos.value.push({
|
||||||
|
id,
|
||||||
|
randomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!queryTaskTimer) {
|
||||||
|
queryTaskTimer = setInterval(() => getSyncTaskStatus(), 3000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getSyncTaskStatus = async () => {
|
||||||
|
const { code, data } = await batchQueryTaskStatus({ ids: downloadTaskInfos.value.map((v) => v.id) });
|
||||||
|
if (code === 200) {
|
||||||
|
let completeTaskNum = 0;
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { status, file, id } = item;
|
||||||
|
if (status !== 0) {
|
||||||
|
completeTaskNum++;
|
||||||
|
|
||||||
|
const notificationId = downloadTaskInfos.value.find((v) => v.id === id)?.randomId;
|
||||||
|
notificationId && Notification.remove(notificationId);
|
||||||
|
|
||||||
|
if (status === 1) {
|
||||||
|
AMessage.success('批量下载已完成,正在下载文件...');
|
||||||
|
downloadByUrl(file);
|
||||||
|
} else if (status === 2) {
|
||||||
|
const onReDownload = () => {
|
||||||
|
startBatchDownload(id);
|
||||||
|
};
|
||||||
|
showFailExportNotification(
|
||||||
|
`${selectedRows.value[0]?.name}”等${selectedRows.value.length}个文件下载失败`,
|
||||||
|
{ onReDownload },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束的任务过滤掉
|
||||||
|
downloadTaskInfos.value = downloadTaskInfos.value.filter((v) => v.id !== id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 全部完成了
|
||||||
|
if (completeTaskNum === data.length) {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
clearSelectedRows();
|
||||||
|
downloadTaskInfos.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (record) => {
|
||||||
|
const { id, name } = record;
|
||||||
|
deleteTaskModalRef.value.open({
|
||||||
|
id,
|
||||||
|
name: `“${name || '-'}”`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleBatchDelete = () => {
|
||||||
|
const ids = selectedRows.value.map((item) => item.id);
|
||||||
|
const names = selectedRows.value.map((item) => `"${item.name || '-'}` + '"').join(',');
|
||||||
|
|
||||||
|
deleteTaskModalRef.value?.open({ id: ids, name: names });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBatchSuccess = () => {
|
||||||
|
selectedRows.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearQueryTaskTimer = () => {
|
||||||
|
if (queryTaskTimer) {
|
||||||
|
clearInterval(queryTaskTimer);
|
||||||
|
queryTaskTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const unloadComp = () => {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
};
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearQueryTaskTimer;
|
||||||
|
});
|
||||||
|
|
||||||
|
expose({ init, reset, unloadComp });
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div class="export-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={clearSelectedRows} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 表格 */}
|
||||||
|
<Table
|
||||||
|
ref="tableRef"
|
||||||
|
data={dataSource.value}
|
||||||
|
column-resizable
|
||||||
|
row-key="id"
|
||||||
|
row-selection={rowSelection.value}
|
||||||
|
selected-keys={selectedRowKeys.value}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: '100%', y: '100%' }}
|
||||||
|
class="w-100% flex-1 overflow-hidden"
|
||||||
|
bordered
|
||||||
|
onSorterChange={handleSorterChange}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
onSelectAll={handleSelectAll}
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 分页 */}
|
||||||
|
{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
|
||||||
|
current={pageInfo.value.page}
|
||||||
|
page-size={pageInfo.value.page_size}
|
||||||
|
onChange={onPageChange}
|
||||||
|
onPageSizeChange={onPageSizeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 删除弹窗 */}
|
||||||
|
<DeleteTaskModal ref={deleteTaskModalRef} onBatchUpdate={onBatchSuccess} onUpdate={getData} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
.export-task-wrap {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.tip-row {
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #f0edff;
|
||||||
|
.label {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&.normal {
|
||||||
|
background: #ebf7f2;
|
||||||
|
.label {
|
||||||
|
color: #211f24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.abnormal {
|
||||||
|
background: #ffe7e4;
|
||||||
|
.label {
|
||||||
|
color: #211f24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.err-btn {
|
||||||
|
background-color: #f64b31 !important;
|
||||||
|
color: var(--BG-white, #fff);
|
||||||
|
font-family: 'PingFang SC';
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.operation-btn {
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--Brand-Brand-6, #6d4cfe);
|
||||||
|
font-family: $font-family-regular;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
&.red {
|
||||||
|
color: #f64b31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.status-box {
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0px 8px;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
span {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&-0 {
|
||||||
|
background: var(--Functional-yellow-1, #fff7e5);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-yellow-6, #ffae00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-1 {
|
||||||
|
background: var(--Functional-Green-1, #ebf7f2);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-Green-6, #25c883);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
background: var(--Functional-Red-1, #ffe9e7);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-Red-6, #f64b31);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
export const INITIAL_FORM = {
|
||||||
|
type: 0,
|
||||||
|
operator_name: '',
|
||||||
|
module: '',
|
||||||
|
sort_column: undefined,
|
||||||
|
sort_order: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TABLE_COLUMNS = [
|
||||||
|
{
|
||||||
|
title: '任务名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属模块',
|
||||||
|
dataIndex: 'module',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '共导入',
|
||||||
|
dataIndex: 'total_number',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '导入成功',
|
||||||
|
dataIndex: 'success_number',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '导入失败',
|
||||||
|
dataIndex: 'fail_number',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '导入时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
width: 180,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人员',
|
||||||
|
dataIndex: 'operator.name',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:title="isBatch ? '批量删除导入记录' : '删除导入记录'"
|
||||||
|
width="400px"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||||
|
<span>确认删除 {{ accountName }} 这条记录吗?</span>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<a-button class="cancel-btn" 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
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
|
|
||||||
|
const emits = defineEmits(['update', 'close', 'batchUpdate']);
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const taskId = ref(null);
|
||||||
|
const accountName = ref('');
|
||||||
|
|
||||||
|
const isBatch = computed(() => Array.isArray(taskId.value));
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
visible.value = false;
|
||||||
|
taskId.value = null;
|
||||||
|
accountName.value = '';
|
||||||
|
emits('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (record) => {
|
||||||
|
const { id = null, name = '' } = record;
|
||||||
|
taskId.value = id;
|
||||||
|
accountName.value = name;
|
||||||
|
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
const _fn = isBatch.value ? deleteBatchTasks : deleteTask;
|
||||||
|
const _params = isBatch.value ? { ids: taskId.value } : taskId.value;
|
||||||
|
const { code } = await _fn(_params);
|
||||||
|
if (code === 200) {
|
||||||
|
AMessage.success('删除成功');
|
||||||
|
isBatch.value ? emits('batchUpdate') : emits('update');
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
@ -0,0 +1,300 @@
|
|||||||
|
<script lang="jsx">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue';
|
||||||
|
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||||
|
import NoData from '@/components/no-data';
|
||||||
|
import { getTask } from '@/api/all/common';
|
||||||
|
import { INITIAL_FORM, TABLE_COLUMNS } from './constants';
|
||||||
|
import { IMPORT_TASK_STATUS, enumTaskStatus } from '../../constants';
|
||||||
|
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||||
|
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||||
|
import { downloadByUrl } from '@/utils/tools';
|
||||||
|
import DeleteTaskModal from './delete-task-modal.vue';
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
// import { showExportNotification } from '@/utils/arcoD';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup(props, { emit, expose }) {
|
||||||
|
const {
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
dataSource,
|
||||||
|
pageInfo,
|
||||||
|
onPageChange,
|
||||||
|
onPageSizeChange,
|
||||||
|
rowSelection,
|
||||||
|
handleSelect,
|
||||||
|
handleSelectAll,
|
||||||
|
DEFAULT_PAGE_INFO,
|
||||||
|
} = useTableSelectionWithPagination({
|
||||||
|
onPageChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
onPageSizeChange: () => {
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const query = ref({ ...INITIAL_FORM });
|
||||||
|
const deleteTaskModalRef = ref(null);
|
||||||
|
|
||||||
|
const checkedAll = computed(() => selectedRows.value.length === dataSource.value.length);
|
||||||
|
const indeterminate = computed(
|
||||||
|
() => selectedRows.value.length > 0 && selectedRows.value.length < dataSource.value.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
query.value = { ...INITIAL_FORM };
|
||||||
|
pageInfo.value = { ...DEFAULT_PAGE_INFO };
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
selectedRows.value = [];
|
||||||
|
dataSource.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const { page, page_size } = pageInfo.value;
|
||||||
|
const { code, data } = await getTask({
|
||||||
|
...query.value,
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
});
|
||||||
|
if (code === 200) {
|
||||||
|
dataSource.value = data?.data ?? [];
|
||||||
|
pageInfo.value.total = data?.total;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const reload = () => {
|
||||||
|
pageInfo.value.page = 1;
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSorterChange = (column, order) => {
|
||||||
|
query.value.sort_column = column;
|
||||||
|
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseTip = () => {
|
||||||
|
selectedRows.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownload = (record) => {
|
||||||
|
downloadByUrl(record.file);
|
||||||
|
// showExportNotification(`正在下载“${record.name}”,请稍后...`)
|
||||||
|
};
|
||||||
|
const handleBatchDownload = () => {
|
||||||
|
// 批量下载逻辑
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (record) => {
|
||||||
|
const { id, name } = record;
|
||||||
|
deleteTaskModalRef.value.open({
|
||||||
|
id,
|
||||||
|
name: `“${name || '-'}”`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleBatchDelete = () => {
|
||||||
|
const ids = selectedRows.value.map((item) => item.id);
|
||||||
|
const names = selectedRows.value.map((item) => `"${item.name || '-'}` + '"').join(',');
|
||||||
|
deleteTaskModalRef.value?.open({ id: ids, name: names });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBatchSuccess = () => {
|
||||||
|
selectedRows.value = [];
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
expose({ init, reset });
|
||||||
|
|
||||||
|
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}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: '100%', y: '100%' }}
|
||||||
|
class="w-100% flex-1 overflow-hidden"
|
||||||
|
bordered
|
||||||
|
onSorterChange={handleSorterChange}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
onSelectAll={handleSelectAll}
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 分页 */}
|
||||||
|
{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
|
||||||
|
current={pageInfo.value.page}
|
||||||
|
page-size={pageInfo.value.page_size}
|
||||||
|
onChange={onPageChange}
|
||||||
|
onPageSizeChange={onPageSizeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 删除弹窗 */}
|
||||||
|
<DeleteTaskModal ref={deleteTaskModalRef} onBatchUpdate={onBatchSuccess} onUpdate={getData} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
.import-task-wrap {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.tip-row {
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #f0edff;
|
||||||
|
.label {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&.normal {
|
||||||
|
background: #ebf7f2;
|
||||||
|
.label {
|
||||||
|
color: #211f24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.abnormal {
|
||||||
|
background: #ffe7e4;
|
||||||
|
.label {
|
||||||
|
color: #211f24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.err-btn {
|
||||||
|
background-color: #f64b31 !important;
|
||||||
|
color: var(--BG-white, #fff);
|
||||||
|
font-family: 'PingFang SC';
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.operation-btn {
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--Brand-Brand-6, #6d4cfe);
|
||||||
|
font-family: $font-family-regular;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
&.red {
|
||||||
|
color: #f64b31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.status-box {
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0px 8px;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
span {
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&-0 {
|
||||||
|
background: var(--Functional-yellow-1, #fff7e5);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-yellow-6, #ffae00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-1 {
|
||||||
|
background: var(--Functional-Green-1, #ebf7f2);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-Green-6, #25c883);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
background: var(--Functional-Red-1, #ffe9e7);
|
||||||
|
span {
|
||||||
|
color: var(--Functional-Red-6, #f64b31);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
export enum enumTaskStatus {
|
||||||
|
Exporting = 0, // 导出中
|
||||||
|
Finished = 1, // 已完成
|
||||||
|
Failed = 2, // 导出失败
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EXPORT_TASK_STATUS = [
|
||||||
|
{
|
||||||
|
label: '导出中',
|
||||||
|
value: enumTaskStatus.Exporting,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已完成',
|
||||||
|
value: enumTaskStatus.Finished,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '导出失败',
|
||||||
|
value: enumTaskStatus.Failed,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const IMPORT_TASK_STATUS = [
|
||||||
|
{
|
||||||
|
label: '导入中',
|
||||||
|
value: enumTaskStatus.Exporting,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已完成',
|
||||||
|
value: enumTaskStatus.Finished,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '导入失败',
|
||||||
|
value: enumTaskStatus.Failed,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="任务中心"
|
||||||
|
modal-class="task-center-modal"
|
||||||
|
width="860px"
|
||||||
|
:mask-closable="false"
|
||||||
|
:footer="false"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<div class="content">
|
||||||
|
<component :is="activeTab === '0' ? ImportTask : ExportTask" ref="componentRef" />
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { Notification } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import ExportTask from './components/export-task';
|
||||||
|
import ImportTask from './components/import-task';
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const componentRef = ref(null);
|
||||||
|
const activeTab = ref('0');
|
||||||
|
|
||||||
|
let timer = null;
|
||||||
|
|
||||||
|
const handleTabClick = (key) => {
|
||||||
|
activeTab.value = key;
|
||||||
|
nextTick(() => {
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
componentRef.value?.init();
|
||||||
|
};
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
getData();
|
||||||
|
|
||||||
|
timer = setInterval(() => {
|
||||||
|
getData();
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
const onClose = () => {
|
||||||
|
activeTab.value = '0';
|
||||||
|
|
||||||
|
clearTimer();
|
||||||
|
componentRef.value?.unloadComp?.();
|
||||||
|
Notification.clear();
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
const clearTimer = () => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import './style.scss';
|
||||||
|
</style>
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
.task-center-modal {
|
||||||
|
.arco-modal-header {
|
||||||
|
border-bottom: none !important;
|
||||||
|
.arco-modal-title {
|
||||||
|
color: var(--Text-1, #211f24);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-modal-body {
|
||||||
|
padding: 0 !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 650px;
|
||||||
|
.filter-row {
|
||||||
|
.filter-row-item {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #211f24;
|
||||||
|
font-family: 'PuHuiTi-Regular';
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 22px; /* 157.143% */
|
||||||
|
}
|
||||||
|
:deep(.arco-space-item) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-tabs {
|
||||||
|
.arco-tabs-nav-top {
|
||||||
|
.arco-tabs-tab {
|
||||||
|
margin: 0 20px;
|
||||||
|
.arco-tabs-tab-title {
|
||||||
|
color: var(--Text-2, #3c4043);
|
||||||
|
font-family: $font-family-regular;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&.arco-tabs-tab-active {
|
||||||
|
.arco-tabs-tab-title {
|
||||||
|
color: #6D4CFE;
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-tabs-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24px 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cts {
|
||||||
|
color: var(--Text-1, #211f24);
|
||||||
|
font-family: $font-family-medium;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,288 +1,34 @@
|
|||||||
<script setup>
|
|
||||||
import { useAppStore } from '@/stores';
|
|
||||||
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
|
||||||
// import { IconExport, IconFile, IconCaretDown } from '@arco-design/web-vue/es/icon';
|
|
||||||
// import { handleUserLogout } from '@/utils/user';
|
|
||||||
// import { fetchLogOut } from '@/api/all/login';
|
|
||||||
import { useSidebarStore } from '@/stores/modules/side-bar';
|
|
||||||
import { useUserStore } from '@/stores';
|
|
||||||
import { MENU_GROUP_IDS } from '@/router/constants';
|
|
||||||
import router from '@/router';
|
|
||||||
// import { useRoute } from 'vue-router';
|
|
||||||
import ExitAccountModal from '@/components/_base/exit-account-modal/index.vue';
|
|
||||||
// import { appRoutes } from '@/router/routes';
|
|
||||||
// import { MENU_LIST } from './constants';
|
|
||||||
|
|
||||||
const sidebarStore = useSidebarStore();
|
|
||||||
const enterpriseStore = useEnterpriseStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
// const route = useRoute();
|
|
||||||
const exitAccountModalRef = ref(null);
|
|
||||||
// const selectedKey = ref([]);
|
|
||||||
|
|
||||||
const selectedKey = computed(() => {
|
|
||||||
return [String(sidebarStore.activeMenuId)];
|
|
||||||
});
|
|
||||||
const menuList = computed(() => {
|
|
||||||
return sidebarStore.menuList;
|
|
||||||
});
|
|
||||||
const enterprises = computed(() => {
|
|
||||||
return userStore.userInfo?.enterprises ?? [];
|
|
||||||
});
|
|
||||||
|
|
||||||
const clickExit = async () => {
|
|
||||||
exitAccountModalRef.value?.open();
|
|
||||||
};
|
|
||||||
|
|
||||||
// const appStore = useAppStore();
|
|
||||||
|
|
||||||
const setServerMenu = () => {
|
|
||||||
router.push('/management/person');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDopdownClick = (item) => {
|
|
||||||
router.push({ name: item.routeName });
|
|
||||||
};
|
|
||||||
const onEnterpriseItemClick = async (item) => {
|
|
||||||
enterpriseStore.setEnterpriseInfo(item);
|
|
||||||
window.location.reload();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar-wrap">
|
||||||
<div class="left-side">
|
<div class="left-wrap">
|
||||||
<a-space class="cursor-pointer" @click="router.push('/')">
|
<a-space class="cursor-pointer" @click="router.push('/')">
|
||||||
<img src="@/assets/logo.svg" alt="" />
|
<img src="@/assets/logo.svg" alt="" />
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<div class="center-side">
|
<div class="flex-1">
|
||||||
<div class="menu-demo h-100%">
|
<NavbarMenu />
|
||||||
<a-menu mode="horizontal" :selected-keys="selectedKey">
|
|
||||||
<a-menu-item v-for="item in menuList" :key="String(item.id)">
|
|
||||||
<template v-if="item.children">
|
|
||||||
<a-dropdown :popup-max-height="false" class="layout-menu-item-dropdown">
|
|
||||||
<a-button>
|
|
||||||
<span class="menu-item-text mr-2px"> {{ item.name }}</span>
|
|
||||||
<icon-caret-down size="16" class="arco-icon-down !mr-0" />
|
|
||||||
</a-button>
|
|
||||||
<template #content>
|
|
||||||
<a-doption v-for="(child, ind) in item.children" :key="ind" @click="handleDopdownClick(child)">
|
|
||||||
<span class="menu-item-text"> {{ child.name }}</span>
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-menu-item :key="String(item.id)" @click="handleDopdownClick(item)">
|
|
||||||
<span class="menu-item-text"> {{ item.name }}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</template>
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<RightSide />
|
||||||
<ul class="right-side">
|
|
||||||
<li>
|
|
||||||
<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">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<img src="@/assets/option.svg" 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="@/assets/change.svg" class="w-16px h-16px mr-8px" />
|
|
||||||
<span>切换企业账号</span>
|
|
||||||
</div>
|
|
||||||
<icon-right size="12" />
|
|
||||||
</a-space>
|
|
||||||
</a-doption>
|
|
||||||
<template #content>
|
|
||||||
<a-doption
|
|
||||||
v-for="(item, index) in enterprises"
|
|
||||||
:key="index"
|
|
||||||
class="rounded-8px hover:bg-#F2F3F5"
|
|
||||||
:class="enterpriseStore.enterpriseInfo?.id === item.id ? '!color-#6D4CFE' : ''"
|
|
||||||
@click="onEnterpriseItemClick(item)"
|
|
||||||
>
|
|
||||||
<div class="flex items-center w-100% justify-between">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<icon-check v-if="enterpriseStore.enterpriseInfo?.id === item.id" size="16" />
|
|
||||||
</div>
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dsubmenu>
|
|
||||||
<a-doption>
|
|
||||||
<a-space class="flex justify-between w-100%" @click="clickExit">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<img src="@/assets/exit.svg" class="w-16px h-16px mr-8px" />
|
|
||||||
<span>退出登录</span>
|
|
||||||
</div>
|
|
||||||
<icon-right size="12" />
|
|
||||||
</a-space>
|
|
||||||
</a-doption>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ExitAccountModal ref="exitAccountModalRef" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import NavbarMenu from './components/navbar-menu';
|
||||||
|
import RightSide from './components/right-side';
|
||||||
|
|
||||||
|
import router from '@/router';
|
||||||
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.navbar {
|
.navbar-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--color-bg-2);
|
background-color: var(--color-bg-2);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
.left-wrap {
|
||||||
.left-side {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
|
||||||
.center-side {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
margin-left: 40px;
|
|
||||||
.menu-item-text {
|
|
||||||
color: var(--Text-2, #3c4043);
|
|
||||||
font-family: $font-family-medium;
|
|
||||||
font-size: 16px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
:deep(.arco-menu) {
|
|
||||||
height: 100%;
|
|
||||||
.arco-menu-inner {
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
.arco-menu-item {
|
|
||||||
padding: 0;
|
|
||||||
position: relative;
|
|
||||||
&.arco-menu-selected {
|
|
||||||
.menu-item-text,
|
|
||||||
.arco-menu-selected-label {
|
|
||||||
color: #6d4cfe;
|
|
||||||
}
|
|
||||||
.arco-menu-selected-label {
|
|
||||||
background: var(--Brand-Brand-6, #6d4cfe);
|
|
||||||
height: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
width: 50%;
|
|
||||||
position: absolute;
|
|
||||||
bottom: -8px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.arco-icon-down {
|
|
||||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
.arco-dropdown-open .arco-icon-down {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cneter-tip {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--color-text-1);
|
|
||||||
}
|
|
||||||
.menu-demo {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.right-side {
|
|
||||||
display: flex;
|
|
||||||
padding-right: 20px;
|
|
||||||
list-style: none;
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: var(--color-text-1);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.nav-btn {
|
|
||||||
border-color: rgb(var(--gray-2));
|
|
||||||
color: rgb(var(--gray-8));
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.layout-menu-item-dropdown,
|
|
||||||
.layout-avatar-dropdown,
|
|
||||||
.enterprises-dsubmenu {
|
|
||||||
.arco-dropdown {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--BG-300, #e6e6e8);
|
|
||||||
background: var(--BG-white, #fff);
|
|
||||||
padding: 12px 0px;
|
|
||||||
.arco-dropdown-option {
|
|
||||||
padding: 0 12px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
&-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; /* 137.5% */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.arco-dropdown-option-content {
|
|
||||||
border-radius: 8px !important;
|
|
||||||
}
|
|
||||||
&:not(.arco-dropdown-option-disabled):hover {
|
|
||||||
background-color: transparent;
|
|
||||||
.arco-dropdown-option-content {
|
|
||||||
// border-radius: 8px;
|
|
||||||
background: var(--BG-200, #f2f3f5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.layout-avatar-dropdown,
|
|
||||||
.enterprises-dsubmenu {
|
|
||||||
width: 200px;
|
|
||||||
.arco-dropdown {
|
|
||||||
padding: 12px 4px;
|
|
||||||
.arco-dropdown-option {
|
|
||||||
padding: 0 !important;
|
|
||||||
&-content {
|
|
||||||
padding: 0 12px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.arco-dropdown-option-suffix {
|
.arco-dropdown-option-suffix {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
35
src/components/svg-icon/index.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="svg-icon"
|
||||||
|
:width="props.size"
|
||||||
|
:height="props.size"
|
||||||
|
>
|
||||||
|
<use :xlink:href="symbolId" :fill="props.color" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
const props = defineProps({
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: "icon",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "#333",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: "12",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
||||||
|
</script>
|
||||||
|
|
||||||
@ -71,6 +71,7 @@ export function useTableSelectionWithPagination(options: UseTableSelectionWithPa
|
|||||||
const rowSelection = computed(() => ({
|
const rowSelection = computed(() => ({
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
showCheckedAll: true,
|
showCheckedAll: true,
|
||||||
|
width: 48,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -83,5 +84,6 @@ export function useTableSelectionWithPagination(options: UseTableSelectionWithPa
|
|||||||
rowSelection,
|
rowSelection,
|
||||||
handleSelect,
|
handleSelect,
|
||||||
handleSelectAll,
|
handleSelectAll,
|
||||||
|
DEFAULT_PAGE_INFO,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.ts
@ -5,18 +5,25 @@
|
|||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import store from './stores';
|
import store from './stores';
|
||||||
import NoData from '@/components/no-data';
|
|
||||||
import '@/api/index';
|
|
||||||
|
|
||||||
|
import NoData from '@/components/no-data';
|
||||||
|
import SvgIcon from "@/components/svg-icon";
|
||||||
|
|
||||||
|
import '@/api/index';
|
||||||
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
|
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
|
||||||
import './core';
|
import './core';
|
||||||
|
|
||||||
import 'normalize.css';
|
import 'normalize.css';
|
||||||
import 'uno.css';
|
import 'uno.css';
|
||||||
|
import 'virtual:svg-icons-register'
|
||||||
|
|
||||||
// import '@/styles/vars.css'; // 优先加载
|
// import '@/styles/vars.css'; // 优先加载
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.component('NoData', NoData);
|
||||||
|
app.component('SvgIcon', SvgIcon);
|
||||||
|
|
||||||
app.use(store);
|
app.use(store);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.component('NoData', NoData);
|
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
import type { AppRouteRecordRaw } from '../types';
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
import { MENU_GROUP_IDS } from '@/router/constants';
|
import { MENU_GROUP_IDS } from '@/router/constants';
|
||||||
|
|
||||||
import IconRepository from '@/assets/svg/icon-repository.svg';
|
import IconRepository from '@/assets/svg/svg-repository.svg';
|
||||||
import IconMediaAccount from '@/assets/svg/icon-mediaAccount.svg';
|
import IconMediaAccount from '@/assets/svg/svg-mediaAccount.svg';
|
||||||
import IconPutAccount from '@/assets/svg/icon-putAccount.svg';
|
import IconPutAccount from '@/assets/svg/svg-putAccount.svg';
|
||||||
import IconIntelligentSolution from '@/assets/svg/icon-intelligentSolution.svg';
|
import IconIntelligentSolution from '@/assets/svg/svg-intelligentSolution.svg';
|
||||||
|
|
||||||
const COMPONENTS: AppRouteRecordRaw[] = [
|
const COMPONENTS: AppRouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
@ -158,43 +158,43 @@ const COMPONENTS: AppRouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
path: '/intelligent-solution',
|
// path: '/intelligent-solution',
|
||||||
name: 'IntelligentSolution',
|
// name: 'IntelligentSolution',
|
||||||
redirect: 'intelligent-solution/businessAnalysisReport',
|
// redirect: 'intelligent-solution/businessAnalysisReport',
|
||||||
meta: {
|
// meta: {
|
||||||
locale: '智能方案管理',
|
// locale: '智能方案管理',
|
||||||
icon: IconIntelligentSolution,
|
// icon: IconIntelligentSolution,
|
||||||
requiresAuth: true,
|
// requiresAuth: true,
|
||||||
requireLogin: true,
|
// requireLogin: true,
|
||||||
roles: ['*'],
|
// roles: ['*'],
|
||||||
id: MENU_GROUP_IDS.PROPERTY_ID,
|
// id: MENU_GROUP_IDS.PROPERTY_ID,
|
||||||
},
|
// },
|
||||||
children: [
|
// children: [
|
||||||
{
|
// {
|
||||||
path: 'businessAnalysisReport',
|
// path: 'businessAnalysisReport',
|
||||||
name: 'IntelligentSolutionBusinessAnalysisReport',
|
// name: 'IntelligentSolutionBusinessAnalysisReport',
|
||||||
meta: {
|
// meta: {
|
||||||
locale: '业务洞察报告',
|
// locale: '业务洞察报告',
|
||||||
requiresAuth: true,
|
// requiresAuth: true,
|
||||||
requireLogin: true,
|
// requireLogin: true,
|
||||||
roles: ['*'],
|
// roles: ['*'],
|
||||||
},
|
// },
|
||||||
component: () => import('@/views/property-marketing/intelligent-solution/businessAnalysisReport'),
|
// component: () => import('@/views/property-marketing/intelligent-solution/businessAnalysisReport'),
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
path: 'competitiveProductAnalysisReport',
|
// path: 'competitiveProductAnalysisReport',
|
||||||
name: 'IntelligentSolutionCompetitiveProductAnalysisReport',
|
// name: 'IntelligentSolutionCompetitiveProductAnalysisReport',
|
||||||
meta: {
|
// meta: {
|
||||||
locale: '竟品对比报告',
|
// locale: '竟品对比报告',
|
||||||
requiresAuth: true,
|
// requiresAuth: true,
|
||||||
requireLogin: true,
|
// requireLogin: true,
|
||||||
roles: ['*'],
|
// roles: ['*'],
|
||||||
},
|
// },
|
||||||
component: () => import('@/views/property-marketing/intelligent-solution/competitiveProductAnalysisReport'),
|
// component: () => import('@/views/property-marketing/intelligent-solution/competitiveProductAnalysisReport'),
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default COMPONENTS;
|
export default COMPONENTS;
|
||||||
|
|||||||
@ -77,14 +77,14 @@ export const MENU_LIST = [
|
|||||||
'guideDetail',
|
'guideDetail',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
name: '智能方案管理',
|
// name: '智能方案管理',
|
||||||
routeName: 'IntelligentSolutionBusinessAnalysisReport',
|
// routeName: 'IntelligentSolutionBusinessAnalysisReport',
|
||||||
includeRouteNames: [
|
// includeRouteNames: [
|
||||||
'IntelligentSolutionBusinessAnalysisReport',
|
// 'IntelligentSolutionBusinessAnalysisReport',
|
||||||
'IntelligentSolutionCompetitiveProductAnalysisReport',
|
// 'IntelligentSolutionCompetitiveProductAnalysisReport',
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -7,18 +7,22 @@ import router from '@/router';
|
|||||||
import type { RouteLocationNormalized } from 'vue-router';
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
import { MENU_LIST } from './constants';
|
import { MENU_LIST } from './constants';
|
||||||
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||||
|
import { getTaskUnread, patchTaskRead } from '@/api/all/common';
|
||||||
|
|
||||||
interface sidebarState {
|
interface sidebarState {
|
||||||
activeMenuId: number | null;
|
activeMenuId: number | null;
|
||||||
menuList: any[];
|
menuList: any[];
|
||||||
allowAccessRoutes: any[];
|
allowAccessRoutes: any[];
|
||||||
|
unreadInfo: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unreadInfoTimer: number | null = null;
|
||||||
|
|
||||||
export const useSidebarStore = defineStore('sidebar', {
|
export const useSidebarStore = defineStore('sidebar', {
|
||||||
state: (): sidebarState => ({
|
state: (): sidebarState => ({
|
||||||
activeMenuId: null,
|
activeMenuId: null, //
|
||||||
menuList: [],
|
menuList: [], // 菜单信息
|
||||||
|
unreadInfo: [], // 未读消息
|
||||||
allowAccessRoutes: [], // 允许访问的路由列表
|
allowAccessRoutes: [], // 允许访问的路由列表
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
@ -42,17 +46,13 @@ export const useSidebarStore = defineStore('sidebar', {
|
|||||||
setActiveMenuIdByRoute(route: RouteLocationNormalized) {
|
setActiveMenuIdByRoute(route: RouteLocationNormalized) {
|
||||||
const appRoutes = router.options?.routes ?? [];
|
const appRoutes = router.options?.routes ?? [];
|
||||||
|
|
||||||
// 查找当前路由所属的菜单组
|
|
||||||
const findMenuGroup = (routes: any[]): number | null => {
|
const findMenuGroup = (routes: any[]): number | null => {
|
||||||
for (const routeItem of routes) {
|
for (const routeItem of routes) {
|
||||||
// 检查子路由
|
|
||||||
if (routeItem.children?.length > 0) {
|
if (routeItem.children?.length > 0) {
|
||||||
// 检查当前路由是否是这个父路由的子路由
|
|
||||||
const isChildRoute = routeItem.children.some((child: any) => child.name === route.name);
|
const isChildRoute = routeItem.children.some((child: any) => child.name === route.name);
|
||||||
if (isChildRoute) {
|
if (isChildRoute) {
|
||||||
return routeItem.meta?.id || null;
|
return routeItem.meta?.id || null;
|
||||||
}
|
}
|
||||||
// 递归检查更深层的子路由
|
|
||||||
const childResult = findMenuGroup(routeItem.children);
|
const childResult = findMenuGroup(routeItem.children);
|
||||||
if (childResult !== null) {
|
if (childResult !== null) {
|
||||||
return routeItem.meta?.id || childResult;
|
return routeItem.meta?.id || childResult;
|
||||||
@ -71,5 +71,31 @@ export const useSidebarStore = defineStore('sidebar', {
|
|||||||
this.activeMenuId = menuId;
|
this.activeMenuId = menuId;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async getTaskUnreadInfo() {
|
||||||
|
const { code, data } = await getTaskUnread();
|
||||||
|
if (code === 200) {
|
||||||
|
this.unreadInfo = data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询未读信息
|
||||||
|
startUnreadInfoPolling() {
|
||||||
|
if (unreadInfoTimer) return;
|
||||||
|
this.getTaskUnreadInfo();
|
||||||
|
unreadInfoTimer = window.setInterval(() => {
|
||||||
|
this.getTaskUnreadInfo();
|
||||||
|
}, 30000);
|
||||||
|
},
|
||||||
|
stopUnreadInfoPolling() {
|
||||||
|
this.unreadInfo = [];
|
||||||
|
if (unreadInfoTimer) {
|
||||||
|
clearInterval(unreadInfoTimer);
|
||||||
|
unreadInfoTimer = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async removeTaskUnreadInfo() {
|
||||||
|
patchTaskRead({ ids: this.unreadInfo });
|
||||||
|
this.unreadInfo = [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const useUserStore = defineStore('user', {
|
|||||||
slsWithCatch('accessToken', this.token);
|
slsWithCatch('accessToken', this.token);
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteToken() {
|
clearToken() {
|
||||||
this.token = '';
|
this.token = '';
|
||||||
rlsWithCatch('accessToken');
|
rlsWithCatch('accessToken');
|
||||||
},
|
},
|
||||||
|
|||||||
@ -24,7 +24,8 @@
|
|||||||
&.arco-checkbox-checked,
|
&.arco-checkbox-checked,
|
||||||
&.arco-checkbox-indeterminate {
|
&.arco-checkbox-indeterminate {
|
||||||
.arco-checkbox-icon {
|
.arco-checkbox-icon {
|
||||||
background-color: #6d4cfe !important;
|
background-color: #6D4CFE !important;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
94
src/utils/arcoD.tsx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { Notification } from '@arco-design/web-vue';
|
||||||
|
import { downloadByUrl } from '@/utils/tools';
|
||||||
|
import { IconLoading } from '@arco-design/web-vue/es/icon';
|
||||||
|
|
||||||
|
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
|
import icon2 from '@/assets/img/media-account/icon-success.png';
|
||||||
|
import icon3 from "@/assets/img/media-account/icon-warn.png"
|
||||||
|
|
||||||
|
interface RenderNotificationData {
|
||||||
|
total_number: number;
|
||||||
|
success_number: number;
|
||||||
|
fail_number: number;
|
||||||
|
file?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载通知框
|
||||||
|
export function showExportNotification(label: string, others: { id?: string, duration?: number }) {
|
||||||
|
const { id = '', duration = 3000 } = others ?? {}
|
||||||
|
Notification.warning({
|
||||||
|
id,
|
||||||
|
showIcon: false,
|
||||||
|
closable: true,
|
||||||
|
content: () => (
|
||||||
|
<div class="flex items-center pr-16px">
|
||||||
|
<IconLoading size={16} class="color-#6D4CFE mr-8px" />
|
||||||
|
<p class="text-14px lh-22px font-400 color-#211F24">{label}</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration,
|
||||||
|
class: 'px-16px py-9px w-450px rounded-2px bg-#F0EDFF',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载失败框
|
||||||
|
export function showFailExportNotification(label: string, others: { id?: string, duration?: number, onReDownload?: Function }) {
|
||||||
|
const { id = '', duration = 0, onReDownload } = others ?? {}
|
||||||
|
Notification.warning({
|
||||||
|
id,
|
||||||
|
showIcon: false,
|
||||||
|
closable: true,
|
||||||
|
content: () => (
|
||||||
|
<div class="flex items-center justify-between pr-16px">
|
||||||
|
<div class="flex items-center mr-10px">
|
||||||
|
<img src={icon3} width={16} height={16} class=" mr-8px" />
|
||||||
|
<p class="text-14px lh-22px font-400 color-#211F24 ">{label}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-14px lh-22px font-400 color-#6D4CFE cursor-pointer" onClick={() => onReDownload?.()}>重新下载</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration,
|
||||||
|
class: 'px-16px py-9px w-500px rounded-2px bg-#FFE9E7',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showImportResultNotification = (data: RenderNotificationData) => {
|
||||||
|
const { total_number, success_number, fail_number, file } = data;
|
||||||
|
const hasError = fail_number > 0;
|
||||||
|
const handleDownloadError = (file?: string) => {
|
||||||
|
file && downloadByUrl(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
Notification.warning({
|
||||||
|
showIcon: false,
|
||||||
|
closable: true,
|
||||||
|
content: () => (
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center mb-4px">
|
||||||
|
<img src={hasError ? icon1 : icon2} width="16" height="16" class="mr-8px" />
|
||||||
|
<span class="text-16px lh-24px font-400 color-#211F24">导入完成</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-14px lh-22px font-400 color-#211F24">
|
||||||
|
共导入 {total_number} 个账号,导入成功 {success_number} 个
|
||||||
|
{hasError && (
|
||||||
|
<span>
|
||||||
|
,失败 <span class="color-#F64B31">{fail_number}</span> 个
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
{hasError && (
|
||||||
|
<div
|
||||||
|
class="mt-8px text-14px lh-22px font-400 color-#6D4CFE cursor-pointer"
|
||||||
|
onClick={() => handleDownloadError(file)}
|
||||||
|
>
|
||||||
|
下载问题表格
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration: 3000,
|
||||||
|
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -106,3 +106,7 @@ export function downloadByUrl(url: string, filename?: string) {
|
|||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function genRandomId() {
|
||||||
|
return `id_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
|
||||||
|
}
|
||||||
@ -12,13 +12,13 @@ import { useSidebarStore } from '@/stores/modules/side-bar';
|
|||||||
export function goUserLogin(query?: any) {
|
export function goUserLogin(query?: any) {
|
||||||
router.push({ name: 'UserLogin', query });
|
router.push({ name: 'UserLogin', query });
|
||||||
}
|
}
|
||||||
// 初始化企业信息、navbar菜单、允许访问的路由
|
|
||||||
export const getUserEnterpriseInfo = async () => {
|
export const getUserEnterpriseInfo = async () => {
|
||||||
const enterpriseStore = useEnterpriseStore();
|
const enterpriseStore = useEnterpriseStore();
|
||||||
const sidebarStore = useSidebarStore();
|
const sidebarStore = useSidebarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
await enterpriseStore.getEnterpriseInfo();
|
await enterpriseStore.getEnterpriseInfo(); // 初始化企业信息
|
||||||
sidebarStore.getUserNavbarMenuList(); // 初始化navbar菜单
|
sidebarStore.getUserNavbarMenuList(); // 初始化navbar菜单
|
||||||
userStore.getUserAllowAccessRoutes(); // 初始化允许访问的路由
|
userStore.getUserAllowAccessRoutes(); // 初始化允许访问的路由
|
||||||
};
|
};
|
||||||
@ -26,9 +26,11 @@ export const getUserEnterpriseInfo = async () => {
|
|||||||
// 登录处理
|
// 登录处理
|
||||||
export async function handleUserLogin() {
|
export async function handleUserLogin() {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const sidebarStore = useSidebarStore();
|
||||||
|
|
||||||
await userStore.getUserInfo(); // 初始化用户信息
|
await userStore.getUserInfo(); // 初始化用户信息
|
||||||
await getUserEnterpriseInfo();
|
await getUserEnterpriseInfo(); // 初始化企业信息、navbar菜单、允许访问的路由
|
||||||
|
sidebarStore.startUnreadInfoPolling(); // 初始化未读信息
|
||||||
|
|
||||||
handleUserHome();
|
handleUserHome();
|
||||||
}
|
}
|
||||||
@ -38,18 +40,19 @@ export function handleUserHome() {
|
|||||||
router.push({ name: 'Home' });
|
router.push({ name: 'Home' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登出处理
|
||||||
export function handleUserLogout() {
|
export function handleUserLogout() {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const enterpriseStore = useEnterpriseStore();
|
const enterpriseStore = useEnterpriseStore();
|
||||||
const sidebarStore = useSidebarStore();
|
const sidebarStore = useSidebarStore();
|
||||||
|
|
||||||
userStore.clearUserInfo();
|
userStore.clearUserInfo(); // 清除用户信息
|
||||||
enterpriseStore.clearUserEnterpriseInfo();
|
userStore.clearToken(); // 清除token
|
||||||
sidebarStore.clearUserNavbarMenuList();
|
enterpriseStore.clearUserEnterpriseInfo(); // 清除企业信息
|
||||||
userStore.clearUserAllowAccessRoutes();
|
sidebarStore.clearUserNavbarMenuList(); // 清除navbar菜单信息
|
||||||
|
userStore.clearUserAllowAccessRoutes(); // 清除权限路由列表
|
||||||
sidebarStore.clearActiveMenuId();
|
sidebarStore.stopUnreadInfoPolling(); // 清除未读消息
|
||||||
userStore.deleteToken();
|
sidebarStore.clearActiveMenuId(); // 清除active菜单id
|
||||||
|
|
||||||
goUserLogin();
|
goUserLogin();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,9 +267,9 @@ const getProfileInfo = async () => {
|
|||||||
let enterprises = data['enterprises'];
|
let enterprises = data['enterprises'];
|
||||||
mobileNumber.value = data['mobile'];
|
mobileNumber.value = data['mobile'];
|
||||||
accounts.value = enterprises;
|
accounts.value = enterprises;
|
||||||
enterpriseStore.setEnterpriseInfo(data);
|
|
||||||
|
|
||||||
if (enterprises.length > 0) {
|
if (enterprises.length > 0) {
|
||||||
|
enterpriseStore.setEnterpriseInfo(data.enterprises[0]);
|
||||||
if (enterprises.length === 1) {
|
if (enterprises.length === 1) {
|
||||||
handleUserLogin();
|
handleUserLogin();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -99,7 +99,9 @@ const addAccountVisible = ref(false);
|
|||||||
const deleteVisible = ref(false);
|
const deleteVisible = ref(false);
|
||||||
const deleteTitle = ref('');
|
const deleteTitle = ref('');
|
||||||
|
|
||||||
const enterpriseInfo = store.enterpriseInfo;
|
const enterpriseInfo = computed(() => {
|
||||||
|
return store.enterpriseInfo ?? {};
|
||||||
|
});
|
||||||
|
|
||||||
const okText = computed(() => {
|
const okText = computed(() => {
|
||||||
if (!canAddAccount.value) {
|
if (!canAddAccount.value) {
|
||||||
@ -109,9 +111,9 @@ const okText = computed(() => {
|
|||||||
});
|
});
|
||||||
const customerServiceVisible = ref(false);
|
const customerServiceVisible = ref(false);
|
||||||
const canAddAccount = computed(() => {
|
const canAddAccount = computed(() => {
|
||||||
if (!enterpriseInfo) return false;
|
if (!enterpriseInfo.value) return false;
|
||||||
|
|
||||||
return enterpriseInfo.sub_account_quota > enterpriseInfo.used_sub_account_count;
|
return enterpriseInfo.value.sub_account_quota > enterpriseInfo.value.used_sub_account_count;
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentSelectAccount = ref();
|
const currentSelectAccount = ref();
|
||||||
|
|||||||
@ -48,7 +48,9 @@ const form = reactive({
|
|||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const enterpriseInfo = store.enterpriseInfo;
|
const enterpriseInfo = computed(() => {
|
||||||
|
return store.enterpriseInfo ?? {};
|
||||||
|
});
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -65,11 +67,13 @@ const infoVisible = ref(false);
|
|||||||
const customerServiceVisible = ref(false);
|
const customerServiceVisible = ref(false);
|
||||||
|
|
||||||
const dataSource = computed(() => {
|
const dataSource = computed(() => {
|
||||||
return enterpriseInfo ? [enterpriseInfo] : [];
|
return enterpriseInfo.value ? [enterpriseInfo.value] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log({ dataSource });
|
||||||
const canUpdate = computed(() => {
|
const canUpdate = computed(() => {
|
||||||
if (!enterpriseInfo) return false;
|
if (!enterpriseInfo.value) return false;
|
||||||
return enterpriseInfo.update_name_quota > enterpriseInfo.used_update_name_count;
|
return enterpriseInfo.value.update_name_quota > enterpriseInfo.value.used_update_name_count;
|
||||||
});
|
});
|
||||||
|
|
||||||
const okText = computed(() => {
|
const okText = computed(() => {
|
||||||
@ -81,7 +85,7 @@ const okText = computed(() => {
|
|||||||
|
|
||||||
function handleUpdate() {
|
function handleUpdate() {
|
||||||
if (!canUpdate.value) {
|
if (!canUpdate.value) {
|
||||||
form.name = enterpriseInfo!.name;
|
form.name = enterpriseInfo.value?.name;
|
||||||
}
|
}
|
||||||
infoVisible.value = true;
|
infoVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,9 @@ import axios from 'axios';
|
|||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
|
|
||||||
const store = useUserStore();
|
const store = useUserStore();
|
||||||
const userInfo = store.userInfo;
|
const userInfo = computed(() => {
|
||||||
|
return store.userInfo ?? {};
|
||||||
|
});
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -115,7 +117,7 @@ const isSendCaptcha = ref(false);
|
|||||||
const uploadInputRef = ref();
|
const uploadInputRef = ref();
|
||||||
|
|
||||||
const dataSource = computed(() => {
|
const dataSource = computed(() => {
|
||||||
return userInfo ? [userInfo] : [];
|
return userInfo.value ? [userInfo.value] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
|
|||||||
@ -65,7 +65,8 @@ import AccountTable from './components/account-table';
|
|||||||
import { getAccountBoardOverview, getAccountBoardList, postAccountBoardExport } from '@/api/all/propertyMarketing';
|
import { getAccountBoardOverview, getAccountBoardList, postAccountBoardExport } from '@/api/all/propertyMarketing';
|
||||||
import { formatNumberShow } from '@/utils/tools';
|
import { formatNumberShow } from '@/utils/tools';
|
||||||
import { INITIAL_QUERY, CARD_FIELDS } from './constants';
|
import { INITIAL_QUERY, CARD_FIELDS } from './constants';
|
||||||
import { downloadByUrl } from '@/utils/tools';
|
// import { downloadByUrl } from '@/utils/tools';
|
||||||
|
import { showExportNotification } from '@/utils/arcoD';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/icon-question.png';
|
import icon1 from '@/assets/img/icon-question.png';
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ const handleExport = () => {
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const { code, data } = res;
|
const { code, data } = res;
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
downloadByUrl(data.download_url);
|
showExportNotification(`正在下载“${data.name}”,请稍后...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -95,7 +95,7 @@
|
|||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.status === 2" class="mask">
|
<div v-if="isSyncFailed(item)" class="mask">
|
||||||
<div class="flex items-center mb-16px box">
|
<div class="flex items-center mb-16px box">
|
||||||
<img :src="icon3" width="16" height="16" class="mr-8px" />
|
<img :src="icon3" width="16" height="16" class="mr-8px" />
|
||||||
<span class="name !mb-0">更新数据失败</span>
|
<span class="name !mb-0">更新数据失败</span>
|
||||||
@ -148,7 +148,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete']);
|
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete', 'updateSyncStatus']);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -174,9 +174,6 @@ const isSyncing = (item) => {
|
|||||||
if (!props.syncMediaAccounts.length) return false;
|
if (!props.syncMediaAccounts.length) return false;
|
||||||
|
|
||||||
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
|
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
|
||||||
if(target) {
|
|
||||||
return target?.status === 0;
|
|
||||||
}
|
|
||||||
return target?.status === 0;
|
return target?.status === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -268,7 +265,7 @@ const goDetail = (item) => {
|
|||||||
|
|
||||||
const onDeleteSyncStatus = async (item) => {
|
const onDeleteSyncStatus = async (item) => {
|
||||||
await deleteSyncStatus(item.id);
|
await deleteSyncStatus(item.id);
|
||||||
item.status = 1;
|
emits('updateSyncStatus', item);
|
||||||
};
|
};
|
||||||
const formatTime = (time) => {
|
const formatTime = (time) => {
|
||||||
return exactFormatTime(time, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
|
return exactFormatTime(time, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
|
||||||
@ -282,6 +279,10 @@ const getLastSyncedAt = (item) => {
|
|||||||
}
|
}
|
||||||
return formatTime(item.last_synced_at);
|
return formatTime(item.last_synced_at);
|
||||||
};
|
};
|
||||||
|
const isSyncFailed = (item) => {
|
||||||
|
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
|
||||||
|
return target?.status === 2;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -22,16 +22,13 @@ import {
|
|||||||
import TagSelect from '@/views/property-marketing/media-account/components/tag-select';
|
import TagSelect from '@/views/property-marketing/media-account/components/tag-select';
|
||||||
import GroupSelect from '@/views/property-marketing/media-account/components/group-select';
|
import GroupSelect from '@/views/property-marketing/media-account/components/group-select';
|
||||||
import AuthorizedAccountModal from '../authorized-account-modal';
|
import AuthorizedAccountModal from '../authorized-account-modal';
|
||||||
import ImportPromptModal from '../import-prompt-modal';
|
// import ImportPromptModal from '../import-prompt-modal';
|
||||||
import StatusBox from '../status-box';
|
import StatusBox from '../status-box';
|
||||||
import SyncDataModal from '../sync-data-modal';
|
import SyncDataModal from '../sync-data-modal';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/media-account/icon-download.png';
|
// import { downloadByUrl } from '@/utils/tools';
|
||||||
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
import { showExportNotification } from '@/utils/arcoD';
|
||||||
import icon3 from '@/assets/img/media-account/icon-dy.png';
|
import { genRandomId } from '@/utils/tools';
|
||||||
import icon4 from '@/assets/img/media-account/icon-xhs.png';
|
|
||||||
import icon5 from '@/assets/img/media-account/icon-warn-1.png';
|
|
||||||
import icon6 from '@/assets/img/media-account/icon-success.png';
|
|
||||||
import {
|
import {
|
||||||
fetchAccountTags,
|
fetchAccountTags,
|
||||||
fetchAccountGroups,
|
fetchAccountGroups,
|
||||||
@ -42,6 +39,13 @@ import {
|
|||||||
batchMediaAccounts,
|
batchMediaAccounts,
|
||||||
} from '@/api/all/propertyMarketing';
|
} from '@/api/all/propertyMarketing';
|
||||||
|
|
||||||
|
import icon1 from '@/assets/img/media-account/icon-download.png';
|
||||||
|
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
import icon3 from '@/assets/img/media-account/icon-dy.png';
|
||||||
|
import icon4 from '@/assets/img/media-account/icon-xhs.png';
|
||||||
|
// import icon5 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
|
// import icon6 from '@/assets/img/media-account/icon-success.png';
|
||||||
|
|
||||||
const UploadStatus = {
|
const UploadStatus = {
|
||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
WAITING: 'waiting',
|
WAITING: 'waiting',
|
||||||
@ -71,13 +75,12 @@ export default {
|
|||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const file = ref(null);
|
const file = ref(null);
|
||||||
const authorizedAccountModalRef = ref(null);
|
const authorizedAccountModalRef = ref(null);
|
||||||
const importPromptModalRef = ref(null);
|
// const importPromptModalRef = ref(null);
|
||||||
const uploadRef = ref(null);
|
const uploadRef = ref(null);
|
||||||
const isCustomCookie = ref(false);
|
const isCustomCookie = ref(false);
|
||||||
const form = ref({ ...INITIAL_FORM });
|
const form = ref(cloneDeep(INITIAL_FORM));
|
||||||
const syncDataModalRef = ref(null);
|
const syncDataModalRef = ref(null);
|
||||||
const importLoading = ref(false);
|
const importLoading = ref(false);
|
||||||
const CustomNotificationVisible = ref(false);
|
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
mobile: [
|
mobile: [
|
||||||
@ -132,12 +135,13 @@ export default {
|
|||||||
function removeFile() {
|
function removeFile() {
|
||||||
fileName.value = '';
|
fileName.value = '';
|
||||||
file.value = null;
|
file.value = null;
|
||||||
|
importLoading.value = false;
|
||||||
uploadStatus.value = UploadStatus.DEFAULT;
|
uploadStatus.value = UploadStatus.DEFAULT;
|
||||||
}
|
}
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
formRef.value?.clearValidate();
|
formRef.value?.clearValidate();
|
||||||
form.value = { ...INITIAL_FORM };
|
form.value = cloneDeep(INITIAL_FORM);
|
||||||
fileName.value = '';
|
fileName.value = '';
|
||||||
file.value = null;
|
file.value = null;
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
@ -175,27 +179,27 @@ export default {
|
|||||||
AMessage.warning('请上传要导入的文件');
|
AMessage.warning('请上传要导入的文件');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
importLoading.value = true;
|
importLoading.value = true;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file.value);
|
formData.append('file', file.value);
|
||||||
const { code } = await batchMediaAccounts(formData, {
|
const { code, data } = await batchMediaAccounts(formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
AMessage.success('导入成功');
|
const id = genRandomId();
|
||||||
emit('update');
|
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
|
||||||
|
emit('startQueryTaskStatus', data.id, id);
|
||||||
onClose();
|
onClose();
|
||||||
|
|
||||||
// const ID = 'IMPORT-ACCOUNT';
|
|
||||||
// renderNotification(ID);
|
|
||||||
importPromptModalRef.value.open();
|
|
||||||
} else {
|
} else {
|
||||||
uploadStatus.value = UploadStatus.ERROR;
|
uploadStatus.value = UploadStatus.ERROR;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uploadStatus.value = UploadStatus.ERROR;
|
uploadStatus.value = UploadStatus.ERROR;
|
||||||
|
} finally {
|
||||||
|
importLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleAddAccount = async () => {
|
const handleAddAccount = async () => {
|
||||||
@ -247,9 +251,6 @@ export default {
|
|||||||
window.open(data.download_url, '_blank');
|
window.open(data.download_url, '_blank');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleDownloadError = () => {
|
|
||||||
console.log('handleDownloadError');
|
|
||||||
};
|
|
||||||
const renderLabel = (label, tooltipContent) => {
|
const renderLabel = (label, tooltipContent) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -260,31 +261,6 @@ export default {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const renderNotification = (id, hasError) => {
|
|
||||||
Notification.warning({
|
|
||||||
id,
|
|
||||||
showIcon: false,
|
|
||||||
closable: true,
|
|
||||||
content: (
|
|
||||||
<div>
|
|
||||||
<div class="flex items-center mb-4px">
|
|
||||||
<img src={hasError ? icon5 : icon6} width="16" height="16" class="mr-8px" />
|
|
||||||
<span class="text-16px lh-24px font-400 color-#211F24">导入完成</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-14px lh-22px font-400 color-#211F24">
|
|
||||||
共导入 37 个账号,导入成功 32 个{hasError ? `,失败 <span class="color-#F64B31">5</span> 个` : ''}
|
|
||||||
</p>
|
|
||||||
{hasError && (
|
|
||||||
<div class="mt-8px text-14px lh-22px font-400 color-#6D4CFE cursor-pointer" onClick={handleDownloadError}>
|
|
||||||
下载问题表格
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
duration: 3000,
|
|
||||||
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
expose({ open });
|
expose({ open });
|
||||||
|
|
||||||
@ -416,8 +392,6 @@ export default {
|
|||||||
auto-size={{ minRows: 3, maxRows: 5 }}
|
auto-size={{ minRows: 3, maxRows: 5 }}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
{!isEdit.value && (
|
|
||||||
<>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label="Cookie值"
|
label="Cookie值"
|
||||||
v-slots={{
|
v-slots={{
|
||||||
@ -432,15 +406,13 @@ export default {
|
|||||||
v-model={form.value.cookie}
|
v-model={form.value.cookie}
|
||||||
placeholder="请输入..."
|
placeholder="请输入..."
|
||||||
size="large"
|
size="large"
|
||||||
max-length={72}
|
max-length={500}
|
||||||
auto-size={{ minRows: 3, maxRows: 5 }}
|
auto-size={{ minRows: 5, maxRows: 8 }}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form>
|
</Form>
|
||||||
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px;">
|
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px;">
|
||||||
<Button size="large" class="cancel-btn" onClick={onClose}>
|
<Button size="large" class="cancel-btn" onClick={onClose}>
|
||||||
@ -452,7 +424,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AuthorizedAccountModal ref={authorizedAccountModalRef} onUpdate={() => emit('update')} />
|
<AuthorizedAccountModal ref={authorizedAccountModalRef} onUpdate={() => emit('update')} />
|
||||||
<ImportPromptModal ref={importPromptModalRef} />
|
{/* <ImportPromptModal ref={importPromptModalRef} /> */}
|
||||||
<SyncDataModal ref={syncDataModalRef} />
|
<SyncDataModal ref={syncDataModalRef} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -87,6 +87,7 @@
|
|||||||
@delete="handleDelete"
|
@delete="handleDelete"
|
||||||
@openEdit="handleOpenEdit"
|
@openEdit="handleOpenEdit"
|
||||||
@update="getData"
|
@update="getData"
|
||||||
|
@updateSyncStatus="handleUpdateSyncStatus"
|
||||||
/>
|
/>
|
||||||
<NoData v-else />
|
<NoData v-else />
|
||||||
|
|
||||||
@ -108,7 +109,7 @@
|
|||||||
|
|
||||||
<GroupManageModal ref="groupManageModalRef" @update="filterBlockRef?.getGroups" />
|
<GroupManageModal ref="groupManageModalRef" @update="filterBlockRef?.getGroups" />
|
||||||
<TagsManageModal ref="tagsManageModalRef" @update="filterBlockRef?.getTags" />
|
<TagsManageModal ref="tagsManageModalRef" @update="filterBlockRef?.getTags" />
|
||||||
<AddAccountModal ref="addAccountModalRef" @update="getData" />
|
<AddAccountModal ref="addAccountModalRef" @update="getData" @startQueryTaskStatus="handleGetImportTaskStatus" />
|
||||||
|
|
||||||
<DeleteAccountModal ref="deleteAccountRef" @update="getData" @batchUpdate="onBatchSuccess" />
|
<DeleteAccountModal ref="deleteAccountRef" @update="getData" @batchUpdate="onBatchSuccess" />
|
||||||
<BatchTagModal ref="batchTagModalRef" @update="onBatchSuccess" />
|
<BatchTagModal ref="batchTagModalRef" @update="onBatchSuccess" />
|
||||||
@ -118,6 +119,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, provide } from 'vue';
|
import { ref, provide } from 'vue';
|
||||||
|
import { Notification } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import FilterBlock from './components/filter-block';
|
import FilterBlock from './components/filter-block';
|
||||||
import AccountTable from './components/account-table';
|
import AccountTable from './components/account-table';
|
||||||
@ -127,10 +129,11 @@ import AddAccountModal from './components/add-account-modal';
|
|||||||
import DeleteAccountModal from './components/account-table/delete-account';
|
import DeleteAccountModal from './components/account-table/delete-account';
|
||||||
import BatchTagModal from './components/batch-tag-modal';
|
import BatchTagModal from './components/batch-tag-modal';
|
||||||
import BatchGroupModal from './components/batch-group-modal';
|
import BatchGroupModal from './components/batch-group-modal';
|
||||||
import { Notification } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
|
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
|
||||||
|
import { showImportResultNotification } from '@/utils/arcoD';
|
||||||
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
|
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
|
||||||
|
import { getTaskStatus } from '@/api/all/common';
|
||||||
import {
|
import {
|
||||||
getMediaAccounts,
|
getMediaAccounts,
|
||||||
getMediaAccountsHealth,
|
getMediaAccountsHealth,
|
||||||
@ -147,6 +150,7 @@ import icon5 from '@/assets/img/media-account/icon-warn.png';
|
|||||||
import icon6 from '@/assets/img/media-account/icon-close.png';
|
import icon6 from '@/assets/img/media-account/icon-close.png';
|
||||||
|
|
||||||
let syncDataTimer = null;
|
let syncDataTimer = null;
|
||||||
|
let queryTaskTimer = null;
|
||||||
|
|
||||||
const groupManageModalRef = ref(null);
|
const groupManageModalRef = ref(null);
|
||||||
const tagsManageModalRef = ref(null);
|
const tagsManageModalRef = ref(null);
|
||||||
@ -294,6 +298,13 @@ const getAsyncStatus = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleUpdateSyncStatus = (item) => {
|
||||||
|
const target = syncMediaAccounts.value.find((v) => v.id === item.id);
|
||||||
|
// 点击取消,设置状态为成功
|
||||||
|
if (target) {
|
||||||
|
target.status = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const startSyncDataPolling = () => {
|
const startSyncDataPolling = () => {
|
||||||
isLoadingTaskStatus.value = true;
|
isLoadingTaskStatus.value = true;
|
||||||
@ -339,18 +350,43 @@ const handleOpenAbnormalAccount = () => {
|
|||||||
query.value.status = 2;
|
query.value.status = 2;
|
||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 查询导入账号任务状态
|
||||||
|
const getSyncTaskStatus = async (id, notificationId) => {
|
||||||
|
const { code, data } = await getTaskStatus(id);
|
||||||
|
if (code === 200) {
|
||||||
|
if (data?.status !== 0) {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
notificationId && Notification.remove(notificationId);
|
||||||
|
showImportResultNotification(data);
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleGetImportTaskStatus = (id, notificationId) => {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
getSyncTaskStatus(id, notificationId);
|
||||||
|
queryTaskTimer = setInterval(() => getSyncTaskStatus(id, notificationId), 3000);
|
||||||
|
};
|
||||||
const clearSyncDataTimer = () => {
|
const clearSyncDataTimer = () => {
|
||||||
if (syncDataTimer) {
|
if (syncDataTimer) {
|
||||||
clearInterval(syncDataTimer);
|
clearInterval(syncDataTimer);
|
||||||
syncDataTimer = null;
|
syncDataTimer = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const clearQueryTaskTimer = () => {
|
||||||
|
if (queryTaskTimer) {
|
||||||
|
clearInterval(queryTaskTimer);
|
||||||
|
queryTaskTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getData();
|
getData();
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearSyncDataTimer();
|
clearSyncDataTimer();
|
||||||
|
clearQueryTaskTimer();
|
||||||
});
|
});
|
||||||
|
|
||||||
provide('handleSyncData', handleSyncData);
|
provide('handleSyncData', handleSyncData);
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
export enum EnumStatus {
|
export enum EnumStatus {
|
||||||
UNAUTHORIZED = 0,
|
UNAUTHORIZED = 0,
|
||||||
NORMAL = 1,
|
NORMAL = 1,
|
||||||
ABNORMAL = 2,
|
ABNORMAL = 3,
|
||||||
PAUSE = 3,
|
PAUSE = 2,
|
||||||
ABNORMAL_LOGIN = 4,
|
ABNORMAL_LOGIN = 4,
|
||||||
ABNORMAL_REQUEST = 5,
|
ABNORMAL_REQUEST = 5,
|
||||||
ABNORMAL_FREEZE = 6,
|
ABNORMAL_FREEZE = 6,
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { STATUS_LIST } from './constants';
|
import { STATUS_LIST } from '@/views/property-marketing/media-account/components/status-select/constants';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@ -135,7 +135,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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/media-account/components/status-select/constants';
|
||||||
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||||
import { TABLE_COLUMNS } from './constants';
|
import { TABLE_COLUMNS } from './constants';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|||||||
@ -81,7 +81,7 @@ import {
|
|||||||
import GroupSelect from '../group-select';
|
import GroupSelect from '../group-select';
|
||||||
|
|
||||||
import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select';
|
import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select';
|
||||||
import StatusSelect from '@/views/property-marketing/put-account/components/status-select';
|
import StatusSelect from '@/views/property-marketing/media-account/components/status-select';
|
||||||
import AccountSelect from '@/views/property-marketing/put-account/components/account-select';
|
import AccountSelect from '@/views/property-marketing/put-account/components/account-select';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -133,7 +133,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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/media-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';
|
||||||
|
|||||||
@ -68,8 +68,9 @@ import {
|
|||||||
postPlacementAccountDataListExport,
|
postPlacementAccountDataListExport,
|
||||||
} from '@/api/all/propertyMarketing';
|
} from '@/api/all/propertyMarketing';
|
||||||
|
|
||||||
|
import { showExportNotification } from '@/utils/arcoD';
|
||||||
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
|
import { INITIAL_QUERY, INITIAL_PAGE_INFO } from './constants';
|
||||||
import { downloadByUrl } from '@/utils/tools';
|
// import { downloadByUrl } from '@/utils/tools';
|
||||||
|
|
||||||
import icon2 from '@/assets/img/media-account/icon-group.png';
|
import icon2 from '@/assets/img/media-account/icon-group.png';
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ const handleExport = () => {
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const { code, data } = res;
|
const { code, data } = res;
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
downloadByUrl(data.download_url);
|
showExportNotification(`正在下载“${data.name}”,请稍后...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, ref, computed } from 'vue';
|
import { defineProps, ref, computed } from 'vue';
|
||||||
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
||||||
import { EnumStatus } from '@/views/property-marketing/put-account/components/status-select/constants';
|
import { EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
|
||||||
|
|
||||||
import { formatNumberShow, exactFormatTime } from '@/utils/tools';
|
import { formatNumberShow, exactFormatTime } from '@/utils/tools';
|
||||||
|
|
||||||
|
|||||||
@ -126,13 +126,13 @@
|
|||||||
<icon-question-circle size="14" class="ml-4px color-#737478" />
|
<icon-question-circle size="14" class="ml-4px color-#737478" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-switch v-model="form.is_sync_project" size="medium" :checked-value="1" :un-checked-value="0" />
|
<a-switch v-model="form.is_sync_project" size="medium" :checked-value="1" :unchecked-value="0" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</a-form>
|
</a-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-button size="large" class="cancel-btn" @click="onClose">取消</a-button>
|
<a-button size="large" class="cancel-btn" @click="onClose">取消</a-button>
|
||||||
<a-button type="primary" size="large" @click="onSubmit">
|
<a-button type="primary" size="large" @click="onSubmit" :loading="importLoading">
|
||||||
{{ confirmBtnText }}
|
{{ confirmBtnText }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
@ -150,6 +150,8 @@ import AuthorizedAccountModal from '../authorized-account-modal';
|
|||||||
import StatusBox from '../status-box';
|
import StatusBox from '../status-box';
|
||||||
import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants';
|
import { PLATFORM_LIST, ENUM_PLATFORM } from '@/views/property-marketing/put-account/common_constants';
|
||||||
|
|
||||||
|
import { showExportNotification } from '@/utils/arcoD';
|
||||||
|
import { genRandomId } from '@/utils/tools';
|
||||||
import {
|
import {
|
||||||
postPlacementAccounts,
|
postPlacementAccounts,
|
||||||
getPlacementAccountsDetail,
|
getPlacementAccountsDetail,
|
||||||
@ -162,6 +164,7 @@ import icon1 from '@/assets/img/media-account/icon-download.png';
|
|||||||
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
import icon2 from '@/assets/img/media-account/icon-delete.png';
|
||||||
|
|
||||||
const update = inject('update');
|
const update = inject('update');
|
||||||
|
const emits = defineEmits(['startQueryTaskStatus']);
|
||||||
|
|
||||||
const UploadStatus = {
|
const UploadStatus = {
|
||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
@ -188,6 +191,7 @@ const authorizedAccountModalRef = ref(null);
|
|||||||
const uploadRef = ref(null);
|
const uploadRef = ref(null);
|
||||||
const file = ref(null);
|
const file = ref(null);
|
||||||
const form = ref(cloneDeep(INITIAL_FORM));
|
const form = ref(cloneDeep(INITIAL_FORM));
|
||||||
|
const importLoading = ref(false);
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
mobile: [
|
mobile: [
|
||||||
@ -213,7 +217,7 @@ const rules = {
|
|||||||
|
|
||||||
const isBatchImport = computed(() => uploadType.value === 'batch');
|
const isBatchImport = computed(() => uploadType.value === 'batch');
|
||||||
const confirmBtnText = computed(() => {
|
const confirmBtnText = computed(() => {
|
||||||
if (isBatchImport.value) return '确定导入';
|
if (isBatchImport.value) return importLoading.value ? '导入中' : '确定导入';
|
||||||
return isEdit.value ? '确定' : '下一步';
|
return isEdit.value ? '确定' : '下一步';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -229,6 +233,7 @@ const handleUpload = async (option) => {
|
|||||||
function removeFile() {
|
function removeFile() {
|
||||||
fileName.value = '';
|
fileName.value = '';
|
||||||
file.value = null;
|
file.value = null;
|
||||||
|
importLoading.value = false;
|
||||||
uploadStatus.value = UploadStatus.DEFAULT;
|
uploadStatus.value = UploadStatus.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,6 +245,7 @@ const reset = () => {
|
|||||||
fileName.value = '';
|
fileName.value = '';
|
||||||
file.value = null;
|
file.value = null;
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
|
importLoading.value = false;
|
||||||
uploadStatus.value = UploadStatus.DEFAULT;
|
uploadStatus.value = UploadStatus.DEFAULT;
|
||||||
uploadType.value = 'manual';
|
uploadType.value = 'manual';
|
||||||
};
|
};
|
||||||
@ -268,23 +274,33 @@ const getAccountDetail = async () => {
|
|||||||
|
|
||||||
const handleBatchImport = async () => {
|
const handleBatchImport = async () => {
|
||||||
try {
|
try {
|
||||||
|
if (!file.value) {
|
||||||
|
AMessage.warning('请上传要导入的文件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
importLoading.value = true;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file.value);
|
formData.append('file', file.value);
|
||||||
|
|
||||||
const { code } = await batchPlacementAccounts(formData, {
|
const { code, data } = await batchPlacementAccounts(formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
AMessage.success('导入成功');
|
const id = genRandomId();
|
||||||
update();
|
showExportNotification(`正在导入“${file.value.name}”,请稍后...`, { id });
|
||||||
|
emit('startQueryTaskStatus', data.id, id);
|
||||||
onClose();
|
onClose();
|
||||||
} else {
|
} else {
|
||||||
uploadStatus.value = UploadStatus.ERROR;
|
uploadStatus.value = UploadStatus.ERROR;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uploadStatus.value = UploadStatus.ERROR;
|
uploadStatus.value = UploadStatus.ERROR;
|
||||||
|
} finally {
|
||||||
|
importLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// importPromptModalRef.value.open();
|
// importPromptModalRef.value.open();
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
:footer="!isLoading"
|
:footer="!isLoading"
|
||||||
@close="close"
|
@close="close"
|
||||||
>
|
>
|
||||||
<div v-if="showSyncTip">
|
<!-- <div v-if="showSyncTip">
|
||||||
<div class="flex items-center mb-20px">
|
<div class="flex items-center mb-20px">
|
||||||
<img :src="icon1" width="16" height="16" class="mr-16px" />
|
<img :src="icon1" width="16" height="16" class="mr-16px" />
|
||||||
<p class="s2">
|
<p class="s2">
|
||||||
@ -25,8 +25,8 @@
|
|||||||
<a-radio value="sync" class="mb-16px">立即同步遗漏数据 - 获取完整的最新数据 (推荐)</a-radio>
|
<a-radio value="sync" class="mb-16px">立即同步遗漏数据 - 获取完整的最新数据 (推荐)</a-radio>
|
||||||
<a-radio value="no_sync">仅授权不更新 - 继续使用当前不完全的数据</a-radio>
|
<a-radio value="no_sync">仅授权不更新 - 继续使用当前不完全的数据</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div> -->
|
||||||
<div v-else class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<template v-if="isLoading">
|
<template v-if="isLoading">
|
||||||
<a-progress
|
<a-progress
|
||||||
:percent="progress"
|
:percent="progress"
|
||||||
@ -81,7 +81,7 @@ import icon2 from '@/assets/img/media-account/icon-feedback-success.png';
|
|||||||
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
import icon3 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||||
|
|
||||||
const update = inject('update');
|
const update = inject('update');
|
||||||
const INITIAL_SYNC_TYPE = 'sync';
|
// const INITIAL_SYNC_TYPE = 'sync';
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const isCompleted = ref(false);
|
const isCompleted = ref(false);
|
||||||
@ -92,10 +92,10 @@ const progress = ref(0);
|
|||||||
const id = ref('');
|
const id = ref('');
|
||||||
|
|
||||||
const selectSubAccountModalRef = ref(null);
|
const selectSubAccountModalRef = ref(null);
|
||||||
const lastSyncedAt = ref(null); // 上次同步时间戳
|
// const lastSyncedAt = ref(null); // 上次同步时间戳
|
||||||
const showSyncTip = ref(false);
|
// const showSyncTip = ref(false);
|
||||||
const shouldSelectSubAccount = ref(false);
|
const shouldSelectSubAccount = ref(false);
|
||||||
const syncType = ref(INITIAL_SYNC_TYPE); // sync | no_sync
|
// const syncType = ref(INITIAL_SYNC_TYPE); // sync | no_sync
|
||||||
const addAccountFormData = ref(null); // 添加账户表单数据
|
const addAccountFormData = ref(null); // 添加账户表单数据
|
||||||
|
|
||||||
const INITIAL_FORM = {
|
const INITIAL_FORM = {
|
||||||
@ -124,20 +124,20 @@ const confirmBtnText = computed(() => {
|
|||||||
return isSuccess.value ? '继续添加' : '重试';
|
return isSuccess.value ? '继续添加' : '重试';
|
||||||
});
|
});
|
||||||
|
|
||||||
const getDaysDiffText = (lastSyncedAt) => {
|
// const getDaysDiffText = (lastSyncedAt) => {
|
||||||
if (!lastSyncedAt) return '0天';
|
// if (!lastSyncedAt) return '0天';
|
||||||
|
|
||||||
const daysDiff = dayjs().diff(dayjs(lastSyncedAt * 1000), 'day');
|
// const daysDiff = dayjs().diff(dayjs(lastSyncedAt * 1000), 'day');
|
||||||
|
|
||||||
if (daysDiff === 0) return '不到1天';
|
// if (daysDiff === 0) return '不到1天';
|
||||||
return `${daysDiff}天`;
|
// return `${daysDiff}天`;
|
||||||
};
|
// };
|
||||||
|
|
||||||
const open = ({ accountId, last_synced_at = null, form = null, needSelectSubAccount = false }) => {
|
const open = ({ accountId, last_synced_at = null, form = null, needSelectSubAccount = false }) => {
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
id.value = accountId;
|
id.value = accountId;
|
||||||
lastSyncedAt.value = last_synced_at;
|
// lastSyncedAt.value = last_synced_at;
|
||||||
addAccountFormData.value = form;
|
addAccountFormData.value = form;
|
||||||
shouldSelectSubAccount.value = needSelectSubAccount;
|
shouldSelectSubAccount.value = needSelectSubAccount;
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
@ -154,9 +154,9 @@ const reset = () => {
|
|||||||
failReason.value = '';
|
failReason.value = '';
|
||||||
progress.value = 0;
|
progress.value = 0;
|
||||||
id.value = '';
|
id.value = '';
|
||||||
lastSyncedAt.value = null;
|
// lastSyncedAt.value = null;
|
||||||
syncType.value = INITIAL_SYNC_TYPE;
|
// syncType.value = INITIAL_SYNC_TYPE;
|
||||||
showSyncTip.value = false;
|
// showSyncTip.value = false;
|
||||||
shouldSelectSubAccount.value = false;
|
shouldSelectSubAccount.value = false;
|
||||||
addAccountFormData.value = null;
|
addAccountFormData.value = null;
|
||||||
clearFakeProgressTimer();
|
clearFakeProgressTimer();
|
||||||
@ -191,6 +191,8 @@ const startStatusPolling = () => {
|
|||||||
clearFakeProgressTimer();
|
clearFakeProgressTimer();
|
||||||
clearStatusPollingTimer();
|
clearStatusPollingTimer();
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
||||||
|
isSuccess.value && postPlacementAccountsSync(id.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@ -237,34 +239,34 @@ const clearFakeProgressTimer = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSyncData = () => {
|
// const handleSyncData = () => {
|
||||||
if (!showSyncTip.value) {
|
// if (!showSyncTip.value) {
|
||||||
formRef.value.validate(async (errors) => {
|
// formRef.value.validate(async (errors) => {
|
||||||
if (!errors) {
|
// if (!errors) {
|
||||||
showSyncTip.value = true;
|
// showSyncTip.value = true;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (syncType.value === INITIAL_SYNC_TYPE) {
|
// if (syncType.value === INITIAL_SYNC_TYPE) {
|
||||||
postPlacementAccountsSync(id.value).then((res) => {
|
// postPlacementAccountsSync(id.value).then((res) => {
|
||||||
if (res.code === 200) {
|
// if (res.code === 200) {
|
||||||
update();
|
// update();
|
||||||
close();
|
// close();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
close();
|
// close();
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
// n天未同步更新
|
// // n天未同步更新
|
||||||
if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
|
// if (lastSyncedAt.value && lastSyncedAt.value < dayjs().startOf('day').valueOf()) {
|
||||||
handleSyncData();
|
// handleSyncData();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 已完成
|
// 已完成
|
||||||
if (isCompleted.value) {
|
if (isCompleted.value) {
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
import { defineEmits, defineProps } from 'vue';
|
import { defineEmits, defineProps } from 'vue';
|
||||||
import { getPlacementAccountOperators } from '@/api/all/propertyMarketing';
|
import { getPlacementAccountOperators } from '@/api/all/propertyMarketing';
|
||||||
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
import { PLATFORM_LIST } from '@/views/property-marketing/put-account/common_constants';
|
||||||
import StatusSelect from '@/views/property-marketing/put-account/components/status-select';
|
import StatusSelect from '@/views/property-marketing/media-account/components/status-select';
|
||||||
import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select';
|
import OperatorSelect from '@/views/property-marketing/put-account/components/operator-select';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -100,7 +100,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="cts color-#3C4043 s1"
|
<span class="cts color-#3C4043 s1"
|
||||||
>已选<span class="color-#6D4CFE num mx-3px">{{ selectedAccounts.length }}个</span>账户</span
|
>已选<span class="color-#6D4CFE num mx-3px">{{ selectedRows.length }}个</span>账户</span
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a-button class="cancel-btn" size="large" @click="onClose">取消</a-button>
|
<a-button class="cancel-btn" size="large" @click="onClose">取消</a-button>
|
||||||
@ -109,7 +109,7 @@
|
|||||||
class="ml-16px"
|
class="ml-16px"
|
||||||
status="danger"
|
status="danger"
|
||||||
size="large"
|
size="large"
|
||||||
:disabled="!selectedAccounts.length"
|
:disabled="!selectedRows.length"
|
||||||
@click="onConfirm"
|
@click="onConfirm"
|
||||||
>添加已选账户</a-button
|
>添加已选账户</a-button
|
||||||
>
|
>
|
||||||
@ -136,7 +136,7 @@ const form = ref(null);
|
|||||||
const ROW_KEY = 'account_id';
|
const ROW_KEY = 'account_id';
|
||||||
const {
|
const {
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
selectedRows: selectedAccounts,
|
selectedRows,
|
||||||
dataSource,
|
dataSource,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
@ -170,7 +170,8 @@ const open = (formData) => {
|
|||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
form.value = null;
|
form.value = null;
|
||||||
selectedAccounts.value = [];
|
selectedRows.value = [];
|
||||||
|
dataSource.value = [];
|
||||||
selectedRowKeys.value = [];
|
selectedRowKeys.value = [];
|
||||||
query.value = cloneDeep(INITIAL_FORM);
|
query.value = cloneDeep(INITIAL_FORM);
|
||||||
pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
|
pageInfo.value = cloneDeep(INITIAL_PAGE_INFO);
|
||||||
@ -178,7 +179,7 @@ const onClose = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onConfirm = async () => {
|
const onConfirm = async () => {
|
||||||
const { code } = await postAddSubAccount({ ...form.value, subaccounts: selectedAccounts.value });
|
const { code } = await postAddSubAccount({ ...form.value, subaccounts: selectedRows.value });
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
update();
|
update();
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { STATUS_LIST, EnumStatus } from '@/views/property-marketing/put-account/components/status-select/constants';
|
import { STATUS_LIST, EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
|
||||||
|
|
||||||
import iconWarn1 from '@/assets/img/media-account/icon-warn-1.png';
|
import iconWarn1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||||
import iconWarn2 from '@/assets/img/media-account/icon-warn-2.png';
|
import iconWarn2 from '@/assets/img/media-account/icon-warn-2.png';
|
||||||
|
|||||||
@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AddAccountModal ref="addAccountModalRef" />
|
<AddPutAccountModal ref="addAccountModalRef" @startQueryTaskStatus="handleGetImportTaskStatus" />
|
||||||
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
|
<DeleteAccountModal ref="deleteAccountRef" @update="onDeleteSuccess" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -96,11 +96,13 @@ import { ref } from 'vue';
|
|||||||
|
|
||||||
import FilterBlock from './components/filter-block';
|
import FilterBlock from './components/filter-block';
|
||||||
import AccountTable from './components/account-table';
|
import AccountTable from './components/account-table';
|
||||||
import AddAccountModal from './components/add-account-modal';
|
import AddPutAccountModal from './components/add-account-modal';
|
||||||
import DeleteAccountModal from './components/account-table/delete-account';
|
import DeleteAccountModal from './components/account-table/delete-account';
|
||||||
|
|
||||||
import { INITIAL_QUERY } from './constants';
|
import { INITIAL_QUERY } from './constants';
|
||||||
import { getPlacementAccounts, getPlacementAccountsHealth } from '@/api/all/propertyMarketing';
|
import { getPlacementAccounts, getPlacementAccountsHealth } from '@/api/all/propertyMarketing';
|
||||||
|
import { getTaskStatus } from '@/api/all/common';
|
||||||
|
import { showImportResultNotification } from '@/utils/arcoD';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/media-account/icon-add.png';
|
import icon1 from '@/assets/img/media-account/icon-add.png';
|
||||||
import icon4 from '@/assets/img/media-account/icon-success.png';
|
import icon4 from '@/assets/img/media-account/icon-success.png';
|
||||||
@ -111,6 +113,7 @@ const groupManageModalRef = ref(null);
|
|||||||
const tagsManageModalRef = ref(null);
|
const tagsManageModalRef = ref(null);
|
||||||
const addAccountModalRef = ref(null);
|
const addAccountModalRef = ref(null);
|
||||||
const deleteAccountRef = ref(null);
|
const deleteAccountRef = ref(null);
|
||||||
|
let queryTaskTimer = null;
|
||||||
|
|
||||||
const pageInfo = ref({
|
const pageInfo = ref({
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -158,10 +161,6 @@ const tipLabel = computed(() => {
|
|||||||
return `共有 ${total_abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join(',')}。`;
|
return `共有 ${total_abnormal_number} 个账号存在授权异常,其中:${abnormalLabels.join(',')}。`;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getData();
|
|
||||||
});
|
|
||||||
|
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
getHealthData();
|
getHealthData();
|
||||||
getAccountData();
|
getAccountData();
|
||||||
@ -257,6 +256,37 @@ const handleOpenAbnormalAccount = () => {
|
|||||||
reload();
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 查询导入账号任务状态
|
||||||
|
const getSyncTaskStatus = async (id, notificationId) => {
|
||||||
|
const { code, data } = await getTaskStatus(id);
|
||||||
|
if (code === 200) {
|
||||||
|
if (data?.status !== 0) {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
notificationId && Notification.remove(notificationId);
|
||||||
|
showImportResultNotification(data);
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleGetImportTaskStatus = (id, notificationId) => {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
getSyncTaskStatus(id, notificationId);
|
||||||
|
queryTaskTimer = setInterval(() => getSyncTaskStatus(id, notificationId), 3000);
|
||||||
|
};
|
||||||
|
const clearQueryTaskTimer = () => {
|
||||||
|
if (queryTaskTimer) {
|
||||||
|
clearInterval(queryTaskTimer);
|
||||||
|
queryTaskTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearQueryTaskTimer();
|
||||||
|
});
|
||||||
|
|
||||||
provide('update', getData);
|
provide('update', getData);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Author: RenXiaoDong
|
|
||||||
* @Date: 2025-07-04 11:18:11
|
|
||||||
*/
|
|
||||||
export enum EnumStatus {
|
|
||||||
UNAUTHORIZED = 0,
|
|
||||||
NORMAL = 1,
|
|
||||||
ABNORMAL = 2,
|
|
||||||
PAUSE = 3,
|
|
||||||
ABNORMAL_LOGIN = 4,
|
|
||||||
ABNORMAL_REQUEST = 5,
|
|
||||||
ABNORMAL_FREEZE = 6,
|
|
||||||
ABNORMAL_MISSING = 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const STATUS_LIST = [
|
|
||||||
{
|
|
||||||
text: '正常',
|
|
||||||
label: '正常',
|
|
||||||
value: EnumStatus.NORMAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '暂停同步',
|
|
||||||
label: '暂停同步',
|
|
||||||
value: EnumStatus.PAUSE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '未授权',
|
|
||||||
label: '未授权',
|
|
||||||
value: EnumStatus.UNAUTHORIZED,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '异常',
|
|
||||||
label: '异常',
|
|
||||||
value: EnumStatus.ABNORMAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '数据缺失',
|
|
||||||
label: '数据缺失',
|
|
||||||
value: EnumStatus.ABNORMAL_MISSING,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '异常-登录状态失效',
|
|
||||||
label: '异常',
|
|
||||||
value: EnumStatus.ABNORMAL_LOGIN,
|
|
||||||
tooltip: '登录状态失效,需重新扫码授权',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '异常-请求过于频繁',
|
|
||||||
label: '异常',
|
|
||||||
value: EnumStatus.ABNORMAL_REQUEST,
|
|
||||||
tooltip: '请求过于频繁,需等待24小时后重试',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '异常-账号被冻结/封禁',
|
|
||||||
label: '异常',
|
|
||||||
value: EnumStatus.ABNORMAL_FREEZE,
|
|
||||||
tooltip: '账号被冻结/封禁',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
<!--
|
|
||||||
* @Author: RenXiaoDong
|
|
||||||
* @Date: 2025-06-25 14:02:40
|
|
||||||
-->
|
|
||||||
<template>
|
|
||||||
<a-select
|
|
||||||
v-model="selectedStatus"
|
|
||||||
:multiple="multiple"
|
|
||||||
size="medium"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
allow-clear
|
|
||||||
@change="handleChange"
|
|
||||||
>
|
|
||||||
<a-option v-for="(item, index) in STATUS_LIST" :key="index" :value="item.value" :label="item.text">
|
|
||||||
{{ item.text }}
|
|
||||||
</a-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
import { STATUS_LIST } from './constants';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: {
|
|
||||||
type: [Array, String, Number],
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
multiple: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '全部',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const emits = defineEmits(['update:modelValue', 'change']);
|
|
||||||
|
|
||||||
const selectedStatus = ref(props.multiple ? [] : '');
|
|
||||||
|
|
||||||
// 监听外部传入的值变化
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(newVal) => {
|
|
||||||
selectedStatus.value = newVal;
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
);
|
|
||||||
// 监听内部值变化,向外部发送更新
|
|
||||||
watch(selectedStatus, (newVal) => {
|
|
||||||
emits('update:modelValue', newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleChange = (value) => {
|
|
||||||
selectedStatus.value = value;
|
|
||||||
emits('change', value);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -16,7 +16,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"types": ["unplugin-icons/types/vue", "unplugin-vue-define-options/macros-global"]
|
"types": ["unplugin-vue-define-options/macros-global"]
|
||||||
},
|
},
|
||||||
|
|
||||||
"references": [
|
"references": [
|
||||||
|
|||||||