diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 73% rename from .eslintrc.js rename to .eslintrc.cjs index fb044f3..5a345e1 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -3,7 +3,13 @@ module.exports = { env: { node: true, }, - extends: ['@vue/typescript/recommended', 'plugin:vue/vue3-essential', 'prettier', '@vue/eslint-config-typescript'], + extends: [ + '@vue/typescript/recommended', + 'plugin:vue/vue3-essential', + 'prettier', + '@vue/eslint-config-typescript', + 'plugin:oxlint/recommended', + ], parserOptions: { ecmaVersion: 'latest', }, @@ -11,6 +17,7 @@ module.exports = { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'vue/component-name-in-template-casing': ['error', 'PascalCase'], + 'vue/no-mutating-props': ['off'], '@typescript-eslint/no-explicit-any': ['off'], '@typescript-eslint/no-var-requires': ['warn'], }, @@ -24,6 +31,7 @@ module.exports = { ], rules: { // TypeScript specific rules... + '@typescript-eslint/no-explicit-any': ['off'], }, }, ], diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..53e066f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +yarn test diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 4aded7b..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,5 +0,0 @@ -// eslint.config.js -import oxlint from 'eslint-plugin-oxlint' -export default [ - oxlint, // oxlint should be the last one -] diff --git a/package.json b/package.json index 73c1f5c..041d245 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,13 @@ "name": "vtsuru.live", "version": "0.1.0", "private": true, + "type": "module", "scripts": { "dev": "vite", "build": "vite build", - "lint": "vite lint" + "lint": "npx oxlint && vite lint", + "postinstall": "husky init", + "prepare": "husky" }, "dependencies": { "@types/node": "^20.11.19", @@ -47,6 +50,7 @@ "xlsx": "^0.18.5" }, "devDependencies": { + "@eslint/eslintrc": "^3.0.1", "@types/eslint": "^8.56.2", "@types/uuid": "^9.0.8", "@typescript-eslint/parser": "^7.0.1", @@ -55,6 +59,7 @@ "@vue/eslint-config-typescript": "^12.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-vue": "^9.21.1", + "husky": "^9.0.11", "naive-ui": "^2.37.3", "stylus": "^0.62.0", "typescript": "^5.3.3" diff --git a/src/Utils.ts b/src/Utils.ts index a5c5c35..2daa54f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -81,7 +81,7 @@ export function getBase64(file: File | undefined | null): Promise import { ResponseLiveInfoModel } from '@/api/api-models' import { Info24Filled } from '@vicons/fluent' -import { NButton, NDivider, NIcon, NNumberAnimation, NPopover, NSpace, NStatistic, NTag, NTime, NTooltip } from 'naive-ui' +import { + NButton, + NDivider, + NIcon, + NNumberAnimation, + NPopover, + NSpace, + NStatistic, + NTag, + NTime, + NTooltip, +} from 'naive-ui' import { ref, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' @@ -12,7 +23,7 @@ const { live } = defineProps<{ live: ResponseLiveInfoModel }>() -let defaultDanmakusCount = ref(0) +const defaultDanmakusCount = ref(0) function OnClickCover() { router.push({ name: 'manage-liveDetail', @@ -50,7 +61,14 @@ watch( - 直播中 + + 直播中 + {{ (((live.stopAt ?? 0) - (live.startAt ?? 0)) / (3600 * 1000)).toFixed(1) }} diff --git a/src/components/SongList.vue b/src/components/SongList.vue index ed72385..9387cb4 100644 --- a/src/components/SongList.vue +++ b/src/components/SongList.vue @@ -4,7 +4,13 @@ import { QueryGetAPI, QueryPostAPI } from '@/api/query' import { SONG_API_URL } from '@/data/constants' import FiveSingIcon from '@/svgs/fivesing.svg' import NeteaseIcon from '@/svgs/netease.svg' -import { Delete24Filled, Info24Filled, NotepadEdit20Filled, Play24Filled, SquareArrowForward24Filled } from '@vicons/fluent' +import { + Delete24Filled, + Info24Filled, + NotepadEdit20Filled, + Play24Filled, + SquareArrowForward24Filled, +} from '@vicons/fluent' import { refDebounced, useLocalStorage } from '@vueuse/core' import { List } from 'linqts' import { @@ -154,7 +160,11 @@ const authorColumn = ref>({ }, filterOptions: authorsOptions.value, render(data) { - return h(NSpace, { size: 5 }, () => data.author.map((a) => h(NButton, { size: 'tiny', type: 'info', secondary: true, onClick: () => onAuthorClick(a) }, () => a))) + return h(NSpace, { size: 5 }, () => + data.author.map((a) => + h(NButton, { size: 'tiny', type: 'info', secondary: true, onClick: () => onAuthorClick(a) }, () => a), + ), + ) }, }) const onAuthorClick = (author: string) => { @@ -179,7 +189,10 @@ function createColumns(): DataTableColumns { width: 300, sorter: 'default', render(data) { - return h(NSpace, { size: 5 }, () => [h(NText, { style: { color: data.options?.scMinPrice ? '#c36767' : '' } }, () => data.name), h(NText, { depth: '3' }, () => data.translateName)]) + return h(NSpace, { size: 5 }, () => [ + h(NText, { style: { color: data.options?.scMinPrice ? '#c36767' : '' } }, () => data.name), + h(NText, { depth: '3' }, () => data.translateName), + ]) }, title: '曲名', }, @@ -195,7 +208,11 @@ function createColumns(): DataTableColumns { }, render(data) { return (data.language?.length ?? 0) > 0 - ? h(NSpace, { size: 5 }, () => data.language?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => songSelectOption.find((s) => s.value == a)?.label))) + ? h(NSpace, { size: 5 }, () => + data.language?.map((a) => + h(NTag, { bordered: false, size: 'small' }, () => songSelectOption.find((s) => s.value == a)?.label), + ), + ) : null }, }, @@ -214,13 +231,40 @@ function createColumns(): DataTableColumns { render(data) { return data.options ? h(NSpace, {}, () => [ - data.options?.needJianzhang ? h(NTag, { color: { textColor: 'white', color: GetGuardColor(3), borderColor: 'white' }, size: 'small' }, () => '舰长') : null, - data.options?.needTidu ? h(NTag, { color: { textColor: 'white', color: GetGuardColor(2), borderColor: 'white' }, size: 'small' }, () => '提督') : null, - data.options?.needZongdu ? h(NTag, { color: { textColor: 'white', color: GetGuardColor(1), borderColor: 'white' }, size: 'small' }, () => '总督') : null, - data.options?.scMinPrice - ? h(NTag, { color: { textColor: 'white', color: GetSCColor(data.options.scMinPrice), borderColor: 'white' }, size: 'small' }, () => 'SC | ' + data.options?.scMinPrice) + data.options?.needJianzhang + ? h( + NTag, + { color: { textColor: 'white', color: GetGuardColor(3), borderColor: 'white' }, size: 'small' }, + () => '舰长', + ) + : null, + data.options?.needTidu + ? h( + NTag, + { color: { textColor: 'white', color: GetGuardColor(2), borderColor: 'white' }, size: 'small' }, + () => '提督', + ) + : null, + data.options?.needZongdu + ? h( + NTag, + { color: { textColor: 'white', color: GetGuardColor(1), borderColor: 'white' }, size: 'small' }, + () => '总督', + ) + : null, + data.options?.scMinPrice + ? h( + NTag, + { + color: { textColor: 'white', color: GetSCColor(data.options.scMinPrice), borderColor: 'white' }, + size: 'small', + }, + () => 'SC | ' + data.options?.scMinPrice, + ) + : null, + data.options?.fanMedalMinLevel + ? h(NTag, { type: 'info', size: 'small' }, () => '粉丝牌 | ' + data.options?.fanMedalMinLevel) : null, - data.options?.fanMedalMinLevel ? h(NTag, { type: 'info', size: 'small' }, () => '粉丝牌 | ' + data.options?.fanMedalMinLevel) : null, ]) : null }, @@ -234,7 +278,9 @@ function createColumns(): DataTableColumns { }, filterOptions: tagsSelectOption.value, render(data) { - return (data.tags?.length ?? 0) > 0 ? h(NSpace, { size: 5 }, () => data.tags?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => a))) : null + return (data.tags?.length ?? 0) > 0 + ? h(NSpace, { size: 5 }, () => data.tags?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => a))) + : null }, }, { @@ -251,7 +297,11 @@ function createColumns(): DataTableColumns { }, () => [ GetPlayButton(data), - data.url?.endsWith('mp3') || data.url?.endsWith('flac') || data.url?.endsWith('ogg') || data.url?.endsWith('wav') || data.url?.endsWith('m4a') + data.url?.endsWith('mp3') || + data.url?.endsWith('flac') || + data.url?.endsWith('ogg') || + data.url?.endsWith('wav') || + data.url?.endsWith('m4a') ? h(NTooltip, null, { trigger: () => h( @@ -571,8 +621,23 @@ onMounted(() => { - - 清除歌手选择 + + + 清除歌手选择 + 共 {{ songsComputed.length }} 首 @@ -582,14 +647,22 @@ onMounted(() => { - 批量编辑 + + 批量编辑 + @@ -598,16 +671,39 @@ onMounted(() => { - + - + - + diff --git a/src/views/manage/EventView.vue b/src/views/manage/EventView.vue index 3f487cf..cbb1ead 100644 --- a/src/views/manage/EventView.vue +++ b/src/views/manage/EventView.vue @@ -34,7 +34,7 @@ import { NUl, useMessage, } from 'naive-ui' -import { computed, onMounted, ref } from 'vue' +import { computed, ref } from 'vue' enum EventType { Guard, @@ -57,7 +57,10 @@ const rangeShortcuts = { 上个月: () => { const cur = new Date() const lastMonth = new Date(cur.getFullYear(), cur.getMonth() - 1) - return [new Date(cur.getFullYear(), cur.getMonth() - 1, 1).getTime(), new Date(cur.getFullYear(), cur.getMonth(), 1).getTime()] as const + return [ + new Date(cur.getFullYear(), cur.getMonth() - 1, 1).getTime(), + new Date(cur.getFullYear(), cur.getMonth(), 1).getTime(), + ] as const }, 本月: () => { const cur = new Date() @@ -143,8 +146,9 @@ function exportData() { } saveAs( new Blob([text], { type: 'text/plain;charset=utf-8' }), - `${format(Date.now(), 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[0], 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[1], 'yyyy-MM-dd HH:mm:ss')}}_${accountInfo.value - ?.id}_${accountInfo.value?.name}_${selectedType.value}.${exportType.value}`, + `${format(Date.now(), 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[0], 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[1], 'yyyy-MM-dd HH:mm:ss')}}_${ + accountInfo.value?.id + }_${accountInfo.value?.name}_${selectedType.value}.${exportType.value}`, ) } function objectsToCSV(arr: any[]) { @@ -163,12 +167,19 @@ function objectsToCSV(arr: any[]) {