mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 00:36:41 +08:00
fix(workflow): recover shared list requests (#709)
This commit is contained in:
@@ -33,12 +33,18 @@ const page = ref(1)
|
|||||||
const keyword = ref(props.keyword)
|
const keyword = ref(props.keyword)
|
||||||
const currentKey = ref(0)
|
const currentKey = ref(0)
|
||||||
|
|
||||||
|
// 关键字切换后,旧请求不得再提交分页数据或加载状态。
|
||||||
|
let requestGeneration = 0
|
||||||
|
|
||||||
// 是否加载中
|
// 是否加载中
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 是否加载完成
|
// 是否加载完成
|
||||||
const isRefreshed = ref(false)
|
const isRefreshed = ref(false)
|
||||||
|
|
||||||
|
// 当前列表请求是否失败;合法空数组仍使用空数据状态。
|
||||||
|
const loadError = ref(false)
|
||||||
|
|
||||||
// 数据列表
|
// 数据列表
|
||||||
const dataList = ref<WorkflowShare[]>([])
|
const dataList = ref<WorkflowShare[]>([])
|
||||||
const currData = ref<WorkflowShare[]>([])
|
const currData = ref<WorkflowShare[]>([])
|
||||||
@@ -58,10 +64,13 @@ async function loadEventTypes() {
|
|||||||
watch(
|
watch(
|
||||||
() => props.keyword,
|
() => props.keyword,
|
||||||
newKeyword => {
|
newKeyword => {
|
||||||
|
requestGeneration++
|
||||||
keyword.value = newKeyword || ''
|
keyword.value = newKeyword || ''
|
||||||
page.value = 1
|
page.value = 1
|
||||||
dataList.value = []
|
dataList.value = []
|
||||||
|
loading.value = false
|
||||||
isRefreshed.value = false
|
isRefreshed.value = false
|
||||||
|
loadError.value = false
|
||||||
currentKey.value++
|
currentKey.value++
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -78,6 +87,7 @@ function getParams() {
|
|||||||
|
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
async function fetchData({ done }: { done: any }) {
|
async function fetchData({ done }: { done: any }) {
|
||||||
|
const generation = requestGeneration
|
||||||
try {
|
try {
|
||||||
// 如果正在加载中,直接返回
|
// 如果正在加载中,直接返回
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
@@ -92,11 +102,15 @@ async function fetchData({ done }: { done: any }) {
|
|||||||
// 设置加载中
|
// 设置加载中
|
||||||
loading.value = true
|
loading.value = true
|
||||||
// 请求API
|
// 请求API
|
||||||
currData.value = await api.get(apipath, {
|
const pageData: WorkflowShare[] = await api.get(apipath, {
|
||||||
params: getParams(),
|
params: getParams(),
|
||||||
})
|
})
|
||||||
// 取消加载中
|
if (generation !== requestGeneration) {
|
||||||
loading.value = false
|
done('ok')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadError.value = false
|
||||||
|
currData.value = pageData
|
||||||
// 标计为已请求完成
|
// 标计为已请求完成
|
||||||
isRefreshed.value = true
|
isRefreshed.value = true
|
||||||
if (currData.value.length === 0) {
|
if (currData.value.length === 0) {
|
||||||
@@ -116,10 +130,15 @@ async function fetchData({ done }: { done: any }) {
|
|||||||
// 设置加载中
|
// 设置加载中
|
||||||
loading.value = true
|
loading.value = true
|
||||||
// 请求API
|
// 请求API
|
||||||
currData.value = await api.get(apipath, {
|
const pageData: WorkflowShare[] = await api.get(apipath, {
|
||||||
params: getParams(),
|
params: getParams(),
|
||||||
})
|
})
|
||||||
loading.value = false
|
if (generation !== requestGeneration) {
|
||||||
|
done('ok')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadError.value = false
|
||||||
|
currData.value = pageData
|
||||||
// 标计为已请求完成
|
// 标计为已请求完成
|
||||||
isRefreshed.value = true
|
isRefreshed.value = true
|
||||||
if (currData.value.length === 0) {
|
if (currData.value.length === 0) {
|
||||||
@@ -135,9 +154,19 @@ async function fetchData({ done }: { done: any }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (generation !== requestGeneration) {
|
||||||
|
done('ok')
|
||||||
|
return
|
||||||
|
}
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
isRefreshed.value = true
|
||||||
|
loadError.value = true
|
||||||
// 返回加载失败
|
// 返回加载失败
|
||||||
done('error')
|
done('error')
|
||||||
|
} finally {
|
||||||
|
if (generation === requestGeneration) {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,9 +183,24 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<VPageContentTitle v-if="keyword" :title="`${t('common.search')}:${keyword}`" />
|
<VPageContentTitle v-if="keyword" :title="`${t('common.search')}:${keyword}`" />
|
||||||
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
||||||
<VInfiniteScroll mode="intersect" side="end" :items="dataList" class="overflow-visible px-2" @load="fetchData" :key="currentKey">
|
<VInfiniteScroll
|
||||||
|
mode="intersect"
|
||||||
|
side="end"
|
||||||
|
:items="dataList"
|
||||||
|
class="overflow-visible px-2"
|
||||||
|
@load="fetchData"
|
||||||
|
:key="currentKey"
|
||||||
|
>
|
||||||
<template #loading />
|
<template #loading />
|
||||||
<template #empty />
|
<template #empty />
|
||||||
|
<template #error="{ props: retryProps }">
|
||||||
|
<div class="d-flex flex-column align-center ga-2 py-4" role="alert">
|
||||||
|
<span class="text-medium-emphasis">{{ t('common.apiRequestFailed') }}</span>
|
||||||
|
<VBtn v-bind="retryProps" prepend-icon="mdi-refresh" size="small" variant="tonal">
|
||||||
|
{{ t('common.retry') }}
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<ProgressiveCardGrid
|
<ProgressiveCardGrid
|
||||||
v-if="dataList.length > 0"
|
v-if="dataList.length > 0"
|
||||||
:items="dataList"
|
:items="dataList"
|
||||||
@@ -175,7 +219,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</ProgressiveCardGrid>
|
</ProgressiveCardGrid>
|
||||||
<NoDataFound
|
<NoDataFound
|
||||||
v-if="dataList.length === 0 && isRefreshed"
|
v-if="dataList.length === 0 && isRefreshed && !loadError"
|
||||||
error-code="404"
|
error-code="404"
|
||||||
:error-title="t('common.noData')"
|
:error-title="t('common.noData')"
|
||||||
:error-description="keyword ? t('common.noContent') : t('workflow.noShareData')"
|
:error-description="keyword ? t('common.noContent') : t('workflow.noShareData')"
|
||||||
|
|||||||
@@ -0,0 +1,304 @@
|
|||||||
|
import type { WorkflowShare } from '@/api/types'
|
||||||
|
import WorkflowShareView from '@/views/workflow/WorkflowShareView.vue'
|
||||||
|
import { fireEvent, screen, waitFor } from '@testing-library/vue'
|
||||||
|
import { renderWithProviders } from '@tests/support/render'
|
||||||
|
import { defineComponent, h, onMounted, ref, type PropType } from 'vue'
|
||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
apiGet: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/api', () => ({
|
||||||
|
default: {
|
||||||
|
get: (...args: unknown[]) => mocks.apiGet(...args),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const LoadingBannerStub = defineComponent({
|
||||||
|
name: 'LoadingBanner',
|
||||||
|
template: '<div role="status">正在加载共享工作流</div>',
|
||||||
|
})
|
||||||
|
|
||||||
|
const NoDataFoundStub = defineComponent({
|
||||||
|
name: 'NoDataFound',
|
||||||
|
props: {
|
||||||
|
errorTitle: String,
|
||||||
|
},
|
||||||
|
template: '<section role="region" aria-label="共享工作流状态">{{ errorTitle }}</section>',
|
||||||
|
})
|
||||||
|
|
||||||
|
const InfiniteScrollStub = defineComponent({
|
||||||
|
name: 'VInfiniteScroll',
|
||||||
|
emits: ['load'],
|
||||||
|
setup(_props, { emit, slots }) {
|
||||||
|
const status = ref<string>()
|
||||||
|
const load = () => emit('load', { done: (value: string) => (status.value = value) })
|
||||||
|
onMounted(load)
|
||||||
|
return () =>
|
||||||
|
h('div', { 'data-testid': 'share-scroll', 'data-status': status.value }, [
|
||||||
|
slots.default?.(),
|
||||||
|
status.value === 'error' ? slots.error?.({ props: { onClick: load }, side: 'end' }) : undefined,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const ProgressiveCardGridStub = defineComponent({
|
||||||
|
name: 'ProgressiveCardGrid',
|
||||||
|
props: {
|
||||||
|
getItemKey: { type: Function as PropType<(item: WorkflowShare) => string | undefined>, required: true },
|
||||||
|
items: { type: Array as PropType<WorkflowShare[]>, required: true },
|
||||||
|
},
|
||||||
|
setup(props, { slots }) {
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
'section',
|
||||||
|
{ 'data-testid': 'share-grid' },
|
||||||
|
props.items.flatMap(item => {
|
||||||
|
props.getItemKey(item)
|
||||||
|
return slots.default?.({ item }) ?? []
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const WorkflowShareCardStub = defineComponent({
|
||||||
|
name: 'WorkflowShareCard',
|
||||||
|
props: {
|
||||||
|
workflow: { type: Object as PropType<WorkflowShare>, required: true },
|
||||||
|
},
|
||||||
|
emits: ['delete', 'update'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
return () =>
|
||||||
|
h('article', { 'data-testid': `share-card-${props.workflow.id}` }, [
|
||||||
|
h('span', props.workflow.share_title),
|
||||||
|
h(
|
||||||
|
'button',
|
||||||
|
{
|
||||||
|
'aria-label': `删除-${props.workflow.id}`,
|
||||||
|
onClick: () => emit('delete'),
|
||||||
|
type: 'button',
|
||||||
|
},
|
||||||
|
'删除',
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'button',
|
||||||
|
{
|
||||||
|
'aria-label': `刷新-${props.workflow.id}`,
|
||||||
|
onClick: () => emit('update'),
|
||||||
|
type: 'button',
|
||||||
|
},
|
||||||
|
'刷新',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function createWorkflowShare(overrides: Partial<WorkflowShare> = {}): WorkflowShare {
|
||||||
|
return {
|
||||||
|
id: 'share-1',
|
||||||
|
share_title: '共享工作流',
|
||||||
|
share_user: '测试用户',
|
||||||
|
...overrides,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareRequests() {
|
||||||
|
return mocks.apiGet.mock.calls.filter(([endpoint]) => endpoint === 'workflow/shares')
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDeferred<T>() {
|
||||||
|
let resolve!: (value: T) => void
|
||||||
|
let reject!: (reason: Error) => void
|
||||||
|
const promise = new Promise<T>((done, fail) => {
|
||||||
|
resolve = done
|
||||||
|
reject = fail
|
||||||
|
})
|
||||||
|
return { promise, reject, resolve }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderList(keyword = '') {
|
||||||
|
return renderWithProviders(WorkflowShareView, {
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
LoadingBanner: LoadingBannerStub,
|
||||||
|
NoDataFound: NoDataFoundStub,
|
||||||
|
ProgressiveCardGrid: ProgressiveCardGridStub,
|
||||||
|
VInfiniteScroll: InfiniteScrollStub,
|
||||||
|
VPageContentTitle: true,
|
||||||
|
WorkflowShareCard: WorkflowShareCardStub,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: { keyword },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('WorkflowShareView', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.apiGet.mockReset()
|
||||||
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
Object.defineProperty(document.body, 'scrollHeight', { configurable: true, value: 1200 })
|
||||||
|
Object.defineProperty(window, 'innerHeight', { configurable: true, value: 600 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('loads the current page with the exact keyword and renders shared workflows', async () => {
|
||||||
|
const share = createWorkflowShare()
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint === 'workflow/shares') return [share]
|
||||||
|
throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await renderList('自动整理')
|
||||||
|
|
||||||
|
expect(await screen.findByText(share.share_title!)).toBeInTheDocument()
|
||||||
|
expect(shareRequests()).toEqual([
|
||||||
|
[
|
||||||
|
'workflow/shares',
|
||||||
|
{
|
||||||
|
params: { count: 30, name: '自动整理', page: 1 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the legitimate empty state after a successful empty response', async () => {
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint === 'workflow/shares') return []
|
||||||
|
throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await renderList()
|
||||||
|
|
||||||
|
expect(await screen.findByRole('region', { name: '共享工作流状态' })).toHaveTextContent('暂无数据')
|
||||||
|
expect(screen.getByTestId('share-scroll')).toHaveAttribute('data-status', 'empty')
|
||||||
|
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
|
||||||
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('prefills consecutive pages until the viewport is full or the backend is exhausted', async () => {
|
||||||
|
Object.defineProperty(document.body, 'scrollHeight', { configurable: true, value: 600 })
|
||||||
|
const firstPage = createWorkflowShare({ share_title: '首个预加载结果' })
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string, options?: { params?: { page?: number } }) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint !== 'workflow/shares') throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
return options?.params?.page === 1 ? [firstPage] : []
|
||||||
|
})
|
||||||
|
|
||||||
|
await renderList()
|
||||||
|
|
||||||
|
expect(await screen.findByText(firstPage.share_title!)).toBeInTheDocument()
|
||||||
|
await waitFor(() => expect(shareRequests()).toHaveLength(2))
|
||||||
|
expect(shareRequests().map(([, options]) => options)).toEqual([
|
||||||
|
{ params: { count: 30, name: '', page: 1 } },
|
||||||
|
{ params: { count: 30, name: '', page: 2 } },
|
||||||
|
])
|
||||||
|
expect(screen.getByTestId('share-scroll')).toHaveAttribute('data-status', 'empty')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('leaves the loading state and retries the same page after an initial failure', async () => {
|
||||||
|
const recovered = createWorkflowShare({ share_title: '恢复后的共享工作流' })
|
||||||
|
let attempt = 0
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint === 'workflow/shares') {
|
||||||
|
attempt += 1
|
||||||
|
if (attempt === 1) return Promise.reject(new Error('network unavailable'))
|
||||||
|
return [recovered]
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await renderList()
|
||||||
|
|
||||||
|
const failure = await screen.findByRole('alert')
|
||||||
|
expect(failure).toHaveTextContent('请求失败')
|
||||||
|
expect(screen.queryByRole('region', { name: '共享工作流状态' })).not.toBeInTheDocument()
|
||||||
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||||
|
const retry = screen.getByRole('button', { name: '重试' })
|
||||||
|
await fireEvent.click(retry)
|
||||||
|
|
||||||
|
expect(await screen.findByText(recovered.share_title!)).toBeInTheDocument()
|
||||||
|
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
|
||||||
|
expect(shareRequests()).toHaveLength(2)
|
||||||
|
expect(shareRequests()[1][1]).toEqual({ params: { count: 30, name: '', page: 1 } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps the newest keyword result when an older request finishes later', async () => {
|
||||||
|
const oldRequest = createDeferred<WorkflowShare[]>()
|
||||||
|
const latest = createWorkflowShare({ id: 'share-new', share_title: '新关键字结果' })
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string, options?: { params?: { name?: string } }) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint !== 'workflow/shares') throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
if (options?.params?.name === '旧关键字') return oldRequest.promise
|
||||||
|
if (options?.params?.name === '新关键字') return [latest]
|
||||||
|
throw new Error(`Unexpected keyword ${options?.params?.name}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { rerender } = await renderList('旧关键字')
|
||||||
|
await waitFor(() => expect(shareRequests()).toHaveLength(1))
|
||||||
|
await rerender({ keyword: '新关键字' })
|
||||||
|
|
||||||
|
expect(await screen.findByText(latest.share_title!)).toBeInTheDocument()
|
||||||
|
oldRequest.resolve([createWorkflowShare({ share_title: '迟到旧结果' })])
|
||||||
|
await oldRequest.promise
|
||||||
|
|
||||||
|
expect(screen.getByText(latest.share_title!)).toBeInTheDocument()
|
||||||
|
expect(screen.queryByText('迟到旧结果')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores an older keyword failure after the newest request succeeds', async () => {
|
||||||
|
const oldRequest = createDeferred<WorkflowShare[]>()
|
||||||
|
const latest = createWorkflowShare({ id: 'share-new', share_title: '新关键字成功结果' })
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string, options?: { params?: { name?: string } }) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint !== 'workflow/shares') throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
if (options?.params?.name === '旧关键字') return oldRequest.promise
|
||||||
|
if (options?.params?.name === '新关键字') return [latest]
|
||||||
|
throw new Error(`Unexpected keyword ${options?.params?.name}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { rerender } = await renderList('旧关键字')
|
||||||
|
await waitFor(() => expect(shareRequests()).toHaveLength(1))
|
||||||
|
await rerender({ keyword: '新关键字' })
|
||||||
|
expect(await screen.findByText(latest.share_title!)).toBeInTheDocument()
|
||||||
|
|
||||||
|
oldRequest.reject(new Error('stale network failure'))
|
||||||
|
await waitFor(() => expect(screen.getByText(latest.share_title!)).toBeInTheDocument())
|
||||||
|
|
||||||
|
expect(screen.queryByRole('button', { name: '重试共享列表' })).not.toBeInTheDocument()
|
||||||
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removes a deleted share locally and forwards fork completion to the parent', async () => {
|
||||||
|
const share = createWorkflowShare()
|
||||||
|
mocks.apiGet.mockImplementation((endpoint: string) => {
|
||||||
|
if (endpoint === 'workflow/event_types') return []
|
||||||
|
if (endpoint === 'workflow/shares') return [share]
|
||||||
|
throw new Error(`Unexpected GET ${endpoint}`)
|
||||||
|
})
|
||||||
|
const update = vi.fn()
|
||||||
|
await renderWithProviders(WorkflowShareView, {
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
LoadingBanner: LoadingBannerStub,
|
||||||
|
NoDataFound: NoDataFoundStub,
|
||||||
|
ProgressiveCardGrid: ProgressiveCardGridStub,
|
||||||
|
VInfiniteScroll: InfiniteScrollStub,
|
||||||
|
VPageContentTitle: true,
|
||||||
|
WorkflowShareCard: WorkflowShareCardStub,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: { onUpdate: update },
|
||||||
|
})
|
||||||
|
expect(await screen.findByText(share.share_title!)).toBeInTheDocument()
|
||||||
|
|
||||||
|
await fireEvent.click(screen.getByRole('button', { name: '刷新-share-1' }))
|
||||||
|
await fireEvent.click(screen.getByRole('button', { name: '删除-share-1' }))
|
||||||
|
|
||||||
|
expect(update).toHaveBeenCalledOnce()
|
||||||
|
expect(screen.queryByText(share.share_title!)).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -425,6 +425,7 @@ export default defineConfig(({ command, mode, isPreview }) => ({
|
|||||||
'src/views/setting/AccountSettingSystem.vue',
|
'src/views/setting/AccountSettingSystem.vue',
|
||||||
'src/views/user/UserListView.vue',
|
'src/views/user/UserListView.vue',
|
||||||
'src/views/workflow/WorkflowListView.vue',
|
'src/views/workflow/WorkflowListView.vue',
|
||||||
|
'src/views/workflow/WorkflowShareView.vue',
|
||||||
'src/views/user/UserProfileView.vue',
|
'src/views/user/UserProfileView.vue',
|
||||||
'src/views/reorganize/DownloadingListView.vue',
|
'src/views/reorganize/DownloadingListView.vue',
|
||||||
'src/views/plugin/PluginCardListView.vue',
|
'src/views/plugin/PluginCardListView.vue',
|
||||||
@@ -630,6 +631,12 @@ export default defineConfig(({ command, mode, isPreview }) => ({
|
|||||||
lines: 85,
|
lines: 85,
|
||||||
statements: 85,
|
statements: 85,
|
||||||
},
|
},
|
||||||
|
'src/views/workflow/WorkflowShareView.vue': {
|
||||||
|
branches: 80,
|
||||||
|
functions: 90,
|
||||||
|
lines: 90,
|
||||||
|
statements: 90,
|
||||||
|
},
|
||||||
'src/views/user/UserProfileView.vue': {
|
'src/views/user/UserProfileView.vue': {
|
||||||
branches: 75,
|
branches: 75,
|
||||||
functions: 80,
|
functions: 80,
|
||||||
|
|||||||
Reference in New Issue
Block a user