Files
douyin/app/src/pages/followd/index.vue
T

1119 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="dept-user-card-container">
<!-- 搜索框 + Tab导航 同一行布局 -->
<div class="search-tab-container">
<div class="tab-wrapper">
<a-tabs v-model:value="activeTabKey" type="line" class="custom-tabs" @change="handleTabChange">
<a-tab-pane v-for="tab in tabList" :key="tab.key" :tab="`${tab.name}(${tab.total || 0})`" />
</a-tabs>
</div>
<div class="search-area">
<!-- 搜索按钮 -->
<a-button type="default" class="search-btn" @click="toggleSearchInput">
<CloseOutlined v-if="searchInputVisible" />
<SearchOutlined v-else />
</a-button>
<transition name="search-input-fade">
<div v-if="searchInputVisible" class="search-input-wrapper">
<a-input v-model:value="quaryData.followUserName" placeholder="输入关注的用户名,按回车" allow-clear @pressEnter="handleSearch" class="search-input" ref="searchInputRef" />
</div>
</transition>
<a-button type="primary" class="sync-btn" @click="handleAdd" :disabled="isAddDisabled">
<PlusOutlined />
<span class="sync-btn-text">新增</span>
</a-button>
<!-- 同步按钮请求期间禁用 -->
<a-button type="danger" class="sync-btn" @click="handleSyncAll" :disabled="isSyncDisabled">
<SyncOutlined />
<span class="sync-btn-text">立即同步</span>
</a-button>
</div>
</div>
<!-- 卡片列表区域 -->
<div class="card-list-container" @scroll="handleScroll">
<a-card v-for="(item, index) in currentTabData" :key="item.id" :data-key="item.id" class="custom-card" :bordered="true" :hoverable="true">
<div class="card-inner">
<div class="card-switch">
<a-switch v-model:checked="item.openSync" @change="(checked) => handleSwitchChange(item, checked)" checked-children="" un-checked-children="" />
</div>
<div class="card-main-content">
<div class="avatar-wrapper">
<a-avatar shape="circle" size="large" :src="item.uperAvatar" v-if="item.uperAvatar" />
<a-avatar shape="circle" size="large" v-else class="avatar-placeholder">
{{ item.uperName.charAt(0) }}
</a-avatar>
</div>
<div class="card-content">
<div class="card-name">{{ item.uperName }}</div>
<!-- 签名多行显示高度控制 + 溢出截断 + Tooltip气泡 -->
<div class="card-desc">
<a-tooltip placement="top" :title="item.signature || '无签名'">
<span class="signature-text">
{{ item.signature || '无签名' }}
</span>
</a-tooltip>
</div>
<div class="card-path-sync-container">
<template v-if="!item.openSync">
<span class="path-placeholder"></span>
</template>
<template v-else>
<div class="path-area">
<template v-if="item.isEditing">
<div class="edit-input-group">
<!-- 输入框禁用item.isSaving true -->
<a-input v-model:value="item.savePath" placeholder="请输入文件夹名称" @keypress.enter="() => handleSavePath(item)" maxlength="10" :disabled="item.isSaving" />
<!-- 保存按钮禁用item.isSaving true -->
<a-button type="text" class="edit-btn" @click="() => handleSavePath(item)" :disabled="item.isSaving">
<SaveOutlined />
</a-button>
</div>
</template>
<template v-else>
<span class="path-text" :class="{ 'path-empty': !item.savePath }">
{{ item.savePath || '默认用作者名字' }}
</span>
<!-- 编辑按钮禁用item.isSaving true -->
<a-button type="text" class="edit-btn" @click="() => handleEditPath(item)" title="编辑文件夹名称" :disabled="item.isSaving">
<EditOutlined />
</a-button>
</template>
</div>
<div class="sync-switch-wrapper">
<span class="sync-label">全量同步</span>
<a-switch v-model:checked="item.fullSync" size="small" @change="(checked) => handleSyncChange(item, checked)" />
</div>
</template>
</div>
</div>
</div>
</div>
</a-card>
<!-- 加载状态 -->
<div v-if="loading" class="loading-container">
<a-spin size="middle" />
<span class="loading-text">加载中...</span>
</div>
<!-- 无更多数据 -->
<div v-if="noMoreData && followData.length > 0" class="no-more-container">暂无更多数据</div>
<!-- 空状态 -->
<div v-if="followData.length === 0 && !loading" class="empty-container">
<Empty description="暂无关注用户,或Cookie设置中未设置SecUserId" />
</div>
</div>
<a-modal v-model:visible="addModalVisible" title="新增非关注博主" :width="600" :confirm-loading="addFormLoading" @ok="handleAddSubmit" @cancel="handleAddCancel">
<a-form :model="addForm" :rules="addFormRules" ref="addFormRef" layout="horizontal" class="add-form" :label-col="{ span: 6 }" :wrapper-col="{ span: 17 }">
<a-form-item name="uperName" label="博主姓名" :validate-status="addFormErrors.uperName ? 'error' : ''" :help="addFormErrors.uperName || ''">
<a-input v-model:value="addForm.uperName" placeholder="请输入博主姓名" maxlength="20" @input="clearFormError('uperName')" />
</a-form-item>
<a-form-item name="博主ID" label="uperId" :validate-status="addFormErrors.uperId ? 'error' : ''" :help="addFormErrors.uperId || ''" class="uper-id-form-item">
<a-input v-model:value="addForm.uperId" placeholder="请输入博主Uid" maxlength="50" @input="clearFormError('uperId')" />
</a-form-item>
<a-form-item name="secUid" label="sec_Uid" :validate-status="addFormErrors.secUid ? 'error' : ''" :help="addFormErrors.secUid || ''">
<a-input v-model:value="addForm.secUid" placeholder="请输入博主secUid" />
</a-form-item>
<a-form-item name="savePath" label="保存文件夹" :validate-status="addFormErrors.savePath ? 'error' : ''" :help="addFormErrors.savePath || ''">
<a-input v-model:value="addForm.savePath" placeholder="不填则默认使用博主姓名" maxlength="20" @input="clearFormError('savePath')" />
</a-form-item>
<a-form-item label="同步设置" :wrapper-col="{ span: 17, offset: 6 }">
<div class="form-switch-group">
<div class="form-switch-item">
<span class="switch-label">是否同步</span>
<a-switch v-model:checked="addForm.openSync" checked-children="" un-checked-children="" />
</div>
<div class="form-switch-item">
<span class="switch-label">是否全量同步</span>
<a-switch v-model:checked="addForm.fullSync" checked-children="" un-checked-children="" :disabled="!addForm.openSync" />
</div>
</div>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted, UnwrapRef, reactive, nextTick, Ref } from 'vue';
import { message, Spin, Empty, Tooltip, Modal, Form, FormInstance } from 'ant-design-vue';
import { useApiStore } from '@/store';
// 类型定义
interface TabItem {
key: string;
name: string;
total?: number;
}
interface FollowItem {
id: string;
mySelfId: string;
uperName: string;
enterprise: string;
signature: string;
uperAvatar: string;
fullSync: boolean;
openSync: boolean;
savePath?: string;
isEditing: boolean;
isSaving?: boolean;
userId?: string; // 新增:UserId字段
}
interface QuaryParam {
pageIndex: number;
pageSize: number;
followUserName: string | null;
mySelfId?: string;
}
interface AddForm {
uperName: string;
secUid: string;
savePath: string;
openSync: boolean;
fullSync: boolean;
mySelfId: string;
uperId: string;
}
// Tab列表数据
const tabList = ref<TabItem[]>([]);
const activeTabKey = ref('');
// 关注用户列表数据
const followData = ref<FollowItem[]>([]);
// 状态变量
const loading = ref(false);
const noMoreData = ref(false);
const hasMore = ref(true);
const searchInputVisible = ref(false);
const searchInputRef = ref<HTMLInputElement | null>(null);
const isSyncDisabled = ref(false);
const isAddDisabled = ref(false);
// 新增表单相关
const addModalVisible = ref(false);
const addFormLoading = ref(false);
const addFormRef = ref<FormInstance | null>(null);
// 新增表单数据
const addForm = ref<AddForm>({
uperName: '',
secUid: '',
savePath: '',
openSync: true,
fullSync: false,
mySelfId: '',
uperId: '',
});
// 表单校验规则
const addFormRules = ref({
uperName: [
{ required: true, message: '请输入博主姓名', trigger: 'blur' },
{ max: 20, message: '姓名长度不能超过20个字符', trigger: 'blur' },
],
uperId: [{ required: true, message: '请输入博主Uid', trigger: 'blur' }],
secUid: [{ required: true, message: '请输入博主secUid', trigger: 'blur' }],
savePath: [{ max: 20, message: '文件夹名称长度不能超过20个字符', trigger: 'blur' }],
});
// 表单错误信息
const addFormErrors = ref({
uperName: '',
uperId: '',
savePath: '',
secUid: '',
});
// 搜索参数
const quaryData: UnwrapRef<QuaryParam> = reactive({
pageIndex: 0,
pageSize: 20,
followUserName: null,
mySelfId: '',
});
// 生命周期 - 挂载时初始化
onMounted(() => {
quaryData.pageIndex = 0;
GetCookies();
});
// 生命周期 - 卸载时移除滚动监听
onUnmounted(() => {
const cardContainer = document.querySelector('.dept-user-card-container .card-list-container');
if (cardContainer) {
cardContainer.removeEventListener('scroll', handleScroll);
}
});
// 获取Cookie列表(Tab数据)
const GetCookies = () => {
useApiStore()
.CookieList()
.then((res) => {
if (res.code === 0) {
const tabs = (res.data || [{ key: 'tab1', name: '部门一' }]).map((tab) => ({
...tab,
total: 0,
}));
tabList.value = tabs;
activeTabKey.value = tabList.value[0]?.key || '';
quaryData.mySelfId = activeTabKey.value;
GetFollows(true);
handleTabChange(activeTabKey.value);
}
});
};
// 获取关注用户列表
const GetFollows = (isReset = false) => {
if (loading.value || (noMoreData.value && !isReset)) return;
loading.value = true;
useApiStore()
.FollowList(quaryData)
.then((res) => {
if (res.code === 0) {
const newData = res.data.data || [];
const total = res.data.total || 0;
// 格式化数据
const formattedData = newData.map((item) => ({
...item,
isSaving: false,
isEditing: item.isEditing ?? false,
userId: item.userId || item.id, // 兼容旧数据,优先使用userId字段
}));
// 更新当前Tab的总数
if (isReset) {
const tabIndex = tabList.value.findIndex((tab) => tab.key === activeTabKey.value);
if (tabIndex !== -1) {
tabList.value[tabIndex].total = total;
}
}
// 判断是否还有更多数据
if (formattedData.length < quaryData.pageSize) {
noMoreData.value = true;
hasMore.value = false;
} else {
noMoreData.value = false;
hasMore.value = true;
}
// 合并数据(避免重复)
if (isReset) {
followData.value = formattedData;
} else {
const existingKeys = followData.value.map((item) => item.id);
const uniqueNewData = formattedData.filter((item) => !existingKeys.includes(item.id));
followData.value = [...followData.value, ...uniqueNewData];
}
quaryData.pageIndex += 1;
} else {
message.error('获取关注用户列表失败');
noMoreData.value = true;
hasMore.value = false;
}
})
.catch((err) => {
console.error('获取关注用户列表异常:', err);
message.error('网络异常,请重试');
noMoreData.value = true;
hasMore.value = false;
})
.finally(() => {
loading.value = false;
});
};
// 切换搜索框显示/隐藏
const toggleSearchInput = () => {
searchInputVisible.value = !searchInputVisible.value;
if (searchInputVisible.value) {
nextTick(() => {
searchInputRef.value?.focus();
});
} else {
// 隐藏时清空搜索条件并重新加载
if (quaryData.followUserName) {
quaryData.followUserName = null;
quaryData.pageIndex = 0;
GetFollows(true);
}
}
};
// 执行搜索
const handleSearch = () => {
quaryData.pageIndex = 0;
noMoreData.value = false;
hasMore.value = true;
GetFollows(true);
};
// Tab切换
const handleTabChange = (key: string) => {
activeTabKey.value = key;
quaryData.mySelfId = key;
quaryData.pageIndex = 0;
noMoreData.value = false;
hasMore.value = true;
searchInputVisible.value = false;
quaryData.followUserName = null;
GetFollows(true);
};
// 计算当前Tab的用户数据
const currentTabData = computed(() => {
return followData.value.filter((item) => item.mySelfId === activeTabKey.value);
});
// 滚动加载更多
const handleScroll = () => {
const cardContainer = document.querySelector('.dept-user-card-container .card-list-container');
if (!cardContainer || loading.value || !hasMore.value || noMoreData.value) return;
const { scrollTop, scrollHeight, clientHeight } = cardContainer as HTMLDivElement;
// 滚动到底部100px内时加载更多
if (scrollTop + clientHeight >= scrollHeight - 100) {
GetFollows(false);
}
};
// 开关状态变更(启用/禁用用户)
const handleSwitchChange = (item: FollowItem, checked: boolean) => {
item.openSync = checked;
uploadSyncStatus(item);
};
// 全量同步开关变更
const handleSyncChange = (item: FollowItem, checked: boolean) => {
item.fullSync = checked;
uploadSyncStatus(item);
};
// 编辑存储路径
const handleEditPath = (item: FollowItem) => {
if (item.isSaving) return;
item.isEditing = true;
// 延迟聚焦输入框
setTimeout(() => {
const input = document.querySelector(
`.dept-user-card-container .custom-card[data-key="${item.id}"] .edit-input-group .ant-input`
) as HTMLInputElement | null;
input?.focus();
}, 100);
};
// 保存存储路径
const handleSavePath = (item: FollowItem) => {
if (item.isSaving) return;
uploadSyncStatus(item);
};
// 上传同步状态
const uploadSyncStatus = (item: FollowItem) => {
item.isSaving = true;
useApiStore()
.OpenOrCloseSync({
Id: item.id,
OpenSync: item.openSync,
FullSync: item.fullSync,
SavePath: item.savePath,
UserId: item.userId, // 新增:传递UserId
})
.then((res) => {
if (res.code === 0) {
message.success(`保存成功,将在下次任务执行时生效`);
item.isEditing = false;
} else {
message.error('保存失败' + (res.msg || '未知错误'));
}
})
.catch((err) => {
console.error('保存失败', err);
message.error('保存失败,请重试');
})
.finally(() => {
item.isSaving = false;
});
};
// 批量同步所有用户
const handleSyncAll = () => {
if (isSyncDisabled.value) return;
isSyncDisabled.value = true;
loading.value = true;
useApiStore()
.StartJobNow()
.then((res) => {
if (res.code === 0) {
message.success('后台开始同步...根据您关注的数量,需要的时间不一定...请耐心等待');
} else {
message.error('同步失败:' + (res.msg || '未知错误'));
}
})
.catch((err) => {
console.error('同步异常:', err);
message.error('同步失败,请重试');
})
.finally(() => {
isSyncDisabled.value = false;
loading.value = false;
});
};
// ===================== 新增功能相关 =====================
// 打开新增弹窗
const handleAdd = () => {
// 重置表单
addForm.value = {
uperName: '',
uperId: '',
savePath: '',
openSync: true,
fullSync: false,
secUid: '',
mySelfId: activeTabKey.value,
};
// 清空错误信息
Object.keys(addFormErrors.value).forEach((key) => {
addFormErrors.value[key as keyof typeof addFormErrors.value] = '';
});
// 重置表单校验状态
addFormRef.value?.resetFields();
// 显示弹窗
addModalVisible.value = true;
};
// 关闭新增弹窗
const handleAddCancel = () => {
addModalVisible.value = false;
// 重置表单
addFormRef.value?.resetFields();
Object.keys(addFormErrors.value).forEach((key) => {
addFormErrors.value[key as keyof typeof addFormErrors.value] = '';
});
};
// 清空表单错误
const clearFormError = (field: keyof typeof addFormErrors.value) => {
addFormErrors.value[field] = '';
};
// 提交新增表单
const handleAddSubmit = () => {
// 手动校验表单
addFormRef.value
?.validate()
.then(() => {
addFormLoading.value = true;
// 构造提交数据
const submitData = {
mySelfId: activeTabKey.value, // 当前选中的Tab的key
uperId: addForm.value.uperId,
uperName: addForm.value.uperName,
savePath: addForm.value.savePath,
openSync: addForm.value.openSync,
fullSync: addForm.value.fullSync,
secUid: addForm.value.secUid,
signature: '', // 默认为空签名
uperAvatar: '', // 默认为空头像
enterprise: '', // 默认为空企业信息
};
// 调用新增接口
useApiStore()
.AddFollow(submitData)
.then((res) => {
if (res.code === 0) {
message.success('新增非关注博主成功!');
addModalVisible.value = false;
// 重新加载数据
quaryData.pageIndex = 0;
GetFollows(true);
// 更新Tab总数
const tabIndex = tabList.value.findIndex((tab) => tab.key === activeTabKey.value);
if (tabIndex !== -1) {
tabList.value[tabIndex].total = (tabList.value[tabIndex].total || 0) + 1;
}
} else {
message.error('新增失败:' + (res.msg || '未知错误'));
}
})
.catch((err) => {
console.error('新增关注博主异常:', err);
message.error('网络异常,请重试');
})
.finally(() => {
addFormLoading.value = false;
});
})
.catch((errors) => {
// 处理表单校验错误
errors.forEach((err: any) => {
if (err.field && addFormErrors.value.hasOwnProperty(err.field)) {
addFormErrors.value[err.field as keyof typeof addFormErrors.value] = err.message;
}
});
});
};
</script>
<style scoped>
/* 原有样式保持不变 */
.dept-user-card-container {
max-width: 1500px;
margin: 0 auto;
padding: 10px;
min-height: 100vh;
}
.search-tab-container {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
margin-bottom: 24px;
flex-wrap: wrap;
}
.tab-wrapper {
flex: 1;
min-width: 200px;
}
.search-area {
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
}
.sync-btn {
height: 40px !important;
padding: 0 16px !important;
white-space: nowrap;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.sync-btn-text {
font-size: 14px;
}
.sync-btn:disabled {
background-color: #f5f5f5 !important;
border-color: #d9d9d9 !important;
color: #bfbfbf !important;
cursor: not-allowed !important;
opacity: 0.8;
}
.search-btn {
height: 40px !important;
padding: 0 16px !important;
white-space: nowrap;
}
.search-input-wrapper {
width: 280px;
transition: all 0.3s ease;
}
.search-input {
width: 100% !important;
height: 40px !important;
}
.search-input-fade-enter-from,
.search-input-fade-leave-to {
width: 0 !important;
opacity: 0;
overflow: hidden;
}
.search-input-fade-enter-active,
.search-input-fade-leave-active {
transition: all 0.3s ease;
}
.custom-tabs {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.ant-tabs-nav-list .ant-tabs-tab:first-child {
padding-left: 24px !important;
}
.ant-tabs-nav-list .ant-tabs-tab {
padding-left: 16px !important;
padding-right: 16px !important;
}
.card-list-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
gap: 28px !important;
max-height: calc(100vh - 140px);
overflow-y: auto;
padding-bottom: 40px;
}
.custom-card {
border-radius: 12px !important;
box-shadow: none !important;
border: 1px solid #e5e7eb !important;
transition: all 0.3s ease !important;
overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
background: transparent !important;
}
.custom-card:hover {
border-color: #c7d2fe !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
}
.card-inner {
position: relative;
padding: 5px !important;
flex: 1 !important;
display: flex !important;
flex-direction: column !important;
}
.card-switch {
position: absolute !important;
top: 12px;
right: 16px;
z-index: 10;
}
.card-main-content {
display: flex;
align-items: flex-start;
gap: 20px;
margin-top: 8px !important;
flex: 1 !important;
}
.avatar-wrapper {
width: 64px;
height: 64px;
flex-shrink: 0;
margin-top: 4px;
}
.ant-avatar-lg {
width: 64px !important;
height: 64px !important;
}
.avatar-placeholder {
background: linear-gradient(135deg, #4096ff, #69b1ff);
color: #fff;
font-size: 24px;
font-weight: 600;
}
.card-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
margin: 4px 0;
}
.card-name {
font-size: 16px !important;
font-weight: 600;
color: #1d2129;
line-height: 1.4;
}
.card-desc {
font-size: 12px !important;
color: #86909c;
line-height: 1.5;
width: 100%;
}
.signature-text {
display: -webkit-box;
width: 250px;
max-height: 54px;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
cursor: pointer;
word-break: break-all;
}
.card-path-sync-container {
display: flex;
align-items: center;
width: 100%;
height: 34px !important;
margin-top: 8px !important;
gap: 8px;
}
.path-area {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
max-width: calc(100% - 120px);
overflow: hidden;
}
.path-placeholder {
width: 100%;
height: 100%;
}
.path-text {
font-size: 13px !important;
color: #4e5969;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 100%;
line-height: 34px !important;
}
.path-empty {
color: #c9cdd4;
font-style: italic;
}
.sync-switch-wrapper {
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
width: 120px !important;
justify-content: flex-end;
}
.sync-label {
font-size: 12px !important;
color: #6b7280;
font-weight: 500;
}
.edit-btn {
width: 32px !important;
height: 32px !important;
padding: 0 !important;
margin: 0 !important;
border-radius: 50% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
background: transparent !important;
color: #4096ff !important;
border: none !important;
}
.edit-btn .anticon {
font-size: 18px !important;
}
.edit-btn:hover {
background: transparent !important;
color: #2563eb !important;
}
.edit-input-group {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
height: 100%;
}
.ant-input {
flex: 1;
height: 100% !important;
font-size: 13px !important;
}
:deep(.ant-card-body) {
padding: 0 !important;
height: 100% !important;
display: flex !important;
flex-direction: column !important;
padding: 1px !important;
}
.ant-card-bordered {
border-width: 1px !important;
}
.loading-container {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 20px 0;
color: #6b7280;
}
.loading-text {
font-size: 14px;
}
.no-more-container {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #9ca3af;
font-size: 14px;
}
.empty-container {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 0;
}
/* 新增表单样式 */
.add-form {
margin-top: 16px;
}
.form-switch-group {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-top: 8px;
}
.form-switch-item {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
min-width: 200px;
}
.switch-label {
font-size: 14px;
color: #4e5969;
white-space: nowrap;
}
/* 移动端适配 */
@media (max-width: 768px) {
.card-list-container {
grid-template-columns: 1fr !important;
gap: 20px !important;
max-height: calc(100vh - 180px);
}
.search-tab-container {
flex-direction: column;
align-items: flex-start;
}
.search-input-wrapper {
width: 100% !important;
}
.card-inner {
padding: 14px !important;
}
.custom-card {
height: auto !important;
}
.path-area {
max-width: calc(100% - 110px);
}
.sync-switch-wrapper {
width: 110px !important;
}
.sync-label {
font-size: 11px !important;
}
.sync-btn-text {
display: none;
}
.sync-btn {
padding: 0 12px !important;
}
.sync-btn:disabled {
padding: 0 12px !important;
}
.signature-text {
width: 250px;
max-height: 30px;
line-height: 1.25;
-webkit-line-clamp: 2;
}
.form-switch-group {
flex-direction: column;
gap: 16px;
}
.form-switch-item {
width: 100%;
}
}
/* 黑暗模式样式 */
html.dark-mode .dept-user-card-container .custom-card {
border-color: #374151 !important;
}
html.dark-mode .dept-user-card-container .custom-card:hover {
border-color: #6366f1 !important;
}
html.dark-mode .dept-user-card-container .ant-card-bordered {
border-color: #374151 !important;
}
html.dark-mode .dept-user-card-container .ant-card-bordered:hover {
border-color: #6366f1 !important;
}
html.dark-mode .dept-user-card-container .card-name {
color: #f3f4f6 !important;
}
html.dark-mode .dept-user-card-container .card-desc {
color: #9ca3af !important;
}
html.dark-mode .dept-user-card-container .path-text {
color: #d1d5db !important;
}
html.dark-mode .dept-user-card-container .path-empty {
color: #6b7280 !important;
}
html.dark-mode .dept-user-card-container .sync-label {
color: #9ca3af !important;
}
html.dark-mode .dept-user-card-container .loading-text,
html.dark-mode .dept-user-card-container .no-more-container span {
color: #9ca3af !important;
}
html.dark-mode .dept-user-card-container .sync-btn {
color: #d1d5db !important;
border-color: #4b5563 !important;
}
html.dark-mode .dept-user-card-container .sync-btn:hover {
color: #f3f4f6 !important;
border-color: #6b7280 !important;
background-color: rgba(255, 255, 255, 0.04) !important;
}
html.dark-mode .switch-label {
color: #d1d5db !important;
}
/* 精准控制uperId表单项的间距 */
.uper-id-form-item {
--ant-form-item-extra-margin-top: 4px !important; /* 核心:减小extra与input的间距(默认12px */
}
/* 表单提醒样式 - 进一步减小间距 */
.form-hint {
display: flex;
align-items: center;
gap: 8px;
margin-top: 0 !important; /* 覆盖可能的默认margin */
padding-top: 2px; /* 微调上内边距,控制最终间距 */
font-size: 12px;
color: #6b7280;
line-height: 1.4; /* 减小行高,让整体更紧凑 */
height: auto;
}
.hint-link {
color: #4096ff;
text-decoration: underline;
cursor: pointer;
transition: color 0.3s ease;
}
.hint-link:hover {
color: #2563eb;
text-decoration: none;
}
.hint-desc {
color: #9ca3af;
font-size: 11px; /* 小一点的字体,更紧凑 */
}
/* 黑暗模式适配 */
html.dark-mode .form-hint {
color: #9ca3af;
}
html.dark-mode .hint-link {
color: #60a5fa;
}
html.dark-mode .hint-link:hover {
color: #3b82f6;
}
html.dark-mode .hint-desc {
color: #6b7280;
}
/* 可选:统一调整所有form-item的extra间距(如果需要) */
:deep(.ant-form-item) {
--ant-form-item-extra-margin-top: 6px; /* 全局调整,优先级低于单独设置的类 */
}
</style>