feat: 账号管理页面
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div class="about">
|
||||||
|
<h1>关于我们</h1>
|
||||||
|
<p>这是关于页面</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 关于页面组件
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/styles/variables.scss' as *;
|
||||||
|
|
||||||
|
.about {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #7f8c8d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,840 @@
|
|||||||
|
<template>
|
||||||
|
<div class="account-management">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>账号管理</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="account-tabs">
|
||||||
|
<el-tabs v-model="activeTab" class="account-tabs-nav">
|
||||||
|
<el-tab-pane label="全部" name="all">
|
||||||
|
<div class="account-list-container">
|
||||||
|
<div class="account-search">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="输入名称或账号搜索"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button type="primary" @click="handleAddAccount">添加账号</el-button>
|
||||||
|
<el-button type="info" @click="fetchAccounts" :loading="false">
|
||||||
|
<el-icon :class="{ 'is-loading': appStore.isAccountRefreshing }"><Refresh /></el-icon>
|
||||||
|
<span v-if="appStore.isAccountRefreshing">刷新中</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredAccounts.length > 0" class="account-list">
|
||||||
|
<el-table :data="filteredAccounts" style="width: 100%">
|
||||||
|
<el-table-column label="头像" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :src="scope.row.avatar" :size="40" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="180" />
|
||||||
|
<el-table-column prop="platform" label="平台">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getPlatformTagType(scope.row.platform)"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.platform }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.status === '正常' ? 'success' : 'danger'"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="empty-data">
|
||||||
|
<el-empty description="暂无账号数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="快手" name="kuaishou">
|
||||||
|
<div class="account-list-container">
|
||||||
|
<div class="account-search">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="输入名称或账号搜索"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button type="primary" @click="handleAddAccount">添加账号</el-button>
|
||||||
|
<el-button type="info" @click="fetchAccounts" :loading="false">
|
||||||
|
<el-icon :class="{ 'is-loading': appStore.isAccountRefreshing }"><Refresh /></el-icon>
|
||||||
|
<span v-if="appStore.isAccountRefreshing">刷新中</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredKuaishouAccounts.length > 0" class="account-list">
|
||||||
|
<el-table :data="filteredKuaishouAccounts" style="width: 100%">
|
||||||
|
<el-table-column label="头像" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :src="scope.row.avatar" :size="40" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="180" />
|
||||||
|
<el-table-column prop="platform" label="平台">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getPlatformTagType(scope.row.platform)"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.platform }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.status === '正常' ? 'success' : 'danger'"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="empty-data">
|
||||||
|
<el-empty description="暂无快手账号数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="抖音" name="douyin">
|
||||||
|
<div class="account-list-container">
|
||||||
|
<div class="account-search">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="输入名称或账号搜索"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button type="primary" @click="handleAddAccount">添加账号</el-button>
|
||||||
|
<el-button type="info" @click="fetchAccounts" :loading="false">
|
||||||
|
<el-icon :class="{ 'is-loading': appStore.isAccountRefreshing }"><Refresh /></el-icon>
|
||||||
|
<span v-if="appStore.isAccountRefreshing">刷新中</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredDouyinAccounts.length > 0" class="account-list">
|
||||||
|
<el-table :data="filteredDouyinAccounts" style="width: 100%">
|
||||||
|
<el-table-column label="头像" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :src="scope.row.avatar" :size="40" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="180" />
|
||||||
|
<el-table-column prop="platform" label="平台">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getPlatformTagType(scope.row.platform)"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.platform }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.status === '正常' ? 'success' : 'danger'"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="empty-data">
|
||||||
|
<el-empty description="暂无抖音账号数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="视频号" name="channels">
|
||||||
|
<div class="account-list-container">
|
||||||
|
<div class="account-search">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="输入名称或账号搜索"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button type="primary" @click="handleAddAccount">添加账号</el-button>
|
||||||
|
<el-button type="info" @click="fetchAccounts" :loading="false">
|
||||||
|
<el-icon :class="{ 'is-loading': appStore.isAccountRefreshing }"><Refresh /></el-icon>
|
||||||
|
<span v-if="appStore.isAccountRefreshing">刷新中</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredChannelsAccounts.length > 0" class="account-list">
|
||||||
|
<el-table :data="filteredChannelsAccounts" style="width: 100%">
|
||||||
|
<el-table-column label="头像" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :src="scope.row.avatar" :size="40" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="180" />
|
||||||
|
<el-table-column prop="platform" label="平台">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getPlatformTagType(scope.row.platform)"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.platform }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.status === '正常' ? 'success' : 'danger'"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="empty-data">
|
||||||
|
<el-empty description="暂无视频号账号数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="小红书" name="xiaohongshu">
|
||||||
|
<div class="account-list-container">
|
||||||
|
<div class="account-search">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="输入名称或账号搜索"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<el-button type="primary" @click="handleAddAccount">添加账号</el-button>
|
||||||
|
<el-button type="info" @click="fetchAccounts" :loading="false">
|
||||||
|
<el-icon :class="{ 'is-loading': appStore.isAccountRefreshing }"><Refresh /></el-icon>
|
||||||
|
<span v-if="appStore.isAccountRefreshing">刷新中</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredXiaohongshuAccounts.length > 0" class="account-list">
|
||||||
|
<el-table :data="filteredXiaohongshuAccounts" style="width: 100%">
|
||||||
|
<el-table-column label="头像" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :src="scope.row.avatar" :size="40" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="180" />
|
||||||
|
<el-table-column prop="platform" label="平台">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getPlatformTagType(scope.row.platform)"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.platform }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.status === '正常' ? 'success' : 'danger'"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="empty-data">
|
||||||
|
<el-empty description="暂无小红书账号数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加/编辑账号对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="dialogType === 'add' ? '添加账号' : '编辑账号'"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="!sseConnecting"
|
||||||
|
:show-close="!sseConnecting"
|
||||||
|
>
|
||||||
|
<el-form :model="accountForm" label-width="80px" :rules="rules" ref="accountFormRef">
|
||||||
|
<el-form-item label="平台" prop="platform">
|
||||||
|
<el-select
|
||||||
|
v-model="accountForm.platform"
|
||||||
|
placeholder="请选择平台"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="sseConnecting"
|
||||||
|
>
|
||||||
|
<el-option label="快手" value="快手" />
|
||||||
|
<el-option label="抖音" value="抖音" />
|
||||||
|
<el-option label="视频号" value="视频号" />
|
||||||
|
<el-option label="小红书" value="小红书" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称" prop="name">
|
||||||
|
<el-input
|
||||||
|
v-model="accountForm.name"
|
||||||
|
placeholder="请输入账号名称"
|
||||||
|
:disabled="sseConnecting"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 二维码显示区域 -->
|
||||||
|
<div v-if="sseConnecting" class="qrcode-container">
|
||||||
|
<div v-if="qrCodeData && !loginStatus" class="qrcode-wrapper">
|
||||||
|
<p class="qrcode-tip">请使用对应平台APP扫描二维码登录</p>
|
||||||
|
<img :src="qrCodeData" alt="登录二维码" class="qrcode-image" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!qrCodeData && !loginStatus" class="loading-wrapper">
|
||||||
|
<el-icon class="is-loading"><Refresh /></el-icon>
|
||||||
|
<span>请求中...</span>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="loginStatus === '200'" class="success-wrapper">
|
||||||
|
<el-icon><CircleCheckFilled /></el-icon>
|
||||||
|
<span>添加成功</span>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="loginStatus === '500'" class="error-wrapper">
|
||||||
|
<el-icon><CircleCloseFilled /></el-icon>
|
||||||
|
<span>添加失败,请稍后再试</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false" :disabled="sseConnecting && !loginStatus">取消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="submitAccountForm"
|
||||||
|
:loading="sseConnecting"
|
||||||
|
:disabled="sseConnecting"
|
||||||
|
>
|
||||||
|
{{ sseConnecting ? '请求中' : '确认' }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
import { Refresh, CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { accountApi } from '@/api/account'
|
||||||
|
import { useAccountStore } from '@/stores/account'
|
||||||
|
import { useAppStore } from '@/stores/app'
|
||||||
|
|
||||||
|
// 获取账号状态管理
|
||||||
|
const accountStore = useAccountStore()
|
||||||
|
// 获取应用状态管理
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
// 当前激活的标签页
|
||||||
|
const activeTab = ref('all')
|
||||||
|
|
||||||
|
// 搜索关键词
|
||||||
|
const searchKeyword = ref('')
|
||||||
|
|
||||||
|
// 获取账号数据
|
||||||
|
const fetchAccounts = async () => {
|
||||||
|
if (appStore.isAccountRefreshing) return
|
||||||
|
|
||||||
|
appStore.setAccountRefreshing(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await accountApi.getValidAccounts()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
accountStore.setAccounts(res.data)
|
||||||
|
ElMessage.success('账号数据获取成功')
|
||||||
|
// 标记为已访问
|
||||||
|
if (appStore.isFirstTimeAccountManagement) {
|
||||||
|
appStore.setAccountManagementVisited()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('获取账号数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取账号数据失败:', error)
|
||||||
|
ElMessage.error('获取账号数据失败')
|
||||||
|
} finally {
|
||||||
|
appStore.setAccountRefreshing(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时获取账号数据
|
||||||
|
onMounted(() => {
|
||||||
|
// 只有第一次进入时才获取数据
|
||||||
|
if (appStore.isFirstTimeAccountManagement) {
|
||||||
|
fetchAccounts()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取平台标签类型
|
||||||
|
const getPlatformTagType = (platform) => {
|
||||||
|
const typeMap = {
|
||||||
|
'快手': 'success',
|
||||||
|
'抖音': 'danger',
|
||||||
|
'视频号': 'warning',
|
||||||
|
'小红书': 'info'
|
||||||
|
}
|
||||||
|
return typeMap[platform] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤后的账号列表
|
||||||
|
const filteredAccounts = computed(() => {
|
||||||
|
if (!searchKeyword.value) return accountStore.accounts
|
||||||
|
return accountStore.accounts.filter(account =>
|
||||||
|
account.name.includes(searchKeyword.value)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按平台过滤的账号列表
|
||||||
|
const filteredKuaishouAccounts = computed(() => {
|
||||||
|
return filteredAccounts.value.filter(account => account.platform === '快手')
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredDouyinAccounts = computed(() => {
|
||||||
|
return filteredAccounts.value.filter(account => account.platform === '抖音')
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredChannelsAccounts = computed(() => {
|
||||||
|
return filteredAccounts.value.filter(account => account.platform === '视频号')
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredXiaohongshuAccounts = computed(() => {
|
||||||
|
return filteredAccounts.value.filter(account => account.platform === '小红书')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 搜索处理
|
||||||
|
const handleSearch = () => {
|
||||||
|
// 搜索逻辑已通过计算属性实现
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对话框相关
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const dialogType = ref('add') // 'add' 或 'edit'
|
||||||
|
const accountFormRef = ref(null)
|
||||||
|
|
||||||
|
// 账号表单
|
||||||
|
const accountForm = reactive({
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
platform: '',
|
||||||
|
status: '正常'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
platform: [{ required: true, message: '请选择平台', trigger: 'change' }],
|
||||||
|
name: [{ required: true, message: '请输入账号名称', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSE连接状态
|
||||||
|
const sseConnecting = ref(false)
|
||||||
|
const qrCodeData = ref('')
|
||||||
|
const loginStatus = ref('')
|
||||||
|
|
||||||
|
// 添加账号
|
||||||
|
const handleAddAccount = () => {
|
||||||
|
dialogType.value = 'add'
|
||||||
|
Object.assign(accountForm, {
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
platform: '',
|
||||||
|
status: '正常'
|
||||||
|
})
|
||||||
|
// 重置SSE状态
|
||||||
|
sseConnecting.value = false
|
||||||
|
qrCodeData.value = ''
|
||||||
|
loginStatus.value = ''
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑账号
|
||||||
|
const handleEdit = (row) => {
|
||||||
|
dialogType.value = 'edit'
|
||||||
|
Object.assign(accountForm, { ...row })
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除账号
|
||||||
|
const handleDelete = (row) => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`确定要删除账号 ${row.name} 吗?`,
|
||||||
|
'警告',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
// 调用API删除账号
|
||||||
|
const response = await accountApi.deleteAccount(row.id)
|
||||||
|
|
||||||
|
if (response.code === 200) {
|
||||||
|
// 从状态管理中删除账号
|
||||||
|
accountStore.deleteAccount(row.id)
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.msg || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除账号失败:', error)
|
||||||
|
ElMessage.error('删除账号失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 取消删除
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSE事件源对象
|
||||||
|
let eventSource = null
|
||||||
|
|
||||||
|
// 关闭SSE连接
|
||||||
|
const closeSSEConnection = () => {
|
||||||
|
if (eventSource) {
|
||||||
|
eventSource.close()
|
||||||
|
eventSource = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建立SSE连接
|
||||||
|
const connectSSE = (platform, name) => {
|
||||||
|
// 关闭可能存在的连接
|
||||||
|
closeSSEConnection()
|
||||||
|
|
||||||
|
// 设置连接状态
|
||||||
|
sseConnecting.value = true
|
||||||
|
qrCodeData.value = ''
|
||||||
|
loginStatus.value = ''
|
||||||
|
|
||||||
|
// 获取平台类型编号
|
||||||
|
const platformTypeMap = {
|
||||||
|
'小红书': '1',
|
||||||
|
'视频号': '2',
|
||||||
|
'抖音': '3',
|
||||||
|
'快手': '4'
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = platformTypeMap[platform] || '1'
|
||||||
|
|
||||||
|
// 创建SSE连接
|
||||||
|
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5409'
|
||||||
|
const url = `${baseUrl}/login?type=${type}&id=${encodeURIComponent(name)}`
|
||||||
|
|
||||||
|
eventSource = new EventSource(url)
|
||||||
|
|
||||||
|
// 监听消息
|
||||||
|
eventSource.onmessage = (event) => {
|
||||||
|
const data = event.data
|
||||||
|
console.log('SSE消息:', data)
|
||||||
|
|
||||||
|
// 如果还没有二维码数据,且数据长度较长,认为是二维码
|
||||||
|
if (!qrCodeData.value && data.length > 100) {
|
||||||
|
try {
|
||||||
|
// 确保数据是有效的base64编码
|
||||||
|
// 如果数据已经包含了data:image前缀,直接使用
|
||||||
|
if (data.startsWith('data:image')) {
|
||||||
|
qrCodeData.value = data
|
||||||
|
} else {
|
||||||
|
// 否则添加前缀
|
||||||
|
qrCodeData.value = `data:image/png;base64,${data}`
|
||||||
|
}
|
||||||
|
console.log('设置二维码数据,长度:', data.length)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理二维码数据出错:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果收到状态码
|
||||||
|
else if (data === '200' || data === '500') {
|
||||||
|
loginStatus.value = data
|
||||||
|
|
||||||
|
// 如果登录成功,添加账号到状态管理
|
||||||
|
if (data === '200') {
|
||||||
|
setTimeout(() => {
|
||||||
|
const newId = accountStore.accounts.length > 0 ?
|
||||||
|
Math.max(...accountStore.accounts.map(a => a.id)) + 1 : 1
|
||||||
|
|
||||||
|
const newAccount = {
|
||||||
|
id: newId,
|
||||||
|
name: accountForm.name,
|
||||||
|
platform: accountForm.platform,
|
||||||
|
status: '正常',
|
||||||
|
avatar: '/vite.svg' // 默认头像
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到状态管理
|
||||||
|
accountStore.addAccount(newAccount)
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
closeSSEConnection()
|
||||||
|
|
||||||
|
// 1秒后关闭对话框
|
||||||
|
setTimeout(() => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
sseConnecting.value = false
|
||||||
|
ElMessage.success('账号添加成功')
|
||||||
|
|
||||||
|
// 显示更新账号信息提示并刷新账号列表
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '更新账号信息中...',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
|
||||||
|
// 触发刷新操作
|
||||||
|
isRefreshing.value = true
|
||||||
|
fetchAccounts()
|
||||||
|
}, 1000)
|
||||||
|
}, 1000)
|
||||||
|
} else {
|
||||||
|
// 登录失败,关闭连接
|
||||||
|
closeSSEConnection()
|
||||||
|
|
||||||
|
// 2秒后重置状态,允许重试
|
||||||
|
setTimeout(() => {
|
||||||
|
sseConnecting.value = false
|
||||||
|
qrCodeData.value = ''
|
||||||
|
loginStatus.value = ''
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听错误
|
||||||
|
eventSource.onerror = (error) => {
|
||||||
|
console.error('SSE连接错误:', error)
|
||||||
|
ElMessage.error('连接服务器失败,请稍后再试')
|
||||||
|
closeSSEConnection()
|
||||||
|
sseConnecting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交账号表单
|
||||||
|
const submitAccountForm = () => {
|
||||||
|
accountFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (dialogType.value === 'add') {
|
||||||
|
// 建立SSE连接
|
||||||
|
connectSSE(accountForm.platform, accountForm.name)
|
||||||
|
} else {
|
||||||
|
// 编辑账号逻辑保持不变
|
||||||
|
try {
|
||||||
|
// 更新状态管理中的账号
|
||||||
|
accountStore.updateAccount(accountForm.id, accountForm)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新账号失败:', error)
|
||||||
|
ElMessage.error('更新账号失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载前关闭SSE连接
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
closeSSEConnection()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/styles/variables.scss' as *;
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-management {
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-tabs {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: $box-shadow-light;
|
||||||
|
|
||||||
|
.account-tabs-nav {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-list-container {
|
||||||
|
.account-search {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.el-icon.is-loading {
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-list {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-data {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 二维码容器样式
|
||||||
|
.qrcode-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 250px;
|
||||||
|
|
||||||
|
.qrcode-wrapper {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.qrcode-tip {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-image {
|
||||||
|
max-width: 200px;
|
||||||
|
max-height: 200px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrapper, .success-wrapper, .error-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-wrapper .el-icon {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-wrapper .el-icon {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user