Merge remote-tracking branch 'upstream/main'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
This commit is contained in:
hao.dai
2024-04-24 15:43:56 +08:00
51 changed files with 770 additions and 528 deletions

View File

@@ -52,70 +52,63 @@ async function fetchData({ done }: { done: any }) {
// 如果正在加载中,直接返回
if (loading.value) {
done('ok')
return
}
// 设置加载中
loading.value = true
// 加载到满屏或者加载出错
if (!hasScroll()) {
// 加载多次
while (!hasScroll()) {
// 设置加载中
loading.value = true
// 请求API
currData.value = await api.get(props.apipath, {
params: getParams(),
})
// 取消加载中
loading.value = false
// 标计为已请求完成
isRefreshed.value = true
if (currData.value.length === 0) {
// 如果没有数据,跳出
done('ok')
done('empty')
return
}
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
// 返回加载成功
done('ok')
}
}
else {
// 加载一次
// 设置加载中
loading.value = true
// 请求API
currData.value = await api.get(props.apipath, {
params: getParams(),
})
// 标计为已请求完成
isRefreshed.value = true
if (currData.value.length === 0) {
// 如果没有数据,跳出
done('empty')
} else {
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
// 返回加载成功
done('ok')
return
}
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
}
// 取消加载中
loading.value = false
// 返回加载成功
done('ok')
}
catch (error) {
console.error(error)
// 返回加载失败
done('error')
}
@@ -123,16 +116,10 @@ async function fetchData({ done }: { done: any }) {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<VInfiniteScroll
mode="intersect"
side="end"
@@ -141,6 +128,7 @@ async function fetchData({ done }: { done: any }) {
@load="fetchData"
>
<template #loading />
<template #empty />
<div
v-if="dataList.length > 0"
class="grid gap-4 grid-media-card mx-3"

View File

@@ -8,7 +8,7 @@ import NoDataFound from '@/components/NoDataFound.vue'
import { doneNProgress, startNProgress } from '@/api/nprogress'
import { formatSeason } from '@/@core/utils/formatters'
import router from '@/router'
import SubscribeEditForm from '@/components/form/SubscribeEditForm.vue'
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
// 输入参数
const mediaProps = defineProps({
@@ -46,11 +46,6 @@ const seasonsSubscribed = ref<{ [key: number]: boolean }>({})
// 订阅编号
const subscribeId = ref<number>()
// 订阅规则
const subscribeRules = ref({
show_edit_dialog: false,
})
// 获得mediaid
function getMediaId() {
return mediaDetail.value?.tmdb_id
@@ -230,9 +225,12 @@ async function addSubscribe(season = 0) {
)
// 显示编辑弹窗
if (result.success && subscribeRules.value.show_edit_dialog) {
subscribeId.value = result.data.id
subscribeEditDialog.value = true
if (result.success) {
const show_edit_dialog = await queryDefaultSubscribeConfig()
if (show_edit_dialog) {
subscribeId.value = result.data.id
subscribeEditDialog.value = true
}
}
}
catch (error) {
@@ -290,20 +288,6 @@ async function removeSubscribe(season: number) {
doneNProgress()
}
// 查询订阅弹窗规则
async function querySubscribeRules() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/DefaultFilterRules',
)
if (result.data?.value)
subscribeRules.value = result.data?.value
}
catch (error) {
console.log(error)
}
}
// 订阅按钮响应
function handleSubscribe(season = 0) {
if (isSubscribed.value)
@@ -450,23 +434,35 @@ async function handlePlay() {
}
}
async function queryDefaultSubscribeConfig() {
try {
let subscribe_config_url = ''
if (mediaProps.type === '电影')
subscribe_config_url = 'system/setting/DefaultMovieSubscribeConfig'
else
subscribe_config_url = 'system/setting/DefaultTvSubscribeConfig'
const result: { [key: string]: any } = await api.get(subscribe_config_url)
if (result.data?.value)
return result.data.value.show_edit_dialog
}
catch (error) {
console.log(error)
}
return false
}
onBeforeMount(() => {
getMediaDetail()
querySubscribeRules()
})
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<div v-if="mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id" class="max-w-8xl mx-auto px-4">
<template v-if="mediaDetail.backdrop_path || mediaDetail.poster_path">
<div class="vue-media-back absolute left-0 top-0 w-full h-96">
@@ -638,16 +634,10 @@ onBeforeMount(() => {
</VExpansionPanelTitle>
<VExpansionPanelText>
<template #default>
<div
<LoadingBanner
v-if="!seasonEpisodesInfo[season.season_number || 0]"
class="mt-3 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-3"
/>
<div class="flex flex-col justify-center divide-y divide-gray-700">
<div v-for="episode in seasonEpisodesInfo[season.season_number || 0]" :key="episode.episode_number" class="flex flex-col space-y-4 py-4 xl:flex-row xl:space-y-4 xl:space-x-4">
<div class="flex-1">
@@ -849,7 +839,7 @@ onBeforeMount(() => {
error-description="未识别到媒体信息"
/>
<!-- 订阅编辑弹窗 -->
<SubscribeEditForm
<SubscribeEditDialog
v-model="subscribeEditDialog"
:subid="subscribeId"
@close="subscribeEditDialog = false"

View File

@@ -42,74 +42,67 @@ async function fetchData({ done }: { done: any }) {
// 如果正在加载中,直接返回
if (loading.value) {
done('ok')
return
}
// 设置加载中
loading.value = true
// 加载到满屏或者加载出错
if (!hasScroll()) {
// 加载多次
while (!hasScroll()) {
// 设置加载中
loading.value = true
// 请求API
currData.value = await api.get(props.apipath, {
params: {
page: page.value,
},
})
// 取消加载中
loading.value = false
// 标计为已请求完成
isRefreshed.value = true
if (currData.value.length === 0) {
// 如果没有数据,跳出
done('empty')
} else {
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
// 返回加载成功
done('ok')
return
}
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
}
}
else {
// 加载一次
// 设置加载中
loading.value = true
// 请求API
currData.value = await api.get(props.apipath, {
params: {
page: page.value,
},
})
// 标计为已请求完成
isRefreshed.value = true
if (currData.value.length === 0) {
// 如果没有数据,跳出
done('empty')
} else {
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
// 返回加载成功
done('ok')
return
}
// 合并数据
dataList.value = [...dataList.value, ...currData.value]
// 页码+1
page.value++
// 取消加载中
loading.value = false
}
// 取消加载中
loading.value = false
// 返回加载成功
done('ok')
}
catch (error) {
console.error(error)
// 返回加载失败
done('error')
}
@@ -117,16 +110,10 @@ async function fetchData({ done }: { done: any }) {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<VInfiniteScroll
mode="intersect"
side="end"
@@ -135,6 +122,7 @@ async function fetchData({ done }: { done: any }) {
@load="fetchData"
>
<template #loading />
<template #empty />
<div
v-if="dataList.length > 0 && props.type === 'tmdb'"
class="grid gap-4 grid-media-card mx-3"

View File

@@ -48,16 +48,10 @@ onBeforeMount(() => {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<div v-if="personDetail.id" class="max-w-8xl mx-auto px-4">
<div class="relative z-10 mt-4 mb-8 flex flex-col items-center lg:flex-row ">
<VAvatar

View File

@@ -117,7 +117,7 @@ function pluginIcon(item: Plugin) {
return noImage
// 如果是网络图片则使用代理后返回
if (item?.plugin_icon?.startsWith('http'))
return `${import.meta.env.VITE_API_BASE_URL}system/img/1/${encodeURIComponent(item?.plugin_icon).replace(/%2F/g, '/')}`
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(item?.plugin_icon)}`
return `./plugin_icon/${item?.plugin_icon}`
}
@@ -211,17 +211,10 @@ onBeforeMount(() => {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isRefreshed"
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<div
v-if="dataList.length > 0"
class="grid gap-4 grid-plugin-card"
@@ -285,17 +278,10 @@ onBeforeMount(() => {
</VToolbar>
</div>
<VCardText>
<div
<LoadingBanner
v-if="!isAppMarketLoaded"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isAppMarketLoaded"
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card">
<PluginAppCard
v-for="data in sortedUninstalledList"

View File

@@ -67,17 +67,10 @@ onUnmounted(() => {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isRefreshed"
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<PullRefresh
v-model="loading"
@refresh="onRefresh"

View File

@@ -3,7 +3,7 @@ import { ref, unref } from 'vue'
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import type { TransferHistory } from '@/api/types'
import ReorganizeForm from '@/components/form/ReorganizeForm.vue'
import ReorganizeDialog from '@/components/dialog/ReorganizeDialog.vue'
// 提示框
const $toast = useToast()
@@ -378,9 +378,12 @@ onMounted(fetchData)
<VIcon :icon="getIcon(item.type || '')" />
</VAvatar>
<div class="d-flex flex-column ms-1">
<span class="d-block text-high-emphasis min-w-20">
<span v-if="item.type === '电视剧'" class="d-block text-high-emphasis min-w-20">
{{ item?.title }} {{ item?.seasons }}{{ item?.episodes }}
</span>
<span v-else class="d-block text-high-emphasis min-w-20">
{{ item?.title }}
</span>
<small>{{ item?.category }}</small>
</div>
</div>
@@ -479,7 +482,7 @@ onMounted(fetchData)
</VCard>
</VBottomSheet>
<!-- 文件整理弹窗 -->
<ReorganizeForm
<ReorganizeDialog
v-if="redoDialog"
v-model="redoDialog"
:logids="redoIds"
@@ -534,7 +537,7 @@ onMounted(fetchData)
@media (width <= 768px) {
.data-table-div {
block-size: calc(100vh - 16rem);
block-size: calc(100vh - 17rem);
}
}
</style>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { calculateTimeDifference } from '@/@core/utils'
import { formatDateDifference } from '@/@core/utils/formatters'
import api from '@/api'
// 系统环境变量
@@ -62,7 +62,7 @@ async function queryAllRelease() {
// 计算发布时间
function releaseTime(releaseDate: string) {
// 上一次更新时间
return `${calculateTimeDifference(releaseDate)}`
return formatDateDifference(releaseDate)
}
onMounted(() => {

View File

@@ -4,7 +4,7 @@ import api from '@/api'
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
import type { Site } from '@/api/types'
import { copyToClipboard } from '@/@core/utils/navigator'
import ImportCodeForm from '@/components/form/ImportCodeForm.vue'
import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue'
// 规则卡片类型
interface FilterCard {
@@ -420,7 +420,7 @@ onMounted(() => {
width="60rem"
scrollable
>
<ImportCodeForm
<ImportCodeDialog
v-model="importCodeString"
title="导入优先级规则"
@close="importCodeDialog = false"

View File

@@ -4,7 +4,7 @@ import api from '@/api'
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
import type { Site } from '@/api/types'
import { copyToClipboard } from '@/@core/utils/navigator'
import ImportCodeForm from '@/components/form/ImportCodeForm.vue'
import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue'
// 规则卡片类型
interface FilterCard {
@@ -41,8 +41,7 @@ const defaultFilterRules = ref({
exclude: '',
movie_size: '',
tv_size: '',
min_seeders: 0,
show_edit_dialog: false,
min_seeders: 0
})
// 订阅模式选择项
@@ -622,13 +621,6 @@ onMounted(() => {
hint="小于该值的资源将被过滤掉0表示不过滤"
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="defaultFilterRules.show_edit_dialog"
label="订阅时编辑更多规则"
hint="开启后,添加订阅时将自动弹出订阅编辑框,要设置更多订阅选项"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
@@ -648,7 +640,7 @@ onMounted(() => {
width="60rem"
scrollable
>
<ImportCodeForm
<ImportCodeDialog
v-model="importCodeString"
title="导入优先级规则"
@close="importCodeDialog = false"

View File

@@ -3,7 +3,7 @@ import api from '@/api'
import type { Site } from '@/api/types'
import SiteCard from '@/components/cards/SiteCard.vue'
import NoDataFound from '@/components/NoDataFound.vue'
import SiteAddEditForm from '@/components/form/SiteAddEditForm.vue'
import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import { useDefer } from '@/@core/utils/dom'
// 数据列表
@@ -35,17 +35,10 @@ onBeforeMount(fetchData)
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isRefreshed"
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<div
v-if="dataList.length > 0"
class="grid gap-3 grid-site-card"
@@ -80,7 +73,7 @@ onBeforeMount(fetchData)
@click="siteAddDialog = true"
/>
<!-- 新增站点弹窗 -->
<SiteAddEditForm
<SiteAddEditDialog
v-if="siteAddDialog"
v-model="siteAddDialog"
oper="add"

View File

@@ -155,7 +155,7 @@ onMounted(() => {
</VCard>
</div>
<div class="md:hidden">
<VTooltip :text="`${arg.event.title} ${arg.event.extendedProps.subtitle}`">
<VTooltip :text="`${arg.event.title} ${arg.event.extendedProps.subtitle}`">
<template #activator="{ props }">
<VImg
height="60"
@@ -384,8 +384,8 @@ onMounted(() => {
}
.v-application .fc .fc-daygrid-day-number {
padding-block: 0rem;
padding-inline: 0rem;
padding-block: 0;
padding-inline: 0;
}
.v-application .fc .fc-list-event-dot {
@@ -435,7 +435,7 @@ onMounted(() => {
margin-inline-end: 0.25rem;
}
@media (max-width: 1264px) {
@media (width <= 1264px) {
.v-application .fc .fc-toolbar-chunk .fc-button-group .fc-drawerToggler-button {
display: block !important;
}
@@ -481,10 +481,10 @@ onMounted(() => {
}
.v-application .fc .fc-button-primary {
background-color: transparent;
border: none;
outline: none;
background-color: transparent;
color: var(--v-theme-on-surface);
outline: none;
}
.v-application .fc .fc-button-primary:hover {
@@ -492,7 +492,7 @@ onMounted(() => {
color: rgb(var(--v-theme-primary));
}
@media (max-width: 776px) {
@media (width <= 776px) {
.fc-daygrid-event-harness {
display: flex;
align-items: center;

View File

@@ -4,7 +4,8 @@ import api from '@/api'
import type { Subscribe } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue'
import SubscribeCard from '@/components/cards/SubscribeCard.vue'
import SubscribeEditForm from '@/components/form/SubscribeEditForm.vue'
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
import SubscribeHistoryDialog from '@/components/dialog/SubscribeHistoryDialog.vue'
import store from '@/store'
// 输入参数
@@ -21,6 +22,9 @@ const dataList = ref<Subscribe[]>([])
// 弹窗
const subscribeEditDialog = ref(false)
// 历史记录弹窗
const historyDialog = ref(false)
// 获取订阅列表数据
async function fetchData() {
try {
@@ -58,17 +62,10 @@ const filteredDataList = computed(() => {
</script>
<template>
<div
<LoadingBanner
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isRefreshed"
size="48"
indeterminate
color="primary"
/>
</div>
class="mt-12"
/>
<PullRefresh
v-model="loading"
@refresh="onRefresh"
@@ -102,8 +99,19 @@ const filteredDataList = computed(() => {
appear
@click="subscribeEditDialog = true"
/>
<VFab
icon="mdi-history"
color="info"
location="bottom end"
class="mb-2"
size="x-large"
fixed
app
appear
@click="historyDialog = true"
/>
<!-- 订阅编辑弹窗 -->
<SubscribeEditForm
<SubscribeEditDialog
v-if="subscribeEditDialog"
v-model="subscribeEditDialog"
:default="true"
@@ -111,6 +119,14 @@ const filteredDataList = computed(() => {
@save="subscribeEditDialog = false"
@close="subscribeEditDialog = false"
/>
<!-- 历史记录弹窗 -->
<SubscribeHistoryDialog
v-if="historyDialog"
v-model="historyDialog"
:type="props.type"
@close="historyDialog = false"
@save="() => {historyDialog = false; fetchData()}"
/>
</template>
<style lang="scss">

View File

@@ -55,38 +55,39 @@ async function loadMessages({ done }: { done: any }) {
done('ok')
return
}
// 设置加载中
loading.value = true
try {
// 设置加载中
loading.value = true
currData.value = await api.get('message/web', {
params: {
page: page.value,
size: 20,
},
})
// 已加载过
isLoaded.value = true
if (currData.value.length > 0) {
// 取最后一条时间为存量消息最新时间
lastTime.value = currData.value[currData.value.length - 1].reg_time ?? ''
// 合并数据
messages.value = [...currData.value, ...messages.value]
// 加载完成
done('ok')
if (page.value === 1) {
// 滚动到底部
emit('scroll')
// 监听SSE消息
startSSEMessager()
}
// 页码+1
page.value++
// 完成
done('ok')
}
else {
done('ok')
// 监听SSE消息
startSSEMessager()
// 没有新数据
done('empty')
}
// 取消加载中
loading.value = false
isLoaded.value = true
// 监听SSE消息
startSSEMessager()
}
catch (error) {
console.error(error)
@@ -99,7 +100,7 @@ function compareTime(time1: string, time2: string) {
return -1
if (!time2)
return 1
return new Date(time1).getTime() - new Date(time2).getTime()
return new Date(time1.replaceAll(/-/g, '/')).getTime() - new Date(time2.replaceAll(/-/g, '/')).getTime()
}
onBeforeUnmount(() => {
@@ -110,20 +111,18 @@ onBeforeUnmount(() => {
<template>
<VInfiniteScroll
mode="intersect"
:mode="!isLoaded ? 'intersect' : 'manual'"
side="start"
:items="messages"
class="overflow-hidden"
@load="loadMessages"
load-more-text="加载更多 ..."
>
<template #loading>
<VProgressCircular
v-if="loading"
indeterminate
size="48"
class="mb-5"
color="primary"
/>
<LoadingBanner />
</template>
<template #empty>
没有更多数据
</template>
<div>
<VRow