1、博主签名字符串长度限制

2、仅下载图片时-一直重复下载BUG修复
3、cookie授权界面优化
This commit is contained in:
jianzhichu
2026-06-16 22:09:22 +08:00
parent e83ad8d85c
commit c5922f9b08
4 changed files with 113 additions and 272 deletions
+55 -270
View File
@@ -29,16 +29,13 @@ columns.value = [
{ title: '收藏路径', dataIndex: 'savePath' }, { title: '收藏路径', dataIndex: 'savePath' },
{ title: '喜欢路径', dataIndex: 'favSavePath' }, { title: '喜欢路径', dataIndex: 'favSavePath' },
{ title: '博主路径', dataIndex: 'upSavePath' }, { title: '博主路径', dataIndex: 'upSavePath' },
// { title: '图文视频', dataIndex: 'imgSavePath' },
// { title: 'Cookie', dataIndex: 'cookies' },
{ title: '状态', dataIndex: 'status', width: 180 }, { title: '状态', dataIndex: 'status', width: 180 },
{ title: '操作', dataIndex: 'edit', width: 200 }, // 加宽操作列宽度 { title: '操作', dataIndex: 'edit', width: 200 },
]; ];
// 定义数组中单个对象的类型:包含uper和uid字段(可选字符串类型,根据实际需求调整是否可选)
interface UpSecUserIdItem { interface UpSecUserIdItem {
uper?: string; // 若要求必填,可去掉问号 `uper: string;` uper?: string;
uid?: string; // 同上,必填则去掉问号 uid?: string;
syncAll: boolean; syncAll: boolean;
} }
@@ -54,8 +51,6 @@ type DataItem = {
upSecUserIdsJson?: UpSecUserIdItem[]; upSecUserIdsJson?: UpSecUserIdItem[];
upSecUserIds?: string; upSecUserIds?: string;
upSavePath?: string; upSavePath?: string;
// imgSavePath?: string;
// useSinglePath?: boolean; // 新增:是否全部用一个地址
useCollectFolder?: boolean; useCollectFolder?: boolean;
downMix?: boolean; downMix?: boolean;
downSeries?: boolean; downSeries?: boolean;
@@ -118,17 +113,15 @@ const newCookie = (cookie?: DataItem) => {
cookie.savePath = undefined; cookie.savePath = undefined;
cookie.favSavePath = undefined; cookie.favSavePath = undefined;
cookie.secUserId = undefined; cookie.secUserId = undefined;
cookie.status = 0; // 0=关闭,1=开启 cookie.status = 0;
cookie.id = '0'; cookie.id = '0';
cookie.upSecUserIdsJson = undefined; cookie.upSecUserIdsJson = undefined;
cookie.upSavePath = undefined; cookie.upSavePath = undefined;
// cookie.imgSavePath = undefined; cookie.useCollectFolder = false;
// cookie.useSinglePath = false; // 新增:默认不使用单一路径 cookie.downMix = false;
cookie.useCollectFolder = false; //是否按收藏夹来下载。 cookie.downSeries = false;
cookie.downMix = false; //是否下载收藏夹的合集 cookie.mixPath = undefined;
cookie.downSeries = false; //是否下载短剧 cookie.seriesPath = undefined;
cookie.mixPath = undefined; //合集存储路径
cookie.seriesPath = undefined; //短剧存储路径
cookie.downCollect = false; cookie.downCollect = false;
cookie.downFavorite = false; cookie.downFavorite = false;
cookie.downFollowd = false; cookie.downFollowd = false;
@@ -144,19 +137,6 @@ const copyObject = (target: any, source?: any) => {
const form = reactive<DataItem>(newCookie()); const form = reactive<DataItem>(newCookie());
// 新增:监听收藏路径变化,当启用单一路径时同步到其他路径
// watch(
// [() => form.savePath, () => form.useSinglePath],
// ([newSavePath, useSinglePath]) => {
// // if (useSinglePath && newSavePath) {
// // form.favSavePath = newSavePath;
// // form.upSavePath = newSavePath;
// // // form.imgSavePath = newSavePath;
// // }
// },
// { immediate: true }
// );
function reset() { function reset() {
return newCookie(form); return newCookie(form);
} }
@@ -177,7 +157,6 @@ function submit() {
?.validateFields() ?.validateFields()
.then((resData: DataItem) => { .then((resData: DataItem) => {
if (form._isNew) { if (form._isNew) {
// authors.push({ ...res });
} else { } else {
copyObject(editRecord.value, resData); copyObject(editRecord.value, resData);
} }
@@ -236,17 +215,10 @@ function edit(record: DataItem) {
editRecord.value = record; editRecord.value = record;
console.log(record); console.log(record);
copyObject(form, record); copyObject(form, record);
// 确保useSinglePath有默认值
// if (form.useSinglePath === undefined) {
// form.useSinglePath = false;
// }
showModal.value = true; showModal.value = true;
} }
// 新增:切换同步状态方法
const switchSyncStatus = (record: DataItem) => { const switchSyncStatus = (record: DataItem) => {
// 保存原始状态,用于弹窗取消后还原
const statusText = record.status === 1 ? '开启' : '停止'; const statusText = record.status === 1 ? '开启' : '停止';
const title = `确认${statusText}同步`; const title = `确认${statusText}同步`;
const content = `确定要${statusText}${record.userName || '该'}】Cookie的同步任务吗?`; const content = `确定要${statusText}${record.userName || '该'}】Cookie的同步任务吗?`;
@@ -267,7 +239,7 @@ const switchSyncStatus = (record: DataItem) => {
loading.value = false; loading.value = false;
if (res.code === 0) { if (res.code === 0) {
message.success(`${statusText}同步成功`); message.success(`${statusText}同步成功`);
GetRecords(); // 刷新列表,更新 switch 状态 GetRecords();
} else { } else {
message.error(`${statusText}同步失败:${res.message || '未知错误'}`); message.error(`${statusText}同步失败:${res.message || '未知错误'}`);
record.status = record.status === 1 ? 0 : 1; record.status = record.status === 1 ? 0 : 1;
@@ -292,19 +264,6 @@ const StatusDict = {
}; };
const dataSource = ref(datas); const dataSource = ref(datas);
const showCookiesModal = ref(false);
let showCookiesData = '';
const showCookies = (recode: DataItem) => {
showCookiesModal.value = true;
showCookiesData = recode.cookies;
};
const showUpersModal = ref(false);
let showUpersData = '';
const showUpers = (recode: DataItem) => {
showUpersModal.value = true;
showUpersData = recode.upSecUserIds;
};
const addRow = () => { const addRow = () => {
if (!form.upSecUserIdsJson) { if (!form.upSecUserIdsJson) {
@@ -319,47 +278,38 @@ const removeRow = (index: number) => {
}; };
const rowCount = 10; const rowCount = 10;
// 组件挂载时获取配置
onMounted(() => { onMounted(() => {
GetRecords(); GetRecords();
}); });
// ========== 抽屉相关响应式数据 ==========
// 抽屉显示状态
const showDrawer = ref(false); const showDrawer = ref(false);
// 抽屉类型(区分收藏夹/合集/短剧)
type DrawerType = 'collect' | 'mix' | 'series'; type DrawerType = 'collect' | 'mix' | 'series';
const cookieId = ref(''); const cookieId = ref('');
const cateType = ref(5); const cateType = ref(5);
const drawerType = ref<DrawerType>('collect'); const drawerType = ref<DrawerType>('collect');
// 抽屉滚动容器引用(用于监听触底分页)
const drawerScrollRef = ref<HTMLDivElement | null>(null); const drawerScrollRef = ref<HTMLDivElement | null>(null);
// 抽屉数据列表
const drawerDataList = ref<DrawerItem[]>([]); const drawerDataList = ref<DrawerItem[]>([]);
// 抽屉分页配置
const drawerPagination = reactive({ const drawerPagination = reactive({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
loading: false, loading: false,
hasMore: true, // 是否还有更多数据 hasMore: true,
}); });
// ========== 定义抽屉数据项接口 ==========
interface DrawerItem { interface DrawerItem {
id: string; // 不显示 id: string;
name: string; // 名称 name: string;
saveFolder: string; // 保存文件夹 saveFolder: string;
sync: boolean; // 是否同步 sync: boolean;
coverUrl: string; // 封面 coverUrl: string;
cookieId: string; // 不显示 cookieId: string;
xId: string; // 不显示 xId: string;
total: number; total: number;
} }
// ========== 3个打开抽屉的方法 ==========
const openCollectFolderSetModal = () => { const openCollectFolderSetModal = () => {
drawerType.value = 'collect'; drawerType.value = 'collect';
cateType.value = 5; cateType.value = 5;
@@ -378,16 +328,13 @@ const openSeriesDownSetModal = () => {
openCommonDrawer(); openCommonDrawer();
}; };
// ========== 公用抽屉打开逻辑(替换原来的) ==========
const openCommonDrawer = () => { const openCommonDrawer = () => {
// 重置抽屉状态
drawerDataList.value = []; drawerDataList.value = [];
drawerPagination.current = 1; drawerPagination.current = 1;
drawerPagination.total = 0; drawerPagination.total = 0;
drawerPagination.hasMore = true; drawerPagination.hasMore = true;
showDrawer.value = true; showDrawer.value = true;
// 确保DOM渲染后再加载 + 绑定事件
nextTick() nextTick()
.then(() => { .then(() => {
loadDrawerData(); loadDrawerData();
@@ -395,54 +342,24 @@ const openCommonDrawer = () => {
}) })
.catch(() => {}); .catch(() => {});
}; };
// ========== 绑定抽屉滚动触底事件 ==========
// const bindDrawerScrollEvent = () => {
// const scrollContainer = drawerScrollRef.value;
// if (!scrollContainer) return;
// // 先移除原有事件,避免重复绑定
// scrollContainer.removeEventListener('scroll', handleDrawerScroll);
// // 添加滚动事件
// scrollContainer.addEventListener('scroll', handleDrawerScroll);
// };
// ========== 绑定抽屉滚动触底事件(替换原来的) ==========
const bindDrawerScrollEvent = () => { const bindDrawerScrollEvent = () => {
const scrollContainer = drawerScrollRef.value; const scrollContainer = drawerScrollRef.value;
if (!scrollContainer) { if (!scrollContainer) {
// 延迟重试绑定,确保DOM渲染完成
setTimeout(() => { setTimeout(() => {
bindDrawerScrollEvent(); bindDrawerScrollEvent();
}, 100); }, 100);
return; return;
} }
// 先清除所有旧事件
scrollContainer.removeEventListener('scroll', handleDrawerScroll); scrollContainer.removeEventListener('scroll', handleDrawerScroll);
// 重新绑定
scrollContainer.addEventListener('scroll', handleDrawerScroll); scrollContainer.addEventListener('scroll', handleDrawerScroll);
// 额外:首次加载如果内容不足一屏,自动加载下一页
setTimeout(() => { setTimeout(() => {
handleDrawerScroll(); handleDrawerScroll();
}, 200); }, 200);
}; };
// ========== 滚动触底处理逻辑 ==========
// const handleDrawerScroll = () => {
// const scrollContainer = drawerScrollRef.value;
// if (!scrollContainer || drawerPagination.loading || !drawerPagination.hasMore) return;
// // 计算滚动位置(触底阈值20px,避免精准触底才触发)
// const { scrollTop, clientHeight, scrollHeight } = scrollContainer;
// const isBottom = scrollTop + clientHeight >= scrollHeight - 20;
// if (isBottom) {
// // 加载下一页
// drawerPagination.current += 1;
// loadDrawerData();
// }
// };
// 防抖函数(防止滚动触发太频繁)
const debounce = (func: Function, delay = 100) => { const debounce = (func: Function, delay = 100) => {
let timeoutId: any; let timeoutId: any;
return (...args: any[]) => { return (...args: any[]) => {
@@ -450,14 +367,13 @@ const debounce = (func: Function, delay = 100) => {
timeoutId = setTimeout(() => func.apply(this, args), delay); timeoutId = setTimeout(() => func.apply(this, args), delay);
}; };
}; };
// ========== 滚动触底处理逻辑(替换原来的) ==========
const handleDrawerScroll = debounce(() => { const handleDrawerScroll = debounce(() => {
const scrollContainer = drawerScrollRef.value; const scrollContainer = drawerScrollRef.value;
if (!scrollContainer) return; if (!scrollContainer) return;
if (drawerPagination.loading || !drawerPagination.hasMore) return; if (drawerPagination.loading || !drawerPagination.hasMore) return;
const { scrollTop, scrollHeight, clientHeight } = scrollContainer; const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
// 兼容所有设备的触底判断(距离底部 50px 就触发)
const isBottom = scrollTop + clientHeight + 50 >= scrollHeight; const isBottom = scrollTop + clientHeight + 50 >= scrollHeight;
if (isBottom) { if (isBottom) {
@@ -465,9 +381,8 @@ const handleDrawerScroll = debounce(() => {
drawerPagination.current += 1; drawerPagination.current += 1;
loadDrawerData(); loadDrawerData();
} }
}, 100); // 100ms 防抖 }, 100);
// ========== 加载抽屉数据(替换原来的) ==========
const loadDrawerData = () => { const loadDrawerData = () => {
if (drawerPagination.loading || !drawerPagination.hasMore) { if (drawerPagination.loading || !drawerPagination.hasMore) {
console.log('🚫 阻止重复加载'); console.log('🚫 阻止重复加载');
@@ -488,10 +403,8 @@ const loadDrawerData = () => {
drawerDataList.value = [...drawerDataList.value, ...newData]; drawerDataList.value = [...drawerDataList.value, ...newData];
drawerPagination.total = res.data.total || 0; drawerPagination.total = res.data.total || 0;
// 判断是否还有更多
drawerPagination.hasMore = drawerDataList.value.length < drawerPagination.total; drawerPagination.hasMore = drawerDataList.value.length < drawerPagination.total;
// 加载完成后立刻检查是否需要继续加载(适配大屏)
setTimeout(() => { setTimeout(() => {
handleDrawerScroll(); handleDrawerScroll();
}, 100); }, 100);
@@ -507,7 +420,6 @@ const loadDrawerData = () => {
}); });
}; };
// ========== 获取抽屉类型名称(用于页面展示) ==========
const getDrawerTypeName = () => { const getDrawerTypeName = () => {
switch (drawerType.value) { switch (drawerType.value) {
case 'collect': case 'collect':
@@ -521,24 +433,21 @@ const getDrawerTypeName = () => {
} }
}; };
// ========== 切换同步状态(可根据需求对接真实接口) ==========
const toggleDrawerItemSync = (item: DrawerItem, index: number) => { const toggleDrawerItemSync = (item: DrawerItem, index: number) => {
if (drawerDataList.value[index]) { if (drawerDataList.value[index]) {
drawerDataList.value[index].sync = !item.sync; drawerDataList.value[index].sync = !item.sync;
console.log(`切换${getDrawerTypeName()}${item.name}】的同步状态为:${!item.sync}`); console.log(`切换${getDrawerTypeName()}${item.name}】的同步状态为:${!item.sync}`);
} }
}; };
// ========== 关闭抽屉清理资源 ==========
const closeDrawer = () => { const closeDrawer = () => {
showDrawer.value = false; showDrawer.value = false;
// 移除滚动事件监听
const scrollContainer = drawerScrollRef.value; const scrollContainer = drawerScrollRef.value;
if (scrollContainer) { if (scrollContainer) {
scrollContainer.removeEventListener('scroll', handleDrawerScroll); scrollContainer.removeEventListener('scroll', handleDrawerScroll);
} }
}; };
// 2. 新增:抽屉右上角保存按钮的点击方法(仅抽屉相关,不影响其他逻辑)
const saveDrawerData = () => { const saveDrawerData = () => {
if (drawerDataList.value.length === 0) { if (drawerDataList.value.length === 0) {
message.info('暂无需要保存的配置数据'); message.info('暂无需要保存的配置数据');
@@ -587,12 +496,10 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复下载收藏视频 - 拆分Form.Item用a-form-item-rest包裹非收集字段 -->
<a-form-item label="下载收藏视频" name="downCollect"> <a-form-item label="下载收藏视频" name="downCollect">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
<a-switch v-model:checked="form.downCollect" @change="switchdownCollect" :checked-value="true" :un-checked-value="false" size="default" /> <a-switch v-model:checked="form.downCollect" @change="switchdownCollect" :checked-value="true" :un-checked-value="false" size="default" />
<!-- 非收集字段用a-form-item-rest包裹 -->
<a-form-item-rest v-if="form.downCollect"> <a-form-item-rest v-if="form.downCollect">
<a-form-item name="savePath" noStyle> <a-form-item name="savePath" noStyle>
<a-input v-model:value="form.savePath" placeholder='请输入容器路径' class="form-item-div-input" /> <a-input v-model:value="form.savePath" placeholder='请输入容器路径' class="form-item-div-input" />
@@ -603,7 +510,6 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复自定义收藏夹 - 拆分Form.Item -->
<a-form-item v-if="form.savePath && form.savePath.length>0" label="自定义收藏夹" name="useCollectFolder"> <a-form-item v-if="form.savePath && form.savePath.length>0" label="自定义收藏夹" name="useCollectFolder">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
@@ -619,7 +525,6 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复下载喜欢视频 - 拆分Form.Item -->
<a-form-item label="下载喜欢视频" name="downFavorite"> <a-form-item label="下载喜欢视频" name="downFavorite">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
@@ -637,7 +542,6 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复下载关注视频 - 拆分Form.Item -->
<a-form-item label="下载关注视频" name="downFollowd"> <a-form-item label="下载关注视频" name="downFollowd">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
@@ -655,7 +559,6 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复下载合集视频 - 拆分Form.Item -->
<a-form-item label="下载合集视频" name="downMix"> <a-form-item label="下载合集视频" name="downMix">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
@@ -673,7 +576,6 @@ const switchdownCollect = (e: any) => {
</div> </div>
</a-form-item> </a-form-item>
<!-- 修复下载短剧视频 - 拆分Form.Item -->
<a-form-item label="下载短剧视频" name="downSeries"> <a-form-item label="下载短剧视频" name="downSeries">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div"> <div class="form-item-div">
@@ -687,12 +589,10 @@ const switchdownCollect = (e: any) => {
</a-button> </a-button>
</a-form-item-rest> </a-form-item-rest>
</div> </div>
<a-alert message="开启后自动下载收藏的短剧视频(还需要开启短剧同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放) <a-alert message="开启后自动下载收藏的短剧视频(还需要开启短剧同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放)" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div> </div>
</a-form-item> </a-form-item>
<!-- 任务同步状态 - 保持原有结构仅一个Switch无警告 -->
<a-form-item label="任务同步状态" name="status"> <a-form-item label="任务同步状态" name="status">
<div style="display: flex; align-items: center; gap: 8px;"> <div style="display: flex; align-items: center; gap: 8px;">
<a-switch v-model:checked="form.status" :checked-value="1" :un-checked-value="0" size="default" /> <a-switch v-model:checked="form.status" :checked-value="1" :un-checked-value="0" size="default" />
@@ -702,9 +602,7 @@ const switchdownCollect = (e: any) => {
</a-form> </a-form>
</a-modal> </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="800px" :z-index="10010" :mask-z-index="10009" @close="closeDrawer" class="common-drawer">
<!-- 抽屉右上角添加保存按钮利用 extra 插槽不改动其他模板 -->
<template #extra> <template #extra>
<a-button type="primary" :loading="drawerPagination.loading" @click="saveDrawerData" class="drawer-save-btn"> <a-button type="primary" :loading="drawerPagination.loading" @click="saveDrawerData" class="drawer-save-btn">
<template #icon> <template #icon>
@@ -714,28 +612,22 @@ const switchdownCollect = (e: any) => {
</a-button> </a-button>
</template> </template>
<!-- 抽屉滚动容器绑定ref和滚动样式 -->
<div ref="drawerScrollRef" class="drawer-scroll-container"> <div ref="drawerScrollRef" class="drawer-scroll-container">
<!-- 加载中状态 -->
<div v-if="drawerPagination.loading && drawerDataList.length === 0" class="drawer-loading"> <div v-if="drawerPagination.loading && drawerDataList.length === 0" class="drawer-loading">
<Spin size="large" /> <Spin size="large" />
</div> </div>
<!-- 卡片网格展示 - 优化布局横向一行展示名称保存路径 -->
<a-card v-else :bordered="false" class="drawer-card-container grid-container"> <a-card v-else :bordered="false" class="drawer-card-container grid-container">
<a-card-grid v-for="(item, index) in drawerDataList" :key="item.id" class="drawer-card-grid"> <a-card-grid v-for="(item, index) in drawerDataList" :key="item.id" class="drawer-card-grid">
<!-- 竖向封面移除预览功能仅保留基础展示 -->
<div class="grid-cover vertical-cover" v-if="drawerType!='collect'"> <div class="grid-cover vertical-cover" v-if="drawerType!='collect'">
<a-image :preview="false" :src="item.coverUrl" fit="cover" /> <a-image :preview="false" :src="item.coverUrl" fit="cover" />
</div> </div>
<!-- 名称横向一行展示标签 + 内容 在同一行 -->
<div class="grid-item horizontal-item name"> <div class="grid-item horizontal-item name">
<label class="drawer-label">名称</label> <label class="drawer-label">名称</label>
<span>{{ item.name || '未命名' }}</span> <span>{{ item.name || '未命名' }}</span>
</div> </div>
<!-- 保存文件夹横向一行展示标签 + 输入框 在同一行 -->
<div class="grid-item horizontal-item save-folder"> <div class="grid-item horizontal-item save-folder">
<label class="drawer-label">保存</label> <label class="drawer-label">保存</label>
<a-input v-model:value="item.saveFolder" size="small" placeholder="默认用名称作文件夹" class="save-path-input" /> <a-input v-model:value="item.saveFolder" size="small" placeholder="默认用名称作文件夹" class="save-path-input" />
@@ -745,7 +637,6 @@ const switchdownCollect = (e: any) => {
<span>{{ item.total || '0' }}</span> <span>{{ item.total || '0' }}</span>
</div> </div>
<!-- 同步开关保持原有逻辑横向对齐 -->
<div class="grid-item horizontal-item sync-switch"> <div class="grid-item horizontal-item sync-switch">
<label class="drawer-label">同步</label> <label class="drawer-label">同步</label>
<a-switch :checked="item.sync" @change="() => toggleDrawerItemSync(item, index)" size="small" /> <a-switch :checked="item.sync" @change="() => toggleDrawerItemSync(item, index)" size="small" />
@@ -753,19 +644,16 @@ const switchdownCollect = (e: any) => {
</a-card-grid> </a-card-grid>
</a-card> </a-card>
<!-- 无更多数据提示 -->
<div v-if="!drawerPagination.hasMore && drawerDataList.length > 0" class="no-more-data"> <div v-if="!drawerPagination.hasMore && drawerDataList.length > 0" class="no-more-data">
<!-- 已加载全部{{ getDrawerTypeName() }}数据 -->
</div> </div>
<!-- 加载下一页中 -->
<div v-if="drawerPagination.loading && drawerDataList.length > 0" class="loading-more"> <div v-if="drawerPagination.loading && drawerDataList.length > 0" class="loading-more">
<Spin size="small" /> <Spin size="small" />
<span>加载中...</span> <span>加载中...</span>
</div> </div>
</div> </div>
</a-drawer> </a-drawer>
<!-- 成员表格 -->
<a-table v-bind="$attrs" :columns="columns" :dataSource="dataSource" :pagination="false"> <a-table v-bind="$attrs" :columns="columns" :dataSource="dataSource" :pagination="false">
<template #title> <template #title>
<div class="flex justify-end pr-4"> <div class="flex justify-end pr-4">
@@ -786,7 +674,6 @@ const switchdownCollect = (e: any) => {
</template> </template>
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'status'"> <template v-if="column.dataIndex === 'status'">
<!-- a-switch 显示状态绑定切换逻辑 -->
<div style="display: flex; align-items: center; gap: 8px;"> <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)" /> <a-switch v-model:checked="record.status" :checked-value="1" :un-checked-value="0" size="small" :disabled="loading" @change="() => switchSyncStatus(record)" />
<span :style="{ <span :style="{
@@ -797,14 +684,7 @@ const switchdownCollect = (e: any) => {
</span> </span>
</div> </div>
</template> </template>
<template v-else-if="column.dataIndex === 'cookies'">
<a-button @click="showCookies(record)">查看</a-button>
</template>
<template v-else-if="column.dataIndex === 'upSecUserIds'">
<a-button @click="showUpers(record)">查看</a-button>
</template>
<template v-else-if="column.dataIndex === 'edit'"> <template v-else-if="column.dataIndex === 'edit'">
<!-- 已删除开启/停止按钮仅保留编辑和删除 -->
<a-button :disabled="showModal || loading" type="link" @click="edit(record)"> <a-button :disabled="showModal || loading" type="link" @click="edit(record)">
<template #icon> <template #icon>
<EditFilled /> <EditFilled />
@@ -824,51 +704,12 @@ const switchdownCollect = (e: any) => {
</div> </div>
</template> </template>
</a-table> </a-table>
<!-- Cookie详情弹窗 -->
<a-modal title="Cookie 详情" :visible="showCookiesModal" style="width:1200px;" @cancel="showCookiesModal = false" @ok="showCookiesModal = false">
<div class="cookie-content">
{{ showCookiesData || '无Cookie数据' }}
</div>
</a-modal>
<!-- 博主信息弹窗 -->
<a-modal title="要同步的博主信息" :visible="showUpersModal" style="width:1200px;" @cancel="showUpersModal = false" @ok="showUpersModal = false">
<div class="cookie-content">
{{ showUpersData || '未设置' }}
</div>
</a-modal>
</template> </template>
<style scoped lang="less"> <style scoped lang="less">
// 原有非抽屉样式(保留,不改动)
.cookie-content {
white-space: pre-wrap;
word-break: break-all;
max-height: 400px;
overflow-y: auto;
padding: 10px;
box-sizing: border-box;
}
.ant-form-item { .ant-form-item {
margin-bottom: 10px; margin-bottom: 10px;
} }
/* 透明滚动条样式 - WebKit内核浏览器 */
.cookie-content::-webkit-scrollbar {
width: 6px; /* 更细的滚动条 */
}
.cookie-content::-webkit-scrollbar-track {
background: transparent;
}
.cookie-content::-webkit-scrollbar-thumb {
background: rgba(150, 150, 150, 0.2); /* 浅灰透明 */
border-radius: 3px;
}
.cookie-content::-webkit-scrollbar-thumb:hover {
background: rgba(150, 150, 150, 0.4); /* 略深一点的透明 */
}
.cookie-content::-webkit-scrollbar-corner {
background: transparent;
}
.form-item-div { .form-item-div {
width: 300px; width: 300px;
} }
@@ -876,13 +717,7 @@ const switchdownCollect = (e: any) => {
width: 180px; width: 180px;
margin-left: 10px; margin-left: 10px;
} }
/* Firefox 透明滚动条适配 */
.cookie-content {
scrollbar-width: thin;
scrollbar-color: rgba(150, 150, 150, 0.2) transparent;
}
/* a-textarea 透明滚动条 */
:deep(.ant-input-textarea-input) { :deep(.ant-input-textarea-input) {
overflow-y: auto; overflow-y: auto;
scrollbar-width: thin; scrollbar-width: thin;
@@ -906,102 +741,81 @@ const switchdownCollect = (e: any) => {
background: transparent; background: transparent;
} }
/* 禁用状态的输入框样式优化 */
:deep(.ant-input-disabled) { :deep(.ant-input-disabled) {
background-color: #f5f5f5 !important; background-color: #f5f5f5 !important;
color: #666 !important; color: #666 !important;
} }
/* 修复:移除对alert的自定义样式修改,还原官方默认样式 */
.alert-wrapper { .alert-wrapper {
flex: 1; flex: 1;
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
// ========== 精准匹配模板:a-card-grid 样式优化 ==========
/* 父容器:解决卡片间隙,避免换行(不改动模板,仅样式控制) */
.drawer-card-container.grid-container { .drawer-card-container.grid-container {
// 清除 a-card 自带内边距,避免与卡片间隙冲突
:deep(.ant-card-body) { :deep(.ant-card-body) {
padding: 16px; padding: 16px;
margin: 0; margin: 0;
} }
// 启用 Flex 布局,实现卡片均匀间隙(核心:gap 不与组件自带样式冲突)
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 20px; /* 卡片之间的横向/纵向间隙(可自定义,不会换行) */ gap: 20px;
box-sizing: border-box; box-sizing: border-box;
/* 固定左对齐:单元素/多元素都保持从左往右排列 */
justify-content: flex-start; justify-content: flex-start;
/* 新增:兜底最小高度,避免空容器塌陷 */
min-height: 200px; min-height: 200px;
/* 新增:左内边距,让单元素和多元素视觉对齐一致 */
padding-left: 5px; padding-left: 5px;
} }
/* 新增:单元素样式修复 - 关键修复(仅防变形,不居中) */
.drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) { .drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) {
/* 单元素时保持3列布局的宽度,不拉伸,防止变形 */
width: calc(33.333% - 10.333px) !important; width: calc(33.333% - 10.333px) !important;
/* 可选:如果觉得单元素太窄,可取消下面注释,设置固定宽度 */ min-width: 200px;
/* width: 300px !important; */
min-width: 200px; /* 强制最小宽度,防止压缩变形 */
} }
/* a-card-grid 核心样式:圆角 + 固定宽度 + 内边距 */
:deep(.drawer-card-grid) { :deep(.drawer-card-grid) {
// 精确宽度计算:3列均分,减去 gap 分摊值,避免换行 width: calc(33.333% - 10.333px) !important;
width: calc(33.333% - 10.333px) !important; /* 20px gap 分摊到3列,每列减 ~13.333px */
/* 新增:最小宽度限制,防止变形(核心) */
min-width: 200px; min-width: 200px;
margin: 5px !important; /* 清除组件自带 margin,避免间隙重复 */ margin: 5px !important;
border-radius: 12px !important; /* 卡片圆角(可自定义大小) */ border-radius: 12px !important;
padding: 16px !important; /* 卡片内部整体内边距 */ padding: 16px !important;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid #f0f0f0; /* 卡片边框,增强圆角视觉感 */ border: 1px solid #f0f0f0;
// 卡片内部竖向排列,确保每个内容块独占一行
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; /* 卡片内部元素之间的间距(可自定义) */ gap: 12px;
} }
/* 图片容器:横向铺满卡片 + 自带内边距 */
:deep(.drawer-card-grid .grid-cover.vertical-cover) { :deep(.drawer-card-grid .grid-cover.vertical-cover) {
width: 100% !important; /* 横向铺满卡片 */ width: 100% !important;
padding: 4px; /* 图片内部内边距(可自定义,实现图片与卡片边框的间距) */ padding: 4px;
box-sizing: border-box; box-sizing: border-box;
border-radius: 8px; /* 图片容器轻微圆角,与卡片圆角呼应 */ border-radius: 8px;
overflow: hidden; overflow: hidden;
} }
/* 图片:强制铺满容器,不拉伸 */
:deep(.drawer-card-grid .vertical-cover .ant-image) { :deep(.drawer-card-grid .vertical-cover .ant-image) {
width: 100% !important; width: 100% !important;
height: auto !important; /* 高度自适应,保持图片比例 */ height: auto !important;
display: block; display: block;
} }
:deep(.drawer-card-grid .vertical-cover .ant-image-img) { :deep(.drawer-card-grid .vertical-cover .ant-image-img) {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
object-fit: cover !important; /* 保持图片比例,裁剪多余部分 */ object-fit: cover !important;
} }
/* 名称、保存路径、同步开关:独占一行样式 */
:deep(.drawer-card-grid .grid-item.horizontal-item) { :deep(.drawer-card-grid .grid-item.horizontal-item) {
width: 100% !important; /* 独占一行,横向铺满卡片 */ width: 100% !important;
display: flex; display: flex;
align-items: center; /* 标签与内容垂直居中 */ align-items: center;
margin: 0 !important; /* 清除多余间距,确保独占一行 */ margin: 0 !important;
padding: 4px 0; /* 上下轻微内边距,增强可读性 */ padding: 4px 0;
} }
// 亮色模式(保留)
:deep(.drawer-label) { :deep(.drawer-label) {
flex-shrink: 0; flex-shrink: 0;
font-size: 12px; font-size: 12px;
color: rgba(0, 0, 0, 0.6); color: rgba(0, 0, 0, 0.6);
} }
/* 名称内容、输入框:占满剩余宽度 */
:deep(.drawer-card-grid .horizontal-item span) { :deep(.drawer-card-grid .horizontal-item span) {
flex: 1; flex: 1;
font-size: 12px; font-size: 12px;
@@ -1016,20 +830,16 @@ const switchdownCollect = (e: any) => {
font-size: 12px; font-size: 12px;
} }
/* 同步开关:两端对齐,保持独占一行 */
:deep(.drawer-card-grid .sync-switch) { :deep(.drawer-card-grid .sync-switch) {
justify-content: space-between; /* 标签左对齐,开关右对齐 */ justify-content: space-between;
} }
/* 响应式适配:小屏幕自动调整列数,保持间隙不换行 */
@media (max-width: 768px) { @media (max-width: 768px) {
:deep(.drawer-card-grid) { :deep(.drawer-card-grid) {
width: calc(50% - 10px) !important; /* 2列均分,20px gap 每列减10px */ width: calc(50% - 10px) !important;
/* 新增:响应式最小宽度 */
min-width: 180px; min-width: 180px;
} }
/* 响应式下单元素适配(保持左对齐) */
.drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) { .drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) {
width: calc(50% - 10px) !important; width: calc(50% - 10px) !important;
min-width: 180px; min-width: 180px;
@@ -1037,51 +847,38 @@ const switchdownCollect = (e: any) => {
} }
@media (max-width: 480px) { @media (max-width: 480px) {
:deep(.drawer-card-grid) { :deep(.drawer-card-grid) {
width: 100% !important; /* 1列铺满,无间距分摊 */ width: 100% !important;
/* 新增:移动端最小宽度铺满 */
min-width: 100%; min-width: 100%;
} }
/* 移动端单元素适配(1列铺满) */
.drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) { .drawer-card-container.grid-container:has(:only-child) :deep(.drawer-card-grid) {
width: 100% !important; width: 100% !important;
min-width: 100%; min-width: 100%;
} }
} }
// ========== 黑暗模式样式(html.dark-mode + 链式类名,光效增强 + hover 效果) ==========
/* 卡片:深色背景 + 隐式边框 + 增强光效(默认状态) */
html.dark-mode .drawer-card-container.grid-container .drawer-card-grid { html.dark-mode .drawer-card-container.grid-container .drawer-card-grid {
border-color: rgba(142, 140, 140, 0.1) !important; /* 边框透明化(隐掉) */ border-color: rgba(142, 140, 140, 0.1) !important;
background-color: #1a1a2e !important; background-color: #1a1a2e !important;
/* 增强光效:双层阴影+浅蓝紫微光,更醒目且不刺眼(核心修改) */ box-shadow: 0 6px 16px rgba(0, 20, 60, 0.4), 0 2px 6px rgba(100, 120, 255, 0.2),
box-shadow: 0 6px 16px rgba(0, 20, 60, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.05) !important;
/* 主阴影:加深蓝紫调,增大模糊半径,光效更明显 */ 0 2px 6px rgba(100, 120, 255, 0.2), transition: all 0.3s ease-in-out !important;
/* 辅阴影:新增浅蓝紫微光,光效更突出 */ inset 0 1px 0 rgba(255, 255, 255, 0.05) !important; /* 内阴影:轻微高光,增强卡片质感 */
/* 可选:完全隐藏边框 */
/* border: none !important; */
transition: all 0.3s ease-in-out !important; /* 过渡动画:hover 效果平滑切换,不生硬 */
} }
/* 卡片 hover 效果:悬浮时光效更突出,层次感拉满 */
html.dark-mode .drawer-card-container.grid-container .drawer-card-grid:hover { html.dark-mode .drawer-card-container.grid-container .drawer-card-grid:hover {
box-shadow: 0 8px 24px rgba(0, 30, 80, 0.5), box-shadow: 0 8px 24px rgba(0, 30, 80, 0.5), 0 4px 12px rgba(120, 140, 255, 0.35),
/* 主阴影:进一步加深、扩大,立体感更强 */ 0 4px 12px rgba(120, 140, 255, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.08) !important;
/* 辅阴影:蓝紫微光更亮,光效更醒目 */ inset 0 1px 0 rgba(255, 255, 255, 0.08) !important; /* 内阴影:高光增强,质感更优 */ transform: translateY(-2px) !important;
transform: translateY(-2px) !important; /* 可选:轻微上浮,增强交互感(可删除) */
} }
/* label:强制纯白色(核心需求,格式不变) */
html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .horizontal-item label { html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .horizontal-item label {
color: #ffffff !important; color: #ffffff !important;
} }
/* 名称内容:浅白色(格式不变) */
html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .horizontal-item span { html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .horizontal-item span {
color: rgba(255, 255, 255, 0.88) !important; color: rgba(255, 255, 255, 0.88) !important;
} }
/* 输入框:深色适配(格式不变) */
html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-input { html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-input {
background-color: #2f2f2f !important; background-color: #2f2f2f !important;
border-color: #404040 !important; border-color: #404040 !important;
@@ -1089,7 +886,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
} }
</style> </style>
<!-- 新增全局样式块确保alert样式不受scoped影响仅针对alert还原默认 -->
<style lang="less"> <style lang="less">
.full-modal { .full-modal {
.ant-modal { .ant-modal {
@@ -1108,11 +904,9 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
} }
} }
/* 还原Ant Design Vue alert官方默认样式,消除scoped样式的间接影响 */
.ant-alert { .ant-alert {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
/* padding: 16px 16px; */
color: rgba(0, 0, 0, 0.88); color: rgba(0, 0, 0, 0.88);
font-size: 14px; font-size: 14px;
line-height: 1.5714285714; line-height: 1.5714285714;
@@ -1127,7 +921,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
font-size: 12px; font-size: 12px;
border-radius: 4px; border-radius: 4px;
} }
/* 还原alert不同类型的官方默认颜色 */
.ant-alert-info { .ant-alert-info {
background-color: #e6f4ff; background-color: #e6f4ff;
border: 1px solid #91caff; border: 1px solid #91caff;
@@ -1135,7 +928,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
.ant-alert-info .ant-alert-message { .ant-alert-info .ant-alert-message {
color: #1677ff; color: #1677ff;
} }
/* 还原alert不同类型的官方默认颜色 */
.ant-alert-success { .ant-alert-success {
background-color: #daf1d3; background-color: #daf1d3;
border: 1px solid #5bbc51; border: 1px solid #5bbc51;
@@ -1144,14 +936,12 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
color: #228b22; color: #228b22;
} }
/* 还原 Alert 各类型的官方默认颜色 */
/* 错误(error)类型 - 红色系 */
.ant-alert-error { .ant-alert-error {
background-color: #fff1f0; /* 错误类型背景色 */ background-color: #fff1f0;
border: 1px solid #ff4d4f; /* 错误类型边框色 */ border: 1px solid #ff4d4f;
} }
.ant-alert-error .ant-alert-message { .ant-alert-error .ant-alert-message {
color: #f5222d; /* 错误类型文字主色 */ color: #f5222d;
} }
.ant-alert-warning { .ant-alert-warning {
@@ -1161,7 +951,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
.ant-alert-warning .ant-alert-message { .ant-alert-warning .ant-alert-message {
color: #faad14; color: #faad14;
} }
/* 确保alert图标颜色同步官方样式 */
.ant-alert-info .ant-alert-icon { .ant-alert-info .ant-alert-icon {
color: #1677ff; color: #1677ff;
} }
@@ -1169,10 +958,8 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
color: #faad14; color: #faad14;
} }
// 抽屉滚动容器样式(核心修复)
.drawer-scroll-container { .drawer-scroll-container {
width: 100%; width: 100%;
/* 固定抽屉内部高度,强制出现滚动区域 */
height: calc(100vh - 130px) !important; height: calc(100vh - 130px) !important;
min-height: 300px !important; min-height: 300px !important;
overflow-y: auto !important; overflow-y: auto !important;
@@ -1182,7 +969,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
position: relative; position: relative;
z-index: 1; z-index: 1;
/* 优化滚动条 */
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 6px; width: 6px;
} }
@@ -1198,7 +984,6 @@ html.dark-mode .drawer-card-container.grid-container .drawer-card-grid .ant-inpu
} }
} }
// 加载中样式
.drawer-loading { .drawer-loading {
display: flex; display: flex;
justify-content: center; justify-content: center;
+2
View File
@@ -23,6 +23,8 @@ const prodRollupOptions = {
}, },
}; };
// vite 配置 // vite 配置
// net stop winnat
// net start winnat
export default ({ command, mode }) => { export default ({ command, mode }) => {
// 获取环境变量 // 获取环境变量
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
+7 -1
View File
@@ -646,6 +646,12 @@ namespace dy.net.job
} }
else else
{ {
//2026-06-16 21:56:40 bug修复 ,之前会一直重复下载这个图片
//判断如果文件路径="/" 说明是仅下载图片的记录,不处理,直接跳过也不用删除记录
if(exitVideo.VideoUrl=="/"&& exitVideo.IsMergeVideo == 1)
{
continue;
}
//文件不存在,直接删掉原始记录 //文件不存在,直接删掉原始记录
await douyinVideoService.DeleteById(exitVideo.Id); await douyinVideoService.DeleteById(exitVideo.Id);
} }
@@ -1212,6 +1218,7 @@ namespace dy.net.job
VideoHeight = height > 0 ? height : 1920, // 视频高度 VideoHeight = height > 0 ? height : 1920, // 视频高度
}; };
// 执行图片合成视频操作 // 执行图片合成视频操作
var mergeResult = await douyinMergeVideoService.MergeToVideo(cookie.Cookies, AppContext.BaseDirectory, reqParams, savePath, fileNamefolder, config.DownImageVideo, config.DownImage, config.DownMp3); var mergeResult = await douyinMergeVideoService.MergeToVideo(cookie.Cookies, AppContext.BaseDirectory, reqParams, savePath, fileNamefolder, config.DownImageVideo, config.DownImage, config.DownMp3);
if (!mergeResult) if (!mergeResult)
@@ -1220,7 +1227,6 @@ namespace dy.net.job
return null; return null;
} }
// 获取不带扩展名的完整路径 // 获取不带扩展名的完整路径
//string fullPathWithoutExtension = Path.Combine(Path.GetDirectoryName(savePath),Path.GetFileNameWithoutExtension(savePath) ); //string fullPathWithoutExtension = Path.Combine(Path.GetDirectoryName(savePath),Path.GetFileNameWithoutExtension(savePath) );
if (config.DownImageVideo) if (config.DownImageVideo)
+48
View File
@@ -111,6 +111,17 @@ namespace dy.net.repository
return (0, 0, false); return (0, 0, false);
} }
//处理list里面 Signature 字段 可能存在一些导致SQL无法执行的特殊字符,需要清洗一下
// ========== 新增:清洗 Signature 字段特殊字符 ==========
foreach (var item in followInfos)
{
if (!string.IsNullOrWhiteSpace(item.Signature))
{
item.Signature = CleanSignature(item.Signature);
}
}
try try
{ {
// 2. 开启SqlSugar事务 // 2. 开启SqlSugar事务
@@ -268,5 +279,42 @@ namespace dy.net.repository
return true; return true;
} }
/// <summary>
/// 清洗签名字段,移除会导致SQL执行异常的特殊字符
/// </summary>
private string CleanSignature(string input,int maxLength=100)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
// 1. 移除所有不可见控制字符(ASCII 0-31、127):空字符、换行、制表符、响铃等
// 如需保留换行/制表符,可放开注释里的判断条件
char[] validChars = input.Where(c =>
{
// if (c == '\n' || c == '\r' || c == '\t') return true; // 保留换行、回车、制表符
return !char.IsControl(c);
}).ToArray();
string result = new string(validChars);
// 2. 转义SQL单引号(SQL语法标准:单引号用双单引号转义,避免语句截断)
result = result.Replace("'", "''");
// 3. 去除首尾空白
result = result.Trim();
// 4. 可选:过滤反斜杠、空字节等其他高危字符(按需开启)
result = result.Replace("\\", string.Empty);
result = result.Replace("\0", string.Empty);
if (result.Length > maxLength)
{
result = result.Substring(0, maxLength);
}
return result;
}
} }
} }