>;
+ onChange: (key: FilterDim["key"], value: string) => void;
+}) {
+ return (
+
+ {dims
+ // 只有一个可选值又没选时不用筛(比如只有一个媒体库目录)
+ .filter((d) => d.options.length > 1 || !!values[d.key])
+ .map((d) => (
+ onChange(d.key, x)} />
+ ))}
+
+ );
+}
+
+function DimButton({ dim, value, onChange }: { dim: FilterDim; value: string; onChange: (v: string) => void }) {
+ const [open, setOpen] = useState(false);
+ const pick = (x: string) => {
+ setOpen(false);
+ onChange(x);
+ };
+ return (
+
+
+
+
+
+
+ pick("")} />
+ {dim.options.map((o) => (
+ pick(value === o.value ? "" : o.value)}
+ />
+ ))}
+
+
+
+ );
+}
+
+function OptionRow({ on, label, n, onClick }: { on: boolean; label: string; n?: number; onClick: () => void }) {
+ return (
+
+ );
+}
+
+/** 维度按钮;Popover 的 asChild 需要转发 ref 与事件,所以透传其余属性 */
+function Chip({
+ label,
+ active,
+ className,
+ ...rest
+}: React.ComponentProps<"button"> & { label: string; active: boolean }) {
+ return (
+
+ );
+}
+
+/** 已选条件:每个一个可移除标签,末尾一个「清除」 */
+export function RecordFilterTags({
+ tags,
+ onRemove,
+ onClear,
+}: {
+ tags: FilterTag[];
+ onRemove: (key: string) => void;
+ onClear: () => void;
+}) {
+ if (!tags.length) return null;
+ return (
+
+ {tags.map((t) => (
+
+ ))}
+
+
+ );
+}
diff --git a/apps/web/app/routes/console/organize.$id.tsx b/apps/web/app/routes/console/organize.$id.tsx
index 118b7465..6501f635 100644
--- a/apps/web/app/routes/console/organize.$id.tsx
+++ b/apps/web/app/routes/console/organize.$id.tsx
@@ -133,7 +133,7 @@ export default function OrganizeRecordPage() {
actions={
<>