update:css样式拆分

This commit is contained in:
xiaozhu
2026-08-25 16:52:10 +08:00
parent 76aa2dcb27
commit 12111de788
27 changed files with 1404 additions and 3059 deletions
@@ -1,6 +1,22 @@
import { type FormEvent } from "react";
import Icon from "../../components/common/Icon";
import type { ScriptType, Visibility } from "../../services/api";
import {
destinationChipClass,
formFieldClass,
formHintClass,
formInputClass,
iconButtonClass,
modalBackdropClass,
modalEyebrowClass,
modalFooterClass,
modalFormClass,
modalHeaderClass,
modalPanelClass,
modalTitleClass,
primaryButtonClass,
secondaryButtonClass,
} from "./modalUi";
type NewScriptForm = {
name: string;
@@ -27,44 +43,23 @@ export function CreateScriptModal({
open,
creating,
form,
scripts,
scripts: _scripts,
onFormChange,
onSubmit,
onClose,
}: CreateScriptModalProps) {
if (!open) return null;
const suffix = form.scriptType === "notebook" ? ".ipynb" : ".py";
const requestedName = form.name.trim();
const normalizedName = requestedName.toLocaleLowerCase().endsWith(suffix)
? requestedName
: `${requestedName}${suffix}`;
const duplicate = scripts.some((script) => {
if (script.script_type !== form.scriptType) return false;
if (script.script_name.toLocaleLowerCase()
!== normalizedName.toLocaleLowerCase()) return false;
// Same name in a different subdirectory is allowed; mirror the
// backend name_clash scope (StorageObjects.relative_path JOIN).
if (!script.relative_path) return true;
const segments = script.relative_path.replaceAll("\\", "/")
.split("/").slice(2);
const existingUserPath = segments.join("/");
const existingParent = existingUserPath.includes("/")
? existingUserPath.slice(0, existingUserPath.lastIndexOf("/"))
: "";
return existingParent === form.parentPath;
});
return (
<div className="modal-backdrop" role="presentation">
<section className="modal" role="dialog" aria-modal="true">
<div className="modal__header">
<div className={modalBackdropClass} role="presentation">
<section className={modalPanelClass} role="dialog" aria-modal="true">
<div className={modalHeaderClass}>
<div>
<span className="modal__eyebrow"></span>
<h2></h2>
<span className={modalEyebrowClass}></span>
<h2 className={modalTitleClass}></h2>
</div>
<button
className="icon-button"
className={iconButtonClass}
type="button"
aria-label="关闭"
onClick={onClose}
@@ -72,14 +67,15 @@ export function CreateScriptModal({
<Icon name="close" />
</button>
</div>
<form onSubmit={onSubmit}>
<div className="destination-chip">
<form className={modalFormClass} onSubmit={onSubmit}>
<div className={destinationChipClass}>
<Icon name="folder" size={16} />
{form.parentPath || "个人根目录"}
</div>
<label className="form-field">
<label className={formFieldClass}>
<span></span>
<input
className={formInputClass}
autoFocus
maxLength={255}
placeholder={form.scriptType === "notebook"
@@ -92,57 +88,78 @@ export function CreateScriptModal({
name: event.target.value,
})}
/>
<small>
<small className={formHintClass}>
{form.scriptType === "notebook" ? " .ipynb" : " .py"}
</small>
</label>
<fieldset className="type-picker">
<legend></legend>
<button
className={form.scriptType === "notebook" ? "is-selected" : ""}
type="button"
onClick={() => onFormChange({
...form,
scriptType: "notebook",
})}
>
<span className="type-picker__icon type-picker__icon--notebook">
<Icon name="notebook" size={22} />
</span>
<span>
<strong>Jupyter Notebook</strong>
<small></small>
</span>
<span className="type-picker__check">
<Icon name="check" size={14} />
</span>
</button>
<button
className={form.scriptType === "python" ? "is-selected" : ""}
type="button"
onClick={() => onFormChange({
...form,
scriptType: "python",
})}
>
<span className="type-picker__icon type-picker__icon--python">
<Icon name="python" size={23} />
</span>
<span>
<strong>Python </strong>
<small></small>
</span>
<span className="type-picker__check">
<Icon name="check" size={14} />
</span>
</button>
<fieldset className="mb-[17px] grid grid-cols-2 gap-[9px] border-0 p-0">
<legend className="mb-[7px] text-xs font-[650] text-[#3c4e62]">
</legend>
{(
[
{
type: "notebook" as const,
title: "Jupyter Notebook",
desc: "交互式数据探索与模型实验",
icon: "notebook" as const,
iconTone: "bg-[#fff0ed] text-[#dd5a4c]",
size: 22,
},
{
type: "python" as const,
title: "Python 脚本",
desc: "批处理、训练和模型调用任务",
icon: "python" as const,
iconTone: "bg-[#e9f3ff] text-[#2a75c5]",
size: 23,
},
] as const
).map((item) => {
const selected = form.scriptType === item.type;
return (
<button
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-[#dae2ea] bg-white hover:border-[#a9c6e1]"
}`}
type="button"
onClick={() => onFormChange({
...form,
scriptType: item.type,
})}
>
<span
className={`grid size-[37px] shrink-0 place-items-center rounded-lg ${item.iconTone}`}
>
<Icon name={item.icon} size={item.size} />
</span>
<span className="flex min-w-0 flex-col">
<strong className="text-[11px] text-[#34475b]">
{item.title}
</strong>
<small className="mt-[3px] text-[8px] leading-[1.35] text-[#919eac]">
{item.desc}
</small>
</span>
{selected && (
<span className="absolute top-1.5 right-1.5 grid size-[17px] place-items-center rounded-full bg-[#2e88dd] text-white">
<Icon name="check" size={14} />
</span>
)}
</button>
);
})}
</fieldset>
<label className="form-field">
<label className={formFieldClass}>
<span></span>
<select
className={formInputClass}
value={form.visibility}
onChange={(event) =>
onFormChange({
@@ -156,16 +173,16 @@ export function CreateScriptModal({
</select>
</label>
<div className="modal__footer">
<div className={modalFooterClass}>
<button
className="secondary-button"
className={secondaryButtonClass}
type="button"
onClick={onClose}
>
</button>
<button
className="primary-button"
className={primaryButtonClass}
type="submit"
disabled={creating || !form.name.trim()}
>