feat: 无界 ticket 登录、AI 写作配置确认与定时任务弹窗修复

无界 onlineTicket 对接 MaxKey 与 consumer 接口并用 sub 登录;新增 setup_writing 写作配置确认流;定时任务创建弹窗限制多行高度并支持滚动。
This commit is contained in:
fengxiangdebaozi committed 2026-06-03 16:20:45 +08:00
1 parent ab876fc7d8
commit ad4b27b7ca
25 files changed
+980 -54

No files matched your search

@@ -445,10 +445,11 @@ export default function AdminScheduledTasksPage() {
</WorkspaceBody>
<Dialog open={!!editing} onOpenChange={(open) => !open && closeDialog()}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogContent className="flex max-h-[85vh] max-w-2xl flex-col gap-4 overflow-hidden sm:max-w-2xl">
<DialogHeader className="shrink-0">
<DialogTitle>编辑定时任务</DialogTitle>
</DialogHeader>
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1">
<div className="flex flex-col gap-3">
<Input
value={form.name}
@@ -475,7 +476,8 @@ export default function AdminScheduledTasksPage() {
placeholder="时区,例如:Asia/Shanghai"
/>
<Textarea
className="min-h-40"
rows={10}
className="field-sizing-fixed min-h-40 max-h-48 resize-y overflow-y-auto"
value={form.prompt}
onChange={(event) =>
setForm((value) => ({ ...value, prompt: event.target.value }))
@@ -483,7 +485,8 @@ export default function AdminScheduledTasksPage() {
placeholder="任务 Prompt"
/>
</div>
<DialogFooter>
</div>
<DialogFooter className="shrink-0">
<Button variant="outline" onClick={closeDialog}>
取消
</Button>
+21 -5
View File
@@ -8,8 +8,10 @@ import { loginByUsername } from "@/core/auth/api";
import { setStoredAuth } from "@/core/auth";
import {
persistExternalLoginParams,
readExternalLoginParams,
resolveThemeFromQuery,
} from "@/core/auth/external-login";
import { resolveUsernameByOnlineTicket } from "@/core/auth/wujie-ticket-login";
import { isWujieEnv, useWujieProps } from "@/core/embed/wujie";
import {
GENERAL_QA_ROUTE,
@@ -54,6 +56,16 @@ export default function LoginPage() {
// hook 内部已把 ticket/userId/env 持久化到 localStorage(复用 external-login 存储)。
const wujieProps = useWujieProps();
/** onlineTicket:无界 props 优先,其次 URL query,最后读 localStorage */
const onlineTicket = useMemo(() => {
const fromWujie = wujieProps.ticket?.trim() ?? "";
const fromQuery = externalParams.ticket.trim();
if (fromWujie || fromQuery) {
return fromWujie || fromQuery;
}
return readExternalLoginParams().ticket.trim();
}, [wujieProps.ticket, externalParams.ticket]);
// 调试:把无界注入的 props 打印出来(仅在无界子应用环境下,避免独立访问时刷日志)。
useEffect(() => {
if (!isWujieEnv()) return;
@@ -71,11 +83,11 @@ export default function LoginPage() {
// ticket / userId / env 持久化(与 login 请求并行做,互不阻塞)
useEffect(() => {
persistExternalLoginParams({
ticket: externalParams.ticket,
ticket: onlineTicket || externalParams.ticket,
userId: externalParams.userId,
env: externalParams.env,
});
}, [externalParams.ticket, externalParams.userId, externalParams.env]);
}, [onlineTicket, externalParams.ticket, externalParams.userId, externalParams.env]);
useEffect(() => {
let cancelled = false;
@@ -84,13 +96,17 @@ export default function LoginPage() {
setStatus("loading");
setErrorMessage("");
try {
if (yUserId) {
let deerflowUsername = resolvedUsername;
if (onlineTicket) {
deerflowUsername = await resolveUsernameByOnlineTicket(onlineTicket);
} else if (yUserId) {
await loginByYUserId(yUserId, toolToken || null);
} else if (toolToken) {
persistToolsToken(toolToken);
}
const auth = await loginByUsername(resolvedUsername);
const auth = await loginByUsername(deerflowUsername);
if (cancelled) {
return;
}
@@ -113,7 +129,7 @@ export default function LoginPage() {
return () => {
cancelled = true;
};
}, [resolvedUsername, router, sidebarEnabled, yUserId, toolToken]);
}, [resolvedUsername, router, sidebarEnabled, yUserId, toolToken, onlineTicket]);
return (
<div className="flex min-h-screen items-center justify-center bg-background px-6">
+22 -5
View File
@@ -112,6 +112,13 @@ const emptyForm = {
require_markdown: false,
};
/** 覆盖 Textarea 默认 field-sizing-content,避免输入过多时撑破弹窗 */
const SCHEDULED_TASK_TEXTAREA_CLASS =
"field-sizing-fixed resize-y overflow-y-auto";
const DESCRIPTION_TEXTAREA_ROWS = 4;
const PROMPT_TEXTAREA_ROWS = 10;
export default function ScheduledTasksPage() {
const { theme } = useTheme();
const darkToastOpts =
@@ -824,10 +831,11 @@ export default function ScheduledTasksPage() {
</Dialog>
<Dialog open={creating || !!editing} onOpenChange={(open) => !open && closeDialog()}>
<DialogContent className="max-w-2xl overflow-hidden">
<DialogHeader className="min-w-0">
<DialogContent className="flex max-h-[85vh] max-w-2xl flex-col gap-4 overflow-hidden sm:max-w-2xl">
<DialogHeader className="min-w-0 shrink-0">
<DialogTitle>{creating ? "新建定时任务" : "编辑定时任务"}</DialogTitle>
</DialogHeader>
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1">
<div className="flex min-w-0 flex-col gap-3">
<Input
value={form.name}
@@ -837,7 +845,11 @@ export default function ScheduledTasksPage() {
placeholder="任务名称"
/>
<Textarea
className="min-h-16"
rows={DESCRIPTION_TEXTAREA_ROWS}
className={cn(
SCHEDULED_TASK_TEXTAREA_CLASS,
"min-h-[4.5rem] max-h-28",
)}
value={form.description}
onChange={(event) =>
setForm((value) => ({ ...value, description: event.target.value }))
@@ -883,7 +895,11 @@ export default function ScheduledTasksPage() {
</SelectContent>
</Select>
<Textarea
className="min-h-40"
rows={PROMPT_TEXTAREA_ROWS}
className={cn(
SCHEDULED_TASK_TEXTAREA_CLASS,
"min-h-40 max-h-48",
)}
value={form.prompt}
onChange={(event) =>
setForm((value) => ({ ...value, prompt: event.target.value }))
@@ -905,7 +921,8 @@ export default function ScheduledTasksPage() {
/>
</div>
</div>
<DialogFooter>
</div>
<DialogFooter className="shrink-0">
<Button variant="outline" onClick={closeDialog}>
取消
</Button>