Improve: 记住单元格详情 JSON 格式化状态 (#2252)
Co-authored-by: staff <staff@qimaos-MacBook-Pro.local>
This commit is contained in:
parent
427d0c548b
commit
c4de3738fc
|
|
@ -158,7 +158,8 @@ import { formatShortcut } from "@/lib/shortcutRegistry";
|
|||
import { SearchableSelect } from "@/components/ui/searchable-select";
|
||||
|
||||
const SqlPreviewPanel = defineAsyncComponent(() => import("@/components/editor/SqlPreviewPanel.vue"));
|
||||
const FORMATTED_JSON_EDIT_WARNING_STORAGE_KEY = "dbx-cell-detail-formatted-json-edit-warning-shown";
|
||||
const FORMATTED_JSON_EDIT_WARNING_COUNT_STORAGE_KEY = "dbx-cell-detail-formatted-json-edit-warning-count";
|
||||
const FORMATTED_JSON_EDIT_WARNING_MAX_COUNT = 10;
|
||||
|
||||
const { t } = useI18n();
|
||||
const slots = useSlots();
|
||||
|
|
@ -448,8 +449,6 @@ const rowDetailDialogOpen = ref(false);
|
|||
const rowDetailDialogRowId = ref<number | null>(null);
|
||||
const columnDetailDialogOpen = ref(false);
|
||||
const columnDetailDialogColumnIndex = ref<number | null>(null);
|
||||
const cellDetailJsonView = ref(false);
|
||||
const sideDetailJsonView = ref(false);
|
||||
const rowDetailSearch = ref("");
|
||||
const columnDetailSearch = ref("");
|
||||
const isResizingDetail = ref(false);
|
||||
|
|
@ -3810,6 +3809,10 @@ const dialogCellDetail = computed(() => {
|
|||
return target ? cellDetailFor(target.rowIndex, target.col) : null;
|
||||
});
|
||||
|
||||
const cellDetailJsonFormatted = computed(() => settingsStore.editorSettings.cellDetailJsonFormatted);
|
||||
const sideDetailJsonView = computed(() => cellDetailJsonFormatted.value && !!activeCellDetail.value?.formattedJson);
|
||||
const cellDetailJsonView = computed(() => cellDetailJsonFormatted.value && !!dialogCellDetail.value?.formattedJson);
|
||||
|
||||
const rowDetail = computed(() => {
|
||||
if (rowDetailDialogRowId.value === null) return null;
|
||||
const item = getRowItem(rowDetailDialogRowId.value);
|
||||
|
|
@ -3854,8 +3857,7 @@ const filteredRowDetailFields = computed(() => (rowDetail.value ? filterDataGrid
|
|||
const filteredColumnDetailFields = computed(() => (columnDetail.value ? filterDataGridDetailFields(columnDetail.value.fields, columnDetailSearch.value) : []));
|
||||
|
||||
watch(cellDetailDialogOpen, (open) => {
|
||||
if (open) cellDetailJsonView.value = false;
|
||||
else cellDetailDialogTarget.value = null;
|
||||
if (!open) cellDetailDialogTarget.value = null;
|
||||
});
|
||||
|
||||
watch(rowDetailDialogOpen, (open) => {
|
||||
|
|
@ -4014,7 +4016,6 @@ async function prefetchDetailSqlCondition() {
|
|||
|
||||
watch(activeCellDetail, (detail) => {
|
||||
void prefetchDetailSqlCondition();
|
||||
sideDetailJsonView.value = false;
|
||||
if (activeCellDetailTab.value !== "valueEditor") return;
|
||||
if (!detail?.isEditable) {
|
||||
resetDetailEdit();
|
||||
|
|
@ -4174,9 +4175,14 @@ function cellDetailEditText(detail: DataGridCellDetail): string {
|
|||
|
||||
function warnFormattedJsonEditIfNeeded(detail: DataGridCellDetail, force = false) {
|
||||
if (!force && (!sideDetailJsonView.value || !detail.formattedJson)) return;
|
||||
if (safeLocalStorageGet(FORMATTED_JSON_EDIT_WARNING_STORAGE_KEY) === "1") return;
|
||||
const count = Number(safeLocalStorageGet(FORMATTED_JSON_EDIT_WARNING_COUNT_STORAGE_KEY)) || 0;
|
||||
if (count >= FORMATTED_JSON_EDIT_WARNING_MAX_COUNT) return;
|
||||
toast(t("grid.formattedJsonEditWarning"), 10000);
|
||||
safeLocalStorageSet(FORMATTED_JSON_EDIT_WARNING_STORAGE_KEY, "1");
|
||||
safeLocalStorageSet(FORMATTED_JSON_EDIT_WARNING_COUNT_STORAGE_KEY, String(count + 1));
|
||||
}
|
||||
|
||||
function toggleCellDetailJsonFormatted() {
|
||||
settingsStore.updateEditorSettings({ cellDetailJsonFormatted: !cellDetailJsonFormatted.value });
|
||||
}
|
||||
|
||||
function startDetailEdit() {
|
||||
|
|
@ -6038,7 +6044,6 @@ async function openDialogCellInSidePanel() {
|
|||
showCellDetails(detail.rowNumber - 1, detail.colIndex);
|
||||
cellDetailDialogOpen.value = false;
|
||||
await nextTick();
|
||||
sideDetailJsonView.value = cellDetailJsonView.value && !!detail.formattedJson;
|
||||
if (detail.isEditable) startDetailEdit();
|
||||
}
|
||||
|
||||
|
|
@ -8917,7 +8922,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
<div class="flex min-h-5 items-center justify-between gap-2">
|
||||
<div class="text-muted-foreground">{{ t("grid.cellValue") }}</div>
|
||||
<div v-if="!isEditingDetail" class="flex items-center gap-1">
|
||||
<Button v-if="activeCellDetail.formattedJson" :variant="sideDetailJsonView ? 'secondary' : 'ghost'" size="sm" class="h-5 gap-1 px-1.5 text-xs" :title="t('grid.formattedJson')" @click="sideDetailJsonView = !sideDetailJsonView">
|
||||
<Button v-if="activeCellDetail.formattedJson" :variant="sideDetailJsonView ? 'secondary' : 'ghost'" size="sm" class="h-5 gap-1 px-1.5 text-xs" :title="t('grid.formattedJson')" @click="toggleCellDetailJsonFormatted">
|
||||
<Code2 class="h-3 w-3" />
|
||||
{{ t("grid.formattedJson") }}
|
||||
</Button>
|
||||
|
|
@ -8959,7 +8964,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
</a>
|
||||
</div>
|
||||
<template v-if="isEditingDetail">
|
||||
<div class="min-h-32 shrink-0" :class="cellDetailPanelIsBottom ? 'h-32' : ''" :style="sideDetailEditorStyle">
|
||||
<div :class="cellDetailPanelIsBottom ? 'min-h-0 flex-1' : 'min-h-32 shrink-0'" :style="sideDetailEditorStyle">
|
||||
<TemporalCellEditor v-if="detailTemporalEditorKind" v-model="detailEditValue" :kind="detailTemporalEditorKind" variant="inline" :commit-on-close="false" @cancel="cancelDetailEdit" @commit="commitDetailEdit" />
|
||||
<div v-else ref="detailsEditorContainer" data-cell-detail-editor-root class="min-h-0 h-full w-full rounded border overflow-hidden" />
|
||||
</div>
|
||||
|
|
@ -9224,7 +9229,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="text-muted-foreground">{{ t("grid.cellValue") }}</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button v-if="dialogCellDetail.formattedJson" :variant="cellDetailJsonView ? 'secondary' : 'ghost'" size="sm" class="h-6 gap-1 px-2 text-xs" :title="t('grid.formattedJson')" @click="cellDetailJsonView = !cellDetailJsonView">
|
||||
<Button v-if="dialogCellDetail.formattedJson" :variant="cellDetailJsonView ? 'secondary' : 'ghost'" size="sm" class="h-6 gap-1 px-2 text-xs" :title="t('grid.formattedJson')" @click="toggleCellDetailJsonFormatted">
|
||||
<Code2 class="h-3 w-3" />
|
||||
{{ t("grid.formattedJson") }}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -798,7 +798,7 @@ export default {
|
|||
copyColumnTsv: "Copy Column (TSV)",
|
||||
editValue: "Edit Value",
|
||||
formatJson: "Format JSON",
|
||||
formattedJsonEditWarning: "You are editing formatted JSON. Saving writes back formatted text and may change the original whitespace and layout",
|
||||
formattedJsonEditWarning: "You are editing formatted JSON. Saving will write it as JSON, so the original formatting may change.",
|
||||
setNull: "Set NULL",
|
||||
restoreOriginalValue: "Restore Original",
|
||||
copyColumnName: "Copy Column Name",
|
||||
|
|
|
|||
|
|
@ -788,6 +788,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "Copiar columna (TSV)",
|
||||
editValue: "Editar valor",
|
||||
formatJson: "Formatear JSON",
|
||||
formattedJsonEditWarning: "Estás editando JSON formateado. Al guardar se escribirá como JSON, por lo que el formato original puede cambiar.",
|
||||
setNull: "Establecer NULL",
|
||||
restoreOriginalValue: "Restaurar original",
|
||||
copyColumnName: "Copiar nombre de columna",
|
||||
|
|
|
|||
|
|
@ -795,6 +795,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "Copia Colonna (TSV)",
|
||||
editValue: "Modifica Valore",
|
||||
formatJson: "Formatta JSON",
|
||||
formattedJsonEditWarning: "Stai modificando JSON formattato. Salvando verrà scritto come JSON, quindi la formattazione originale potrebbe cambiare.",
|
||||
setNull: "Imposta NULL",
|
||||
restoreOriginalValue: "Ripristina Originale",
|
||||
copyColumnName: "Copia Nome Colonna",
|
||||
|
|
|
|||
|
|
@ -786,6 +786,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "列をコピー (TSV)",
|
||||
editValue: "値を編集",
|
||||
formatJson: "JSONをフォーマット",
|
||||
formattedJsonEditWarning: "フォーマット済みJSONを編集中です。保存するとJSONとして書き込まれるため、元の書式が変わる場合があります。",
|
||||
setNull: "NULLに設定",
|
||||
restoreOriginalValue: "元の値に戻す",
|
||||
copyColumnName: "列名をコピー",
|
||||
|
|
|
|||
|
|
@ -800,6 +800,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "Copiar Coluna (TSV)",
|
||||
editValue: "Editar Valor",
|
||||
formatJson: "Formatar JSON",
|
||||
formattedJsonEditWarning: "Você está editando JSON formatado. Ao salvar, ele será gravado como JSON, então a formatação original pode mudar.",
|
||||
setNull: "Definir NULL",
|
||||
restoreOriginalValue: "Restaurar Original",
|
||||
copyColumnName: "Copiar Nome da Coluna",
|
||||
|
|
|
|||
|
|
@ -801,7 +801,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "复制列 (TSV)",
|
||||
editValue: "编辑值",
|
||||
formatJson: "格式化 JSON",
|
||||
formattedJsonEditWarning: "正在编辑格式化后的 JSON。保存后会写回格式化文本,原始空白和排版可能变化",
|
||||
formattedJsonEditWarning: "正在编辑格式化后的 JSON。保存后会以JSON格式写入,原始排版可能变化。",
|
||||
setNull: "设为 NULL",
|
||||
restoreOriginalValue: "恢复原值",
|
||||
copyColumnName: "复制列名",
|
||||
|
|
|
|||
|
|
@ -789,7 +789,7 @@ export default withEnglishFallback({
|
|||
copyColumnTsv: "複製欄 (TSV)",
|
||||
editValue: "編輯值",
|
||||
formatJson: "格式化 JSON",
|
||||
formattedJsonEditWarning: "正在編輯格式化後的 JSON。儲存後會寫回格式化文字,原始空白和排版可能改變",
|
||||
formattedJsonEditWarning: "正在編輯格式化後的 JSON。儲存後會以 JSON 格式寫入,原始排版可能改變。",
|
||||
setNull: "設為 NULL",
|
||||
restoreOriginalValue: "復原資料",
|
||||
copyColumnName: "複製欄位名稱",
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ export interface EditorSettings {
|
|||
tableInfoDrawerWidth: number;
|
||||
cellDetailDrawerWidth: number;
|
||||
cellDetailPanelLayout: CellDetailPanelLayout;
|
||||
cellDetailJsonFormatted: boolean;
|
||||
shortcuts: ShortcutSettings;
|
||||
sqlFormatter: SqlFormatterSettings;
|
||||
sidebarActivation: SidebarActivation;
|
||||
|
|
@ -459,6 +460,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||
tableInfoDrawerWidth: 320,
|
||||
cellDetailDrawerWidth: 380,
|
||||
cellDetailPanelLayout: "bottom",
|
||||
cellDetailJsonFormatted: false,
|
||||
shortcuts: normalizeShortcutSettings(),
|
||||
sqlFormatter: normalizeSqlFormatterSettings(DEFAULT_SQL_FORMATTER_SETTINGS),
|
||||
sidebarActivation: "single",
|
||||
|
|
@ -656,6 +658,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
|||
tableInfoDrawerWidth: normalizeDrawerWidth(settings.tableInfoDrawerWidth, 240, DEFAULT_EDITOR_SETTINGS.tableInfoDrawerWidth),
|
||||
cellDetailDrawerWidth: normalizeDrawerWidth(settings.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth),
|
||||
cellDetailPanelLayout: normalizeCellDetailPanelLayout(settings.cellDetailPanelLayout),
|
||||
cellDetailJsonFormatted: typeof settings.cellDetailJsonFormatted === "boolean" ? settings.cellDetailJsonFormatted : DEFAULT_EDITOR_SETTINGS.cellDetailJsonFormatted,
|
||||
shortcuts: normalizeShortcutSettings(settings.shortcuts),
|
||||
sqlFormatter: normalizeSqlFormatterSettings(settings.sqlFormatter),
|
||||
sidebarActivation: settings.sidebarActivation === "single" || settings.sidebarActivation === "double" ? settings.sidebarActivation : DEFAULT_EDITOR_SETTINGS.sidebarActivation,
|
||||
|
|
@ -835,6 +838,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
if (partial.tableInfoDrawerWidth !== undefined) editorSettings.value.tableInfoDrawerWidth = normalizeDrawerWidth(partial.tableInfoDrawerWidth, 240, DEFAULT_EDITOR_SETTINGS.tableInfoDrawerWidth);
|
||||
if (partial.cellDetailDrawerWidth !== undefined) editorSettings.value.cellDetailDrawerWidth = normalizeDrawerWidth(partial.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth);
|
||||
if (partial.cellDetailPanelLayout !== undefined) editorSettings.value.cellDetailPanelLayout = normalizeCellDetailPanelLayout(partial.cellDetailPanelLayout);
|
||||
if (partial.cellDetailJsonFormatted !== undefined) editorSettings.value.cellDetailJsonFormatted = partial.cellDetailJsonFormatted === true;
|
||||
if (partial.shortcuts !== undefined) editorSettings.value.shortcuts = normalizeShortcutSettings(partial.shortcuts);
|
||||
if (partial.sqlFormatter !== undefined) editorSettings.value.sqlFormatter = normalizeSqlFormatterSettings(partial.sqlFormatter);
|
||||
if (partial.sidebarActivation !== undefined) editorSettings.value.sidebarActivation = partial.sidebarActivation;
|
||||
|
|
|
|||
|
|
@ -228,15 +228,19 @@ test("normalizes grid drawer widths", () => {
|
|||
assert.equal(DEFAULT_EDITOR_SETTINGS.tableInfoDrawerWidth, 320);
|
||||
assert.equal(DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth, 380);
|
||||
assert.equal(DEFAULT_EDITOR_SETTINGS.cellDetailPanelLayout, "bottom");
|
||||
assert.equal(DEFAULT_EDITOR_SETTINGS.cellDetailJsonFormatted, false);
|
||||
assert.equal(normalizeEditorSettings({}).tableInfoDrawerWidth, 320);
|
||||
assert.equal(normalizeEditorSettings({}).cellDetailDrawerWidth, 380);
|
||||
assert.equal(normalizeEditorSettings({}).cellDetailPanelLayout, "bottom");
|
||||
assert.equal(normalizeEditorSettings({}).cellDetailJsonFormatted, false);
|
||||
assert.equal(normalizeEditorSettings({ tableInfoDrawerWidth: 200 } as any).tableInfoDrawerWidth, 240);
|
||||
assert.equal(normalizeEditorSettings({ cellDetailDrawerWidth: 200 } as any).cellDetailDrawerWidth, 260);
|
||||
assert.equal(normalizeEditorSettings({ tableInfoDrawerWidth: 1000 } as any).tableInfoDrawerWidth, 900);
|
||||
assert.equal(normalizeEditorSettings({ cellDetailDrawerWidth: 456.7 } as any).cellDetailDrawerWidth, 457);
|
||||
assert.equal(normalizeEditorSettings({ cellDetailPanelLayout: "right" } as any).cellDetailPanelLayout, "right");
|
||||
assert.equal(normalizeEditorSettings({ cellDetailPanelLayout: "invalid" } as any).cellDetailPanelLayout, "bottom");
|
||||
assert.equal(normalizeEditorSettings({ cellDetailJsonFormatted: true } as any).cellDetailJsonFormatted, true);
|
||||
assert.equal(normalizeEditorSettings({ cellDetailJsonFormatted: "true" } as any).cellDetailJsonFormatted, false);
|
||||
});
|
||||
|
||||
test("keeps saved active tab sidebar selection", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue