更新
This commit is contained in:
@@ -63,7 +63,7 @@ const handleLogout = () => {
|
||||
</a-menu-item>
|
||||
<a-menu-item key="/accounts">
|
||||
<template #icon><UserOutlined /></template>
|
||||
<span>账号管理</span>
|
||||
<span>{{ auth.isAdmin ? '账号管理' : '我的账号' }}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="/messages">
|
||||
<template #icon><MessageOutlined /></template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import api from '../api'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
import { useIsMobile } from '../composables/useIsMobile'
|
||||
|
||||
const auth = useAuthStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const isMobile = useIsMobile()
|
||||
const profileModalWidth = computed(() => (isMobile.value ? 'calc(100vw - 32px)' : 860))
|
||||
@@ -983,6 +984,10 @@ const goAccountRulesPage = (accountId) => {
|
||||
}
|
||||
|
||||
const openAddModal = () => {
|
||||
if (!auth.canWrite) {
|
||||
message.warning('当前账号为只读角色,不能添加托管账号')
|
||||
return
|
||||
}
|
||||
if (!canAddAccount.value) {
|
||||
if (canPurchaseSlots.value) {
|
||||
purchaseVisible.value = true
|
||||
@@ -1705,13 +1710,21 @@ const clearCookie = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
auth.fetchMe()
|
||||
fetchPaymentConfig()
|
||||
fetchDeviceProfiles()
|
||||
fetchAccounts()
|
||||
fetchRules()
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
auth.fetchMe(),
|
||||
fetchPaymentConfig(),
|
||||
fetchDeviceProfiles(),
|
||||
fetchAccounts(),
|
||||
fetchRules(),
|
||||
])
|
||||
startReplyQueueSummaryPolling()
|
||||
if (route.query.action === 'add') {
|
||||
openAddModal()
|
||||
const nextQuery = { ...route.query }
|
||||
delete nextQuery.action
|
||||
router.replace({ path: route.path, query: nextQuery })
|
||||
}
|
||||
// 不再定时轮询账号列表;进入页面加载一次,之后由各操作(启动/停止/登录等)
|
||||
// 或手动点击「刷新」按钮按需刷新
|
||||
})
|
||||
@@ -1797,6 +1810,7 @@ onUnmounted(() => {
|
||||
购买额度
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="auth.canWrite"
|
||||
type="primary"
|
||||
class="gradient-btn"
|
||||
:disabled="!canAddAccount && !canPurchaseSlots"
|
||||
@@ -2052,7 +2066,7 @@ onUnmounted(() => {
|
||||
<UserOutlined style="font-size: 4rem; color: var(--text-muted); margin-bottom: 16px;" />
|
||||
<h3>暂无托管账号</h3>
|
||||
<p style="color: var(--text-secondary); margin-bottom: 20px;">添加一个抖音账号,开始自动化回复工作吧</p>
|
||||
<a-button type="primary" class="gradient-btn" @click="openAddModal">
|
||||
<a-button v-if="auth.canWrite" type="primary" class="gradient-btn" @click="openAddModal">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
立即添加
|
||||
</a-button>
|
||||
@@ -2257,7 +2271,7 @@ onUnmounted(() => {
|
||||
placeholder="0 或留空则继承系统默认"
|
||||
/>
|
||||
<div class="field-hint">
|
||||
设置 N 秒后,同一账号的待回复会话会依次排队:第 1 条在 N 秒后发送,第 2 条在 2N 秒后发送,以此类推;各账号队列互不影响。0 或留空表示继承系统默认,当前生效 {{ editForm.reply_delay_effective }} 秒(0 表示不启用兜底排队、立即回复)。
|
||||
设置 N 秒后,同一账号队列为空时,首条回复等待 0 秒并立即发送;后续待回复会话按 N 秒、2N 秒依次排队。各账号队列互不影响,实际发送仍受全局带宽队列保护。0 或留空表示继承系统默认,当前生效 {{ editForm.reply_delay_effective }} 秒(0 表示不启用兜底排队、收到消息后立即回复)。
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
@@ -2,18 +2,25 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import api from '../api'
|
||||
import MessageBubble from '../components/MessageBubble.vue'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import {
|
||||
UserOutlined,
|
||||
MessageOutlined,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
ArrowRightOutlined,
|
||||
ThunderboltOutlined
|
||||
ThunderboltOutlined,
|
||||
SettingOutlined,
|
||||
PlusOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const stats = ref({
|
||||
totalAccounts: 0,
|
||||
activeAccounts: 0,
|
||||
myAccounts: 0,
|
||||
myActiveAccounts: 0,
|
||||
totalMessages: 0,
|
||||
repliedMessages: 0,
|
||||
replyRate: '0%'
|
||||
@@ -24,14 +31,15 @@ const loading = ref(true)
|
||||
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const [accountsRes, statsRes] = await Promise.all([
|
||||
api.get(`/accounts`),
|
||||
const [accountStatsRes, statsRes] = await Promise.all([
|
||||
api.get(`/dashboard/account-stats`),
|
||||
api.get(`/logs/stats`)
|
||||
])
|
||||
|
||||
const accounts = accountsRes.data
|
||||
stats.value.totalAccounts = accounts.length
|
||||
stats.value.activeAccounts = accounts.filter(a => a.status === 'online').length
|
||||
stats.value.totalAccounts = accountStatsRes.data.total_accounts || 0
|
||||
stats.value.activeAccounts = accountStatsRes.data.online_accounts || 0
|
||||
stats.value.myAccounts = accountStatsRes.data.my_accounts || 0
|
||||
stats.value.myActiveAccounts = accountStatsRes.data.my_online_accounts || 0
|
||||
|
||||
// 后端全量统计所有消息,不受列表条数限制
|
||||
stats.value.totalMessages = statsRes.data.total || 0
|
||||
@@ -84,8 +92,27 @@ onUnmounted(() => {
|
||||
多账户自动回复RPA后台。支持快捷扫码登录、状态持久化保存、以及自定义关键字规则精准答复。
|
||||
</p>
|
||||
</div>
|
||||
<div class="banner-icon">
|
||||
<ThunderboltOutlined style="font-size: 4rem; color: #c084fc; opacity: 0.3;" />
|
||||
<div class="banner-side">
|
||||
<div v-if="auth.canWrite" class="banner-actions">
|
||||
<router-link to="/accounts">
|
||||
<a-button size="large">
|
||||
<template #icon><UserOutlined /></template>
|
||||
{{ auth.isAdmin ? '账号管理' : `我的账号(${stats.myAccounts})` }}
|
||||
</a-button>
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="auth.canWrite"
|
||||
:to="{ path: '/accounts', query: { action: 'add' } }"
|
||||
>
|
||||
<a-button type="primary" size="large" class="gradient-btn">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
{{ auth.isAdmin ? '添加账号' : '添加自己的账号' }}
|
||||
</a-button>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="banner-icon">
|
||||
<ThunderboltOutlined style="font-size: 4rem; color: #c084fc; opacity: 0.3;" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -98,7 +125,7 @@ onUnmounted(() => {
|
||||
<UserOutlined />
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<span class="stat-label">托管账号</span>
|
||||
<span class="stat-label">全平台托管账号</span>
|
||||
<h2 class="stat-value">{{ stats.totalAccounts }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,7 +138,7 @@ onUnmounted(() => {
|
||||
<CheckCircleOutlined />
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<span class="stat-label">在线运行</span>
|
||||
<span class="stat-label">全平台在线运行</span>
|
||||
<h2 class="stat-value text-green">{{ stats.activeAccounts }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
@@ -199,8 +226,9 @@ onUnmounted(() => {
|
||||
<div class="quick-actions-grid" style="margin-top: 20px;">
|
||||
<router-link to="/accounts" class="quick-action-card">
|
||||
<UserOutlined class="action-icon text-gradient" />
|
||||
<span>账号配置</span>
|
||||
<p>扫码登录并托管多个抖音账号</p>
|
||||
<span>{{ auth.isAdmin ? '账号管理' : '我的账号' }}</span>
|
||||
<p v-if="auth.isAdmin">管理全平台账号配置与运行状态</p>
|
||||
<p v-else>仅查看和管理自己添加的账号,当前 {{ stats.myAccounts }} 个</p>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/rules" class="quick-action-card">
|
||||
@@ -225,6 +253,20 @@ onUnmounted(() => {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.banner-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.banner-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -436,6 +478,20 @@ onUnmounted(() => {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.banner-side,
|
||||
.banner-actions {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.banner-actions > a {
|
||||
flex: 1 1 180px;
|
||||
}
|
||||
|
||||
.banner-actions :deep(.ant-btn) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -740,7 +740,7 @@ onMounted(() => {
|
||||
/>
|
||||
|
||||
<div class="field-hint">
|
||||
账号未单独设置时使用此间隔。同一账号的第 1 条待回复在 N 秒后发送,第 2 条在 2N 秒后发送,以此类推;各账号队列互不影响。设为 0 表示不启用兜底排队,收到消息后立即回复。
|
||||
账号未单独设置时使用此间隔。同一账号队列为空时,首条回复等待 0 秒并立即发送;后续待回复会话按 N 秒、2N 秒依次排队。各账号队列互不影响,实际发送仍受全局带宽队列保护。设为 0 表示不启用兜底排队,收到消息后立即回复。
|
||||
</div>
|
||||
|
||||
</a-form-item>
|
||||
|
||||
Reference in New Issue
Block a user