feat(bug-collect): 所有视图新增应用选择器,API 调用补充 appKey 参数
这个提交包含在:
父节点
4c6be2c489
当前提交
5e87a17765
@ -88,10 +88,11 @@ export interface BugCollectPageResult<T> {
|
||||
|
||||
export const bugCollectApi = {
|
||||
// Overview
|
||||
overview: () => client.get<{ data: BugCollectOverview }>('/bugcollect/v1/overview'),
|
||||
overview: (appKey: string) =>
|
||||
client.get<{ data: BugCollectOverview }>('/bugcollect/v1/overview', { params: { appKey } }),
|
||||
|
||||
// Issues
|
||||
issues(params: {
|
||||
issues(appKey: string, params: {
|
||||
type?: string
|
||||
platform?: string
|
||||
startDate?: string
|
||||
@ -99,7 +100,7 @@ export const bugCollectApi = {
|
||||
page?: number
|
||||
size?: number
|
||||
}) {
|
||||
return client.get<{ data: BugCollectPageResult<BugCollectIssue> }>('/bugcollect/v1/issues', { params })
|
||||
return client.get<{ data: BugCollectPageResult<BugCollectIssue> }>('/bugcollect/v1/issues', { params: { appKey, ...params } })
|
||||
},
|
||||
|
||||
issueDetail(id: string) {
|
||||
@ -107,16 +108,16 @@ export const bugCollectApi = {
|
||||
},
|
||||
|
||||
// Rankings
|
||||
frequencyRanking() {
|
||||
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/frequency')
|
||||
frequencyRanking(appKey: string) {
|
||||
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/frequency', { params: { appKey } })
|
||||
},
|
||||
|
||||
riskRanking() {
|
||||
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/risk')
|
||||
riskRanking(appKey: string) {
|
||||
return client.get<{ data: BugCollectIssueRanking[] }>('/bugcollect/v1/issues/rankings/risk', { params: { appKey } })
|
||||
},
|
||||
|
||||
// Events
|
||||
events(params: {
|
||||
events(appKey: string, params: {
|
||||
eventName?: string
|
||||
userId?: string
|
||||
startDate?: string
|
||||
@ -124,21 +125,21 @@ export const bugCollectApi = {
|
||||
page?: number
|
||||
size?: number
|
||||
}) {
|
||||
return client.get<{ data: BugCollectPageResult<BugCollectEventItem> }>('/bugcollect/v1/events', { params })
|
||||
return client.get<{ data: BugCollectPageResult<BugCollectEventItem> }>('/bugcollect/v1/events', { params: { appKey, ...params } })
|
||||
},
|
||||
|
||||
// Funnel
|
||||
funnel(steps: string[]) {
|
||||
funnel(appKey: string, steps: string[]) {
|
||||
return client.get<{ data: BugCollectFunnelStep[] }>('/bugcollect/v1/events/funnel', {
|
||||
params: { steps: steps.join(',') },
|
||||
params: { appKey, steps: steps.join(',') },
|
||||
})
|
||||
},
|
||||
|
||||
// Webhooks
|
||||
webhooks: {
|
||||
list: () => client.get<{ data: BugCollectWebhook[] }>('/bugcollect/v1/webhooks'),
|
||||
create: (data: Omit<BugCollectWebhook, 'id' | 'createdAt' | 'updatedAt'>) =>
|
||||
client.post<{ data: BugCollectWebhook }>('/bugcollect/v1/webhooks', data),
|
||||
list: (appKey: string) => client.get<{ data: BugCollectWebhook[] }>('/bugcollect/v1/webhooks', { params: { appKey } }),
|
||||
create: (appKey: string, data: Omit<BugCollectWebhook, 'id' | 'createdAt' | 'updatedAt'>) =>
|
||||
client.post<{ data: BugCollectWebhook }>('/bugcollect/v1/webhooks', data, { params: { appKey } }),
|
||||
update: (id: string, data: Partial<Omit<BugCollectWebhook, 'id' | 'createdAt' | 'updatedAt'>>) =>
|
||||
client.put<{ data: BugCollectWebhook }>(`/bugcollect/v1/webhooks/${id}`, data),
|
||||
delete: (id: string) => client.delete(`/bugcollect/v1/webhooks/${id}`),
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { appApi, type App } from '@/api/app'
|
||||
|
||||
const STORAGE_KEY = 'bugcollect_selected_app_key'
|
||||
|
||||
export function useBugCollectApp() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const apps = ref<App[]>([])
|
||||
const loadingApps = ref(false)
|
||||
const selectedAppKey = ref(localStorage.getItem(STORAGE_KEY) ?? '')
|
||||
|
||||
const appKey = computed(() => {
|
||||
const q = route.query.appKey
|
||||
if (typeof q === 'string' && q.trim()) return q.trim()
|
||||
return selectedAppKey.value
|
||||
})
|
||||
|
||||
async function loadApps() {
|
||||
loadingApps.value = true
|
||||
try {
|
||||
const res = await appApi.list()
|
||||
apps.value = res.data.data ?? []
|
||||
} catch {
|
||||
apps.value = []
|
||||
} finally {
|
||||
loadingApps.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function setApp(key: string) {
|
||||
selectedAppKey.value = key
|
||||
localStorage.setItem(STORAGE_KEY, key)
|
||||
router.replace({ query: { ...route.query, appKey: key } })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const q = route.query.appKey
|
||||
if (typeof q === 'string' && q.trim()) {
|
||||
selectedAppKey.value = q.trim()
|
||||
localStorage.setItem(STORAGE_KEY, q.trim())
|
||||
}
|
||||
loadApps()
|
||||
})
|
||||
|
||||
return { apps, loadingApps, appKey, setApp }
|
||||
}
|
||||
@ -196,10 +196,10 @@
|
||||
</div>
|
||||
<div class="service-actions">
|
||||
<template v-if="isServiceEnabled('BUG_COLLECT')">
|
||||
<el-button size="small" type="primary" plain @click="$router.push('/bugcollect/overview')">
|
||||
<el-button size="small" type="primary" plain @click="$router.push({ path: '/bugcollect/overview', query: { appKey: app.appKey } })">
|
||||
崩溃概览 →
|
||||
</el-button>
|
||||
<el-button size="small" @click="$router.push('/bugcollect/issues')">
|
||||
<el-button size="small" @click="$router.push({ path: '/bugcollect/issues', query: { appKey: app.appKey } })">
|
||||
错误列表 →
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">事件流水</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="toolbar responsive-toolbar">
|
||||
<el-input
|
||||
@ -67,12 +83,16 @@
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { bugCollectApi, type BugCollectEventItem } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const events = ref<BugCollectEventItem[]>([])
|
||||
const loading = ref(false)
|
||||
@ -94,7 +114,7 @@ function formatTime(ts: string) {
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.events({
|
||||
const res = await bugCollectApi.events(appKey.value, {
|
||||
eventName: filters.value.eventName || undefined,
|
||||
userId: filters.value.userId || undefined,
|
||||
startDate: filters.value.dateRange?.[0] || undefined,
|
||||
@ -126,6 +146,8 @@ onMounted(loadData)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.responsive-toolbar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">漏斗分析</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card style="margin-bottom: 16px">
|
||||
<template #header>配置漏斗步骤</template>
|
||||
<div class="funnel-builder">
|
||||
@ -37,12 +53,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { bugCollectApi, type BugCollectFunnelStep } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const steps = ref(['', ''])
|
||||
const funnelData = ref<BugCollectFunnelStep[]>([])
|
||||
@ -66,7 +86,7 @@ async function analyze() {
|
||||
if (validSteps.length < 2) return
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.funnel(validSteps)
|
||||
const res = await bugCollectApi.funnel(appKey.value, validSteps)
|
||||
funnelData.value = res.data.data
|
||||
} catch {
|
||||
} finally {
|
||||
@ -76,6 +96,8 @@ async function analyze() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.funnel-builder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">错误列表</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="toolbar responsive-toolbar">
|
||||
<el-select v-model="filters.type" placeholder="错误类型" style="width: 150px" clearable @change="loadData">
|
||||
@ -66,12 +82,16 @@
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { bugCollectApi, type BugCollectIssue } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const issues = ref<BugCollectIssue[]>([])
|
||||
const loading = ref(false)
|
||||
@ -103,7 +123,7 @@ function formatTime(ts: string) {
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.issues({
|
||||
const res = await bugCollectApi.issues(appKey.value, {
|
||||
type: filters.value.type || undefined,
|
||||
platform: filters.value.platform || undefined,
|
||||
startDate: filters.value.dateRange?.[0] || undefined,
|
||||
@ -135,6 +155,8 @@ onMounted(loadData)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.responsive-toolbar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">Bug 概览</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
@ -77,6 +93,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -84,6 +101,9 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { bugCollectApi, type BugCollectOverview } from '@/api/bugcollect'
|
||||
import { Warning, Plus, User } from '@element-plus/icons-vue'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const overview = ref<BugCollectOverview>({
|
||||
totalIssues: 0,
|
||||
@ -115,13 +135,15 @@ function issueTypeTag(type: string) {
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await bugCollectApi.overview()
|
||||
const res = await bugCollectApi.overview(appKey.value)
|
||||
overview.value = res.data.data
|
||||
} catch {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.trend-chart {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">高频错误排行</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table :data="rankings" v-loading="loading" border stripe>
|
||||
<el-table-column type="index" label="#" width="60" />
|
||||
@ -27,12 +43,16 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const rankings = ref<BugCollectIssueRanking[]>([])
|
||||
const loading = ref(false)
|
||||
@ -55,7 +75,7 @@ function formatTime(ts: string) {
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.frequencyRanking()
|
||||
const res = await bugCollectApi.frequencyRanking(appKey.value)
|
||||
rankings.value = res.data.data
|
||||
} catch {
|
||||
} finally {
|
||||
@ -65,6 +85,8 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.time-text {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
<div>
|
||||
<h2 style="margin-bottom: 24px">高危排行</h2>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table :data="rankings" v-loading="loading" border stripe>
|
||||
<el-table-column type="index" label="#" width="60" />
|
||||
@ -32,12 +48,16 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const rankings = ref<BugCollectIssueRanking[]>([])
|
||||
const loading = ref(false)
|
||||
@ -67,7 +87,7 @@ function formatTime(ts: string) {
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.riskRanking()
|
||||
const res = await bugCollectApi.riskRanking(appKey.value)
|
||||
rankings.value = res.data.data
|
||||
} catch {
|
||||
} finally {
|
||||
@ -77,6 +97,8 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.time-text {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
|
||||
@ -5,6 +5,22 @@
|
||||
<el-button type="primary" @click="openDialog()">新增 Webhook</el-button>
|
||||
</div>
|
||||
|
||||
<!-- App selector bar -->
|
||||
<div class="app-selector-bar">
|
||||
<span class="selector-label">选择应用</span>
|
||||
<el-select
|
||||
:model-value="appKey"
|
||||
placeholder="请选择应用"
|
||||
style="width:220px"
|
||||
:loading="loadingApps"
|
||||
@change="setApp"
|
||||
>
|
||||
<el-option v-for="a in apps" :key="a.appKey" :label="a.name" :value="a.appKey" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-empty v-if="!appKey" description="请选择一个应用" style="margin-top:80px" />
|
||||
<template v-else>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table :data="webhooks" v-loading="loading" border stripe>
|
||||
<el-table-column prop="url" label="回调地址" min-width="300" show-overflow-tooltip />
|
||||
@ -33,6 +49,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<!-- Dialog -->
|
||||
<el-dialog
|
||||
@ -73,6 +90,9 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { bugCollectApi, type BugCollectWebhook } from '@/api/bugcollect'
|
||||
import { useBugCollectApp } from '@/composables/useBugCollectApp'
|
||||
|
||||
const { apps, loadingApps, appKey, setApp } = useBugCollectApp()
|
||||
|
||||
const webhooks = ref<BugCollectWebhook[]>([])
|
||||
const loading = ref(false)
|
||||
@ -90,7 +110,7 @@ const form = ref({
|
||||
async function loadWebhooks() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await bugCollectApi.webhooks.list()
|
||||
const res = await bugCollectApi.webhooks.list(appKey.value)
|
||||
webhooks.value = res.data.data
|
||||
} catch {
|
||||
} finally {
|
||||
@ -122,7 +142,7 @@ async function handleSave() {
|
||||
await bugCollectApi.webhooks.update(editingId.value, form.value)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await bugCollectApi.webhooks.create(form.value)
|
||||
await bugCollectApi.webhooks.create(appKey.value, form.value)
|
||||
ElMessage.success('创建成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
@ -145,6 +165,8 @@ onMounted(loadWebhooks)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-selector-bar { display:flex; align-items:center; gap:12px; margin-bottom:20px; }
|
||||
.selector-label { font-size:14px; color:#606266; }
|
||||
.toolbar-space-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户