添加 VCronInput 公共组件,用于快速录入CRON表达式

This commit is contained in:
jxxghp
2025-01-11 13:28:46 +08:00
parent 047e99e27c
commit afa333243f
10 changed files with 108 additions and 85 deletions
+1
View File
@@ -26,6 +26,7 @@
"@fullcalendar/timegrid": "^6.1.7", "@fullcalendar/timegrid": "^6.1.7",
"@fullcalendar/vue3": "^6.1.8", "@fullcalendar/vue3": "^6.1.8",
"@iconify/utils": "^2.1.22", "@iconify/utils": "^2.1.22",
"@vue-js-cron/vuetify": "^5.0.9",
"@vueuse/core": "^10.1.2", "@vueuse/core": "^10.1.2",
"@vueuse/math": "^10.1.2", "@vueuse/math": "^10.1.2",
"ace-builds": "^1.32.6", "ace-builds": "^1.32.6",
+4 -22
View File
@@ -131,24 +131,6 @@ function onClose() {
emit('close') emit('close')
} }
// 下载路径更新
function updateDownloadPath(value: string) {
downloadPath.value = value
emit('update:modelValue', {
download: downloadPath.value,
library: libraryPath.value,
})
}
// 媒体库路径更新
function updateLibraryPath(value: string) {
libraryPath.value = value
emit('update:modelValue', {
download: downloadPath.value,
library: libraryPath.value,
})
}
// 根据选中的媒体类型,获取对应的媒体类别 // 根据选中的媒体类型,获取对应的媒体类别
const getCategories = computed(() => { const getCategories = computed(() => {
const default_value = [{ title: '全部', value: '' }] const default_value = [{ title: '全部', value: '' }]
@@ -228,10 +210,10 @@ watch(
/> />
</VCol> </VCol>
<VCol cols="8"> <VCol cols="8">
<VPathField @update:modelValue="updateDownloadPath" :storage="props.directory.storage"> <VPathField v-model="props.directory.download_path" :storage="props.directory.storage">
<template #activator="{ menuprops }"> <template #activator="{ menuprops }">
<VTextField <VTextField
v-model="props.directory.download_path" :model-value="props.directory.download_path"
v-bind="menuprops" v-bind="menuprops"
variant="underlined" variant="underlined"
label="下载目录/源目录" label="下载目录/源目录"
@@ -275,10 +257,10 @@ watch(
/> />
</VCol> </VCol>
<VCol cols="8"> <VCol cols="8">
<VPathField @update:modelValue="updateLibraryPath" :storage="props.directory.library_storage"> <VPathField v-model="props.directory.library_path" :storage="props.directory.library_storage">
<template #activator="{ menuprops }"> <template #activator="{ menuprops }">
<VTextField <VTextField
v-model="props.directory.library_path" :modelValue="props.directory.library_path"
v-bind="menuprops" v-bind="menuprops"
variant="underlined" variant="underlined"
label="媒体库目录" label="媒体库目录"
+2 -3
View File
@@ -3,7 +3,6 @@ import type { Site, SiteUserData } from '@/api/types'
import api from '@/api' import api from '@/api'
import { useDisplay, useTheme } from 'vuetify' import { useDisplay, useTheme } from 'vuetify'
import { formatFileSize } from '@/@core/utils/formatters' import { formatFileSize } from '@/@core/utils/formatters'
import VueApexCharts from 'vue3-apexcharts'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue' import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
// 显示器宽度 // 显示器宽度
@@ -440,7 +439,7 @@ onBeforeMount(async () => {
<VCol> <VCol>
<VCard title="历史流量"> <VCard title="历史流量">
<VCardText> <VCardText>
<VueApexCharts type="line" :options="historyChartOptions" :series="historySeries" :height="300" /> <VApexChart type="line" :options="historyChartOptions" :series="historySeries" :height="300" />
</VCardText> </VCardText>
</VCard> </VCard>
</VCol> </VCol>
@@ -449,7 +448,7 @@ onBeforeMount(async () => {
<VCol> <VCol>
<VCard title="做种分布"> <VCard title="做种分布">
<VCardText> <VCardText>
<VueApexCharts type="scatter" :options="seedingChartOptions" :series="seedingSeries" :height="300" /> <VApexChart type="scatter" :options="seedingChartOptions" :series="seedingSeries" :height="300" />
</VCardText> </VCardText>
</VCard> </VCard>
</VCol> </VCol>
+30
View File
@@ -0,0 +1,30 @@
<script setup lang="ts">
import api from '@/api'
import { FileItem } from '@/api/types'
const props = defineProps({
cron: {
type: String,
default: '* * * * *',
},
})
const emit = defineEmits(['update:modelValue'])
const currentCron = ref(props.cron)
watch(currentCron, newVal => {
emit('update:modelValue', currentCron.value)
})
</script>
<template>
<div>
<VMenu :close-on-content-click="false" content-class="cursor-default">
<template v-slot:activator="{ props }">
<slot name="activator" :menuprops="props" />
</template>
<VCronVuetify v-model="currentCron" locale="zh-CN" :chip-props="{ color: 'success' }" class="mt-1" />
</VMenu>
</div>
</template>
+23 -21
View File
@@ -78,25 +78,27 @@ function handleUserSelect() {
</script> </script>
<template> <template>
<VMenu :close-on-content-click="false" content-class="cursor-default"> <div>
<template v-slot:activator="{ props }"> <VMenu :close-on-content-click="false" content-class="cursor-default">
<slot name="activator" :menuprops="props" /> <template v-slot:activator="{ props }">
</template> <slot name="activator" :menuprops="props" />
<VTreeview </template>
v-model:activated="activedDirs" <VTreeview
v-model:opened="openedDirs" v-model:activated="activedDirs"
:items="treeItems" v-model:opened="openedDirs"
:load-children="fetchDirs" :items="treeItems"
item-key="path" :load-children="fetchDirs"
item-title="name" item-key="path"
item-value="path" item-title="name"
item-type="unknown" item-value="path"
activatable item-type="unknown"
return-object activatable
max-height="20rem" return-object
expand-icon="mdi-folder" max-height="20rem"
collapse-icon="mdi-folder-open" expand-icon="mdi-folder"
@update:activated="handleUserSelect" collapse-icon="mdi-folder-open"
/> @update:activated="handleUserSelect"
</VMenu> />
</VMenu>
</div>
</template> </template>
+18 -12
View File
@@ -1,17 +1,12 @@
import './ace-config'
import '@/@core/utils/compatibility' import '@/@core/utils/compatibility'
import '@/@iconify/icons-bundle' import '@/@iconify/icons-bundle'
import '@/plugins/webfontloader' import '@/plugins/webfontloader'
import App from '@/App.vue' import App from '@/App.vue'
import vuetify from '@/plugins/vuetify' import vuetify from '@/plugins/vuetify'
import router from '@/router' import router from '@/router'
import store from '@/store' import store from '@/store'
import { createApp } from 'vue'
import { removeEl } from './@core/utils/dom'
import { fetchGlobalSettings } from './api'
import { isPWA } from './@core/utils/navigator'
import './ace-config'
import { VAceEditor } from 'vue3-ace-editor'
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import ToastPlugin from 'vue-toast-notification' import ToastPlugin from 'vue-toast-notification'
import VuetifyUseDialog from 'vuetify-use-dialog' import VuetifyUseDialog from 'vuetify-use-dialog'
import VueApexCharts from 'vue3-apexcharts' import VueApexCharts from 'vue3-apexcharts'
@@ -24,11 +19,22 @@ import MediaInfoCard from './components/cards/MediaInfoCard.vue'
import TorrentCard from './components/cards/TorrentCard.vue' import TorrentCard from './components/cards/TorrentCard.vue'
import MediaIdSelector from './components/misc/MediaIdSelector.vue' import MediaIdSelector from './components/misc/MediaIdSelector.vue'
import PathField from './components/input/PathField.vue' import PathField from './components/input/PathField.vue'
import CronInput from './components/input/CronInput.vue'
import { createApp } from 'vue'
import { VAceEditor } from 'vue3-ace-editor'
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import { CronVuetify } from '@vue-js-cron/vuetify'
import { removeEl } from './@core/utils/dom'
import { fetchGlobalSettings } from './api'
import { isPWA } from './@core/utils/navigator'
import '@core/scss/template/index.scss' import '@core/scss/template/index.scss'
import '@layouts/styles/index.scss' import '@layouts/styles/index.scss'
import '@styles/styles.scss' import '@styles/styles.scss'
import 'vue-toast-notification/dist/theme-bootstrap.css' import 'vue-toast-notification/dist/theme-bootstrap.css'
import 'vue3-perfect-scrollbar/style.css' import 'vue3-perfect-scrollbar/style.css'
import '@vue-js-cron/vuetify/dist/vuetify.css'
// 创建Vue实例 // 创建Vue实例
const app = createApp(App) const app = createApp(App)
@@ -49,13 +55,13 @@ async function initializeApp() {
// 注册全局组件 // 注册全局组件
initializeApp().then(() => { initializeApp().then(() => {
// 优先注册框架 // 优先注册框架
app app.use(vuetify)
.use(vuetify)
// 注册全局组件 // 注册全局组件
app app
.component('VAceEditor', VAceEditor) .component('VAceEditor', VAceEditor)
.component('VApexChart', VueApexCharts) .component('VApexChart', VueApexCharts)
.component('VCronVuetify', CronVuetify)
.component('VDialogCloseBtn', DialogCloseBtn) .component('VDialogCloseBtn', DialogCloseBtn)
.component('VMediaCard', MediaCard) .component('VMediaCard', MediaCard)
.component('VPosterCard', PosterCard) .component('VPosterCard', PosterCard)
@@ -65,11 +71,13 @@ initializeApp().then(() => {
.component('VTorrentCard', TorrentCard) .component('VTorrentCard', TorrentCard)
.component('VMediaIdSelector', MediaIdSelector) .component('VMediaIdSelector', MediaIdSelector)
.component('VPathField', PathField) .component('VPathField', PathField)
.component('VCronInput', CronInput)
// 注册插件 // 注册插件
app app
.use(router) .use(router)
.use(store) .use(store)
.use(PerfectScrollbarPlugin)
.use(ToastPlugin, { .use(ToastPlugin, {
position: 'bottom-right', position: 'bottom-right',
}) })
@@ -93,8 +101,6 @@ initializeApp().then(() => {
cancellationText: '取消', cancellationText: '取消',
}, },
}) })
.use(PerfectScrollbarPlugin)
.use(VueApexCharts)
.mount('#app') .mount('#app')
.$nextTick(() => removeEl('#loading-bg')) .$nextTick(() => removeEl('#loading-bg'))
}) })
+1 -3
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import VueApexCharts from 'vue3-apexcharts'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import { hexToRgb } from '@layouts/utils' import { hexToRgb } from '@layouts/utils'
import api from '@/api' import api from '@/api'
@@ -143,8 +142,7 @@ onUnmounted(() => {
<VCardTitle>CPU</VCardTitle> <VCardTitle>CPU</VCardTitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VueApexCharts type="line" :options="chartOptions" :series="series" :height="150" /> <VApexChart type="line" :options="chartOptions" :series="series" :height="150" />
<p class="text-center font-weight-medium mb-0">当前{{ current }}%</p> <p class="text-center font-weight-medium mb-0">当前{{ current }}%</p>
</VCardText> </VCardText>
</VCard> </VCard>
+1 -3
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import VueApexCharts from 'vue3-apexcharts'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import { hexToRgb } from '@layouts/utils' import { hexToRgb } from '@layouts/utils'
import api from '@/api' import api from '@/api'
@@ -149,8 +148,7 @@ onUnmounted(() => {
<VCardTitle>内存</VCardTitle> <VCardTitle>内存</VCardTitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VueApexCharts type="area" :options="chartOptions" :series="series" :height="150" /> <VApexChart type="area" :options="chartOptions" :series="series" :height="150" />
<p class="text-center font-weight-medium mb-0">当前{{ formatBytes(usedMemory) }}</p> <p class="text-center font-weight-medium mb-0">当前{{ formatBytes(usedMemory) }}</p>
</VCardText> </VCardText>
</VCard> </VCard>
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import VueApexCharts from 'vue3-apexcharts'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import api from '@/api' import api from '@/api'
import { hexToRgb } from '@layouts/utils' import { hexToRgb } from '@layouts/utils'
@@ -127,8 +126,7 @@ onMounted(() => {
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VueApexCharts type="bar" :options="options" :series="series" :height="160" /> <VApexChart type="bar" :options="options" :series="series" :height="160" />
<div class="d-flex align-center mb-3"> <div class="d-flex align-center mb-3">
<h5 class="text-h5 me-4"> <h5 class="text-h5 me-4">
{{ totalCount }} {{ totalCount }}
+27 -18
View File
@@ -2269,6 +2269,21 @@
"@volar/language-core" "2.2.0-alpha.5" "@volar/language-core" "2.2.0-alpha.5"
path-browserify "^1.0.1" path-browserify "^1.0.1"
"@vue-js-cron/core@5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@vue-js-cron/core/-/core-5.4.1.tgz#5c9b4a3b65f215f5f6767b56f2bc7e7d87e32834"
integrity sha512-Z2dPQWyBlsCvoFAZMSx+CWqWRXyf09AabvG/jkEvHx9hbydhHDQApNhQHKVjbMMbFzu4HWRmjRjrx3Cq6bCOOw==
dependencies:
mustache "^4.2.0"
"@vue-js-cron/vuetify@^5.0.9":
version "5.0.9"
resolved "https://registry.yarnpkg.com/@vue-js-cron/vuetify/-/vuetify-5.0.9.tgz#e78bf266fe958332aae931081a59423712d1e164"
integrity sha512-/j/fBPiWAnc8ZUWILBCGlUwowhksOZGqID8gN1KyCUCUnovwR3aNE5YWbYAuV3P8RxelJstCdgvlBFl5hleu9A==
dependencies:
"@vue-js-cron/core" "5.4.1"
vuetify "^3.4.0"
"@vue-macros/common@1.10.2": "@vue-macros/common@1.10.2":
version "1.10.2" version "1.10.2"
resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-1.10.2.tgz#4c886082cfd94de2fb16e8e1df99d141873450e7" resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-1.10.2.tgz#4c886082cfd94de2fb16e8e1df99d141873450e7"
@@ -5538,6 +5553,11 @@ muggle-string@^0.4.0:
resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328"
integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
mustache@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
mz@^2.7.0: mz@^2.7.0:
version "2.7.0" version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
@@ -6618,16 +6638,7 @@ stop-iteration-iterator@^1.0.0:
dependencies: dependencies:
internal-slot "^1.0.4" internal-slot "^1.0.4"
"string-width-cjs@npm:string-width@^4.2.0": "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -6700,14 +6711,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1" is-obj "^1.0.1"
is-regexp "^1.0.0" is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1": "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -7629,6 +7633,11 @@ vuetify@3.6.8:
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.6.8.tgz#89ab0b68aa5488c7b54a04fa4a02a1b802892aaa" resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.6.8.tgz#89ab0b68aa5488c7b54a04fa4a02a1b802892aaa"
integrity sha512-j0v0iTeSVRj2ZEM9Q8HxejHxmxrQLYQSalhH82hfcraORaiDoqf1XV05N3P5ERXkKiJjJc/LfxFAUUvYSldxeg== integrity sha512-j0v0iTeSVRj2ZEM9Q8HxejHxmxrQLYQSalhH82hfcraORaiDoqf1XV05N3P5ERXkKiJjJc/LfxFAUUvYSldxeg==
vuetify@^3.4.0:
version "3.7.6"
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.7.6.tgz#f966dbe74666cb847d67b3e3d8c6fee75cf972d3"
integrity sha512-lol0Va5HtMIqZfjccSD5DLv5v31R/asJXzc6s7ULy51PHr1DjXxWylZejhq0kVpMGW64MiV1FmA/p8eYQfOWfQ==
vuex-persistedstate@^4.1.0: vuex-persistedstate@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz#127165f85f5b4534fb3170a5d3a8be9811bd2a53" resolved "https://registry.yarnpkg.com/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz#127165f85f5b4534fb3170a5d3a8be9811bd2a53"