Merge remote-tracking branch 'origin/main' into feature/v1.2灵机空间-内容上传审核_rxd

# Conflicts:
#	src/components/_base/navbar/components/navbar-menu/index.vue
This commit is contained in:
rd
2025-07-31 11:27:09 +08:00
22 changed files with 617 additions and 308 deletions

View File

@ -0,0 +1,168 @@
import {
EnumStatus,
EnumErrorStatus,
getStatusInfo,
} from '@/views/property-marketing/media-account/components/status-select/status-box';
import { Dropdown, Doption, Button, Tooltip } from '@arco-design/web-vue';
export default defineComponent({
name: 'FooterBtn',
props: {
item: {
type: Object,
default: () => ({}),
},
},
emits: ['syncData', 'handleReauthorize', 'handlePause', 'openDelete', 'openEdit'],
setup(props, { emit }) {
const statusInfo = computed(() => {
const { status, error_status, to_be_expire_for_cookie } = props.item;
return getStatusInfo(status, error_status, to_be_expire_for_cookie) ?? {};
});
const renderEditDoption = () => {
return (
<Doption class="color-#211F24" onClick={() => emit('openEdit', props.item)}>
</Doption>
);
};
const renderReauthorizeDoption = (text = '重新授权') => {
return (
<Doption class="color-#211F24" onClick={() => emit('handleReauthorize', props.item)}>
{text}
</Doption>
);
};
const renderPauseDoption = () => {
return (
<Doption class="color-#211F24" onClick={() => emit('handlePause', props.item)}>
</Doption>
);
};
const renderUpdateBtn = () => {
return (
<Button type="outline" size="mini" onClick={() => emit('syncData', props.item)}>
</Button>
);
};
const renderDeleteDoption = () => {
return (
<Doption class="color-#F64B31" onClick={() => emit('openDelete', props.item)}>
</Doption>
);
};
const renderNormal = () => {
return (
<>
<Dropdown
trigger="hover"
v-slots={{
default: () => (
<Button type="outline" class="mr-8px" size="mini">
</Button>
),
content: () => (
<>
{renderEditDoption()}
{renderReauthorizeDoption()}
{renderPauseDoption()}
{renderDeleteDoption()}
</>
),
}}
></Dropdown>
{renderUpdateBtn()}
</>
);
};
const renderPause = () => {
return (
<>
<Dropdown
trigger="hover"
v-slots={{
default: () => (
<Button type="outline" class="mr-8px" size="mini">
</Button>
),
content: () => (
<>
{renderEditDoption()}
{renderDeleteDoption()}
</>
),
}}
></Dropdown>
<Button type="outline" size="mini" onClick={() => emit('handleReauthorize', props.item)}>
</Button>
</>
);
};
// 异常情况
const renderAbnormal = () => {
const { error_status } = props.item;
const isMissing = error_status === EnumErrorStatus.MISSING;
const isUnauthorized = error_status === EnumErrorStatus.UNAUTHORIZED;
const renderSyncBtn = () => {
if (isMissing) {
return renderUpdateBtn();
} else if ([EnumErrorStatus.REQUEST, EnumErrorStatus.FREEZE].includes(error_status)) {
return (
<Tooltip content={statusInfo.value.disabledBtnTooltip}>
<Button type="outline" size="mini" disabled>
</Button>
</Tooltip>
);
} else {
return (
<Button type="outline" size="mini" onClick={() => emit('handleReauthorize', props.item)}>
{isUnauthorized ? '去授权' : '重新授权'}
</Button>
);
}
};
return (
<>
<Dropdown
trigger="hover"
v-slots={{
default: () => (
<Button type="outline" class="mr-8px" size="mini">
</Button>
),
content: () => (
<>
{renderEditDoption()}
{isMissing && renderReauthorizeDoption()}
{renderPauseDoption()}
{renderDeleteDoption()}
</>
),
}}
></Dropdown>
{renderSyncBtn()}
</>
);
};
const renderContent = () => {
const { status } = props.item;
if (status === EnumStatus.NORMAL) {
return renderNormal();
} else if (status === EnumStatus.PAUSE) {
return renderPause();
} else {
return renderAbnormal();
}
};
return () => renderContent();
},
});

View File

@ -24,7 +24,7 @@
</a-tooltip>
<div class="field-row">
<span class="label">状态</span>
<StatusBox :status="item.status" />
<StatusBox :item="item" />
</div>
<div class="field-row">
<span class="label">数据更新时间</span>
@ -99,34 +99,26 @@
</div>
</div>
<div class="operate-row">
<a-dropdown trigger="hover">
<a-button class="w-52px mr-8px" type="outline" size="mini">
<template #default>更多</template>
</a-button>
<template #content>
<a-doption class="color-#211F24" @click="openEdit(item)">编辑</a-doption>
<a-doption v-if="item.status === EnumStatus.NORMAL" class="color-#211F24" @click="handleReauthorize(item)"
>重新授权</a-doption
>
<a-doption v-if="showPauseButton(item.status)" class="color-#211F24" @click="handlePause(item)"
>暂停同步</a-doption
>
<a-doption class="color-#F64B31" @click="openDelete(item)">删除</a-doption>
</template>
<a-button type="outline" size="mini" @click="onBtnClick(item)">
<template #default>{{ getBtnText(item) }}</template>
</a-button>
</a-dropdown>
<FooterBtn
:item="item"
@handleReauthorize="handleReauthorize"
@syncData="syncData"
@openEdit="openEdit"
@openDelete="openDelete"
@handlePause="handlePause"
/>
</div>
</div>
<div v-if="isSyncFailed(item)" class="mask">
<div class="flex items-center mb-16px box">
<img :src="icon3" width="16" height="16" class="mr-8px" />
<span class="name !mb-0">更新数据失败</span>
<span class="name !mb-0">{{ getErrorStatusText(item) }}</span>
</div>
<div class="flex items-center">
<a-button type="outline" class="mr-8px" size="mini" @click="onDeleteSyncStatus(item)">取消</a-button>
<a-button type="outline" class="" size="mini" @click="syncData(item)">重新更新</a-button>
<a-button type="outline" class="mr-8px" size="mini" @click="handleCancel(item)">取消</a-button>
<a-button type="outline" size="mini" @click="handleConfirm(item)" v-if="showConfirmBtn(item)">{{
getConfirmBtnText(item)
}}</a-button>
</div>
</div>
</a-spin>
@ -139,19 +131,23 @@
<script setup>
import { defineProps, ref, computed, inject } from 'vue';
import { useRouter } from 'vue-router';
import { STATUS_LIST, EnumStatus } from '@/views/property-marketing/media-account/components/status-select/constants';
import { deleteSyncStatus } from '@/api/all/propertyMarketing';
import { exactFormatTime } from '@/utils/tools';
import {
EnumErrorStatus,
errorStatusMap,
EnumStatus,
} from '@/views/property-marketing/media-account/components/status-select/status-box';
import PauseAccountPatchModal from './pause-account-patch';
import StatusBox from '../status-box';
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
import ReauthorizeAccountModal from '../reauthorize-account-modal';
import AuthorizedAccountModal from '../authorized-account-modal';
import FooterBtn from './footer-btn';
import icon1 from '@/assets/img/media-account/icon-dy.png';
import icon2 from '@/assets/img/media-account/icon-xhs.png';
import icon3 from '@/assets/img/media-account/icon-warn.png';
// import icon3 from '@/assets/img/media-account/icon-delete.png';
const props = defineProps({
dataSource: {
@ -173,6 +169,7 @@ const props = defineProps({
});
const emits = defineEmits(['openEdit', 'update', 'selectionChange', 'delete', 'updateSyncStatus']);
const syncData = inject('handleSyncData');
const router = useRouter();
@ -194,12 +191,6 @@ const toggleSelect = (item) => {
}
emits('selectionChange', newSelected);
};
const isSyncing = (item) => {
if (!props.syncMediaAccounts.length) return false;
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
return target?.status === 0;
};
const openEdit = (item) => {
emits('openEdit', item);
@ -210,8 +201,8 @@ const openDelete = (item) => {
};
const handleReauthorize = (item) => {
const { id, platform, status } = item;
const isUnauthorized = isUnauthorizedStatus(status);
const { id, platform, error_status } = item;
const isUnauthorized = isUnauthorizedStatus(error_status);
if (isUnauthorized) {
authorizedAccountModalRef.value?.open(id, platform);
} else {
@ -223,79 +214,26 @@ const handlePause = (item) => {
pauseAccountPatchModalRef.value?.open(item);
};
const showPauseButton = (status) => {
return ![EnumStatus.PAUSE, EnumStatus.UNAUTHORIZED].includes(status);
};
const showSyncDataButton = (status) => {
return [EnumStatus.NORMAL, EnumStatus.ABNORMAL_MISSING].includes(status);
};
const isUnauthorizedStatus = (status) => {
return [EnumStatus.UNAUTHORIZED].includes(status);
};
// 三种异常情况
const isAbnormalStatus = (status) => {
return [
EnumStatus.ABNORMAL,
EnumStatus.ABNORMAL_LOGIN,
EnumStatus.ABNORMAL_REQUEST,
EnumStatus.ABNORMAL_FREEZE,
].includes(status);
};
// const getTooltipText = (status) => {
// return STATUS_LIST.find((v) => v.value === status)?.tooltip ?? '-';
// };
const syncData = inject('handleSyncData');
const onBtnClick = (item) => {
if (showSyncDataButton(item.status)) {
syncData(item);
return;
}
if (isUnauthorizedStatus(item.status)) {
handleReauthorize(item);
return;
}
if ([EnumStatus.PAUSE].includes(item.status) || isAbnormalStatus(item.status)) {
handleReauthorize(item);
return;
}
handlePause(item);
};
const getBtnText = (item) => {
if (showSyncDataButton(item.status)) {
return '更新数据';
}
if (isUnauthorizedStatus(item.status)) {
return '去授权';
}
if ([EnumStatus.PAUSE].includes(item.status) || isAbnormalStatus(item.status)) {
return '重新授权';
}
return '暂停同步';
const isUnauthorizedStatus = (error_status) => {
return [EnumErrorStatus.UNAUTHORIZED].includes(error_status);
};
const goDetail = (item) => {
router.push(`/media-account/detail/${item.id}`);
};
const onDeleteSyncStatus = async (item) => {
await deleteSyncStatus(item.id);
emits('updateSyncStatus', item);
};
const formatTime = (time) => {
return exactFormatTime(time, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
};
const getSyncMediaAccount = (item) => {
return props.syncMediaAccounts.find((v) => v.id === item.id);
};
const isSyncing = (item) => {
if (!props.syncMediaAccounts.length) return false;
return getSyncMediaAccount(item)?.status === 0;
};
const getLastSyncedAt = (item) => {
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
const target = getSyncMediaAccount(item);
if (props.isLoadingTaskStatus && target) {
if (target?.status !== 0) {
return formatTime(target.last_synced_at);
@ -304,8 +242,42 @@ const getLastSyncedAt = (item) => {
return formatTime(item.last_synced_at);
};
const isSyncFailed = (item) => {
const target = props.syncMediaAccounts.find((v) => v.id === item.id);
return target?.status === 2;
return getSyncMediaAccount(item)?.status === 2;
};
const getErrorStatusText = (item) => {
const error_status = getSyncMediaAccount(item)?.error_status;
return `异常(${errorStatusMap.get(error_status)?.text ?? ''}`;
};
const handleCancel = async (item) => {
const error_status = getSyncMediaAccount(item)?.error_status;
await deleteSyncStatus(item.id);
item.status = EnumStatus.ABNORMAL;
item.error_status = error_status;
emits('updateSyncStatus', item);
};
const showConfirmBtn = (item) => {
const error_status = getSyncMediaAccount(item)?.error_status;
return [EnumErrorStatus.MISSING, EnumErrorStatus.LOGIN].includes(error_status);
};
const handleConfirm = (item) => {
const error_status = getSyncMediaAccount(item)?.error_status;
if (error_status === EnumErrorStatus.MISSING) {
syncData(item);
}
if (error_status === EnumErrorStatus.LOGIN) {
handleReauthorize(item);
}
};
const getConfirmBtnText = (item) => {
const error_status = getSyncMediaAccount(item)?.error_status;
if (error_status === EnumErrorStatus.MISSING) {
return '重新更新';
}
if (error_status === EnumErrorStatus.LOGIN) {
return '重新授权';
}
};
</script>

View File

@ -21,7 +21,7 @@ import {
} from '@arco-design/web-vue';
import AuthorizedAccountModal from '../authorized-account-modal';
// import ImportPromptModal from '../import-prompt-modal';
import StatusBox from '../status-box';
import StatusBox from '@/views/property-marketing/media-account/components/status-select/status-box.tsx';
import SyncDataModal from '../sync-data-modal';
import CommonSelect from '@/components/common-select';
@ -353,7 +353,7 @@ export default {
<Input v-model={form.value.account_id} placeholder="请输入..." size="large" disabled />
</FormItem>
<FormItem label="状态" field="status">
<StatusBox status={form.value.status} />
<StatusBox item={form.value} />
</FormItem>
</>
)}

View File

@ -1,97 +0,0 @@
<!--
* @Author: RenXiaoDong
* @Date: 2025-06-25 15:31:15
-->
<template>
<div class="status-box" :class="`status-box-${status}`">
<span class="text">{{ statusText }}</span>
<a-tooltip v-if="showTooltip" :content="tooltipText">
<img v-if="showIcon" :src="iconSrc" width="12" height="12" class="ml-4px" />
</a-tooltip>
</div>
</template>
<script setup>
import { computed } from 'vue';
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 iconWarn2 from '@/assets/img/media-account/icon-warn-2.png';
const props = defineProps({
status: {
type: Number,
required: true,
},
});
const statusText = computed(() => {
return STATUS_LIST.find((v) => v.value === props.status)?.label ?? '-';
});
const showTooltip = computed(() => {
return isDisabledReauthorize(props.status);
});
const tooltipText = computed(() => {
return STATUS_LIST.find((v) => v.value === props.status)?.tooltip ?? '-';
});
const showIcon = computed(() => {
return ![EnumStatus.NORMAL, EnumStatus.UNAUTHORIZED].includes(props.status);
});
const iconSrc = computed(() => {
return props.status === EnumStatus.PAUSE ? iconWarn1 : iconWarn2;
});
// 判断是否为禁用重新授权的状态
const isDisabledReauthorize = (status) => {
return [EnumStatus.ABNORMAL_LOGIN, EnumStatus.ABNORMAL_REQUEST, EnumStatus.ABNORMAL_FREEZE].includes(status);
};
</script>
<style scoped lang="scss">
.status-box {
display: flex;
padding: 0px 8px;
align-items: center;
border-radius: 2px;
background: #f2f3f5;
.text {
color: var(--BG-700, #737478);
font-family: $font-family-medium;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
}
&-1 {
background: #ebf7f2;
.text {
color: #25c883;
}
}
&-2,
&-4,
&-5,
&-6,
&-7 {
background: #ffe7e4;
.text {
color: #f64b31;
}
}
&-3 {
background: #fff7e5;
color: #ffae00;
.text {
color: #ffae00;
}
}
}
</style>