update: style
This commit is contained in:
+18
-61
@@ -3,9 +3,18 @@
|
||||
@theme {
|
||||
--font-sans: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
|
||||
|
||||
/* STYLE_GUIDE §3.2 字号补充 token */
|
||||
--text-2xs: 0.625rem; /* 10px */
|
||||
--text-3xs: 0.5625rem; /* 9px */
|
||||
--text-4xs: 0.5rem; /* 8px */
|
||||
|
||||
/* STYLE_GUIDE §3.3 字重补充 token */
|
||||
--font-weight-medium-plus: 650;
|
||||
|
||||
--color-brand: #1978d4;
|
||||
--color-brand-strong: #0e5fb9;
|
||||
--color-brand-soft: #edf6ff;
|
||||
--color-brand-deep: #0e5bad;
|
||||
|
||||
--color-success: #18b979;
|
||||
--color-success-strong: #0f7d55;
|
||||
@@ -21,6 +30,7 @@
|
||||
--color-ink: #27394d;
|
||||
--color-ink-muted: #5d7186;
|
||||
--color-ink-subtle: #8b99a8;
|
||||
--color-ink-caption: #748598;
|
||||
|
||||
--color-line: #dce4ec;
|
||||
--color-line-soft: #edf1f5;
|
||||
@@ -29,6 +39,13 @@
|
||||
--color-bg-panel: #ffffff;
|
||||
--color-bg-canvas: #f9fbfd;
|
||||
--color-bg-log: #17212b;
|
||||
|
||||
/* STYLE_GUIDE §4.2 间距补充 token */
|
||||
--spacing-page-x: 22px;
|
||||
--spacing-button-x: 15px;
|
||||
--spacing-gap-icon: 7px;
|
||||
--spacing-gap-sm: 5px;
|
||||
--spacing-gap-md: 9px;
|
||||
}
|
||||
@theme inline {
|
||||
--color-sidebar: var(--sidebar-background);
|
||||
@@ -256,19 +273,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shared UI classes still used by admin / schedules pages */
|
||||
.icon-button {
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
place-items: center;
|
||||
border: 1px solid #dfe6ed;
|
||||
border: 1px solid var(--color-line);
|
||||
border-radius: 7px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
@@ -289,58 +300,4 @@
|
||||
background: linear-gradient(145deg, #3b92ed, #1869c9);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.primary-button,
|
||||
.secondary-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
min-height: 37px;
|
||||
padding: 0 15px;
|
||||
border: 1px solid #126ac3;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #1881e7, #1268c9);
|
||||
box-shadow: 0 6px 15px rgb(20 111 202 / 18%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.primary-button:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, #1175d8, #0e5bad);
|
||||
}
|
||||
|
||||
.primary-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
.secondary-button {
|
||||
min-height: 37px;
|
||||
padding: 0 15px;
|
||||
border: 1px solid #d8e1e9;
|
||||
color: #56687c;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.button-spinner {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border: 2px solid rgb(255 255 255 / 45%);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite;
|
||||
}
|
||||
|
||||
.button-spinner.button-spinner--blue,
|
||||
.button-spinner--blue {
|
||||
border-color: rgb(34 121 199 / 22%);
|
||||
border-top-color: #2279c7;
|
||||
}
|
||||
@@ -9,13 +9,14 @@ import {
|
||||
} from "~/components/ui/dialog"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
import { primaryGradientButtonClass } from "./buttonClasses"
|
||||
|
||||
/** 与系统管理弹窗 footer 一致的次要按钮样式 */
|
||||
export const dialogSecondaryButtonClass =
|
||||
"h-[37px] gap-[7px] rounded-md border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]"
|
||||
|
||||
/** 与系统管理弹窗 footer 一致的主按钮样式 */
|
||||
export const dialogPrimaryButtonClass =
|
||||
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]"
|
||||
/** 与系统管理弹窗 footer 一致的主按钮样式(STYLE_GUIDE §2.2 官方渐变) */
|
||||
export const dialogPrimaryButtonClass = primaryGradientButtonClass
|
||||
|
||||
const EYEBROW_CLASS =
|
||||
"text-[9px] font-extrabold tracking-[0.12em] text-[#2d82d4]"
|
||||
@@ -59,13 +60,13 @@ function AppFormDialog({
|
||||
<DialogContent
|
||||
showCloseButton={showCloseButton}
|
||||
className={cn(
|
||||
"flex max-h-[80vh] flex-col gap-0 overflow-hidden rounded-[11px] border-[#dce4eb] bg-white p-0 text-[#1c2d42] shadow-[0_24px_70px_rgb(8_27_48/25%)] ring-0",
|
||||
"flex max-h-[80vh] flex-col gap-0 overflow-hidden rounded-[11px] border-[#dce4eb] bg-white p-0 text-[#1c2d42] shadow-xl ring-0",
|
||||
maxWidth,
|
||||
contentClassName,
|
||||
)}
|
||||
>
|
||||
{eyebrow || title ? (
|
||||
<DialogHeader className="shrink-0 space-y-0 border-b border-[#e7ecf1] px-[22px] pb-4 pt-5 text-left">
|
||||
<DialogHeader className="shrink-0 space-y-0 border-b border-line px-[22px] pb-4 pt-5 text-left">
|
||||
{eyebrow ? <span className={EYEBROW_CLASS}>{eyebrow}</span> : null}
|
||||
{title ? (
|
||||
<DialogTitle id={titleId} className={TITLE_CLASS}>
|
||||
@@ -78,7 +79,7 @@ function AppFormDialog({
|
||||
{children}
|
||||
</div>
|
||||
{footer ? (
|
||||
<DialogFooter className="mt-0 shrink-0 flex-row justify-end gap-2 border-t border-[#e8edf2] bg-white px-[22px] py-3.5 sm:justify-end">
|
||||
<DialogFooter className="mt-0 shrink-0 flex-row justify-end gap-2 border-t border-line bg-white px-[22px] py-3.5 sm:justify-end">
|
||||
{footer}
|
||||
</DialogFooter>
|
||||
) : null}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
dialogPrimaryButtonClass,
|
||||
dialogSecondaryButtonClass,
|
||||
} from "./AppFormDialog"
|
||||
import { dangerSolidButtonClass } from "./buttonClasses"
|
||||
|
||||
type ConfirmDialogProps = {
|
||||
open: boolean
|
||||
@@ -58,7 +59,7 @@ function ConfirmDialog({
|
||||
size="sm"
|
||||
className={cn(
|
||||
destructive
|
||||
? "h-[37px] gap-[7px] rounded-md border-0 bg-[#d75b5b] px-[15px] text-xs font-[650] text-white hover:bg-[#a94f4f]"
|
||||
? dangerSolidButtonClass
|
||||
: dialogPrimaryButtonClass,
|
||||
)}
|
||||
onClick={() => void onConfirm()}
|
||||
|
||||
@@ -54,15 +54,15 @@ export function Topbar({
|
||||
<span
|
||||
className={`size-[7px] rounded-full ${
|
||||
apiOnline
|
||||
? "bg-[#19b878] shadow-[0_0_0_3px_rgb(25_184_120_/_11%)]"
|
||||
: "bg-[#d05151] shadow-[0_0_0_3px_rgb(208_81_81_/_10%)]"
|
||||
? "bg-[#19b878] ring-3 ring-success/10"
|
||||
: "bg-[#d05151] ring-3 ring-danger/10"
|
||||
}`}
|
||||
/>
|
||||
{apiOnline ? "服务已连接" : "服务未连接"}
|
||||
</div>
|
||||
<div className="relative flex items-center gap-[9px]">
|
||||
<button
|
||||
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-[#e0e7ee] bg-white px-[11px] hover:border-[#b9cce0]"
|
||||
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-line bg-white px-[11px] hover:border-[#b9cce0]"
|
||||
type="button"
|
||||
onClick={() => onSetWorkspaceMenuOpen(!workspaceMenuOpen)}
|
||||
>
|
||||
@@ -78,7 +78,7 @@ export function Topbar({
|
||||
<ChevronRight size={15} />
|
||||
</button>
|
||||
{workspaceMenuOpen && (
|
||||
<div className="absolute top-[calc(100%+7px)] right-0 z-50 w-[260px] rounded-lg border border-[#dce5ed] bg-white p-1.5 shadow-[0_14px_38px_rgb(17_43_70_/_18%)]">
|
||||
<div className="absolute top-[calc(100%+7px)] right-0 z-50 w-[260px] rounded-lg border border-line bg-white p-1.5 shadow-lg">
|
||||
{workspaces.map((workspace) => {
|
||||
const isSelected =
|
||||
workspace.workspace_id === currentWorkspace.workspace_id;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* STYLE_GUIDE §2.2 官方按钮类。
|
||||
* 收敛自 modalUi.primaryButtonClass / AppFormDialog.dialogPrimaryButtonClass /
|
||||
* admin 三页 PRIMARY_BUTTON_CLASS 的重复定义,色值统一走 @theme token。
|
||||
*/
|
||||
|
||||
export const primaryGradientButtonClass =
|
||||
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-gap-icon rounded-md border border-brand bg-gradient-to-br from-brand to-brand-deep px-button-x text-xs font-medium-plus text-white shadow-md enabled:hover:from-brand-strong enabled:hover:to-brand-deep disabled:cursor-not-allowed disabled:opacity-[0.58]";
|
||||
|
||||
/** 危险确认按钮(ConfirmDialog destructive 态),token 化自原 bg-[#d75b5b] 写法 */
|
||||
export const dangerSolidButtonClass =
|
||||
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-gap-icon rounded-md border-0 bg-danger px-button-x text-xs font-medium-plus text-white enabled:hover:bg-danger-strong disabled:cursor-not-allowed disabled:opacity-[0.58]";
|
||||
@@ -38,7 +38,7 @@ export function DashboardPage({
|
||||
|
||||
return (
|
||||
<section className="h-full overflow-auto bg-bg p-6">
|
||||
<div className="dashboard-hero-shell flex items-center justify-between rounded-xl px-[34px] py-[34px] text-white shadow-[0_12px_30px_rgb(19_80_137/18%)]">
|
||||
<div className="dashboard-hero-shell flex items-center justify-between rounded-xl px-[34px] py-[34px] text-white shadow-lg">
|
||||
<div>
|
||||
<span className="text-[10px] tracking-[0.12em] opacity-80">
|
||||
MODEL DEVELOPMENT PLATFORM
|
||||
@@ -78,7 +78,7 @@ export function DashboardPage({
|
||||
</span>
|
||||
<span className="flex flex-col">
|
||||
<b className="text-[21px] text-[#20364c]">{item.value}</b>
|
||||
<small className="text-[#8796a6]">{item.label}</small>
|
||||
<small className="text-ink-subtle">{item.label}</small>
|
||||
</span>
|
||||
</article>
|
||||
))}
|
||||
@@ -114,7 +114,7 @@ export function DashboardPage({
|
||||
</div>
|
||||
|
||||
<div className="mt-[18px] grid gap-3.5 pb-[25px] max-[1200px]:grid-cols-1 min-[1201px]:grid-cols-[minmax(0,1.6fr)_minmax(310px,0.9fr)]">
|
||||
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)]">
|
||||
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-md">
|
||||
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
|
||||
<div>
|
||||
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
|
||||
@@ -136,7 +136,7 @@ export function DashboardPage({
|
||||
{Math.round(value / 7)}
|
||||
</span>
|
||||
<i
|
||||
className="block min-h-3 w-[min(36px,72%)] rounded-t-[5px] rounded-b-[2px] bg-gradient-to-b from-[#49a0eb] to-[#207bd1] shadow-[0_4px_9px_rgb(32_123_209/18%)]"
|
||||
className="block min-h-3 w-[min(36px,72%)] rounded-t-[5px] rounded-b-[2px] bg-gradient-to-b from-[#49a0eb] to-[#207bd1] shadow-md"
|
||||
style={{ height: `${value}%` }}
|
||||
/>
|
||||
<small className="text-[9px] text-[#7890a3]">
|
||||
@@ -157,7 +157,7 @@ export function DashboardPage({
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)]">
|
||||
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-md">
|
||||
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
|
||||
<div>
|
||||
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
|
||||
@@ -170,7 +170,7 @@ export function DashboardPage({
|
||||
<div className="dashboard-donut relative grid size-32 shrink-0 place-items-center rounded-full">
|
||||
<span className="z-[1] flex flex-col items-center">
|
||||
<b className="text-xl text-[#273d53]">{scriptCount}</b>
|
||||
<small className="text-[8px] text-[#8a9aaa]">全部脚本</small>
|
||||
<small className="text-[8px] text-ink-subtle">全部脚本</small>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[15px]">
|
||||
@@ -206,7 +206,7 @@ export function DashboardPage({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="col-span-full rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)] max-[1200px]:col-auto">
|
||||
<section className="col-span-full rounded-[10px] border border-[#dfe7ee] bg-white shadow-md max-[1200px]:col-auto">
|
||||
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
|
||||
<div>
|
||||
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
|
||||
|
||||
@@ -67,7 +67,7 @@ export function ImportMemberDialog({
|
||||
>
|
||||
<div className={modalFormClass}>
|
||||
<label className={`${formFieldClass} pb-[120px]`}>
|
||||
<span>选择用户<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>选择用户<span className="text-danger">*</span></span>
|
||||
<UserMultiSelect
|
||||
users={availableUsers}
|
||||
selectedUserIds={selectedUserIds}
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
} from "../platform/modalUi";
|
||||
|
||||
const FORM_INPUT_CLASS =
|
||||
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
|
||||
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
|
||||
const FORM_TEXTAREA_CLASS =
|
||||
"min-h-[92px] w-full resize-y rounded-md border border-[#d6e0e9] bg-white px-[11px] py-2.5 text-xs leading-[1.55] text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)]";
|
||||
"min-h-[92px] w-full resize-y rounded-md border border-[#d6e0e9] bg-white px-[11px] py-2.5 text-xs leading-[1.55] text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10";
|
||||
|
||||
export function ProjectEditDialog({
|
||||
open,
|
||||
@@ -47,7 +47,7 @@ export function ProjectEditDialog({
|
||||
<form className={modalFormClass} onSubmit={(event) => void onSubmit(event)}>
|
||||
{!editingProject && (
|
||||
<label className={formFieldClass}>
|
||||
<span>项目编码<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>项目编码<span className="text-danger">*</span></span>
|
||||
<Input
|
||||
className={FORM_INPUT_CLASS}
|
||||
autoFocus
|
||||
@@ -58,7 +58,7 @@ export function ProjectEditDialog({
|
||||
</label>
|
||||
)}
|
||||
<label className={formFieldClass}>
|
||||
<span>项目名称<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>项目名称<span className="text-danger">*</span></span>
|
||||
<Input
|
||||
className={FORM_INPUT_CLASS}
|
||||
value={projectForm.workspace_name}
|
||||
@@ -86,7 +86,7 @@ export function ProjectEditDialog({
|
||||
rows={4}
|
||||
/>
|
||||
</label>
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -32,6 +32,8 @@ import {
|
||||
statusPillClass,
|
||||
} from "./adminUi";
|
||||
|
||||
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
|
||||
|
||||
const EMPTY_PROJECT_FORM = {
|
||||
workspace_code: "",
|
||||
workspace_name: "",
|
||||
@@ -39,9 +41,6 @@ const EMPTY_PROJECT_FORM = {
|
||||
description: "",
|
||||
};
|
||||
|
||||
const PRIMARY_BUTTON_CLASS =
|
||||
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
|
||||
|
||||
export function ProjectManagementPage({
|
||||
onNotify,
|
||||
onConnectionChange,
|
||||
@@ -272,7 +271,7 @@ export function ProjectManagementPage({
|
||||
type="button"
|
||||
disabled={!canManage}
|
||||
onClick={openCreateProject}
|
||||
className={PRIMARY_BUTTON_CLASS}
|
||||
className={primaryGradientButtonClass}
|
||||
>
|
||||
<Plus size={15} />
|
||||
新建项目
|
||||
@@ -280,15 +279,15 @@ export function ProjectManagementPage({
|
||||
</div>
|
||||
|
||||
{!canManage && <div className={adminReadonlyClass}>当前为开发人员,只能查看项目列表。</div>}
|
||||
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
|
||||
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
|
||||
<AdminColgroup widths={USER_PROJECT_COL_WIDTHS} />
|
||||
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
|
||||
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
|
||||
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">项目名称</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">项目编码</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">状态</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">配额</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">操作</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">项目名称</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">项目编码</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">状态</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">配额</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type Workspace, type WorkspaceMember } from "../../services/api";
|
||||
import { Plus, X } from "lucide-react";
|
||||
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
|
||||
|
||||
export function ProjectMembersDrawer({
|
||||
open,
|
||||
@@ -75,7 +76,7 @@ export function ProjectMembersDrawer({
|
||||
共 {members.length} 名成员
|
||||
</span>
|
||||
<button
|
||||
className="primary-button"
|
||||
className={primaryGradientButtonClass}
|
||||
type="button"
|
||||
onClick={() => onAddMembers()}
|
||||
style={{ fontSize: 13, padding: "6px 12px" }}
|
||||
|
||||
@@ -167,7 +167,7 @@ export function RoleEditDialog({
|
||||
</label>
|
||||
) : (
|
||||
<label className={formFieldClass}>
|
||||
<span>角色编码<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>角色编码<span className="text-danger">*</span></span>
|
||||
<input
|
||||
className={formInputClass}
|
||||
autoFocus
|
||||
@@ -182,7 +182,7 @@ export function RoleEditDialog({
|
||||
</label>
|
||||
)}
|
||||
<label className={formFieldClass}>
|
||||
<span>角色名称<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>角色名称<span className="text-danger">*</span></span>
|
||||
<input
|
||||
className={formInputClass}
|
||||
value={form.role_name}
|
||||
@@ -214,7 +214,7 @@ export function RoleEditDialog({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -37,9 +37,7 @@ import {
|
||||
rowButtonClass,
|
||||
rowDangerButtonClass,
|
||||
} from "./adminUi";
|
||||
|
||||
const PRIMARY_BUTTON_CLASS =
|
||||
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
|
||||
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
|
||||
|
||||
export function RoleManagementPage({
|
||||
onNotify,
|
||||
@@ -208,7 +206,7 @@ export function RoleManagementPage({
|
||||
type="button"
|
||||
disabled={!canManage}
|
||||
onClick={openCreate}
|
||||
className={PRIMARY_BUTTON_CLASS}
|
||||
className={primaryGradientButtonClass}
|
||||
>
|
||||
<Plus size={15} />
|
||||
新建角色
|
||||
@@ -220,15 +218,15 @@ export function RoleManagementPage({
|
||||
)}
|
||||
{loadError && <div className={adminReadonlyClass}>{loadError}</div>}
|
||||
|
||||
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
|
||||
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
|
||||
<AdminColgroup widths={ROLE_COL_WIDTHS} />
|
||||
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
|
||||
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
|
||||
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">角色编码</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">角色名称</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">类型</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">权限</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">操作</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">角色编码</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">角色名称</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">类型</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">权限</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -41,6 +41,8 @@ import {
|
||||
statusPillClass,
|
||||
} from "./adminUi";
|
||||
|
||||
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
|
||||
|
||||
const EMPTY_FORM = {
|
||||
username: "",
|
||||
display_name: "",
|
||||
@@ -50,10 +52,8 @@ const EMPTY_FORM = {
|
||||
status: "active" as "active" | "disabled" | "locked",
|
||||
};
|
||||
|
||||
const PRIMARY_BUTTON_CLASS =
|
||||
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
|
||||
const FORM_INPUT_CLASS =
|
||||
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
|
||||
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
|
||||
export function UserManagementPage({
|
||||
onNotify,
|
||||
onConnectionChange,
|
||||
@@ -216,7 +216,7 @@ export function UserManagementPage({
|
||||
type="button"
|
||||
disabled={!canManage}
|
||||
onClick={openCreate}
|
||||
className={PRIMARY_BUTTON_CLASS}
|
||||
className={primaryGradientButtonClass}
|
||||
>
|
||||
<Plus size={15} />
|
||||
新建用户
|
||||
@@ -224,15 +224,15 @@ export function UserManagementPage({
|
||||
</div>
|
||||
|
||||
{!canManage && <div className={adminReadonlyClass}>当前为开发人员,只能查看用户列表。</div>}
|
||||
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
|
||||
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
|
||||
<AdminColgroup widths={USER_PROJECT_COL_WIDTHS} />
|
||||
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
|
||||
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
|
||||
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">用户</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">账号</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">角色</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">状态</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]">操作</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">用户</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">账号</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">角色</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">状态</TableHead>
|
||||
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -301,7 +301,7 @@ export function UserManagementPage({
|
||||
>
|
||||
<form className={modalFormClass} onSubmit={(event) => void submit(event)}>
|
||||
<label className={formFieldClass}>
|
||||
<span>姓名<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>姓名<span className="text-danger">*</span></span>
|
||||
<Input
|
||||
className={FORM_INPUT_CLASS}
|
||||
autoFocus={!editing}
|
||||
@@ -311,7 +311,7 @@ export function UserManagementPage({
|
||||
/>
|
||||
</label>
|
||||
<label className={formFieldClass}>
|
||||
<span>登录账号<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>登录账号<span className="text-danger">*</span></span>
|
||||
<Input
|
||||
className={FORM_INPUT_CLASS}
|
||||
disabled={Boolean(editing)}
|
||||
@@ -323,7 +323,7 @@ export function UserManagementPage({
|
||||
</label>
|
||||
{!editing && (
|
||||
<label className={formFieldClass}>
|
||||
<span>密码<span className="text-[#e74c3c]">*</span></span>
|
||||
<span>密码<span className="text-danger">*</span></span>
|
||||
<Input
|
||||
className={FORM_INPUT_CLASS}
|
||||
type="password"
|
||||
@@ -381,7 +381,7 @@ export function UserManagementPage({
|
||||
</select>
|
||||
</label>
|
||||
)}
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cn } from "~/lib/utils";
|
||||
export const adminPageClass = "h-full overflow-auto bg-[#f3f6f9] p-6";
|
||||
|
||||
export const adminToolbarClass =
|
||||
"mb-3.5 flex items-center justify-between rounded-lg border border-[#dfe7ef] bg-white px-3.5 py-3";
|
||||
"mb-3.5 flex items-center justify-between rounded-lg border border-line bg-white px-3.5 py-3";
|
||||
|
||||
export const adminToolbarSearchClass =
|
||||
"flex w-[280px] items-center gap-2 rounded-md border border-[#d9e2eb] bg-[#f8fafb] px-3 py-2 [&_svg]:text-[#8a99a8]";
|
||||
@@ -16,7 +16,7 @@ export const adminToolbarInputClass =
|
||||
export const adminReadonlyClass =
|
||||
"mb-3 rounded-md border border-[#f1d79c] bg-[#fff8e8] px-3.5 py-2.5 text-[11px] text-[#8a6416]";
|
||||
|
||||
export const adminEmptyClass = "p-[25px] text-center text-[#8796a6]";
|
||||
export const adminEmptyClass = "p-[25px] text-center text-ink-subtle";
|
||||
|
||||
export const employeeNameClass =
|
||||
"flex items-center gap-2.5 [&_small]:mt-[3px] [&_small]:text-[#93a0ae] [&_strong]:text-[#23384e] [&>span]:flex [&>span]:min-w-0 [&>span]:flex-col";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type FormEvent } from "react";
|
||||
import { Folder } from "lucide-react";
|
||||
import { Folder, Loader2Icon } from "lucide-react";
|
||||
import {
|
||||
AppFormDialog,
|
||||
dialogPrimaryButtonClass,
|
||||
@@ -57,7 +57,7 @@ export function CreateFolderModal({
|
||||
onChange={(event) => onNameChange(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -75,7 +75,7 @@ export function CreateFolderModal({
|
||||
className={dialogPrimaryButtonClass}
|
||||
>
|
||||
{busy
|
||||
? <span className="button-spinner" />
|
||||
? <Loader2Icon className="size-4 animate-spin" />
|
||||
: <Folder size={16} />}
|
||||
{busy ? "正在创建…" : "创建文件夹"}
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type FormEvent } from "react";
|
||||
import { BookOpen, Check, FileCode, Folder, Plus } from "lucide-react";
|
||||
import { BookOpen, Check, FileCode, Folder, Loader2Icon, Plus } from "lucide-react";
|
||||
import type { ScriptType, Visibility } from "../../services/api";
|
||||
import {
|
||||
AppFormDialog,
|
||||
@@ -113,7 +113,7 @@ export function CreateScriptModal({
|
||||
key={item.type}
|
||||
className={`relative flex min-h-[72px] cursor-pointer items-center gap-[9px] rounded-[7px] border p-2.5 text-left ${
|
||||
selected
|
||||
? "border-[#4d98df] bg-[#f4f9ff] shadow-[0_0_0_2px_rgb(49_133_216/7%)]"
|
||||
? "border-[#4d98df] bg-[#f4f9ff] ring-2 ring-brand/10"
|
||||
: "border-[#dae2ea] bg-white hover:border-[#a9c6e1]"
|
||||
}`}
|
||||
type="button"
|
||||
@@ -162,7 +162,7 @@ export function CreateScriptModal({
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -179,7 +179,7 @@ export function CreateScriptModal({
|
||||
disabled={creating || !form.name.trim()}
|
||||
className={dialogPrimaryButtonClass}
|
||||
>
|
||||
{creating ? <span className="button-spinner" /> : <Plus size={16} />}
|
||||
{creating ? <Loader2Icon className="size-4 animate-spin" /> : <Plus size={16} />}
|
||||
{creating ? "正在创建…" : "创建脚本"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type FormEvent } from "react";
|
||||
import { Upload } from "lucide-react";
|
||||
import { Loader2Icon, Upload } from "lucide-react";
|
||||
import type { Visibility } from "../../services/api";
|
||||
import {
|
||||
AppFormDialog,
|
||||
@@ -121,7 +121,7 @@ export function DataResourceUploadModal({
|
||||
onChange={(event) => onDescriptionChange(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -140,7 +140,7 @@ export function DataResourceUploadModal({
|
||||
className={dialogPrimaryButtonClass}
|
||||
>
|
||||
{uploading ? (
|
||||
<span className="button-spinner" />
|
||||
<Loader2Icon className="size-4 animate-spin" />
|
||||
) : (
|
||||
<Upload size={16} />
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type FormEvent } from "react";
|
||||
import { ArrowUpFromLine } from "lucide-react";
|
||||
import { ArrowUpFromLine, Loader2Icon } from "lucide-react";
|
||||
import type { ScriptItem, Visibility } from "../../services/api";
|
||||
import { scriptIcon } from "../../features/platform/WorkspaceTree";
|
||||
import {
|
||||
@@ -97,7 +97,7 @@ export function PublishModal({
|
||||
<option value="public">公开</option>
|
||||
</select>
|
||||
</label>
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -115,7 +115,7 @@ export function PublishModal({
|
||||
className={dialogPrimaryButtonClass}
|
||||
>
|
||||
{publishing
|
||||
? <span className="button-spinner" />
|
||||
? <Loader2Icon className="size-4 animate-spin" />
|
||||
: <ArrowUpFromLine size={16} />}
|
||||
{publishing ? "正在发布…" : "确认发布"}
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import { Info } from "lucide-react";
|
||||
import { Info, Loader2Icon } from "lucide-react";
|
||||
|
||||
import type { ScriptItem } from "../../services/api";
|
||||
|
||||
@@ -68,11 +68,11 @@ export function PythonEditor({
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-white" ref={containerRef}>
|
||||
<section
|
||||
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#dce5ed] bg-white/95 p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)]"
|
||||
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-line bg-white/95 p-9 text-center text-[#66788d] shadow-md"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div className="mb-4 grid size-[54px] place-items-center rounded-full bg-[#edf6ff] text-[#247bc5]">
|
||||
<span className="button-spinner button-spinner--blue size-6 border-[3px]" />
|
||||
<Loader2Icon className="size-6 animate-spin text-brand" />
|
||||
</div>
|
||||
<strong className="text-base text-[#1d344d]">正在打开 Python 编辑器</strong>
|
||||
<p className="mt-[9px] max-w-[430px] text-xs leading-[1.7] text-[#7a8a9b]">
|
||||
@@ -87,7 +87,7 @@ export function PythonEditor({
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-white" ref={containerRef}>
|
||||
<section
|
||||
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#f0cfcc] bg-white/95 p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)]"
|
||||
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#f0cfcc] bg-white/95 p-9 text-center text-[#66788d] shadow-md"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div className="mb-4 grid size-[54px] place-items-center rounded-full bg-[#fff1f0] text-[#c84f49]">
|
||||
|
||||
@@ -163,8 +163,8 @@ export function ScriptExplorer({
|
||||
}, [filteredScripts, directories, user, dataByOwner, members]);
|
||||
|
||||
return (
|
||||
<aside className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-[0_2px_7px_rgb(25_46_73/3%)]">
|
||||
<div className="flex min-h-16 items-center justify-between border-b border-[#e8edf2] px-3.5">
|
||||
<aside className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-md">
|
||||
<div className="flex min-h-16 items-center justify-between border-b border-line px-3.5">
|
||||
<div>
|
||||
<h2 className="mb-0.5 text-base text-[#1d2d42]">脚本目录</h2>
|
||||
</div>
|
||||
@@ -197,7 +197,7 @@ export function ScriptExplorer({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-3 mt-[11px] mb-[7px] flex h-[38px] items-center gap-[7px] rounded-md border border-[#dce5ed] bg-[#fbfcfd] px-[9px] text-[#95a2b1] focus-within:border-[#6aa9e9] focus-within:shadow-[0_0_0_3px_rgb(31_119_207/8%)]">
|
||||
<div className="mx-3 mt-[11px] mb-[7px] flex h-[38px] items-center gap-[7px] rounded-md border border-line bg-[#fbfcfd] px-[9px] text-[#95a2b1] focus-within:border-[#6aa9e9] focus-within:ring-3 focus-within:ring-brand/10">
|
||||
<Search size={17} />
|
||||
<input
|
||||
className="min-w-0 flex-1 border-0 bg-transparent text-xs text-[#26384d] outline-none placeholder:text-[#a7b2bf]"
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ExternalLink,
|
||||
FileCode,
|
||||
Info,
|
||||
Loader2Icon,
|
||||
Lock,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
@@ -504,7 +505,7 @@ export function ScriptWorkspace({
|
||||
onClick={() => onSavePythonEditor(script.script_id)}
|
||||
>
|
||||
{activePythonBuf?.saving
|
||||
? <><span className="button-spinner button-spinner--blue size-3" /> 保存中</>
|
||||
? <><Loader2Icon className="size-3 animate-spin text-brand" /> 保存中</>
|
||||
: "保存"}
|
||||
</button>
|
||||
)}
|
||||
@@ -592,7 +593,7 @@ export function ScriptWorkspace({
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
{readOnlyLoading ? (
|
||||
<div className="flex min-h-[200px] flex-col items-center justify-center gap-4 text-sm text-[#666]">
|
||||
<span className="button-spinner button-spinner--blue" />
|
||||
<Loader2Icon className="size-4 animate-spin text-brand" />
|
||||
<span>加载内容中...</span>
|
||||
</div>
|
||||
) : readOnlyError ? (
|
||||
@@ -683,7 +684,7 @@ export function ScriptWorkspace({
|
||||
>
|
||||
<div className="flex min-h-[34px] items-center gap-[18px] border-b border-[#dfe6ec] bg-[#f8fafc] px-3.5 text-[9px] text-[#718195]">
|
||||
<span className="inline-flex items-center gap-1.5 font-[650] text-[#197b59]">
|
||||
<i className="size-[7px] rounded-full bg-[#20b77d] shadow-[0_0_0_3px_rgb(32_183_125/12%)]" />
|
||||
<i className="size-[7px] rounded-full bg-[#20b77d] ring-3 ring-success/10" />
|
||||
Workspace Jupyter Server
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
@@ -709,10 +710,10 @@ export function ScriptWorkspace({
|
||||
{!sessionCache.has(script.script_id) ? (
|
||||
isNotebook ? (
|
||||
<section
|
||||
className={`mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)] ${
|
||||
className={`mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border p-9 text-center text-[#66788d] shadow-md ${
|
||||
openError
|
||||
? "border-[#f0cfcc] bg-white/95"
|
||||
: "border-[#dce5ed] bg-white/95"
|
||||
: "border-line bg-white/95"
|
||||
}`}
|
||||
aria-live="polite"
|
||||
>
|
||||
@@ -725,7 +726,7 @@ export function ScriptWorkspace({
|
||||
>
|
||||
{openError
|
||||
? <Info size={28} />
|
||||
: <span className="button-spinner button-spinner--blue size-6 border-[3px]" />}
|
||||
: <Loader2Icon className="size-6 animate-spin text-brand" />}
|
||||
</div>
|
||||
<strong className="text-base text-[#1d344d]">
|
||||
{openError ? "Notebook 打开失败" : "正在打开 Notebook"}
|
||||
@@ -743,7 +744,7 @@ export function ScriptWorkspace({
|
||||
onClick={onOpenEditor}
|
||||
>
|
||||
{editBusy
|
||||
? <span className="button-spinner button-spinner--blue" />
|
||||
? <Loader2Icon className="size-4 animate-spin text-brand" />
|
||||
: <RefreshCw size={16} />}
|
||||
{editBusy ? "正在重试…" : "重试打开"}
|
||||
</button>
|
||||
@@ -751,7 +752,7 @@ export function ScriptWorkspace({
|
||||
</section>
|
||||
) : isPython && !activePythonBuf ? (
|
||||
<section className="mx-auto mb-[38px] mt-6 w-[min(1020px,calc(100%-48px))] max-xl:w-[calc(100%-30px)]">
|
||||
<div className="flex items-start justify-between gap-5 rounded-t-[9px] border border-[#dce5ed] bg-white px-[26px] py-[23px]">
|
||||
<div className="flex items-start justify-between gap-5 rounded-t-[9px] border border-line bg-white px-[26px] py-[23px]">
|
||||
<div>
|
||||
<span className="text-[9px] font-extrabold tracking-[0.13em] text-[#2b7fca]">
|
||||
PYTHON SCRIPT
|
||||
@@ -774,7 +775,7 @@ export function ScriptWorkspace({
|
||||
onClick={onOpenEditor}
|
||||
>
|
||||
{editBusy
|
||||
? <span className="button-spinner button-spinner--blue" />
|
||||
? <Loader2Icon className="size-4 animate-spin text-brand" />
|
||||
: <ExternalLink size={17} />}
|
||||
{editBusy
|
||||
? "正在准备编辑器…"
|
||||
@@ -784,7 +785,7 @@ export function ScriptWorkspace({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 border-x border-b border-[#dce5ed] bg-[#f9fbfd]">
|
||||
<div className="grid grid-cols-4 border-x border-b border-line bg-[#f9fbfd]">
|
||||
{[
|
||||
["脚本类型", "Python"],
|
||||
[
|
||||
@@ -804,12 +805,12 @@ export function ScriptWorkspace({
|
||||
index < 3 ? "border-r border-[#e4eaf0]" : ""
|
||||
}`}
|
||||
>
|
||||
<span className="text-[10px] text-[#8a9aaa]">{label}</span>
|
||||
<span className="text-[10px] text-ink-subtle">{label}</span>
|
||||
<strong className="text-[13px] text-[#2a3f55]">{value}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="overflow-hidden rounded-b-[9px] border border-t-0 border-[#dce5ed] bg-white">
|
||||
<div className="overflow-hidden rounded-b-[9px] border border-t-0 border-line bg-white">
|
||||
<div className="flex items-center justify-between border-b border-[#e8edf3] bg-[#f7f9fb] px-3.5 py-2 text-[11px] text-[#6b7c8f]">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="size-2.5 rounded-full bg-[#ef6b62]" />
|
||||
|
||||
@@ -323,7 +323,7 @@ export default function ScriptsPage() {
|
||||
onHandleUpload={handleUpload}
|
||||
/>
|
||||
|
||||
<section className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-[0_2px_7px_rgb(25_46_73/3%)]">
|
||||
<section className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-md">
|
||||
{selected ? (
|
||||
<ScriptWorkspace
|
||||
script={selected}
|
||||
|
||||
@@ -59,7 +59,7 @@ export function TreeContextMenu({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed z-[1000] flex w-[180px] flex-col rounded-lg border border-[#d9e2ea] bg-white p-1.5 shadow-[0_14px_34px_rgb(18_43_68/20%)]"
|
||||
className="fixed z-[1000] flex w-[180px] flex-col rounded-lg border border-[#d9e2ea] bg-white p-1.5 shadow-lg"
|
||||
role="menu"
|
||||
style={{
|
||||
left: contextMenu.x,
|
||||
@@ -179,7 +179,7 @@ export function TreeContextMenu({
|
||||
</button>
|
||||
{contextMenu.kind === "directory" && (
|
||||
<>
|
||||
<span className="mx-[3px] my-[5px] h-px bg-[#e7ecf1]" />
|
||||
<span className="mx-[3px] my-[5px] h-px bg-line-soft" />
|
||||
<button
|
||||
className={dangerItemClass}
|
||||
type="button"
|
||||
|
||||
@@ -29,7 +29,7 @@ export function VersionReceiptModal({
|
||||
contentClassName="sm:max-w-[min(470px,calc(100vw-40px))] px-7 py-[30px] pb-[25px] text-center"
|
||||
bodyClassName="overflow-visible"
|
||||
>
|
||||
<div className="mx-auto mb-3.5 grid size-[58px] place-items-center rounded-full bg-gradient-to-br from-[#24b67d] to-[#149566] text-white shadow-[0_9px_24px_rgb(25_157_104/25%)]">
|
||||
<div className="mx-auto mb-3.5 grid size-[58px] place-items-center rounded-full bg-gradient-to-br from-[#24b67d] to-[#149566] text-white shadow-lg">
|
||||
<Check size={28} />
|
||||
</div>
|
||||
<span className={modalEyebrowClass}>STABLE VERSION</span>
|
||||
|
||||
@@ -9,7 +9,7 @@ type WelcomePanelProps = {
|
||||
export function WelcomePanel({ onCreateScript }: WelcomePanelProps) {
|
||||
return (
|
||||
<div className="welcome-panel-shell flex min-h-full flex-col items-center justify-center p-[35px] text-center">
|
||||
<div className="mb-[21px] grid size-[86px] place-items-center rounded-[25px] border border-[#cfe0f0] bg-gradient-to-br from-white to-[#eaf4fd] text-[#3d8cd9] shadow-[0_16px_35px_rgb(40_103_165/12%)]">
|
||||
<div className="mb-[21px] grid size-[86px] place-items-center rounded-[25px] border border-[#cfe0f0] bg-gradient-to-br from-white to-[#eaf4fd] text-[#3d8cd9] shadow-lg">
|
||||
<SquareChartGantt size={42}/>
|
||||
</div>
|
||||
<span className="text-[10px] font-extrabold tracking-[0.12em] text-[#2e81ce]">
|
||||
|
||||
@@ -302,7 +302,7 @@ function WorkspaceTreeItems({
|
||||
)}
|
||||
{item.visibility !== "private" && (
|
||||
<span
|
||||
className="size-1.5 shrink-0 rounded-full bg-[#1dbd7c] shadow-[0_0_0_3px_rgb(29_189_124/10%)]"
|
||||
className="size-1.5 shrink-0 rounded-full bg-[#1dbd7c] ring-3 ring-success/10"
|
||||
title="Workspace 可见"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -4,13 +4,13 @@ export const modalBackdropClass =
|
||||
"fixed inset-0 z-20 grid place-items-center bg-[rgb(10_27_45/43%)] backdrop-blur-[2px]";
|
||||
|
||||
export const modalPanelClass =
|
||||
"modal-panel w-[min(520px,calc(100vw-40px))] overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-[0_24px_70px_rgb(8_27_48/25%)]";
|
||||
"modal-panel w-[min(520px,calc(100vw-40px))] overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-xl";
|
||||
|
||||
export const modalCompactClass =
|
||||
"modal-panel flex max-h-[80vh] w-[min(480px,calc(100vw-40px))] flex-col overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-[0_24px_70px_rgb(8_27_48/25%)]";
|
||||
"modal-panel flex max-h-[80vh] w-[min(480px,calc(100vw-40px))] flex-col overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-xl";
|
||||
|
||||
export const modalHeaderClass =
|
||||
"flex shrink-0 items-center justify-between border-b border-[#e7ecf1] px-[22px] pb-4 pt-5";
|
||||
"flex shrink-0 items-center justify-between border-b border-line px-[22px] pb-4 pt-5";
|
||||
|
||||
export const modalEyebrowClass =
|
||||
"text-[9px] font-extrabold tracking-[0.12em] text-[#2d82d4]";
|
||||
@@ -20,13 +20,12 @@ export const modalTitleClass = "mt-1 text-[19px] text-[#1c2d42]";
|
||||
export const modalFormClass = "flex-1 overflow-y-auto px-[22px] pt-[19px]";
|
||||
|
||||
export const modalFooterClass =
|
||||
"mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white rounded-b-[11px] px-[22px] py-3.5";
|
||||
"mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white rounded-b-[11px] px-[22px] py-3.5";
|
||||
|
||||
export const iconButtonClass =
|
||||
"grid size-[34px] cursor-pointer place-items-center rounded-[7px] border border-[#dfe6ed] bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]";
|
||||
"grid size-[34px] cursor-pointer place-items-center rounded-[7px] border border-line bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]";
|
||||
|
||||
export const primaryButtonClass =
|
||||
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] hover:from-[#1175d8] hover:to-[#0e5bad] disabled:cursor-not-allowed disabled:opacity-[0.58]";
|
||||
export { primaryGradientButtonClass as primaryButtonClass } from "~/components/common/buttonClasses";
|
||||
|
||||
export const secondaryButtonClass =
|
||||
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-[7px] rounded-md border border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]";
|
||||
@@ -38,7 +37,7 @@ export const formFieldClass =
|
||||
"mb-[17px] flex flex-col gap-[7px] text-xs font-[650] text-[#3c4e62]";
|
||||
|
||||
export const formControlClass =
|
||||
"w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)]";
|
||||
"w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10";
|
||||
|
||||
export const formInputClass = `${formControlClass} h-[39px]`;
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ function ArtifactListImpl({
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-[#dce5ed] bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]">
|
||||
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-line bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:ring-3 focus-within:ring-brand/10">
|
||||
<Search size={15} />
|
||||
<input
|
||||
className="min-w-0 flex-1 border-0 bg-transparent text-[12px] text-[#33475d] outline-none placeholder:text-[#a6b2be]"
|
||||
@@ -95,7 +95,7 @@ function ArtifactListImpl({
|
||||
return (
|
||||
<div
|
||||
key={scriptName}
|
||||
className="mb-1.5 overflow-hidden rounded-[8px] border border-line-soft bg-white transition-[border-color,box-shadow] duration-[160ms] hover:border-[#e1e8ef]"
|
||||
className="mb-1.5 overflow-hidden rounded-[8px] border border-line-soft bg-white transition-[border-color,box-shadow] duration-150 hover:border-[#e1e8ef]"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@@ -106,7 +106,7 @@ function ArtifactListImpl({
|
||||
<span className="flex min-w-0 items-center gap-[7px]">
|
||||
<span
|
||||
data-state={isExpanded ? "open" : "closed"}
|
||||
className="h-[14px] w-[14px] flex-none text-[#9aa7b5] transition-transform duration-[180ms] data-[state=open]:rotate-90"
|
||||
className="h-[14px] w-[14px] flex-none text-[#9aa7b5] transition-transform duration-200 data-[state=open]:rotate-90"
|
||||
>
|
||||
<ChevronRight size={14} />
|
||||
</span>
|
||||
@@ -140,7 +140,7 @@ function ArtifactListImpl({
|
||||
<div className="flex flex-col gap-[5px] border-t border-line-soft bg-[#f8fafc] p-[7px_7px_7px_27px] min-[1281px]:pl-[29px]">
|
||||
{items.map((artifact) => (
|
||||
<div
|
||||
className="group flex min-h-[34px] cursor-grab select-none items-center justify-between gap-[7px] rounded-[6px] border border-[#e4e9ee] bg-white p-1.5 transition-[border-color,background-color] duration-[140ms] hover:border-[#a8c8e9] hover:bg-[#fbfdff] active:cursor-grabbing min-[1281px]:px-[7px]"
|
||||
className="group flex min-h-[34px] cursor-grab select-none items-center justify-between gap-[7px] rounded-[6px] border border-[#e4e9ee] bg-white p-1.5 transition-[border-color,background-color] duration-150 hover:border-[#a8c8e9] hover:bg-[#fbfdff] active:cursor-grabbing min-[1281px]:px-[7px]"
|
||||
draggable
|
||||
key={artifact.versions_id}
|
||||
onDragStart={(event) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ export function NodeInspector({
|
||||
}) {
|
||||
return (
|
||||
<div className="flex min-h-full flex-col">
|
||||
<section className="flex flex-row items-center gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-row items-center gap-[10px] border-b border-line p-[14px]">
|
||||
<span
|
||||
className={`grid h-[31px] w-[31px] flex-none place-items-center rounded-[6px] ${
|
||||
node.version.script_type === "notebook"
|
||||
@@ -41,7 +41,7 @@ export function NodeInspector({
|
||||
</strong>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">稳定版本</h3>
|
||||
<div className="flex flex-col gap-[5px] rounded-[6px] border border-[#dae6f2] bg-[#f6faff] p-[9px]">
|
||||
<strong className="text-[11px] text-[#35516e]">{node.version.script_name}</strong>
|
||||
@@ -56,12 +56,12 @@ export function NodeInspector({
|
||||
</small>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">节点信息</h3>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">节点名称</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">节点名称</span>
|
||||
<input
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.nodeName}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -71,11 +71,11 @@ export function NodeInspector({
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">超时(秒)</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">超时(秒)</span>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.timeoutSeconds}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -84,12 +84,12 @@ export function NodeInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">重试次数</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">重试次数</span>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="10"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.retryCount}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -100,11 +100,11 @@ export function NodeInspector({
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">重试间隔(秒)</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">重试间隔(秒)</span>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.retryIntervalSec}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -113,9 +113,9 @@ export function NodeInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">Python 版本</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">Python 版本</span>
|
||||
<select
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.pythonVersion}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -131,12 +131,12 @@ export function NodeInspector({
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">运行参数</h3>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">arguments_json</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">arguments_json</span>
|
||||
<textarea
|
||||
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
rows={5}
|
||||
spellCheck={false}
|
||||
value={form.argumentsJson}
|
||||
@@ -147,9 +147,9 @@ export function NodeInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">env_refs_json</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">env_refs_json</span>
|
||||
<textarea
|
||||
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
rows={4}
|
||||
spellCheck={false}
|
||||
value={form.envRefsJson}
|
||||
|
||||
@@ -216,8 +216,8 @@ export function RunHistory({
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="flex min-h-0 flex-col border-t border-[#e0e7ee] bg-[#fbfcfe]">
|
||||
<header className="flex min-h-[44px] items-center justify-between border-b border-[#e8edf2] bg-white px-3">
|
||||
<section className="flex min-h-0 flex-col border-t border-line bg-[#fbfcfe]">
|
||||
<header className="flex min-h-[44px] items-center justify-between border-b border-line bg-white px-3">
|
||||
<div className="flex items-center gap-[7px]">
|
||||
<strong className="text-[12px] text-[#2b4056]">运行记录</strong>
|
||||
<span className="inline-grid h-[18px] min-w-[18px] place-items-center rounded-[9px] bg-[#edf2f7] text-[9px] text-[#71849a]">
|
||||
@@ -250,7 +250,7 @@ export function RunHistory({
|
||||
return (
|
||||
<article
|
||||
data-state={expanded ? "expanded" : "collapsed"}
|
||||
className="flex-none overflow-hidden rounded-[6px] border border-[#e1e8ef] bg-white data-[state=expanded]:border-[#b9d6f1] data-[state=expanded]:shadow-[0_3px_10px_rgb(38_117_185_/_8%)]"
|
||||
className="flex-none overflow-hidden rounded-[6px] border border-[#e1e8ef] bg-white data-[state=expanded]:border-[#b9d6f1] data-[state=expanded]:shadow-md"
|
||||
key={run.run_id}
|
||||
>
|
||||
<div
|
||||
@@ -295,7 +295,7 @@ export function RunHistory({
|
||||
onClick={() => toggleRun(run.run_id)}
|
||||
/>
|
||||
<section
|
||||
className="fixed left-1/2 top-1/2 z-[41] grid max-h-[min(760px,calc(100vh-64px))] w-[min(780px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-[12px] border border-[#d8e2eb] bg-white shadow-[0_24px_80px_rgb(8_27_48_/_30%)] [grid-template-rows:auto_minmax(0,1fr)]"
|
||||
className="fixed left-1/2 top-1/2 z-[41] grid max-h-[min(760px,calc(100vh-64px))] w-[min(780px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-[12px] border border-[#d8e2eb] bg-white shadow-xl [grid-template-rows:auto_minmax(0,1fr)]"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="调度运行结果"
|
||||
@@ -311,7 +311,7 @@ export function RunHistory({
|
||||
</code>
|
||||
</div>
|
||||
<button
|
||||
className="grid h-[34px] w-[34px] cursor-pointer place-items-center rounded-[7px] border border-[#dfe6ed] bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]"
|
||||
className="grid h-[34px] w-[34px] cursor-pointer place-items-center rounded-[7px] border border-line bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]"
|
||||
type="button"
|
||||
aria-label="关闭运行结果"
|
||||
onClick={() => toggleRun(run.run_id)}
|
||||
@@ -321,13 +321,13 @@ export function RunHistory({
|
||||
</header>
|
||||
<div className="min-h-0 overflow-auto bg-[#f8fbfe] p-[18px]">
|
||||
{detailLoadingId === run.run_id && !detail ? (
|
||||
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6]">
|
||||
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle">
|
||||
正在加载运行详情…
|
||||
</p>
|
||||
) : detailErrors[run.run_id] ? (
|
||||
<p
|
||||
data-state="error"
|
||||
className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6] data-[state=error]:text-[#bf5656]"
|
||||
className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle data-[state=error]:text-[#bf5656]"
|
||||
>
|
||||
{detailErrors[run.run_id]}
|
||||
</p>
|
||||
@@ -369,7 +369,7 @@ export function RunHistory({
|
||||
)}
|
||||
<div className="flex flex-col gap-2.5">
|
||||
{detail.node_runs.length === 0 ? (
|
||||
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6]">
|
||||
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle">
|
||||
节点尚未开始执行
|
||||
</p>
|
||||
) : detail.node_runs.map((nodeRun) => {
|
||||
|
||||
@@ -43,12 +43,12 @@ export function ScheduleInspector({
|
||||
}
|
||||
return (
|
||||
<div className="flex min-h-full flex-col">
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">基本信息</h3>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">调度名称</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">调度名称</span>
|
||||
<input
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.scheduleName}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -57,9 +57,9 @@ export function ScheduleInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">说明</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">说明</span>
|
||||
<textarea
|
||||
className="w-full resize-y rounded-[5px] border border-line bg-white p-[7px_8px] text-[11px] leading-[1.5] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="w-full resize-y rounded-[5px] border border-line bg-white p-[7px_8px] text-[11px] leading-[1.5] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
rows={2}
|
||||
value={form.description}
|
||||
onChange={(event) => onChange({
|
||||
@@ -85,12 +85,12 @@ export function ScheduleInspector({
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">触发器</h3>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">触发方式</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">触发方式</span>
|
||||
<select
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.triggerType}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -106,9 +106,9 @@ export function ScheduleInspector({
|
||||
{form.triggerType === "cron" && (
|
||||
<>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">Cron(分 时 日 月 周)</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">Cron(分 时 日 月 周)</span>
|
||||
<input
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.cronExpression}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -117,9 +117,9 @@ export function ScheduleInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">时区</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">时区</span>
|
||||
<input
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.timezone}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -150,16 +150,16 @@ export function ScheduleInspector({
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
|
||||
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
|
||||
<h3 className="mb-[2px] text-[13px] text-[#273b50]">执行策略</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">最大并发</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">最大并发</span>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="64"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.maxConcurrency}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
@@ -168,9 +168,9 @@ export function ScheduleInspector({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-[5px]">
|
||||
<span className="text-[10px] font-semibold text-[#758497]">失败策略</span>
|
||||
<span className="text-[10px] font-semibold text-ink-caption">失败策略</span>
|
||||
<select
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
|
||||
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
|
||||
value={form.failurePolicy}
|
||||
onChange={(event) => onChange({
|
||||
...form,
|
||||
|
||||
@@ -35,7 +35,7 @@ export function ScheduleList({
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-[#dce5ed] bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]">
|
||||
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-line bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:ring-3 focus-within:ring-brand/10">
|
||||
<Search size={15} />
|
||||
<input
|
||||
className="min-w-0 flex-1 border-0 bg-transparent text-[12px] text-[#33475d] outline-none placeholder:text-[#a6b2be]"
|
||||
@@ -64,7 +64,7 @@ export function ScheduleList({
|
||||
>
|
||||
<span
|
||||
data-state={item.enabled ? "enabled" : "disabled"}
|
||||
className="h-2 w-2 flex-none rounded-full bg-[#aeb9c5] shadow-[0_0_0_3px_rgb(142_156_171_/_11%)] data-[state=enabled]:bg-success data-[state=enabled]:shadow-[0_0_0_3px_rgb(24_185_121_/_12%)]"
|
||||
className="h-2 w-2 flex-none rounded-full bg-[#aeb9c5] ring-3 ring-ink-subtle/15 data-[state=enabled]:bg-success data-[state=enabled]:ring-3 data-[state=enabled]:ring-success/10"
|
||||
/>
|
||||
<span className="flex min-w-0 flex-col gap-1">
|
||||
<strong className="max-w-[205px] truncate text-[12px] text-[#283b50]">
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
CalendarClock,
|
||||
Check,
|
||||
FileCode2,
|
||||
Loader2Icon,
|
||||
PackageOpen,
|
||||
Play,
|
||||
Plus,
|
||||
@@ -367,7 +368,7 @@ export default function SchedulePage({
|
||||
|
||||
return (
|
||||
<section className="relative flex min-h-0 flex-1 flex-col gap-[10px] overflow-hidden p-[10px]">
|
||||
<header className="flex min-h-[56px] items-center justify-between rounded-[7px] border border-line bg-white px-[14px] shadow-[0_2px_7px_rgb(25_46_73_/_3%)]">
|
||||
<header className="flex min-h-[56px] items-center justify-between rounded-[7px] border border-line bg-white px-[14px] shadow-md">
|
||||
<div className="flex items-baseline gap-[10px]">
|
||||
<strong className="text-[16px] text-[#1c2d42]">图形化调度</strong>
|
||||
<span className="hidden text-[11px] text-[#8b99a9] 2xl:inline">
|
||||
@@ -423,7 +424,7 @@ export default function SchedulePage({
|
||||
</header>
|
||||
|
||||
<div className="grid min-h-0 flex-1 gap-[10px] [grid-template-columns:225px_minmax(360px,1fr)_270px] min-[1281px]:[grid-template-columns:250px_minmax(380px,1fr)_292px] 2xl:[grid-template-columns:278px_minmax(420px,1fr)_316px]">
|
||||
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)] [grid-template-rows:minmax(210px,42%)_minmax(260px,58%)]">
|
||||
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-md [grid-template-rows:minmax(210px,42%)_minmax(260px,58%)]">
|
||||
<ScheduleList
|
||||
schedules={filteredSchedules}
|
||||
selectedScheduleId={schedule?.schedule_id ?? null}
|
||||
@@ -443,7 +444,7 @@ export default function SchedulePage({
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<main className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)]">
|
||||
<main className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-line bg-white shadow-md">
|
||||
<ScheduleCanvasHeader
|
||||
linkSourceId={linkSourceId}
|
||||
onCancelLink={() => setLinkSourceId(null)}
|
||||
@@ -510,7 +511,7 @@ export default function SchedulePage({
|
||||
|
||||
{schedule?.nodes.map((node) => (
|
||||
<div
|
||||
className={`schedule-node absolute z-[2] flex cursor-grab touch-none select-none flex-col rounded-[8px] border border-[#91b7de] bg-white p-[11px_12px_9px] text-[#34516f] shadow-[0_7px_18px_rgb(36_75_117_/_11%)] active:cursor-grabbing data-[state=selected]:border-brand data-[state=selected]:shadow-[0_0_0_3px_rgb(25_120_212_/_11%),0_8px_22px_rgb(36_75_117_/_13%)]`}
|
||||
className={`schedule-node absolute z-[2] flex cursor-grab touch-none select-none flex-col rounded-[8px] border border-[#91b7de] bg-white p-[11px_12px_9px] text-[#34516f] shadow-md active:cursor-grabbing data-[state=selected]:border-brand data-[state=selected]:ring-3 data-[state=selected]:ring-brand/10 data-[state=selected]:shadow-md`}
|
||||
data-state={selectedNodeId === node.node_id ? "selected" : "idle"}
|
||||
key={node.node_id}
|
||||
style={{
|
||||
@@ -529,7 +530,7 @@ export default function SchedulePage({
|
||||
})}
|
||||
>
|
||||
<button
|
||||
className="absolute left-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)]"
|
||||
className="absolute left-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:ring-4 hover:ring-brand/15"
|
||||
type="button"
|
||||
aria-label={`连接到${node.node_name}`}
|
||||
title={linkSourceId ? "点击完成连线" : "输入端口"}
|
||||
@@ -580,7 +581,7 @@ export default function SchedulePage({
|
||||
<b className="text-[10px] text-brand">{node.version.version_label}</b>
|
||||
</footer>
|
||||
<button
|
||||
className="absolute right-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)] data-[state=active]:border-[#176ec5] data-[state=active]:bg-[#4e92db] data-[state=active]:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)]"
|
||||
className="absolute right-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:ring-4 hover:ring-brand/15 data-[state=active]:border-[#176ec5] data-[state=active]:bg-[#4e92db] data-[state=active]:ring-4 data-[state=active]:ring-brand/15"
|
||||
data-state={linkSourceId === node.node_id ? "active" : "idle"}
|
||||
type="button"
|
||||
aria-label={`从${node.node_name}开始连线`}
|
||||
@@ -647,7 +648,7 @@ export default function SchedulePage({
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)] [grid-template-rows:repeat(2,minmax(0,1fr))]">
|
||||
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-md [grid-template-rows:repeat(2,minmax(0,1fr))]">
|
||||
<div className="min-h-0 overflow-auto">
|
||||
{selectedNode ? (
|
||||
<NodeInspector
|
||||
@@ -682,7 +683,7 @@ export default function SchedulePage({
|
||||
|
||||
{contextMenu && (
|
||||
<div
|
||||
className="fixed z-[60] min-w-[168px] rounded-[8px] border border-[#d9e3ec] bg-white p-[5px] shadow-[0_14px_36px_rgb(14_35_57_/_22%)]"
|
||||
className="fixed z-[60] min-w-[168px] rounded-[8px] border border-[#d9e3ec] bg-white p-[5px] shadow-lg"
|
||||
role="menu"
|
||||
style={{ left: contextMenu.x, top: contextMenu.y }}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
@@ -783,7 +784,7 @@ export default function SchedulePage({
|
||||
onFocus={(event) => event.currentTarget.select()}
|
||||
/>
|
||||
</label>
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -802,7 +803,7 @@ export default function SchedulePage({
|
||||
className={dialogPrimaryButtonClass}
|
||||
>
|
||||
{busy === "create-schedule"
|
||||
? <span className="button-spinner" />
|
||||
? <Loader2Icon className="size-4 animate-spin" />
|
||||
: <Plus size={15} />}
|
||||
{busy === "create-schedule" ? "正在创建…" : "创建调度"}
|
||||
</Button>
|
||||
@@ -812,7 +813,7 @@ export default function SchedulePage({
|
||||
|
||||
{busy && (
|
||||
<div
|
||||
className="absolute bottom-[22px] right-[22px] z-[20] flex min-h-[38px] items-center gap-2 rounded-[7px] border border-[#c9dcee] bg-white px-[13px] text-[11px] text-[#4e657d] shadow-[0_8px_25px_rgb(28_56_86_/_15%)]"
|
||||
className="absolute bottom-[22px] right-[22px] z-[20] flex min-h-[38px] items-center gap-2 rounded-[7px] border border-[#c9dcee] bg-white px-[13px] text-[11px] text-[#4e657d] shadow-lg"
|
||||
aria-live="polite"
|
||||
>
|
||||
<span className="h-[15px] w-[15px] rounded-full border-2 border-[#b9d4ef] border-t-brand" />
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function LoginRoute() {
|
||||
<main className="flex min-h-screen items-center justify-center bg-[radial-gradient(circle_at_20%_15%,rgba(22,119,255,0.12),transparent_34%),linear-gradient(145deg,#eef5fb,#f8fafc_55%,#edf3f8)] p-12">
|
||||
<section
|
||||
aria-labelledby="login-title"
|
||||
className="w-[420px] rounded-[18px] border border-[#dbe6ef] bg-white/[0.96] p-[42px] shadow-[0_22px_60px_rgba(37,63,88,0.14)]"
|
||||
className="w-[420px] rounded-[18px] border border-[#dbe6ef] bg-white/[0.96] p-[42px] shadow-xl"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
@@ -65,7 +65,7 @@ export default function LoginRoute() {
|
||||
onChange={(event) => setUsername(event.target.value)}
|
||||
placeholder="请输入用户名"
|
||||
disabled={busy}
|
||||
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:shadow-[0_0_0_3px_rgba(22,119,255,0.12)] disabled:opacity-65 disabled:cursor-not-allowed"
|
||||
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:ring-3 focus:ring-brand/10 disabled:opacity-65 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</label>
|
||||
<label className="grid gap-2 text-[13px] text-[#465b70]">
|
||||
@@ -77,7 +77,7 @@ export default function LoginRoute() {
|
||||
onChange={(event) => setPassword(event.target.value)}
|
||||
placeholder="请输入密码"
|
||||
disabled={busy}
|
||||
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:shadow-[0_0_0_3px_rgba(22,119,255,0.12)] disabled:opacity-65 disabled:cursor-not-allowed"
|
||||
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:ring-3 focus:ring-brand/10 disabled:opacity-65 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</label>
|
||||
{error && (
|
||||
|
||||
Reference in New Issue
Block a user