mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-05 22:41:28 +08:00
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -1,10 +1,12 @@
|
|||||||
name: Build Moviepilot-Frontend
|
name: Build Moviepilot-Frontend v2
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- v2
|
||||||
|
paths:
|
||||||
|
- 'package.json'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -43,14 +45,14 @@ jobs:
|
|||||||
- name: Delete Release
|
- name: Delete Release
|
||||||
uses: dev-drprasad/delete-tag-and-release@v1.1
|
uses: dev-drprasad/delete-tag-and-release@v1.1
|
||||||
with:
|
with:
|
||||||
tag_name: dev_${{ env.frontend_version }}
|
tag_name: ${{ env.frontend_version }}
|
||||||
delete_release: true
|
delete_release: true
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Generate Release
|
- name: Generate Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: dev_${{ env.frontend_version }}
|
tag_name: ${{ env.frontend_version }}
|
||||||
name: ${{ env.frontend_version }}
|
name: ${{ env.frontend_version }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "2.0.0-alpha",
|
"version": "2.0.0-beta",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": "dist/service.js",
|
"bin": "dist/service.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
3
src/@layouts/types.d.ts
vendored
3
src/@layouts/types.d.ts
vendored
@@ -123,7 +123,8 @@ export interface NavLink extends NavLinkProps, Partial<AclProperties> {
|
|||||||
export interface NavMenu extends NavLink {
|
export interface NavMenu extends NavLink {
|
||||||
header: string
|
header: string
|
||||||
description?: string
|
description?: string
|
||||||
permission?: string
|
admin?: boolean
|
||||||
|
footer?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 👉 Vertical nav group
|
// 👉 Vertical nav group
|
||||||
|
|||||||
@@ -1042,6 +1042,8 @@ export interface TransferDirectoryConf {
|
|||||||
download_category_folder?: boolean
|
download_category_folder?: boolean
|
||||||
// 监控方式 downloader/monitor,None为不监控
|
// 监控方式 downloader/monitor,None为不监控
|
||||||
monitor_type?: string
|
monitor_type?: string
|
||||||
|
// 监控模式 fast/compatibility
|
||||||
|
monitor_mode?: string
|
||||||
// 整理方式 move/copy/link/softlink
|
// 整理方式 move/copy/link/softlink
|
||||||
transfer_type?: string
|
transfer_type?: string
|
||||||
// 文件覆盖模式 always/size/never/latest
|
// 文件覆盖模式 always/size/never/latest
|
||||||
@@ -1058,6 +1060,8 @@ export interface TransferDirectoryConf {
|
|||||||
library_type_folder?: boolean
|
library_type_folder?: boolean
|
||||||
// 媒体库类别子目录
|
// 媒体库类别子目录
|
||||||
library_category_folder?: boolean
|
library_category_folder?: boolean
|
||||||
|
// 是否发送通知
|
||||||
|
notify?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义规则项
|
// 自定义规则项
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ function onClose() {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard variant="tonal" @click="openRuleInfoDialog">
|
<VCard variant="tonal" @click="openRuleInfoDialog">
|
||||||
|
<span class="absolute top-3 right-12">
|
||||||
|
<IconBtn>
|
||||||
|
<VIcon class="cursor-move" icon="mdi-drag" />
|
||||||
|
</IconBtn>
|
||||||
|
</span>
|
||||||
<DialogCloseBtn @click="onClose" />
|
<DialogCloseBtn @click="onClose" />
|
||||||
<VCardText class="flex justify-space-between align-center gap-3">
|
<VCardText class="flex justify-space-between align-center gap-3">
|
||||||
<div class="align-self-start">
|
<div class="align-self-start">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TransferDirectoryConf } from '@/api/types'
|
import type { TransferDirectoryConf } from '@/api/types'
|
||||||
import { VTextField } from 'vuetify/lib/components/index.mjs'
|
import { VDivider, VSpacer, VTextField } from 'vuetify/lib/components/index.mjs'
|
||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
@@ -26,6 +26,9 @@ const downloadPath = ref<string>('')
|
|||||||
// 媒体库路径
|
// 媒体库路径
|
||||||
const libraryPath = ref<string>('')
|
const libraryPath = ref<string>('')
|
||||||
|
|
||||||
|
// 卡版是否折叠状态
|
||||||
|
const isCollapsed = ref(true)
|
||||||
|
|
||||||
// 类型下拉字典
|
// 类型下拉字典
|
||||||
const typeItems = [
|
const typeItems = [
|
||||||
{ title: '全部', value: '' },
|
{ title: '全部', value: '' },
|
||||||
@@ -48,6 +51,12 @@ const transferSourceItems = [
|
|||||||
{ title: '目录监控', value: 'monitor' },
|
{ title: '目录监控', value: 'monitor' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 监控模式下拉字典
|
||||||
|
const MonitorModeItems = [
|
||||||
|
{ title: '性能模式', value: 'fast' },
|
||||||
|
{ title: '兼容模式', value: 'compatibility' },
|
||||||
|
]
|
||||||
|
|
||||||
// 整理方式下拉字典
|
// 整理方式下拉字典
|
||||||
const transferTypeItems = ref<{ title: string; value: string }[]>([])
|
const transferTypeItems = ref<{ title: string; value: string }[]>([])
|
||||||
|
|
||||||
@@ -182,7 +191,7 @@ watch(
|
|||||||
</IconBtn>
|
</IconBtn>
|
||||||
</span>
|
</span>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText v-if="!isCollapsed">
|
||||||
<VForm>
|
<VForm>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
@@ -236,6 +245,14 @@ watch(
|
|||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow v-if="$props.directory.monitor_type">
|
<VRow v-if="$props.directory.monitor_type">
|
||||||
|
<VCol cols="12" v-if="$props.directory.monitor_type == 'monitor'">
|
||||||
|
<VSelect
|
||||||
|
v-model="props.directory.monitor_mode"
|
||||||
|
variant="underlined"
|
||||||
|
:items="MonitorModeItems"
|
||||||
|
label="监控模式"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol cols="4">
|
<VCol cols="4">
|
||||||
<VSelect
|
<VSelect
|
||||||
v-model="props.directory.library_storage"
|
v-model="props.directory.library_storage"
|
||||||
@@ -285,8 +302,16 @@ watch(
|
|||||||
<VCol cols="6">
|
<VCol cols="6">
|
||||||
<VSwitch v-model="props.directory.scraping" label="刮削元数据"></VSwitch>
|
<VSwitch v-model="props.directory.scraping" label="刮削元数据"></VSwitch>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol cols="6">
|
||||||
|
<VSwitch v-model="props.directory.notify" label="发送通知"></VSwitch>
|
||||||
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
<VCardActions class="text-center py-0">
|
||||||
|
<VSpacer />
|
||||||
|
<VBtn :icon="isCollapsed ? 'mdi-chevron-down' : 'mdi-chevron-up'" @click.stop="isCollapsed = !isCollapsed" />
|
||||||
|
<VSpacer />
|
||||||
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -209,6 +209,11 @@ function onClose() {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard variant="tonal" @click="opengroupInfoDialog">
|
<VCard variant="tonal" @click="opengroupInfoDialog">
|
||||||
|
<span class="absolute top-3 right-12">
|
||||||
|
<IconBtn>
|
||||||
|
<VIcon class="cursor-move" icon="mdi-drag" />
|
||||||
|
</IconBtn>
|
||||||
|
</span>
|
||||||
<DialogCloseBtn @click="onClose" />
|
<DialogCloseBtn @click="onClose" />
|
||||||
<VCardText class="flex justify-space-between align-center gap-3">
|
<VCardText class="flex justify-space-between align-center gap-3">
|
||||||
<div class="align-self-start">
|
<div class="align-self-start">
|
||||||
|
|||||||
@@ -117,6 +117,11 @@ function onClose() {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard variant="tonal" @click="openNotificationInfoDialog">
|
<VCard variant="tonal" @click="openNotificationInfoDialog">
|
||||||
|
<span class="absolute top-3 right-12">
|
||||||
|
<IconBtn>
|
||||||
|
<VIcon class="cursor-move" icon="mdi-drag" />
|
||||||
|
</IconBtn>
|
||||||
|
</span>
|
||||||
<DialogCloseBtn @click="onClose" />
|
<DialogCloseBtn @click="onClose" />
|
||||||
<VCardText class="flex justify-space-between align-center gap-3">
|
<VCardText class="flex justify-space-between align-center gap-3">
|
||||||
<div class="align-self-start">
|
<div class="align-self-start">
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ async function forkSubscribe() {
|
|||||||
</template>
|
</template>
|
||||||
</VImg>
|
</VImg>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-center pl-2 xl:pl-4 line-clamp-2 overflow-hidden text-ellipsis ...">
|
<div class="flex flex-col justify-center pl-2 xl:pl-4">
|
||||||
<div class="mr-2 min-w-0 text-lg font-bold text-white">
|
<div class="mr-2 min-w-0 text-lg font-bold text-white line-clamp-2 overflow-hidden text-ellipsis ...">
|
||||||
{{ props.media?.share_title }}
|
{{ props.media?.share_title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm font-medium text-gray-200 sm:pt-1 line-clamp-3 overflow-hidden text-ellipsis ...">
|
<div class="text-sm font-medium text-gray-200 sm:pt-1 line-clamp-3 overflow-hidden text-ellipsis ...">
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 当前用户名称
|
// 当前用户的ID
|
||||||
const currentLoginUser = store.state.auth.userName
|
const currentLoginUserId = computed(() => store.state.auth.userID)
|
||||||
|
|
||||||
|
// 当前用户是否是管理员
|
||||||
|
const currentUserIsSuperuser = computed(() => store.state.auth.superUser)
|
||||||
|
|
||||||
// 定义触发的自定义事件
|
// 定义触发的自定义事件
|
||||||
const emit = defineEmits(['remove', 'save'])
|
const emit = defineEmits(['remove', 'save'])
|
||||||
@@ -57,7 +60,7 @@ async function fetchSubscriptions() {
|
|||||||
|
|
||||||
// 删除用户
|
// 删除用户
|
||||||
async function removeUser() {
|
async function removeUser() {
|
||||||
if (props.user.name == currentLoginUser) {
|
if (props.user.id === currentLoginUserId.value) {
|
||||||
$toast.error('不能删除当前登录用户!')
|
$toast.error('不能删除当前登录用户!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -84,19 +87,6 @@ function editUser() {
|
|||||||
userEditDialog.value = true
|
userEditDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算是否有用户编辑权限
|
|
||||||
const canEditUser = computed(() => {
|
|
||||||
if (store.state.auth.superUser && props.user.name !== currentLoginUser) return true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
// 计算是否有用户管理权限
|
|
||||||
const canManageUser = computed(() => {
|
|
||||||
if (props.user.name == currentLoginUser) return false
|
|
||||||
if (store.state.auth.superUser) return true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
// 用户重新完成时
|
// 用户重新完成时
|
||||||
function onUserUpdate() {
|
function onUserUpdate() {
|
||||||
userEditDialog.value = false
|
userEditDialog.value = false
|
||||||
@@ -139,8 +129,9 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText class="pb-6">
|
<VCardText class="pb-6">
|
||||||
<h5 class="text-h6">详情</h5>
|
<VDivider class="my-2">
|
||||||
<VDivider class="my-2" />
|
<h5 class="text-h6">详情</h5>
|
||||||
|
</VDivider>
|
||||||
<VList lines="one">
|
<VList lines="one">
|
||||||
<VListItem>
|
<VListItem>
|
||||||
<VListItemTitle class="text-sm">
|
<VListItemTitle class="text-sm">
|
||||||
@@ -170,8 +161,22 @@ onMounted(() => {
|
|||||||
</VList>
|
</VList>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VCardText class="flex flex-row justify-center">
|
<VCardText class="flex flex-row justify-center">
|
||||||
<VBtn v-if="canEditUser" color="primary" class="me-4" @click="editUser">编辑</VBtn>
|
<VBtn
|
||||||
<VBtn v-if="canManageUser" color="error" variant="outlined" @click="removeUser"> 删除 </VBtn>
|
v-if="currentUserIsSuperuser"
|
||||||
|
color="primary"
|
||||||
|
class="me-4"
|
||||||
|
@click="editUser"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</VBtn>
|
||||||
|
<VBtn
|
||||||
|
v-if="currentUserIsSuperuser && props.user.id != currentLoginUserId"
|
||||||
|
color="error"
|
||||||
|
variant="outlined"
|
||||||
|
@click="removeUser"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</VBtn>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
<!-- 用户编辑弹窗 -->
|
<!-- 用户编辑弹窗 -->
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const systemMenus = ref<NavMenu[]>([])
|
|||||||
|
|
||||||
// 根据分类获取菜单列表
|
// 根据分类获取菜单列表
|
||||||
const getMenuList = (header: string) => {
|
const getMenuList = (header: string) => {
|
||||||
return SystemNavMenus.filter((item: NavMenu) => item.header === header && superUser)
|
return SystemNavMenus.filter((item: NavMenu) => item.header === header && (superUser || !item.admin))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回上一页
|
// 返回上一页
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ onMounted(() => {
|
|||||||
<VAvatar size="48" variant="tonal">
|
<VAvatar size="48" variant="tonal">
|
||||||
<VIcon icon="mdi-filter-cog-outline" />
|
<VIcon icon="mdi-filter-cog-outline" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
<h6 class="text-base font-weight-medium mt-2 mb-0">优先级</h6>
|
<h6 class="text-base font-weight-medium mt-2 mb-0">规则</h6>
|
||||||
<span class="text-sm">优先级规则测试</span>
|
<span class="text-sm">规则测试</span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -241,7 +241,7 @@ onMounted(() => {
|
|||||||
</VDialog>
|
</VDialog>
|
||||||
<!-- 规则测试弹窗 -->
|
<!-- 规则测试弹窗 -->
|
||||||
<VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="50rem" scrollable>
|
<VDialog v-if="ruleTestDialog" v-model="ruleTestDialog" max-width="50rem" scrollable>
|
||||||
<VCard title="优先级测试">
|
<VCard title="规则测试">
|
||||||
<DialogCloseBtn @click="ruleTestDialog = false" />
|
<DialogCloseBtn @click="ruleTestDialog = false" />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<RuleTestView />
|
<RuleTestView />
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import router from '@/router'
|
|||||||
import avatar1 from '@images/avatars/avatar-1.png'
|
import avatar1 from '@images/avatars/avatar-1.png'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
|
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
|
||||||
import { useDisplay } from 'vuetify'
|
|
||||||
|
|
||||||
const display = useDisplay()
|
|
||||||
|
|
||||||
// Vuex Store
|
// Vuex Store
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import '@/@core/utils/compatibility'
|
import '@/@core/utils/compatibility'
|
||||||
import './ace-config'
|
|
||||||
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 { VAceEditor } from 'vue3-ace-editor'
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { removeEl } from './@core/utils/dom'
|
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 { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
|
||||||
import { VTreeview } from 'vuetify/labs/VTreeview'
|
import { VTreeview } from 'vuetify/labs/VTreeview'
|
||||||
import ToastPlugin from 'vue-toast-notification'
|
import ToastPlugin from 'vue-toast-notification'
|
||||||
@@ -23,8 +25,6 @@ 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 { 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'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const appOrder = ref<string[]>([])
|
|||||||
|
|
||||||
// 根据分类获取菜单列表
|
// 根据分类获取菜单列表
|
||||||
const getMenuList = () => {
|
const getMenuList = () => {
|
||||||
return SystemNavMenus.filter((item: NavMenu) => !item.admin || superUser)
|
return SystemNavMenus.filter((item: NavMenu) => (!item.admin || superUser) && !item.footer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// APP列表
|
// APP列表
|
||||||
@@ -48,7 +48,7 @@ onMounted(() => {
|
|||||||
:component-data="{ 'class': 'ma-0 mt-n1' }"
|
:component-data="{ 'class': 'ma-0 mt-n1' }"
|
||||||
>
|
>
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<VCol cols="6" md="4" lg="3" class="text-center cursor-pointer shortcut-icon select-none">
|
<VCol cols="6" md="3" lg="2" class="text-center cursor-pointer shortcut-icon select-none">
|
||||||
<VCard class="pa-4" :to="element.to" variant="flat">
|
<VCard class="pa-4" :to="element.to" variant="flat">
|
||||||
<VAvatar size="64" variant="text">
|
<VAvatar size="64" variant="text">
|
||||||
<VIcon size="48" :icon="element.icon" color="primary" />
|
<VIcon size="48" :icon="element.icon" color="primary" />
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ const { global: globalTheme } = useTheme()
|
|||||||
// Vuex Store
|
// Vuex Store
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
||||||
// 从 provide 中获取全局设置
|
|
||||||
const globalSettings: any = inject('globalSettings')
|
|
||||||
|
|
||||||
// 表单
|
// 表单
|
||||||
const form = ref({
|
const form = ref({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -55,15 +52,8 @@ let intervalTimer: NodeJS.Timeout | null = null
|
|||||||
// 获取背景图片
|
// 获取背景图片
|
||||||
async function fetchBackgroundImage() {
|
async function fetchBackgroundImage() {
|
||||||
try {
|
try {
|
||||||
const results: string[] = await api.get('/login/wallpapers')
|
backgroundImages.value = await api.get('/login/wallpapers')
|
||||||
if (results && results.length > 0) {
|
if (backgroundImages.value && backgroundImages.value.length > 0) {
|
||||||
results.map((url: string) => {
|
|
||||||
if (globalSettings.GLOBAL_IMAGE_CACHE)
|
|
||||||
backgroundImages.value.push(
|
|
||||||
`${import.meta.env.VITE_API_BASE_URL}system/cache/image?url=${encodeURIComponent(url)}`,
|
|
||||||
)
|
|
||||||
else backgroundImages.value.push(url)
|
|
||||||
})
|
|
||||||
// 随机打乱排序
|
// 随机打乱排序
|
||||||
backgroundImages.value.sort(() => Math.random() - 0.5)
|
backgroundImages.value.sort(() => Math.random() - 0.5)
|
||||||
backgroundImageUrl.value = backgroundImages.value[0]
|
backgroundImageUrl.value = backgroundImages.value[0]
|
||||||
@@ -171,6 +161,7 @@ function login() {
|
|||||||
// 获取token
|
// 获取token
|
||||||
const token = response.access_token
|
const token = response.access_token
|
||||||
const superUser = response.super_user
|
const superUser = response.super_user
|
||||||
|
const userID = response.user_id
|
||||||
const userName = response.user_name
|
const userName = response.user_name
|
||||||
const avatar = response.avatar
|
const avatar = response.avatar
|
||||||
const level = response.level
|
const level = response.level
|
||||||
@@ -178,7 +169,16 @@ function login() {
|
|||||||
const permissions = response.permissions
|
const permissions = response.permissions
|
||||||
|
|
||||||
// 更新token和remember状态到Vuex Store
|
// 更新token和remember状态到Vuex Store
|
||||||
store.dispatch('auth/login', { token, remember, superUser, userName, avatar, level, permissions })
|
store.dispatch('auth/login', {
|
||||||
|
token,
|
||||||
|
remember,
|
||||||
|
superUser,
|
||||||
|
userID,
|
||||||
|
userName,
|
||||||
|
avatar,
|
||||||
|
level,
|
||||||
|
permissions,
|
||||||
|
})
|
||||||
|
|
||||||
// 登录后处理
|
// 登录后处理
|
||||||
afterLogin(superUser)
|
afterLogin(superUser)
|
||||||
|
|||||||
@@ -5,21 +5,23 @@ export const SystemNavMenus = [
|
|||||||
icon: 'mdi-home-outline',
|
icon: 'mdi-home-outline',
|
||||||
to: '/dashboard',
|
to: '/dashboard',
|
||||||
header: '开始',
|
header: '开始',
|
||||||
permission: 'dashboard',
|
admin: false,
|
||||||
|
footer: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '推荐',
|
title: '推荐',
|
||||||
icon: 'mdi-star-outline',
|
icon: 'mdi-star-outline',
|
||||||
to: '/ranking',
|
to: '/ranking',
|
||||||
header: '发现',
|
header: '发现',
|
||||||
permission: 'ranking',
|
admin: false,
|
||||||
|
footer: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '资源搜索',
|
title: '资源搜索',
|
||||||
icon: 'mdi-magnify',
|
icon: 'mdi-magnify',
|
||||||
to: '/resource',
|
to: '/resource',
|
||||||
header: '发现',
|
header: '发现',
|
||||||
permission: 'resource.search',
|
admin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '电影',
|
title: '电影',
|
||||||
@@ -27,7 +29,8 @@ export const SystemNavMenus = [
|
|||||||
icon: 'mdi-movie-open-outline',
|
icon: 'mdi-movie-open-outline',
|
||||||
to: '/subscribe/movie',
|
to: '/subscribe/movie',
|
||||||
header: '订阅',
|
header: '订阅',
|
||||||
permission: 'subscribe.movie',
|
admin: false,
|
||||||
|
footer: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '电视剧',
|
title: '电视剧',
|
||||||
@@ -35,7 +38,8 @@ export const SystemNavMenus = [
|
|||||||
icon: 'mdi-television',
|
icon: 'mdi-television',
|
||||||
to: '/subscribe/tv',
|
to: '/subscribe/tv',
|
||||||
header: '订阅',
|
header: '订阅',
|
||||||
permission: 'subscribe.tv',
|
admin: false,
|
||||||
|
footer: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '日历',
|
title: '日历',
|
||||||
@@ -43,56 +47,56 @@ export const SystemNavMenus = [
|
|||||||
icon: 'mdi-calendar',
|
icon: 'mdi-calendar',
|
||||||
to: '/calendar',
|
to: '/calendar',
|
||||||
header: '订阅',
|
header: '订阅',
|
||||||
permission: 'subscribe.calendar',
|
admin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '正在下载',
|
title: '正在下载',
|
||||||
icon: 'mdi-download-outline',
|
icon: 'mdi-download-outline',
|
||||||
to: '/downloading',
|
to: '/downloading',
|
||||||
header: '整理',
|
header: '整理',
|
||||||
permission: 'downloading.view',
|
admin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '历史记录',
|
title: '历史记录',
|
||||||
icon: 'mdi-history',
|
icon: 'mdi-history',
|
||||||
to: '/history',
|
to: '/history',
|
||||||
header: '整理',
|
header: '整理',
|
||||||
permission: 'admin',
|
admin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '文件管理',
|
title: '文件管理',
|
||||||
icon: 'mdi-folder-multiple-outline',
|
icon: 'mdi-folder-multiple-outline',
|
||||||
to: '/filemanager',
|
to: '/filemanager',
|
||||||
header: '整理',
|
header: '整理',
|
||||||
permission: 'admin',
|
admin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '插件',
|
title: '插件',
|
||||||
icon: 'mdi-apps',
|
icon: 'mdi-apps',
|
||||||
to: '/plugins',
|
to: '/plugins',
|
||||||
header: '系统',
|
header: '系统',
|
||||||
permission: 'admin',
|
admin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '站点管理',
|
title: '站点管理',
|
||||||
icon: 'mdi-web',
|
icon: 'mdi-web',
|
||||||
to: '/site',
|
to: '/site',
|
||||||
header: '系统',
|
header: '系统',
|
||||||
permission: 'admin',
|
admin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '用户管理',
|
title: '用户管理',
|
||||||
icon: 'mdi-account-group',
|
icon: 'mdi-account-group',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
header: '系统',
|
header: '系统',
|
||||||
permission: 'usermanage',
|
admin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '设定',
|
title: '设定',
|
||||||
icon: 'mdi-cog',
|
icon: 'mdi-cog',
|
||||||
to: '/setting',
|
to: '/setting',
|
||||||
header: '系统',
|
header: '系统',
|
||||||
permission: 'admin',
|
admin: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ interface AuthState {
|
|||||||
token: string | null
|
token: string | null
|
||||||
remember: boolean
|
remember: boolean
|
||||||
superUser: boolean
|
superUser: boolean
|
||||||
|
userID: number
|
||||||
userName: string
|
userName: string
|
||||||
avatar: string
|
avatar: string
|
||||||
originalPath: string | null
|
originalPath: string | null
|
||||||
@@ -24,6 +25,7 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
token: null, // 用户令牌
|
token: null, // 用户令牌
|
||||||
remember: false, // 记住我
|
remember: false, // 记住我
|
||||||
superUser: false, // 超级管理员
|
superUser: false, // 超级管理员
|
||||||
|
userID: 999, // 用户ID
|
||||||
userName: '', // 用户名
|
userName: '', // 用户名
|
||||||
avatar: '', // 头像
|
avatar: '', // 头像
|
||||||
originalPath: null, // 原始路径
|
originalPath: null, // 原始路径
|
||||||
@@ -43,6 +45,9 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
setSuperUser(state, superUser: boolean) {
|
setSuperUser(state, superUser: boolean) {
|
||||||
state.superUser = superUser
|
state.superUser = superUser
|
||||||
},
|
},
|
||||||
|
setUserID(state, userID: number) {
|
||||||
|
state.userID = userID
|
||||||
|
},
|
||||||
setUserName(state, userName: string) {
|
setUserName(state, userName: string) {
|
||||||
state.userName = userName
|
state.userName = userName
|
||||||
},
|
},
|
||||||
@@ -60,10 +65,11 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
login({ commit }, { token, remember, superUser, userName, avatar, level, permissions }) {
|
login({ commit }, { token, remember, superUser, userID, userName, avatar, level, permissions }) {
|
||||||
commit('setToken', token)
|
commit('setToken', token)
|
||||||
commit('setRemember', remember)
|
commit('setRemember', remember)
|
||||||
commit('setSuperUser', superUser)
|
commit('setSuperUser', superUser)
|
||||||
|
commit('setUserID', userID)
|
||||||
commit('setUserName', userName)
|
commit('setUserName', userName)
|
||||||
commit('setAvatar', avatar)
|
commit('setAvatar', avatar)
|
||||||
commit('setLevel', level)
|
commit('setLevel', level)
|
||||||
@@ -78,6 +84,7 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
getToken: state => state.token,
|
getToken: state => state.token,
|
||||||
getRemember: state => state.remember,
|
getRemember: state => state.remember,
|
||||||
getSuperUser: state => state.superUser,
|
getSuperUser: state => state.superUser,
|
||||||
|
getUserID: state => state.userID,
|
||||||
getUserName: state => state.userName,
|
getUserName: state => state.userName,
|
||||||
getAvatar: state => state.avatar,
|
getAvatar: state => state.avatar,
|
||||||
getOriginalPath: state => state.originalPath,
|
getOriginalPath: state => state.originalPath,
|
||||||
|
|||||||
@@ -166,7 +166,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid-directory-card {
|
.grid-directory-card {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||||
padding-block-end: 1rem;
|
padding-block-end: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ async function loadLatest(server: string) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadMediaServerSetting()
|
await loadMediaServerSetting()
|
||||||
for (const server of mediaServers.value) {
|
const enabledServers = mediaServers.value.filter(server => server.enabled)
|
||||||
|
for (const server of enabledServers) {
|
||||||
loadLatest(server.name)
|
loadLatest(server.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ async function loadLibrary(server: string) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadMediaServerSetting()
|
await loadMediaServerSetting()
|
||||||
for (const server of mediaServers.value) {
|
const enabledServers = mediaServers.value.filter(server => server.enabled)
|
||||||
|
for (const server of enabledServers) {
|
||||||
loadLibrary(server.name)
|
loadLibrary(server.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ async function loadPlayingList(server: string) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadMediaServerSetting()
|
await loadMediaServerSetting()
|
||||||
for (const server of mediaServers.value) {
|
const enabledServers = mediaServers.value.filter(server => server.enabled)
|
||||||
|
for (const server of enabledServers) {
|
||||||
loadPlayingList(server.name)
|
loadPlayingList(server.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,15 +2,16 @@
|
|||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { requiredValidator } from '@/@validators'
|
import { requiredValidator } from '@/@validators'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import { FilterRuleGroup } from '@/api/types'
|
||||||
|
|
||||||
// 识别结果
|
// 识别结果
|
||||||
const ruleTestResult = ref('')
|
const ruleTestResult = ref('')
|
||||||
|
|
||||||
// 名称识别表单
|
// 名称识别表单
|
||||||
const ruleTestForm = reactive({
|
const ruleTestForm = reactive({
|
||||||
title: '',
|
title: null,
|
||||||
subtitle: '',
|
subtitle: null,
|
||||||
ruletype: '1',
|
rulegroup: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 识别按钮状态
|
// 识别按钮状态
|
||||||
@@ -22,10 +23,27 @@ const ruleTestText = ref('测试')
|
|||||||
// 是否显示结果
|
// 是否显示结果
|
||||||
const showResult = ref(false)
|
const showResult = ref(false)
|
||||||
|
|
||||||
|
// 所有规则组列表
|
||||||
|
const filterRuleGroups = ref<FilterRuleGroup[]>([])
|
||||||
|
|
||||||
|
// 规则组选项
|
||||||
|
const filterRuleGroupItems = computed(() => {
|
||||||
|
return filterRuleGroups.value.map(item => ({ title: item.name, value: item.name }))
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载规则组
|
||||||
|
async function queryFilterRuleGroups() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/setting/UserFilterRuleGroups')
|
||||||
|
filterRuleGroups.value = result.data?.value ?? []
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 调用API识别
|
// 调用API识别
|
||||||
async function ruleTest() {
|
async function ruleTest() {
|
||||||
if (!ruleTestForm.title)
|
if (!ruleTestForm.title) return
|
||||||
return
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ruleTestLoading.value = true
|
ruleTestLoading.value = true
|
||||||
@@ -35,69 +53,41 @@ async function ruleTest() {
|
|||||||
params: {
|
params: {
|
||||||
title: ruleTestForm.title,
|
title: ruleTestForm.title,
|
||||||
subtitle: ruleTestForm.subtitle,
|
subtitle: ruleTestForm.subtitle,
|
||||||
ruletype: ruleTestForm.ruletype,
|
rulegroup_name: ruleTestForm.rulegroup,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (result.success)
|
if (result.success) ruleTestResult.value = `优先级:${result.data.priority}`
|
||||||
ruleTestResult.value = `优先级:${result.data.priority}`
|
else ruleTestResult.value = '未命中任何优先级规则!'
|
||||||
|
|
||||||
else
|
|
||||||
ruleTestResult.value = '未命中任何优先级规则!'
|
|
||||||
|
|
||||||
ruleTestLoading.value = false
|
ruleTestLoading.value = false
|
||||||
ruleTestText.value = '重新测试'
|
ruleTestText.value = '重新测试'
|
||||||
showResult.value = true
|
showResult.value = true
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
queryFilterRuleGroups()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VForm @submit.prevent="() => {}">
|
<VForm @submit.prevent="() => {}">
|
||||||
<VRow class="pt-2">
|
<VRow class="pt-2">
|
||||||
<VCol cols="12" md="8">
|
<VCol cols="12" md="8">
|
||||||
<VTextField
|
<VTextField v-model="ruleTestForm.title" label="标题" :rules="[requiredValidator]" />
|
||||||
v-model="ruleTestForm.title"
|
|
||||||
label="标题"
|
|
||||||
:rules="[requiredValidator]"
|
|
||||||
/>
|
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="4">
|
<VCol cols="12" md="4">
|
||||||
<VSelect
|
<VSelect v-model="ruleTestForm.rulegroup" label="规则组" :items="filterRuleGroupItems" />
|
||||||
v-model="ruleTestForm.ruletype"
|
|
||||||
label="规则类型"
|
|
||||||
:items="[{
|
|
||||||
title: '订阅优先级',
|
|
||||||
value: '1',
|
|
||||||
}, {
|
|
||||||
title: '洗版优先级',
|
|
||||||
value: '2',
|
|
||||||
}, {
|
|
||||||
title: '搜索优先级',
|
|
||||||
value: '3',
|
|
||||||
}]"
|
|
||||||
/>
|
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VTextarea
|
<VTextarea v-model="ruleTestForm.subtitle" label="副标题" rows="2" auto-grow />
|
||||||
v-model="ruleTestForm.subtitle"
|
|
||||||
label="副标题"
|
|
||||||
rows="2"
|
|
||||||
auto-grow
|
|
||||||
/>
|
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol
|
<VCol cols="12" class="text-center">
|
||||||
cols="12"
|
<VBtn :disabled="ruleTestLoading" @click="ruleTest">
|
||||||
class="text-center"
|
|
||||||
>
|
|
||||||
<VBtn
|
|
||||||
:disabled="ruleTestLoading"
|
|
||||||
@click="ruleTest"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VIcon icon="mdi-filter-check-outline" />
|
<VIcon icon="mdi-filter-check-outline" />
|
||||||
</template>
|
</template>
|
||||||
@@ -109,9 +99,7 @@ async function ruleTest() {
|
|||||||
<VExpandTransition>
|
<VExpandTransition>
|
||||||
<div v-show="showResult">
|
<div v-show="showResult">
|
||||||
<VCol>
|
<VCol>
|
||||||
<VAlert
|
<VAlert icon="mdi-alert-circle-outline">
|
||||||
icon="mdi-alert-circle-outline"
|
|
||||||
>
|
|
||||||
{{ ruleTestResult }}
|
{{ ruleTestResult }}
|
||||||
</VAlert>
|
</VAlert>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|||||||
Reference in New Issue
Block a user