update
Deploy / deploy (push) Canceled after 0s

This commit is contained in:
xuyue committed 2026-09-27 19:36:28 +08:00
1 parent 23844ae301
commit d25b5dbe6d
268 files changed
+3684 -8861

No files matched your search

@@ -1,7 +1,4 @@
import {
breakpointsTailwind,
useBreakpoints as useVueUseBreakpoints,
} from "@vueuse/core"
import { breakpointsTailwind, useBreakpoints as useVueUseBreakpoints } from "@vueuse/core"
/**
* 响应式断点检测 composable
@@ -11,9 +11,7 @@ import { Chart as ChartJS } from "chart.js"
export function useChartTheme() {
const isDark = useDark()
const textColor = computed(() =>
isDark.value ? "rgba(255, 255, 255, 0.75)" : "#606266",
)
const textColor = computed(() => (isDark.value ? "rgba(255, 255, 255, 0.75)" : "#606266"))
const gridColor = computed(() =>
isDark.value ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.1)",
)
+11 -20
View File
@@ -47,21 +47,16 @@ export function useCollabDoc() {
if (doc) stop()
const myGeneration = ++generation
const [
Y,
awarenessProtocol,
syncProtocol,
encoding,
decoding,
{ yCollab },
] = await Promise.all([
import("yjs"),
import("y-protocols/awareness"),
import("y-protocols/sync"),
import("lib0/encoding"),
import("lib0/decoding"),
import("y-codemirror.next"),
])
const [Y, awarenessProtocol, syncProtocol, encoding, decoding, { yCollab }] = await Promise.all(
[
import("yjs"),
import("y-protocols/awareness"),
import("y-protocols/sync"),
import("lib0/encoding"),
import("lib0/decoding"),
import("y-codemirror.next"),
],
)
// 等 chunk 的这段时间里房间关了(或者又开了新的一轮),整个放弃
if (myGeneration !== generation) return
@@ -112,11 +107,7 @@ export function useCollabDoc() {
detachDocUpdate = () => doc?.off("update", onDocUpdate)
const onAwarenessUpdate = (
{
added,
updated,
removed,
}: { added: number[]; updated: number[]; removed: number[] },
{ added, updated, removed }: { added: number[]; updated: number[]; removed: number[] },
origin: any,
) => {
if (origin === "remote") return
@@ -1,9 +1,6 @@
import { useConfigStore } from "shared/store/config"
import { useUserStore } from "shared/store/user"
import {
useConfigWebSocket,
type ConfigUpdate,
} from "shared/composables/websocket"
import { useConfigWebSocket, type ConfigUpdate } from "shared/composables/websocket"
/**
* 收 /ws/config 的站点配置广播,写进 configStore —— 超管改完配置,所有开着页面
@@ -19,8 +16,7 @@ export function useConfigUpdate() {
const handleConfigUpdate = (data: ConfigUpdate) => {
// 认不出来的键直接忽略:后端将来多推一个字段,不该把 store 撑出个野字段
if (!(data.key in configStore.config)) return
;(configStore.config as unknown as Record<string, unknown>)[data.key] =
data.value
;(configStore.config as unknown as Record<string, unknown>)[data.key] = data.value
// getConfig() 里也是这么设的,站点改名后标签页跟着变,别只更新页面里那份
if (data.key === "websiteName") document.title = data.value
}
@@ -40,10 +40,7 @@ export function useDarkTransition() {
.ready.then(() => {
document.documentElement.animate(
{
clipPath: [
`circle(0px at ${x}px ${y}px)`,
`circle(${radius}px at ${x}px ${y}px)`,
],
clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`],
},
{
duration: 400,
@@ -64,9 +64,7 @@ export function useHiddenStudents(storageKey: string) {
// 把已经到期的清掉再落一次盘,否则这张表只增不减
const now = Date.now()
const cleaned = Object.fromEntries(
Object.entries(hiddenStudents.value).filter(
([, expiresAt]) => expiresAt > now,
),
Object.entries(hiddenStudents.value).filter(([, expiresAt]) => expiresAt > now),
)
hiddenStudents.value = cleaned
save(cleaned)
@@ -5,10 +5,10 @@ export type TutorialType = "python" | "c"
// 模块级单例:学习页写入、导航栏读取,必须共用同一个响应式引用,
// 否则导航栏的链接不会随学习进度更新
const learnStep = useStorage<Record<TutorialType, number>>(
STORAGE_KEY.LEARN_CURRENT_STEP,
{ python: 1, c: 1 },
)
const learnStep = useStorage<Record<TutorialType, number>>(STORAGE_KEY.LEARN_CURRENT_STEP, {
python: 1,
c: 1,
})
export function useLearnProgress() {
return { learnStep }
@@ -27,11 +27,7 @@ export function usePagination<T extends Record<string, any>>(
initialQuery: Omit<T, "page" | "limit"> = {} as Omit<T, "page" | "limit">,
options: UsePaginationOptions = {},
) {
const {
defaultLimit = 10,
defaultPage = 1,
resetPageOnChange = true,
} = options
const { defaultLimit = 10, defaultPage = 1, resetPageOnChange = true } = options
const route = useRoute()
const router = useRouter()
+1 -3
View File
@@ -9,7 +9,5 @@ import { RARITY_TEXT_COLOR } from "utils/constants"
*/
export function useRarityColor() {
const isDark = useDark()
return computed(() =>
isDark.value ? RARITY_TEXT_COLOR.dark : RARITY_TEXT_COLOR.light,
)
return computed(() => (isDark.value ? RARITY_TEXT_COLOR.dark : RARITY_TEXT_COLOR.light))
}
@@ -286,10 +286,7 @@ export function useMermaid() {
const renderSuccess = ref(false)
let renderGeneration = 0
const renderFlowchart = async (
container: HTMLElement | null,
mermaidCode: string,
) => {
const renderFlowchart = async (container: HTMLElement | null, mermaidCode: string) => {
renderError.value = null
renderSuccess.value = false
@@ -308,10 +305,7 @@ export function useMermaid() {
renderSuccess.value = true
} catch (error) {
if (gen !== renderGeneration) return
renderError.value =
error instanceof Error
? error.message
: "流程图渲染失败,请检查代码格式"
renderError.value = error instanceof Error ? error.message : "流程图渲染失败,请检查代码格式"
}
}
+9 -28
View File
@@ -35,11 +35,7 @@ function handleForceLogout(reason: string) {
/**
* WebSocket 连接状态
*/
export type ConnectionStatus =
| "disconnected"
| "connecting"
| "connected"
| "error"
export type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error"
/**
* WebSocket 消息类型
@@ -72,9 +68,7 @@ function channelUrl(path: string) {
/**
* WebSocket 消息处理器
*/
export type MessageHandler<T extends WebSocketMessage = WebSocketMessage> = (
data: T,
) => void
export type MessageHandler<T extends WebSocketMessage = WebSocketMessage> = (data: T) => void
/**
* WebSocket 基础连接管理类
@@ -113,8 +107,7 @@ export class BaseWebSocket<T extends WebSocketMessage = WebSocketMessage> {
if (
this.ws &&
(this.ws.readyState === WebSocket.OPEN ||
this.ws.readyState === WebSocket.CONNECTING)
(this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)
) {
return
}
@@ -185,9 +178,7 @@ export class BaseWebSocket<T extends WebSocketMessage = WebSocketMessage> {
// 收尾由 disconnect() 自己做 —— 也就不会再像以前那样「断完立刻重连」
if (ws !== this.ws) return
this.ws = null
console.log(
`[WebSocket] 连接关闭: code=${event.code}, reason=${event.reason}`,
)
console.log(`[WebSocket] 连接关闭: code=${event.code}, reason=${event.reason}`)
this.status.value = "disconnected"
this.stopHeartbeat()
this.onDisconnected(event)
@@ -214,9 +205,7 @@ export class BaseWebSocket<T extends WebSocketMessage = WebSocketMessage> {
RECONNECT_MAX_DELAY,
)
const delay = Math.round(base * (0.5 + Math.random() * 0.5))
console.log(
`[WebSocket] 将在 ${delay}ms 后重连 (第 ${this.reconnectAttempts} 次)`,
)
console.log(`[WebSocket] 将在 ${delay}ms 后重连 (第 ${this.reconnectAttempts} 次)`)
this.reconnectTimer = window.setTimeout(() => {
this.reconnectTimer = null
this.connect()
@@ -238,8 +227,7 @@ export class BaseWebSocket<T extends WebSocketMessage = WebSocketMessage> {
if (this.closedByUser) return
if (
this.ws &&
(this.ws.readyState === WebSocket.OPEN ||
this.ws.readyState === WebSocket.CONNECTING)
(this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)
) {
return
}
@@ -481,10 +469,7 @@ export interface SubmissionUpdate extends WebSocketMessage {
*
* 每次调用都新建一条连接(和原来一致,不是单例),并在组件卸载时摘掉 handler、断开。
*/
function useChannel<T extends WebSocketMessage>(
path: string,
handler?: MessageHandler<T>,
) {
function useChannel<T extends WebSocketMessage>(path: string, handler?: MessageHandler<T>) {
const ws = new BaseWebSocket<T>(path)
// 同步注册,不放 onMounted:调用方(如 useConfigUpdate)在 setup 阶段就 connect(),
@@ -521,9 +506,7 @@ export interface SubmissionUpdate extends WebSocketMessage {
}
/** 判题进度。subscribe(submissionId) 认领,断线重连会自动补订阅 */
export function useSubmissionWebSocket(
handler?: MessageHandler<SubmissionUpdate>,
) {
export function useSubmissionWebSocket(handler?: MessageHandler<SubmissionUpdate>) {
return useChannel<SubmissionUpdate>("/ws/submissions", handler)
}
@@ -548,9 +531,7 @@ export interface FlowchartEvaluationUpdate extends WebSocketMessage {
* 流程图评分进度。和判题走的是**同一条** `/ws/submissions` ——
* 服务端按 submissionId 分辨是代码提交还是流程图,这里只是换一套消息类型。
*/
export function useFlowchartWebSocket(
handler?: MessageHandler<FlowchartEvaluationUpdate>,
) {
export function useFlowchartWebSocket(handler?: MessageHandler<FlowchartEvaluationUpdate>) {
return useChannel<FlowchartEvaluationUpdate>("/ws/submissions", handler)
}