feat: add fnOS packaging, storage workflows and release pipeline
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { getBase64 } from '@/utils/file';
|
||||
import { FormInstance } from 'ant-design-vue';
|
||||
import { reactive, ref, onMounted, UnwrapRef, watch, nextTick } from 'vue';
|
||||
import { computed, reactive, ref, onMounted, onBeforeUnmount, UnwrapRef, watch, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import {
|
||||
@@ -18,20 +18,22 @@ import { useApiStore } from '@/store';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { StarOutlined, StarFilled, StarTwoTone } from '@ant-design/icons-vue';
|
||||
const columns = ref([]);
|
||||
columns.value = [
|
||||
{
|
||||
title: 'Cookie名称',
|
||||
dataIndex: 'userName',
|
||||
width: 180,
|
||||
},
|
||||
{ title: 'Cookie状态', dataIndex: 'statusMsg' },
|
||||
{ title: '收藏路径', dataIndex: 'savePath' },
|
||||
{ title: '喜欢路径', dataIndex: 'favSavePath' },
|
||||
{ title: '博主路径', dataIndex: 'upSavePath' },
|
||||
|
||||
const storageType = ref(0);
|
||||
const isRemoteStorage = computed(() => storageType.value === 1 || storageType.value === 2);
|
||||
const columns = computed(() => [
|
||||
{ title: 'Cookie名称', dataIndex: 'userName', width: 180 },
|
||||
{ title: 'Cookie状态', dataIndex: 'statusMsg', width: 120 },
|
||||
{ title: '收藏路径', dataIndex: isRemoteStorage.value ? 'webDavCollectPath' : 'savePath', width: 240 },
|
||||
{ title: '喜欢路径', dataIndex: isRemoteStorage.value ? 'webDavFavoritePath' : 'favSavePath', width: 240 },
|
||||
{ title: '博主路径', dataIndex: isRemoteStorage.value ? 'webDavFollowPath' : 'upSavePath', width: 240 },
|
||||
{ title: '状态', dataIndex: 'status', width: 180 },
|
||||
{ title: '操作', dataIndex: 'edit', width: 200 },
|
||||
];
|
||||
]);
|
||||
|
||||
const isMobile = ref(window.innerWidth <= 768);
|
||||
const updateViewport = () => (isMobile.value = window.innerWidth <= 768);
|
||||
const drawerWidth = computed(() => (isMobile.value ? '100%' : '800px'));
|
||||
|
||||
interface UpSecUserIdItem {
|
||||
uper?: string;
|
||||
@@ -47,6 +49,8 @@ type DataItem = {
|
||||
favSavePath?: string;
|
||||
secUserId?: string;
|
||||
status?: number;
|
||||
statusMsg?: string;
|
||||
statusCode?: number;
|
||||
_isNew?: boolean;
|
||||
upSecUserIdsJson?: UpSecUserIdItem[];
|
||||
upSecUserIds?: string;
|
||||
@@ -59,6 +63,16 @@ type DataItem = {
|
||||
downCollect?: boolean;
|
||||
downFavorite?: boolean;
|
||||
downFollowd?: boolean;
|
||||
webDavCollectPath?: string;
|
||||
webDavFavoritePath?: string;
|
||||
webDavFollowPath?: string;
|
||||
webDavMixPath?: string;
|
||||
webDavSeriesPath?: string;
|
||||
sourceCooldownUntil?: string;
|
||||
sourceRequiresAuthorization?: boolean;
|
||||
sourceProbePending?: boolean;
|
||||
lastSourceStatusCode?: number;
|
||||
lastSourceError?: string;
|
||||
};
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -125,6 +139,11 @@ const newCookie = (cookie?: DataItem) => {
|
||||
cookie.downCollect = false;
|
||||
cookie.downFavorite = false;
|
||||
cookie.downFollowd = false;
|
||||
cookie.webDavCollectPath = undefined;
|
||||
cookie.webDavFavoritePath = undefined;
|
||||
cookie.webDavFollowPath = undefined;
|
||||
cookie.webDavMixPath = undefined;
|
||||
cookie.webDavSeriesPath = undefined;
|
||||
return cookie;
|
||||
};
|
||||
|
||||
@@ -258,7 +277,7 @@ const switchSyncStatus = (record: DataItem) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
const StatusDict = {
|
||||
const StatusDict: Record<number, string> = {
|
||||
0: '同步已停止',
|
||||
1: '同步已开启',
|
||||
};
|
||||
@@ -277,9 +296,25 @@ const removeRow = (index: number) => {
|
||||
}
|
||||
};
|
||||
const rowCount = 10;
|
||||
|
||||
onMounted(() => {
|
||||
const handlePageChange = (page: number, pageSize: number) => {
|
||||
pagination.value.current = page;
|
||||
pagination.value.defaultPageSize = pageSize;
|
||||
GetRecords();
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('resize', updateViewport);
|
||||
try {
|
||||
const res = await useApiStore().StorageConfig();
|
||||
if (res.code === 0) storageType.value = Number(res.data.storageType ?? 0);
|
||||
} catch {
|
||||
message.warning('读取存储模式失败,路径表单暂按本地存储显示');
|
||||
}
|
||||
GetRecords();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateViewport);
|
||||
});
|
||||
|
||||
const showDrawer = ref(false);
|
||||
@@ -475,8 +510,8 @@ const switchdownCollect = (e: any) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal :title="form._isNew ? '新增' : '编辑'" v-model:visible="showModal" @ok="submit" @cancel="cancel" width="100%" wrap-class-name="full-modal">
|
||||
<a-form ref="formModel" :model="form" :labelCol="{ span: 3 }" :wrapperCol="{ span: 20 }">
|
||||
<a-modal :title="form._isNew ? '新增授权' : '编辑授权'" v-model:visible="showModal" @ok="submit" @cancel="cancel" width="100%" wrap-class-name="full-modal">
|
||||
<a-form ref="formModel" :model="form" :labelCol="isMobile ? { span: 24 } : { span: 3 }" :wrapperCol="isMobile ? { span: 24 } : { span: 20 }">
|
||||
<a-form-item label="Cookie名称" required name="userName">
|
||||
<a-input v-model:value="form.userName" />
|
||||
</a-form-item>
|
||||
@@ -497,99 +532,105 @@ const switchdownCollect = (e: any) => {
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下载收藏视频" name="downCollect">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.downCollect" @change="switchdownCollect" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.downCollect">
|
||||
<a-form-item name="savePath" noStyle>
|
||||
<a-input v-model:value="form.savePath" placeholder='请输入容器路径' class="form-item-div-input" />
|
||||
<a-form-item :name="isRemoteStorage ? 'webDavCollectPath' : 'savePath'" noStyle>
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavCollectPath" placeholder="OpenList 相对路径,留空自动生成" class="form-item-div-input" />
|
||||
<a-input v-else v-model:value="form.savePath" placeholder="请输入容器路径" class="form-item-div-input" />
|
||||
</a-form-item>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载默认收藏夹视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert :message="isRemoteStorage ? '开启后写入目标基础目录下的这个 OpenList 相对路径;留空保存时会自动生成。' : '开启后自动下载默认收藏夹视频,记得填写映射路径(容器内部路径)。'" type="info" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="form.savePath && form.savePath.length>0" label="自定义收藏夹" name="useCollectFolder">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<a-form-item v-if="isRemoteStorage || (form.savePath && form.savePath.length>0)" label="自定义收藏夹" name="useCollectFolder">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.useCollectFolder" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.useCollectFolder">
|
||||
<a-input v-model:value="form.savePath" :disabled="form.useCollectFolder&&form.downCollect" placeholder="" class="form-item-div-input" />
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavCollectPath" :disabled="form.useCollectFolder&&form.downCollect" placeholder="与收藏路径一致" class="form-item-div-input" />
|
||||
<a-input v-else v-model:value="form.savePath" :disabled="form.useCollectFolder&&form.downCollect" placeholder="" class="form-item-div-input" />
|
||||
<a-button @click="openCollectFolderSetModal" shape="circle" type="dashed" style="margin-left:5px;" v-if="form.useCollectFolder">
|
||||
<star-outlined />
|
||||
</a-button>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载自定义分类后的收藏夹,开启后不在下载默认收藏夹视频,存储路径与默认收藏夹存储路径一致" :type="form.useCollectFolder?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert message="开启后按收藏夹分类同步,存储根路径与默认收藏路径一致。" :type="form.useCollectFolder?'error':'info'" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下载喜欢视频" name="downFavorite">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.downFavorite" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.downFavorite">
|
||||
<a-form-item name="favSavePath" noStyle>
|
||||
<a-input v-model:value="form.favSavePath" placeholder='请输入容器路径' class="form-item-div-input" />
|
||||
<a-form-item :name="isRemoteStorage ? 'webDavFavoritePath' : 'favSavePath'" noStyle>
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavFavoritePath" placeholder="OpenList 相对路径,留空自动生成" class="form-item-div-input" />
|
||||
<a-input v-else v-model:value="form.favSavePath" placeholder="请输入容器路径" class="form-item-div-input" />
|
||||
</a-form-item>
|
||||
<a-button shape="circle" @click="()=>{message.success('别点了,这只是为了好看的😄')}" type="dashed" style="margin-left:5px;">
|
||||
<like-outlined />
|
||||
</a-button>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载喜欢(点赞)的视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert :message="isRemoteStorage ? '喜欢的视频会写入目标基础目录下的这个 OpenList 相对路径。' : '开启后自动下载喜欢(点赞)的视频,记得填写映射路径(容器内部路径)。'" type="info" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下载关注视频" name="downFollowd">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.downFollowd" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.downFollowd">
|
||||
<a-form-item name="upSavePath" noStyle>
|
||||
<a-input v-model:value="form.upSavePath" placeholder='请输入容器路径' class="form-item-div-input" />
|
||||
<a-form-item :name="isRemoteStorage ? 'webDavFollowPath' : 'upSavePath'" noStyle>
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavFollowPath" placeholder="OpenList 相对路径,留空自动生成" class="form-item-div-input" />
|
||||
<a-input v-else v-model:value="form.upSavePath" placeholder="请输入容器路径" class="form-item-div-input" />
|
||||
</a-form-item>
|
||||
<a-button shape="circle" @click="()=>{message.success('别点了,这只是为了好看的😄')}" type="dashed" style="margin-left:5px;">
|
||||
<heart-outlined />
|
||||
</a-button>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载关注的博主视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert :message="isRemoteStorage ? '关注博主的视频会写入目标基础目录下的这个 OpenList 相对路径。' : '开启后自动下载关注的博主视频,记得填写映射路径(容器内部路径)。'" type="info" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下载合集视频" name="downMix">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.downMix" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.downMix">
|
||||
<a-form-item name="mixPath" noStyle>
|
||||
<a-input v-model:value="form.mixPath" class="form-item-div-input" placeholder='默认使用收藏夹路径' />
|
||||
<a-form-item :name="isRemoteStorage ? 'webDavMixPath' : 'mixPath'" noStyle>
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavMixPath" class="form-item-div-input" placeholder="OpenList 相对路径,留空自动生成" />
|
||||
<a-input v-else v-model:value="form.mixPath" class="form-item-div-input" placeholder="默认使用收藏夹路径" />
|
||||
</a-form-item>
|
||||
<a-button @click="openMixDownSetModal" shape="circle" type="dashed" style="margin-left:5px;">
|
||||
<gift-outlined />
|
||||
</a-button>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载收藏的合集视频(还需要开启合集同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放)" :type="form.downMix?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert :message="isRemoteStorage ? '收藏的合集会同步到该 OpenList 相对路径;付费视频下载后可能无法播放。' : '开启后自动下载收藏的合集视频;不填路径默认使用收藏目录,付费视频下载后可能无法播放。'" :type="form.downMix?'error':'info'" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="下载短剧视频" name="downSeries">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div class="sync-option-row">
|
||||
<div class="form-item-div">
|
||||
<a-switch v-model:checked="form.downSeries" :checked-value="true" :un-checked-value="false" size="default" />
|
||||
<a-form-item-rest v-if="form.downSeries">
|
||||
<a-form-item name="seriesPath" noStyle>
|
||||
<a-input v-model:value="form.seriesPath" placeholder='默认使用收藏夹路径' class="form-item-div-input" />
|
||||
<a-form-item :name="isRemoteStorage ? 'webDavSeriesPath' : 'seriesPath'" noStyle>
|
||||
<a-input v-if="isRemoteStorage" v-model:value="form.webDavSeriesPath" placeholder="OpenList 相对路径,留空自动生成" class="form-item-div-input" />
|
||||
<a-input v-else v-model:value="form.seriesPath" placeholder="默认使用收藏夹路径" class="form-item-div-input" />
|
||||
</a-form-item>
|
||||
<a-button @click="openSeriesDownSetModal" shape="circle" type="dashed" style="margin-left:5px;">
|
||||
<fire-outlined />
|
||||
</a-button>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
<a-alert message="开启后自动下载收藏的短剧视频(还需要开启短剧同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放)" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
|
||||
<a-alert :message="isRemoteStorage ? '收藏的短剧会同步到该 OpenList 相对路径;付费视频下载后可能无法播放。' : '开启后自动下载收藏的短剧视频;不填路径默认使用收藏目录,付费视频下载后可能无法播放。'" :type="form.downSeries?'error':'info'" size="small" class="path-alert" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
@@ -602,7 +643,7 @@ const switchdownCollect = (e: any) => {
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<a-drawer :title="getDrawerTypeName()+'配置'" v-model:visible="showDrawer" placement="right" width="800px" :z-index="10010" :mask-z-index="10009" @close="closeDrawer" class="common-drawer">
|
||||
<a-drawer :title="getDrawerTypeName()+'配置'" v-model:visible="showDrawer" placement="right" :width="drawerWidth" :z-index="10010" :mask-z-index="10009" @close="closeDrawer" class="common-drawer">
|
||||
<template #extra>
|
||||
<a-button type="primary" :loading="drawerPagination.loading" @click="saveDrawerData" class="drawer-save-btn">
|
||||
<template #icon>
|
||||
@@ -654,26 +695,29 @@ const switchdownCollect = (e: any) => {
|
||||
</div>
|
||||
</a-drawer>
|
||||
|
||||
<a-table v-bind="$attrs" :columns="columns" :dataSource="dataSource" :pagination="false">
|
||||
<template #title>
|
||||
<div class="flex justify-end pr-4">
|
||||
<a-button type="primary" @click="GetRecords()" :loading="formLoading" class="mr-2">
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<div class="cookie-toolbar">
|
||||
<a-button @click="GetRecords()" :loading="loading">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
刷新
|
||||
</a-button>
|
||||
<a-button type="primary" @click="addNew" :loading="formLoading">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增授权
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<a-button type="primary" @click="addNew" :loading="formLoading">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
新增
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-table v-if="!isMobile" v-bind="$attrs" :columns="columns" :dataSource="dataSource" :pagination="false" :scroll="{ x: isRemoteStorage ? 1400 : 1200 }">
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'status'">
|
||||
<template v-if="column.dataIndex === 'statusMsg'">
|
||||
<a-tooltip v-if="record.sourceRequiresAuthorization" :title="record.lastSourceError || '请编辑并保存新的 Cookie'">
|
||||
<a-tag color="error">需要重新授权</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="record.sourceCooldownUntil || record.sourceProbePending" :title="record.lastSourceError || '等待自动探测'">
|
||||
<a-tag color="warning">{{ record.sourceCooldownUntil ? `冷却至 ${dayjs(record.sourceCooldownUntil).format('HH:mm:ss')}` : '等待来源探测' }}</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tag v-else :color="record.statusCode === 0 ? 'success' : 'default'">{{ record.statusMsg || '未知' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<a-switch v-model:checked="record.status" :checked-value="1" :un-checked-value="0" size="small" :disabled="loading" @change="() => switchSyncStatus(record)" />
|
||||
<span :style="{
|
||||
@@ -704,6 +748,54 @@ const switchdownCollect = (e: any) => {
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-spin v-else :spinning="loading">
|
||||
<a-empty v-if="!loading && dataSource.length === 0" description="暂无抖音授权" />
|
||||
<div class="mobile-cookie-list">
|
||||
<article v-for="record in dataSource" :key="record.id" class="mobile-cookie-card">
|
||||
<div class="mobile-cookie-header">
|
||||
<div>
|
||||
<h3>{{ record.userName || '未命名授权' }}</h3>
|
||||
<span :class="record.status === 1 ? 'status-on' : 'status-off'">{{ record.statusMsg || StatusDict[record.status || 0] }}</span>
|
||||
<a-tag v-if="record.sourceRequiresAuthorization" color="error">需要重新授权</a-tag>
|
||||
<a-tag v-else-if="record.sourceCooldownUntil || record.sourceProbePending" color="warning">
|
||||
{{ record.sourceCooldownUntil ? `冷却至 ${dayjs(record.sourceCooldownUntil).format('HH:mm:ss')}` : '等待来源探测' }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<a-switch v-model:checked="record.status" :checked-value="1" :un-checked-value="0" size="small" :disabled="loading" @change="() => switchSyncStatus(record)" />
|
||||
</div>
|
||||
<dl class="mobile-cookie-paths">
|
||||
<template v-if="isRemoteStorage">
|
||||
<div><dt>收藏</dt><dd>{{ record.webDavCollectPath || '未设置' }}</dd></div>
|
||||
<div><dt>喜欢</dt><dd>{{ record.webDavFavoritePath || '未设置' }}</dd></div>
|
||||
<div><dt>关注</dt><dd>{{ record.webDavFollowPath || '未设置' }}</dd></div>
|
||||
<div><dt>合集</dt><dd>{{ record.webDavMixPath || '未设置' }}</dd></div>
|
||||
<div><dt>短剧</dt><dd>{{ record.webDavSeriesPath || '未设置' }}</dd></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div><dt>收藏</dt><dd>{{ record.savePath || '未设置' }}</dd></div>
|
||||
<div><dt>喜欢</dt><dd>{{ record.favSavePath || '未设置' }}</dd></div>
|
||||
<div><dt>关注</dt><dd>{{ record.upSavePath || '未设置' }}</dd></div>
|
||||
</template>
|
||||
</dl>
|
||||
<div class="mobile-cookie-actions">
|
||||
<a-button :disabled="showModal || loading" @click="edit(record)"><EditFilled />编辑</a-button>
|
||||
<a-button :disabled="loading || !record.id" danger @click="record.id && deleted(record.id)"><DeleteOutlined />删除</a-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
<a-pagination
|
||||
v-if="pagination.total > 0"
|
||||
class="cookie-pagination"
|
||||
:simple="isMobile"
|
||||
:current="pagination.current"
|
||||
:page-size="pagination.defaultPageSize"
|
||||
:total="pagination.total"
|
||||
:show-size-changer="false"
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -711,12 +803,100 @@ const switchdownCollect = (e: any) => {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-item-div {
|
||||
width: 300px;
|
||||
width: 430px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.form-item-div-input {
|
||||
width: 180px;
|
||||
width: 310px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.sync-option-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.path-alert {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.cookie-toolbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 8px 12px 12px;
|
||||
}
|
||||
|
||||
.cookie-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 16px 12px 4px;
|
||||
}
|
||||
|
||||
.mobile-cookie-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.mobile-cookie-card {
|
||||
padding: 14px;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 14px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.mobile-cookie-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mobile-cookie-header h3 {
|
||||
margin: 0 0 3px;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mobile-cookie-header .status-on {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.mobile-cookie-header .status-off {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.mobile-cookie-paths {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.mobile-cookie-paths > div {
|
||||
display: grid;
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
padding: 3px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mobile-cookie-paths dt {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.mobile-cookie-paths dd {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.mobile-cookie-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
:deep(.ant-input-textarea-input) {
|
||||
overflow-y: auto;
|
||||
@@ -835,6 +1015,68 @@ const switchdownCollect = (e: any) => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cookie-toolbar {
|
||||
position: sticky;
|
||||
z-index: 20;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--mobile-border, #e5e7eb);
|
||||
border-radius: 16px;
|
||||
background: var(--mobile-card, #fff);
|
||||
box-shadow: 0 6px 20px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.cookie-toolbar .ant-btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cookie-pagination {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sync-option-row {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-item-div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-item-div-input {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.mobile-cookie-list { padding: 0; }
|
||||
.mobile-cookie-card {
|
||||
border-color: var(--mobile-border, #e8e8e8);
|
||||
border-radius: 16px;
|
||||
background: var(--mobile-card, #fff);
|
||||
}
|
||||
|
||||
.mobile-cookie-header h3 { color: var(--mobile-text, #1f2937); }
|
||||
.mobile-cookie-paths dd { color: var(--mobile-text, #334155); }
|
||||
|
||||
:deep(.ant-modal-footer) {
|
||||
position: sticky;
|
||||
z-index: 5;
|
||||
bottom: 0;
|
||||
padding-bottom: max(10px, env(safe-area-inset-bottom));
|
||||
background: var(--mobile-card, #fff);
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-label) {
|
||||
padding-bottom: 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-label > label) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.drawer-card-grid) {
|
||||
width: calc(50% - 10px) !important;
|
||||
min-width: 180px;
|
||||
@@ -901,6 +1143,22 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
|
||||
}
|
||||
.ant-modal-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.full-modal {
|
||||
.ant-modal-header,
|
||||
.ant-modal-footer {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100dvh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,4 +1273,4 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user