feat: harden query read-only workflow
This commit is contained in:
1 parent
58bbc394e9
commit
617c15dbfe
29 files changed
+1417
-395
No files matched your search
@@ -0,0 +1,75 @@
|
||||
import { Copy } from 'lucide-react';
|
||||
import { FormDialog } from '@/components/FormDialog';
|
||||
import { STRING_FORMAT_OPTIONS, type RedisLineEditorState } from '../types';
|
||||
import { formatStringByMode } from '../utils';
|
||||
|
||||
interface RedisLineEditorDialogProps {
|
||||
state: RedisLineEditorState;
|
||||
readOnlyField?: boolean;
|
||||
saving?: boolean;
|
||||
onChange: (state: RedisLineEditorState) => void;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
onCopy?: (value: string) => void;
|
||||
}
|
||||
|
||||
export function RedisLineEditorDialog({
|
||||
state,
|
||||
readOnlyField,
|
||||
saving,
|
||||
onChange,
|
||||
onClose,
|
||||
onConfirm,
|
||||
onCopy,
|
||||
}: RedisLineEditorDialogProps) {
|
||||
return (
|
||||
<FormDialog open={state.open} onClose={onClose} title="编辑字段" width="max-w-4xl"
|
||||
footer={<>
|
||||
<button onClick={onClose} className="px-4 py-2 bg-[var(--dg-bg-tertiary)] rounded-lg text-sm">取消</button>
|
||||
<button onClick={onConfirm} disabled={saving} className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm disabled:opacity-50">保存</button>
|
||||
</>}>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm text-[var(--dg-text-muted)]">Field</label>
|
||||
<input
|
||||
readOnly={readOnlyField}
|
||||
value={state.field}
|
||||
onChange={function (e) { onChange(Object.assign({}, state, { field: e.target.value })); }}
|
||||
className="mt-1 h-10 w-full rounded-md border border-[var(--dg-border)] bg-[var(--dg-bg-primary)] px-3 font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<label className="text-sm text-[var(--dg-text-muted)]">Value</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-md border border-blue-400/30 bg-blue-500/10 px-2 py-1 text-xs text-blue-400">
|
||||
Size: {new TextEncoder().encode(state.value).length}B
|
||||
</span>
|
||||
<button onClick={function () { onCopy ? onCopy(state.value) : navigator.clipboard.writeText(state.value); }} className="inline-flex items-center gap-1.5 text-xs text-blue-400 hover:text-blue-300">
|
||||
<Copy className="h-3.5 w-3.5" />Copy
|
||||
</button>
|
||||
<select
|
||||
value={state.format}
|
||||
onChange={function (e) { onChange(Object.assign({}, state, { format: e.target.value as RedisLineEditorState['format'] })); }}
|
||||
className="h-8 w-44 rounded-md border border-[var(--dg-border)] bg-[var(--dg-bg-primary)] px-2 text-xs text-[var(--dg-text-secondary)]"
|
||||
>
|
||||
{STRING_FORMAT_OPTIONS.map(function (format) {
|
||||
return <option key={format} value={format}>{format}</option>;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
value={state.value}
|
||||
onChange={function (e) { onChange(Object.assign({}, state, { value: e.target.value })); }}
|
||||
spellCheck={false}
|
||||
className="min-h-[160px] w-full resize-y rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-primary)] p-3 font-mono text-xs leading-relaxed outline-none focus:border-[var(--dg-blue)]"
|
||||
/>
|
||||
<pre className="mt-2 max-h-72 overflow-auto whitespace-pre-wrap break-all rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-primary)] p-3 font-mono text-xs leading-relaxed text-[var(--dg-text-secondary)]">
|
||||
{formatStringByMode(state.value, state.format)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</FormDialog>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user