修复cookie过期bug,增加排序字段切换
This commit is contained in:
parent
e3452e6a3c
commit
a4cf9b310b
@ -34,11 +34,17 @@ async def verify_password(request: PasswordRequest):
|
||||
|
||||
@router.get("/list", response_model=FileListResponse, dependencies=[Depends(verify_token)])
|
||||
async def get_files_list(
|
||||
page: int = Query(1, ge=1, description="页码"), limit: int = Query(50, ge=1, le=100, description="每页文件数")
|
||||
page: int = Query(1, ge=1, description="页码"),
|
||||
limit: int = Query(50, ge=1, le=100, description="每页文件数"),
|
||||
sort_by: str = Query("created_at", description="排序字段: created_at 或 updated_at")
|
||||
):
|
||||
"""获取文件列表(需要认证)"""
|
||||
try:
|
||||
files = file_service.list_files(page, limit)
|
||||
# 验证排序参数
|
||||
if sort_by not in ["created_at", "updated_at"]:
|
||||
sort_by = "created_at"
|
||||
|
||||
files = file_service.list_files(page, limit, sort_by)
|
||||
total = file_service.get_total_files_count()
|
||||
|
||||
return FileListResponse(files=files, total=total, page=page, limit=limit)
|
||||
|
||||
@ -12,6 +12,7 @@ class TextFile(BaseModel):
|
||||
class FileListItem(BaseModel):
|
||||
filename: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
size: int
|
||||
preview: Optional[str] = None
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ class FileService:
|
||||
"""创建新文件"""
|
||||
return self.write_file(filename, "")
|
||||
|
||||
def list_files(self, page: int = 1, limit: int = 50) -> List[FileListItem]:
|
||||
def list_files(self, page: int = 1, limit: int = 50, sort_by: str = "created_at") -> List[FileListItem]:
|
||||
if page < 1:
|
||||
page = 1
|
||||
|
||||
@ -125,6 +125,7 @@ class FileService:
|
||||
FileListItem(
|
||||
filename=file_path.name,
|
||||
created_at=datetime.fromtimestamp(stat.st_ctime),
|
||||
updated_at=datetime.fromtimestamp(stat.st_mtime),
|
||||
size=stat.st_size,
|
||||
preview=preview,
|
||||
)
|
||||
@ -132,6 +133,10 @@ class FileService:
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error listing files: {str(e)}")
|
||||
|
||||
# 根据参数排序
|
||||
if sort_by == "updated_at":
|
||||
files.sort(key=lambda x: x.updated_at, reverse=True)
|
||||
else: # 默认按创建时间排序
|
||||
files.sort(key=lambda x: x.created_at, reverse=True)
|
||||
|
||||
start = (page - 1) * limit
|
||||
|
||||
@ -21,9 +21,10 @@ api.interceptors.response.use(
|
||||
if (error.response?.status === 401) {
|
||||
// 令牌过期,清除本地存储
|
||||
localStorage.removeItem('authToken')
|
||||
// 只在非密码验证页面刷新
|
||||
// 只在非密码验证页面触发认证状态变化事件
|
||||
if (!error.config.url?.includes('verify-password')) {
|
||||
window.location.reload()
|
||||
// 通过自定义事件通知应用认证状态变化
|
||||
window.dispatchEvent(new CustomEvent('auth-expired'))
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
@ -41,6 +42,7 @@ export interface TextFile {
|
||||
export interface FileListItem {
|
||||
filename: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
size: number
|
||||
preview?: string
|
||||
}
|
||||
@ -64,9 +66,9 @@ export const notesApi = {
|
||||
}
|
||||
|
||||
export const filesApi = {
|
||||
getList: (page = 1, limit = 50) =>
|
||||
getList: (page = 1, limit = 50, sortBy = 'created_at') =>
|
||||
api.get<FileListResponse>(
|
||||
'/notepad/files/list', { params: { page, limit } }
|
||||
'/notepad/files/list', { params: { page, limit, sort_by: sortBy } }
|
||||
),
|
||||
verifyPassword: (password: string) =>
|
||||
api.post<AuthToken>('/notepad/files/verify-password', { password }),
|
||||
|
||||
@ -344,10 +344,16 @@ const loadFile = async (filename: string) => {
|
||||
const response = await notesApi.getFile(filename)
|
||||
editorStore.setContent(response.data.content)
|
||||
editorStore.setSaveStatus('saved')
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Load failed:', error)
|
||||
if (error.response?.status === 401 || error.response?.status === 403) {
|
||||
// 认证过期或未授权,跳转到文件列表页面
|
||||
disconnect()
|
||||
router.push('/list')
|
||||
} else {
|
||||
editorStore.setContent('')
|
||||
editorStore.setSaveStatus('error')
|
||||
}
|
||||
} finally {
|
||||
editorStore.setLoading(false)
|
||||
}
|
||||
@ -367,6 +373,19 @@ onMounted(() => {
|
||||
emitter.on('connect-websocket', (filename: string) => {
|
||||
connect(filename)
|
||||
})
|
||||
|
||||
// 监听认证过期事件
|
||||
const handleAuthExpired = () => {
|
||||
disconnect()
|
||||
router.push('/list')
|
||||
}
|
||||
|
||||
window.addEventListener('auth-expired', handleAuthExpired)
|
||||
|
||||
// 组件卸载时移除事件监听器
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('auth-expired', handleAuthExpired)
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@ -52,6 +52,11 @@
|
||||
清除
|
||||
</button>
|
||||
</div>
|
||||
<div class="sort-box">
|
||||
<button @click="toggleSort" class="btn btn-secondary btn-sm">
|
||||
{{ sortBy === 'created_at' ? '按创建时间' : '按修改时间' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<RouterLink to="/" class="btn btn-primary">
|
||||
新建文件
|
||||
@ -100,7 +105,8 @@
|
||||
{{ file.preview }}
|
||||
</div>
|
||||
<div class="file-meta">
|
||||
创建时间: {{ formatDate(file.created_at) }} |
|
||||
创建: {{ formatDate(file.created_at) }} |
|
||||
修改: {{ formatDate(file.updated_at) }} |
|
||||
大小: {{ formatFileSize(file.size) }}
|
||||
</div>
|
||||
</div>
|
||||
@ -165,7 +171,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
import { filesApi, type FileListItem } from '@/services/api'
|
||||
import { emitter } from '@/utils/eventBus'
|
||||
@ -176,6 +182,7 @@ const filteredFiles = ref<FileListItem[]>([])
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const searchQuery = ref('')
|
||||
const sortBy = ref<'created_at' | 'updated_at'>('updated_at')
|
||||
const deleting = ref<string | null>(null)
|
||||
const renamingFile = ref<string | null>(null)
|
||||
const newFilename = ref('')
|
||||
@ -190,13 +197,14 @@ const fetchFiles = async () => {
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await filesApi.getList()
|
||||
const response = await filesApi.getList(1, 50, sortBy.value)
|
||||
files.value = response.data.files
|
||||
filterFiles()
|
||||
} catch (err: any) {
|
||||
if (err.response?.status === 401) {
|
||||
// 需要认证
|
||||
if (err.response?.status === 401 || err.response?.status === 403) {
|
||||
// 需要认证或令牌过期
|
||||
isAuthenticated.value = false
|
||||
error.value = null
|
||||
} else {
|
||||
error.value = err instanceof Error ? err.message : '获取文件列表失败'
|
||||
}
|
||||
@ -342,6 +350,11 @@ const confirmRename = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const toggleSort = () => {
|
||||
sortBy.value = sortBy.value === 'created_at' ? 'updated_at' : 'created_at'
|
||||
fetchFiles()
|
||||
}
|
||||
|
||||
const checkAuth = () => {
|
||||
const token = localStorage.getItem('authToken')
|
||||
if (token) {
|
||||
@ -392,6 +405,20 @@ const getDisplayName = (filename: string) => {
|
||||
|
||||
onMounted(() => {
|
||||
checkAuth()
|
||||
|
||||
// 监听认证过期事件
|
||||
const handleAuthExpired = () => {
|
||||
isAuthenticated.value = false
|
||||
error.value = null
|
||||
files.value = []
|
||||
}
|
||||
|
||||
window.addEventListener('auth-expired', handleAuthExpired)
|
||||
|
||||
// 组件卸载时移除事件监听器
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('auth-expired', handleAuthExpired)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -630,6 +657,11 @@ onMounted(() => {
|
||||
background-color: #7f8c8d;
|
||||
}
|
||||
|
||||
.sort-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
@ -683,6 +715,7 @@ onMounted(() => {
|
||||
|
||||
.header-right {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@ -701,6 +734,14 @@ onMounted(() => {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sort-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sort-box .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-actions .btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user