feat(bugcollect): 将崩溃收集移为服务管理子菜单,并增加服务开通校验
- MainLayout: 移除独立的 bug-collect 顶级子菜单,改为嵌套在服务管理下的 services-bugcollect 子菜单(桌面 + 移动抽屉均已同步) - openedMenus: /bugcollect/* 路径下同时展开 services 和 services-bugcollect - FeatureService.serviceType: 补充 BUG_COLLECT 枚举值 - appApi.requestActivation: 支持 BUG_COLLECT 服务类型 - useBugCollectApp: 增加 gateStatus / serviceEnabled / checkingService;appKey 变化时自动调用 getServices 检查 BUG_COLLECT 是否已开通;提供 applyDialogVisible / submitActivation 申请流程 - 全部 7 个 BugCollect 视图: 替换原 !appKey 判断为 gateStatus 四态门控(no-app / loading / not-enabled / enabled),并附加申请开通对话框 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
2d650d2b7a
当前提交
fb5e9753a3
@ -27,7 +27,7 @@ export interface FeatureService {
|
|||||||
id: string
|
id: string
|
||||||
appKey: string
|
appKey: string
|
||||||
platform: 'ANDROID' | 'IOS' | 'HARMONY'
|
platform: 'ANDROID' | 'IOS' | 'HARMONY'
|
||||||
serviceType: 'IM' | 'PUSH' | 'UPDATE' | 'LICENSE'
|
serviceType: 'IM' | 'PUSH' | 'UPDATE' | 'LICENSE' | 'BUG_COLLECT'
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
config?: string | null
|
config?: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
@ -202,7 +202,7 @@ export const appApi = {
|
|||||||
regenerateConfigFile: (appKey: string) =>
|
regenerateConfigFile: (appKey: string) =>
|
||||||
client.post<{ data: null }>(`/apps/${appKey}/config-file/regenerate`),
|
client.post<{ data: null }>(`/apps/${appKey}/config-file/regenerate`),
|
||||||
|
|
||||||
requestActivation: (appKey: string, serviceType: 'IM' | 'PUSH' | 'UPDATE' | 'LICENSE', reason: string) =>
|
requestActivation: (appKey: string, serviceType: 'IM' | 'PUSH' | 'UPDATE' | 'LICENSE' | 'BUG_COLLECT', reason: string) =>
|
||||||
client.post<{ data: null }>(`/apps/${appKey}/services/request-activation`, null, {
|
client.post<{ data: null }>(`/apps/${appKey}/services/request-activation`, null, {
|
||||||
params: { platform: 'ANDROID', serviceType, applyReason: reason },
|
params: { platform: 'ANDROID', serviceType, applyReason: reason },
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { appApi, type App } from '@/api/app'
|
import { appApi, type App } from '@/api/app'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const STORAGE_KEY = 'bugcollect_selected_app_key'
|
const STORAGE_KEY = 'bugcollect_selected_app_key'
|
||||||
|
|
||||||
@ -10,6 +11,11 @@ export function useBugCollectApp() {
|
|||||||
const apps = ref<App[]>([])
|
const apps = ref<App[]>([])
|
||||||
const loadingApps = ref(false)
|
const loadingApps = ref(false)
|
||||||
const selectedAppKey = ref(localStorage.getItem(STORAGE_KEY) ?? '')
|
const selectedAppKey = ref(localStorage.getItem(STORAGE_KEY) ?? '')
|
||||||
|
const serviceEnabled = ref<boolean | null>(null)
|
||||||
|
const checkingService = ref(false)
|
||||||
|
const applyDialogVisible = ref(false)
|
||||||
|
const applyReason = ref('')
|
||||||
|
const applyLoading = ref(false)
|
||||||
|
|
||||||
const appKey = computed(() => {
|
const appKey = computed(() => {
|
||||||
const q = route.query.appKey
|
const q = route.query.appKey
|
||||||
@ -17,6 +23,13 @@ export function useBugCollectApp() {
|
|||||||
return selectedAppKey.value
|
return selectedAppKey.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 'no-app' | 'loading' | 'not-enabled' | 'enabled'
|
||||||
|
const gateStatus = computed(() => {
|
||||||
|
if (!appKey.value) return 'no-app'
|
||||||
|
if (checkingService.value || serviceEnabled.value === null) return 'loading'
|
||||||
|
return serviceEnabled.value ? 'enabled' : 'not-enabled'
|
||||||
|
})
|
||||||
|
|
||||||
async function loadApps() {
|
async function loadApps() {
|
||||||
loadingApps.value = true
|
loadingApps.value = true
|
||||||
try {
|
try {
|
||||||
@ -29,12 +42,49 @@ export function useBugCollectApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkService(key: string) {
|
||||||
|
if (!key) {
|
||||||
|
serviceEnabled.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkingService.value = true
|
||||||
|
serviceEnabled.value = null
|
||||||
|
try {
|
||||||
|
const res = await appApi.getServices(key)
|
||||||
|
const found = (res.data.data ?? []).find(s => s.serviceType === 'BUG_COLLECT')
|
||||||
|
serviceEnabled.value = found?.enabled ?? false
|
||||||
|
} catch {
|
||||||
|
serviceEnabled.value = false
|
||||||
|
} finally {
|
||||||
|
checkingService.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setApp(key: string) {
|
function setApp(key: string) {
|
||||||
selectedAppKey.value = key
|
selectedAppKey.value = key
|
||||||
localStorage.setItem(STORAGE_KEY, key)
|
localStorage.setItem(STORAGE_KEY, key)
|
||||||
router.replace({ query: { ...route.query, appKey: key } })
|
router.replace({ query: { ...route.query, appKey: key } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function submitActivation() {
|
||||||
|
if (!appKey.value || !applyReason.value.trim()) return
|
||||||
|
applyLoading.value = true
|
||||||
|
try {
|
||||||
|
await appApi.requestActivation(appKey.value, 'BUG_COLLECT', applyReason.value.trim())
|
||||||
|
ElMessage.success('申请已提交,请等待审核')
|
||||||
|
applyDialogVisible.value = false
|
||||||
|
applyReason.value = ''
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('申请提交失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
applyLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(appKey, (key) => {
|
||||||
|
checkService(key)
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const q = route.query.appKey
|
const q = route.query.appKey
|
||||||
if (typeof q === 'string' && q.trim()) {
|
if (typeof q === 'string' && q.trim()) {
|
||||||
@ -42,7 +92,20 @@ export function useBugCollectApp() {
|
|||||||
localStorage.setItem(STORAGE_KEY, q.trim())
|
localStorage.setItem(STORAGE_KEY, q.trim())
|
||||||
}
|
}
|
||||||
loadApps()
|
loadApps()
|
||||||
|
if (appKey.value) checkService(appKey.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
return { apps, loadingApps, appKey, setApp }
|
return {
|
||||||
|
apps,
|
||||||
|
loadingApps,
|
||||||
|
appKey,
|
||||||
|
setApp,
|
||||||
|
gateStatus,
|
||||||
|
serviceEnabled,
|
||||||
|
checkingService,
|
||||||
|
applyDialogVisible,
|
||||||
|
applyReason,
|
||||||
|
applyLoading,
|
||||||
|
submitActivation,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@ -84,6 +90,13 @@
|
|||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -92,7 +105,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { bugCollectApi, type BugCollectEventItem } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectEventItem } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const events = ref<BugCollectEventItem[]>([])
|
const events = ref<BugCollectEventItem[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card style="margin-bottom: 16px">
|
<el-card style="margin-bottom: 16px">
|
||||||
@ -54,6 +60,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -62,7 +75,7 @@ import { ref } from 'vue'
|
|||||||
import { bugCollectApi, type BugCollectFunnelStep } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectFunnelStep } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const steps = ref(['', ''])
|
const steps = ref(['', ''])
|
||||||
const funnelData = ref<BugCollectFunnelStep[]>([])
|
const funnelData = ref<BugCollectFunnelStep[]>([])
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@ -83,6 +89,13 @@
|
|||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -91,7 +104,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { bugCollectApi, type BugCollectIssue } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectIssue } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const issues = ref<BugCollectIssue[]>([])
|
const issues = ref<BugCollectIssue[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<!-- Stats Cards -->
|
<!-- Stats Cards -->
|
||||||
@ -94,6 +100,13 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -103,7 +116,7 @@ import { bugCollectApi, type BugCollectOverview } from '@/api/bugcollect'
|
|||||||
import { Warning, Plus, User } from '@element-plus/icons-vue'
|
import { Warning, Plus, User } from '@element-plus/icons-vue'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const overview = ref<BugCollectOverview>({
|
const overview = ref<BugCollectOverview>({
|
||||||
totalIssues: 0,
|
totalIssues: 0,
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@ -44,6 +50,13 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -52,7 +65,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const rankings = ref<BugCollectIssueRanking[]>([])
|
const rankings = ref<BugCollectIssueRanking[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@ -15,7 +15,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@ -49,6 +55,13 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -57,7 +70,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const rankings = ref<BugCollectIssueRanking[]>([])
|
const rankings = ref<BugCollectIssueRanking[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@ -18,7 +18,13 @@
|
|||||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
<el-empty v-if="gateStatus === 'no-app'" description="请选择一个应用" style="margin-top:80px" />
|
||||||
|
<div v-else-if="gateStatus === 'loading'" v-loading="true" style="min-height:200px" />
|
||||||
|
<div v-else-if="gateStatus === 'not-enabled'" style="margin-top:60px;text-align:center">
|
||||||
|
<el-empty description="当前应用未开通崩溃收集服务">
|
||||||
|
<el-button type="primary" @click="applyDialogVisible = true">申请开通</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@ -83,6 +89,13 @@
|
|||||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog v-model="applyDialogVisible" title="申请开通崩溃收集" width="400px" :close-on-click-modal="false">
|
||||||
|
<el-input v-model="applyReason" type="textarea" placeholder="请说明申请原因(选填)" :rows="3" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="applyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="applyLoading" @click="submitActivation">提交申请</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -92,7 +105,7 @@ import { ElMessage } from 'element-plus'
|
|||||||
import { bugCollectApi, type BugCollectWebhook } from '@/api/bugcollect'
|
import { bugCollectApi, type BugCollectWebhook } from '@/api/bugcollect'
|
||||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||||
|
|
||||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
const { apps, loadingApps, appKey, setApp, gateStatus, applyDialogVisible, applyReason, applyLoading, submitActivation } = useBugCollectApp()
|
||||||
|
|
||||||
const webhooks = ref<BugCollectWebhook[]>([])
|
const webhooks = ref<BugCollectWebhook[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@ -21,17 +21,8 @@
|
|||||||
<el-menu-item index="/services/push"><el-icon><Bell /></el-icon><span>离线推送</span></el-menu-item>
|
<el-menu-item index="/services/push"><el-icon><Bell /></el-icon><span>离线推送</span></el-menu-item>
|
||||||
<el-menu-item index="/services/update"><el-icon><Upload /></el-icon><span>版本管理</span></el-menu-item>
|
<el-menu-item index="/services/update"><el-icon><Upload /></el-icon><span>版本管理</span></el-menu-item>
|
||||||
<el-menu-item index="/services/license"><el-icon><Key /></el-icon><span>授权管理</span></el-menu-item>
|
<el-menu-item index="/services/license"><el-icon><Key /></el-icon><span>授权管理</span></el-menu-item>
|
||||||
<el-menu-item index="/services/bugcollect"><el-icon><DataLine /></el-icon><span>崩溃收集</span></el-menu-item>
|
<el-sub-menu index="services-bugcollect">
|
||||||
</el-sub-menu>
|
<template #title><el-icon><DataLine /></el-icon><span>崩溃收集</span></template>
|
||||||
<el-menu-item index="/security"><el-icon><Lock /></el-icon><span>安全中心</span></el-menu-item>
|
|
||||||
<el-sub-menu index="ops">
|
|
||||||
<template #title><el-icon><Setting /></el-icon><span>运维管理</span></template>
|
|
||||||
<el-menu-item v-if="isPrivateDeploy" index="/system-logs"><el-icon><Monitor /></el-icon><span>服务日志</span></el-menu-item>
|
|
||||||
<el-menu-item v-if="isPrivateDeploy" index="/database"><el-icon><Coin /></el-icon><span>数据库管理</span></el-menu-item>
|
|
||||||
<el-menu-item index="/operation-logs"><el-icon><Document /></el-icon><span>操作日志</span></el-menu-item>
|
|
||||||
</el-sub-menu>
|
|
||||||
<el-sub-menu index="bug-collect">
|
|
||||||
<template #title><el-icon><DataLine /></el-icon><span>Bug收集</span></template>
|
|
||||||
<el-menu-item index="/bugcollect/overview"><el-icon><Odometer /></el-icon><span>概览</span></el-menu-item>
|
<el-menu-item index="/bugcollect/overview"><el-icon><Odometer /></el-icon><span>概览</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/issues"><el-icon><Warning /></el-icon><span>错误列表</span></el-menu-item>
|
<el-menu-item index="/bugcollect/issues"><el-icon><Warning /></el-icon><span>错误列表</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/events"><el-icon><List /></el-icon><span>事件流水</span></el-menu-item>
|
<el-menu-item index="/bugcollect/events"><el-icon><List /></el-icon><span>事件流水</span></el-menu-item>
|
||||||
@ -40,6 +31,14 @@
|
|||||||
<el-menu-item index="/bugcollect/rank/risk"><el-icon><Warning /></el-icon><span>高危排行</span></el-menu-item>
|
<el-menu-item index="/bugcollect/rank/risk"><el-icon><Warning /></el-icon><span>高危排行</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/webhooks"><el-icon><Link /></el-icon><span>Webhook</span></el-menu-item>
|
<el-menu-item index="/bugcollect/webhooks"><el-icon><Link /></el-icon><span>Webhook</span></el-menu-item>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
|
</el-sub-menu>
|
||||||
|
<el-menu-item index="/security"><el-icon><Lock /></el-icon><span>安全中心</span></el-menu-item>
|
||||||
|
<el-sub-menu index="ops">
|
||||||
|
<template #title><el-icon><Setting /></el-icon><span>运维管理</span></template>
|
||||||
|
<el-menu-item v-if="isPrivateDeploy" index="/system-logs"><el-icon><Monitor /></el-icon><span>服务日志</span></el-menu-item>
|
||||||
|
<el-menu-item v-if="isPrivateDeploy" index="/database"><el-icon><Coin /></el-icon><span>数据库管理</span></el-menu-item>
|
||||||
|
<el-menu-item index="/operation-logs"><el-icon><Document /></el-icon><span>操作日志</span></el-menu-item>
|
||||||
|
</el-sub-menu>
|
||||||
<el-menu-item v-if="auth.user?.type === 'MAIN'" index="/accounts"><el-icon><User /></el-icon><span>子账号管理</span></el-menu-item>
|
<el-menu-item v-if="auth.user?.type === 'MAIN'" index="/accounts"><el-icon><User /></el-icon><span>子账号管理</span></el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
@ -72,17 +71,8 @@
|
|||||||
<el-menu-item index="/services/push"><el-icon><Bell /></el-icon><span>离线推送</span></el-menu-item>
|
<el-menu-item index="/services/push"><el-icon><Bell /></el-icon><span>离线推送</span></el-menu-item>
|
||||||
<el-menu-item index="/services/update"><el-icon><Upload /></el-icon><span>版本管理</span></el-menu-item>
|
<el-menu-item index="/services/update"><el-icon><Upload /></el-icon><span>版本管理</span></el-menu-item>
|
||||||
<el-menu-item index="/services/license"><el-icon><Key /></el-icon><span>授权管理</span></el-menu-item>
|
<el-menu-item index="/services/license"><el-icon><Key /></el-icon><span>授权管理</span></el-menu-item>
|
||||||
<el-menu-item index="/services/bugcollect"><el-icon><DataLine /></el-icon><span>崩溃收集</span></el-menu-item>
|
<el-sub-menu index="services-bugcollect">
|
||||||
</el-sub-menu>
|
<template #title><el-icon><DataLine /></el-icon><span>崩溃收集</span></template>
|
||||||
<el-menu-item index="/security"><el-icon><Lock /></el-icon><span>安全中心</span></el-menu-item>
|
|
||||||
<el-sub-menu index="ops">
|
|
||||||
<template #title><el-icon><Setting /></el-icon><span>运维管理</span></template>
|
|
||||||
<el-menu-item v-if="isPrivateDeploy" index="/system-logs"><el-icon><Monitor /></el-icon><span>服务日志</span></el-menu-item>
|
|
||||||
<el-menu-item v-if="isPrivateDeploy" index="/database"><el-icon><Coin /></el-icon><span>数据库管理</span></el-menu-item>
|
|
||||||
<el-menu-item index="/operation-logs"><el-icon><Document /></el-icon><span>操作日志</span></el-menu-item>
|
|
||||||
</el-sub-menu>
|
|
||||||
<el-sub-menu index="bug-collect">
|
|
||||||
<template #title><el-icon><DataLine /></el-icon><span>Bug收集</span></template>
|
|
||||||
<el-menu-item index="/bugcollect/overview"><el-icon><Odometer /></el-icon><span>概览</span></el-menu-item>
|
<el-menu-item index="/bugcollect/overview"><el-icon><Odometer /></el-icon><span>概览</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/issues"><el-icon><Warning /></el-icon><span>错误列表</span></el-menu-item>
|
<el-menu-item index="/bugcollect/issues"><el-icon><Warning /></el-icon><span>错误列表</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/events"><el-icon><List /></el-icon><span>事件流水</span></el-menu-item>
|
<el-menu-item index="/bugcollect/events"><el-icon><List /></el-icon><span>事件流水</span></el-menu-item>
|
||||||
@ -91,6 +81,14 @@
|
|||||||
<el-menu-item index="/bugcollect/rank/risk"><el-icon><Warning /></el-icon><span>高危排行</span></el-menu-item>
|
<el-menu-item index="/bugcollect/rank/risk"><el-icon><Warning /></el-icon><span>高危排行</span></el-menu-item>
|
||||||
<el-menu-item index="/bugcollect/webhooks"><el-icon><Link /></el-icon><span>Webhook</span></el-menu-item>
|
<el-menu-item index="/bugcollect/webhooks"><el-icon><Link /></el-icon><span>Webhook</span></el-menu-item>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
|
</el-sub-menu>
|
||||||
|
<el-menu-item index="/security"><el-icon><Lock /></el-icon><span>安全中心</span></el-menu-item>
|
||||||
|
<el-sub-menu index="ops">
|
||||||
|
<template #title><el-icon><Setting /></el-icon><span>运维管理</span></template>
|
||||||
|
<el-menu-item v-if="isPrivateDeploy" index="/system-logs"><el-icon><Monitor /></el-icon><span>服务日志</span></el-menu-item>
|
||||||
|
<el-menu-item v-if="isPrivateDeploy" index="/database"><el-icon><Coin /></el-icon><span>数据库管理</span></el-menu-item>
|
||||||
|
<el-menu-item index="/operation-logs"><el-icon><Document /></el-icon><span>操作日志</span></el-menu-item>
|
||||||
|
</el-sub-menu>
|
||||||
<el-menu-item v-if="auth.user?.type === 'MAIN'" index="/accounts"><el-icon><User /></el-icon><span>子账号管理</span></el-menu-item>
|
<el-menu-item v-if="auth.user?.type === 'MAIN'" index="/accounts"><el-icon><User /></el-icon><span>子账号管理</span></el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
@ -152,7 +150,7 @@ const openedMenus = computed(() => {
|
|||||||
const menus: string[] = []
|
const menus: string[] = []
|
||||||
if (route.path.startsWith('/services/')) menus.push('services')
|
if (route.path.startsWith('/services/')) menus.push('services')
|
||||||
if (['/system-logs', '/database', '/operation-logs'].includes(route.path)) menus.push('ops')
|
if (['/system-logs', '/database', '/operation-logs'].includes(route.path)) menus.push('ops')
|
||||||
if (route.path.startsWith('/bugcollect/')) menus.push('bug-collect')
|
if (route.path.startsWith('/bugcollect/')) menus.push('services', 'services-bugcollect')
|
||||||
return menus
|
return menus
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户