diff --git a/eslint.config.ts b/eslint.config.ts index 2496aca..0540af3 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -4,7 +4,6 @@ import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescri import pluginImport from 'eslint-plugin-import-x'; import oxlint from 'eslint-plugin-oxlint'; import perfectionist from 'eslint-plugin-perfectionist'; -import perfectionistPlugin from 'eslint-plugin-perfectionist'; import pluginVue from 'eslint-plugin-vue'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -13,8 +12,6 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const gitignorePath = path.resolve(__dirname, '.gitignore'); -import type { Linter } from 'eslint'; - // To allow more languages other than `ts` in `.vue` files, uncomment the following lines: import { configureVueProject } from '@vue/eslint-config-typescript'; @@ -24,13 +21,13 @@ configureVueProject({ scriptLangs: ['ts', 'tsx', 'js', 'jsx'] }); export default defineConfigWithVueTs( includeIgnoreFile(gitignorePath), // oxlint . --fix -D correctness --ignore-path .gitignore { - name: 'app/files-to-lint', files: ['**/*.{ts,mts,tsx,vue}'], + name: 'app/files-to-lint', }, { - name: 'app/files-to-ignore', ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'], + name: 'app/files-to-ignore', }, pluginVue.configs['flat/essential'], @@ -48,43 +45,9 @@ export default defineConfigWithVueTs( oxlint.configs['flat/recommended'], skipFormatting, - importPluginConfig(), - { - rules: { - 'vue/multi-word-component-names': 'off', - '@typescript-eslint/no-explicit-any': 'off', - 'vue/block-order': [ - 'error', - { - order: ['script', 'template', 'style'], - }, - ], - 'vue/define-macros-order': [ - 'error', - { - order: ['defineOptions', 'defineProps', 'defineEmits', 'defineSlots'], - }, - ], - }, - }, - - // https://perfectionist.dev/guide/getting-started - perfectionistPlugin.configs['recommended-natural'], - { - plugins: { - perfectionist, - }, - rules: { - 'perfectionist/sort-imports': ['error'], - }, - }, -); - -function importPluginConfig(): Linter.Config[] { - return [ + [ { plugins: { - // @ts-expect-error - This is a dynamic import import: pluginImport, }, rules: { @@ -98,5 +61,35 @@ function importPluginConfig(): Linter.Config[] { // 'import/no-webpack-loader-syntax': 'error', }, }, - ]; -} + ], + + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + 'vue/block-order': [ + 'error', + { + order: ['script', 'template', 'style'], + }, + ], + 'vue/define-macros-order': [ + 'error', + { + order: ['defineOptions', 'defineProps', 'defineEmits', 'defineSlots'], + }, + ], + 'vue/multi-word-component-names': 'off', + }, + }, + + // https://perfectionist.dev/guide/getting-started + + [ + perfectionist.configs['recommended-natural'], + { + rules: { + 'perfectionist/sort-imports': ['error'], + }, + }, + ], +); diff --git a/fake/upload.fake.ts b/fake/upload.fake.ts index 18eb3aa..737e226 100644 --- a/fake/upload.fake.ts +++ b/fake/upload.fake.ts @@ -4,14 +4,7 @@ import { defineFakeRoute } from 'vite-plugin-fake-server/client'; let fail = !false; export default defineFakeRoute([ { - timeout: 2000, method: 'POST', - url: '/fake/upload', - response: () => { - return { - url: 'https://picsum.photos/200/300', - }; - }, rawResponse(req, res) { fail = !fail; if (fail) { @@ -22,5 +15,12 @@ export default defineFakeRoute([ res.end(JSON.stringify({ url: 'https://picsum.photos/200/300' })); } }, + response: () => { + return { + url: 'https://picsum.photos/200/300', + }; + }, + timeout: 2000, + url: '/fake/upload', }, ]); diff --git a/fake/user.fake.ts b/fake/user.fake.ts index 8be1926..dd5d4fd 100644 --- a/fake/user.fake.ts +++ b/fake/user.fake.ts @@ -5,30 +5,30 @@ import { defineFakeRoute } from 'vite-plugin-fake-server/client'; export default defineFakeRoute([ { - url: '/mock/get-user-info', response: () => { return Mock.mock({ - id: '@guid', - username: '@first', - email: '@email', avatar: '@image("200x200")', + email: '@email', + id: '@guid', role: 'admin', + username: '@first', }); }, + url: '/mock/get-user-info', }, { - url: '/fake/get-user-info', response: () => { return { - id: faker.string.uuid(), avatar: faker.image.avatar(), birthday: faker.date.birthdate(), email: faker.internet.email(), firstName: faker.person.firstName(), + id: faker.string.uuid(), lastName: faker.person.lastName(), - sex: faker.person.sexType(), role: 'admin', + sex: faker.person.sexType(), }; }, + url: '/fake/get-user-info', }, ]); diff --git a/playwright.config.ts b/playwright.config.ts index 4204e9d..ce88ec8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -12,29 +12,13 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests/e2e', - /* Run tests in files in parallel */ - fullyParallel: true, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: process.env.BASE_URL || 'https://vue-ts-example.oo1.dev', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - }, expect: { timeout: 30 * 1000, }, - + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Run tests in files in parallel */ + fullyParallel: true, /* Configure projects for major browsers */ projects: [ { @@ -72,6 +56,22 @@ export default defineConfig({ // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // }, ], + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + testDir: './tests/e2e', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.BASE_URL || 'https://vue-ts-example.oo1.dev', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, /* Run your local dev server before starting the tests */ // webServer: { diff --git a/src/components/UseIntersectionObserverInfiniteLoading.vue b/src/components/UseIntersectionObserverInfiniteLoading.vue index 890e1e7..f6f3447 100644 --- a/src/components/UseIntersectionObserverInfiniteLoading.vue +++ b/src/components/UseIntersectionObserverInfiniteLoading.vue @@ -6,7 +6,7 @@ function checkIsVisible(el: Element, root: Element | null = null) { const elRect = el.getBoundingClientRect(); const rootRect = root ? root.getBoundingClientRect() - : { top: 0, left: 0, bottom: window.innerHeight, right: window.innerWidth }; + : { bottom: window.innerHeight, left: 0, right: window.innerWidth, top: 0 }; return ( elRect.bottom >= rootRect.top && @@ -32,26 +32,26 @@ function checkIsVisible(el: Element, root: Element | null = null) { */ const props = defineProps<{ - loading: boolean; complete: boolean; error: boolean; errorText: string; + loading: boolean; }>(); const emit = defineEmits<{ - load: []; clickError: []; + load: []; }>(); defineSlots<{ - // 加载中 - loading(): unknown; - // 加载完成(还有更多) - loaded(): unknown; // 加载完成(没有更多了) complete(): unknown; // 加载失败 error(): unknown; + // 加载完成(还有更多) + loaded(): unknown; + // 加载中 + loading(): unknown; }>(); const check = (reason?: string) => { diff --git a/src/components/primevue/upload-demo.vue b/src/components/primevue/upload-demo.vue index 1ad1bb8..a1ca4f6 100644 --- a/src/components/primevue/upload-demo.vue +++ b/src/components/primevue/upload-demo.vue @@ -6,15 +6,15 @@ interface FileExt extends File { } interface FileUploadInst extends FileUploadState { + chooseDisabled?: boolean; files: FileExt[]; uploadedFiles: { - rawFile: FileExt; name: string; - url: string; - status: 'uploading' | 'uploaded' | 'failed'; progress: number; + rawFile: FileExt; + status: 'failed' | 'uploaded' | 'uploading'; + url: string; }[]; - chooseDisabled?: boolean; } @@ -30,11 +30,11 @@ const onUploader = (event: FileUploadUploaderEvent) => { const files = event.files as FileExt[]; for (const file of files) { fileUploadRef.value!.uploadedFiles.push({ - rawFile: file, name: file.name, - url: '', - status: 'uploading', progress: 0, + rawFile: file, + status: 'uploading', + url: '', }); const formData = new FormData(); formData.append('file', file); diff --git a/src/components/send-sms.vue b/src/components/send-sms.vue index 31de86a..6a145b1 100644 --- a/src/components/send-sms.vue +++ b/src/components/send-sms.vue @@ -1,5 +1,5 @@ diff --git a/src/layouts/sakai-vue/AppLayout.vue b/src/layouts/sakai-vue/AppLayout.vue index bd95ae1..d3cf646 100644 --- a/src/layouts/sakai-vue/AppLayout.vue +++ b/src/layouts/sakai-vue/AppLayout.vue @@ -8,9 +8,9 @@ import AppSidebar from './AppSidebar.vue'; import AppTopbar from './AppTopbar.vue'; import { useLayout } from './composables/layout'; -const { layoutConfig, layoutState, isSidebarActive } = useLayout(); +const { isSidebarActive, layoutConfig, layoutState } = useLayout(); -const outsideClickListener = ref(null as Parameters[1] | null); +const outsideClickListener = ref(null as null | Parameters[1]); watch(isSidebarActive, (newVal) => { if (newVal) { @@ -22,11 +22,11 @@ watch(isSidebarActive, (newVal) => { const containerClass = computed(() => { return { + 'layout-mobile-active': layoutState.staticMenuMobileActive, 'layout-overlay': layoutConfig.menuMode === 'overlay', + 'layout-overlay-active': layoutState.overlayMenuActive, 'layout-static': layoutConfig.menuMode === 'static', 'layout-static-inactive': layoutState.staticMenuDesktopInactive && layoutConfig.menuMode === 'static', - 'layout-overlay-active': layoutState.overlayMenuActive, - 'layout-mobile-active': layoutState.staticMenuMobileActive, }; }); @@ -43,13 +43,6 @@ function bindOutsideClickListener() { } } -function unbindOutsideClickListener() { - if (outsideClickListener.value) { - document.removeEventListener('click', outsideClickListener.value); - outsideClickListener.value = null; - } -} - function isOutsideClicked(event: Event) { const sidebarEl = document.querySelector('.layout-sidebar')!; const topbarEl = document.querySelector('.layout-menu-button')!; @@ -61,6 +54,13 @@ function isOutsideClicked(event: Event) { topbarEl.contains(event.target as never) ); } + +function unbindOutsideClickListener() { + if (outsideClickListener.value) { + document.removeEventListener('click', outsideClickListener.value); + outsideClickListener.value = null; + } +}