Skip to content

Commit

Permalink
feat: added json editor
Browse files Browse the repository at this point in the history
  • Loading branch information
tikazyq committed Sep 29, 2024
1 parent 6f5cd04 commit 5ab8327
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 78 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"vue-tsc": "^2.0.24"
},
"dependencies": {
"json-editor-vue": "^0.17.0",
"uuid": "^10.0.0"
}
}
71 changes: 31 additions & 40 deletions src/components/core/database/DatabaseTableDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,49 +98,40 @@ const onCommit = async () => {
};
const createTable = async () => {
try {
await post(`/databases/${props.activeId}/tables/create`, {
database_name: props.databaseName,
table: internalTable.value,
});
await getTable();
store.commit(`${ns}/setActiveNavItem`, {
...state.activeNavItem,
id: `${props.databaseName}:${internalTable.value?.name}`,
new: false,
});
} catch (error: any) {
ElMessage.error(error.message);
throw error;
}
await post(`/databases/${props.activeId}/tables/create`, {
database_name: props.databaseName,
table: internalTable.value,
});
await getTable();
store.commit(`${ns}/setActiveNavItem`, {
...state.activeNavItem,
id: `${props.databaseName}:${internalTable.value?.name}`,
new: false,
});
emit('refresh');
};
const modifyTable = async () => {
try {
await post(`/databases/${props.activeId}/tables/modify`, {
database_name: props.databaseName,
table: {
...internalTable.value,
columns: internalTable.value?.columns?.map(c => {
return {
...c,
status: getColumnStatus(c, activeTable.value),
};
}),
indexes: internalTable.value?.indexes?.map(i => {
return {
...i,
status: getIndexStatus(i, activeTable.value),
};
}),
},
});
await getTable();
emit('refresh');
} catch (error: any) {
ElMessage.error(error.message);
throw error;
}
await post(`/databases/${props.activeId}/tables/modify`, {
database_name: props.databaseName,
table: {
...internalTable.value,
columns: internalTable.value?.columns?.map(c => {
return {
...c,
status: getColumnStatus(c, activeTable.value),
};
}),
indexes: internalTable.value?.indexes?.map(i => {
return {
...i,
status: getIndexStatus(i, activeTable.value),
};
}),
},
});
await getTable();
emit('refresh');
};
const activeTabName = ref<string>(defaultTabName.value || TAB_NAME_DATA);
Expand Down
63 changes: 33 additions & 30 deletions src/components/core/database/tables/DatabaseTableDetailData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,48 +48,51 @@ const getHeaderIcon = (column: DatabaseColumn) => {
return ['fa', 'calendar-alt'];
case 'object':
return ['fa', 'object-group'];
case 'array':
return ['fa', 'list'];
default:
return ['fa', 'font'];
}
};
const tableColumns = computed<TableColumns>(() => {
const tableColumns = computed<TableColumns<DatabaseTableRow>>(() => {
const { columns } = props.activeTable || {};
if (!columns) return [];
return columns.map(c => {
const value = (row: DatabaseTableRow) => (
<ClTableEditCell
modelValue={row[c.name as string]}
isEdit={row.__edit__?.[c.name as string]}
dataType={getDataType(c.type as string)}
onChange={(val: any) => {
const colName = c.name as string;
row[colName] = val;
}}
onEdit={(val: boolean) => {
const colName = c.name as string;
row =
tableData.value.find(r => getRowHash(r) === getRowHash(row)) || row;
if (!row.__edit__) row.__edit__ = { [colName]: val };
row.__edit__[colName] = val;
}}
/>
);
const header = () => (
<div>
<span style={{ marginRight: '5px' }}>
<ClIcon size="small" icon={getHeaderIcon(c)} />
</span>
<span>{c.name}</span>
</div>
);
return {
label: c.name,
key: c.name,
width: 200,
value: (row: DatabaseTableRow) => (
<ClTableEditCell
modelValue={row[c.name as string]}
isEdit={row.__edit__?.[c.name as string]}
dataType={getDataType(c.type as string)}
onChange={(val: any) => {
const colName = c.name as string;
row[colName] = val;
}}
onEdit={(val: boolean) => {
const colName = c.name as string;
row =
tableData.value.find(r => getRowHash(r) === getRowHash(row)) ||
row;
if (!row.__edit__) row.__edit__ = { [colName]: val };
row.__edit__[colName] = val;
}}
/>
),
header: () => (
<div>
<span style={{ marginRight: '5px' }}>
<ClIcon size="small" icon={getHeaderIcon(c)} />
</span>
<span>{c.name}</span>
</div>
),
} as TableColumn;
});
value,
header,
};
}) as TableColumns<DatabaseTableRow>;
});
const tableData = ref<TableData<DatabaseTableRow>>([]);
const tablePagination = ref<TablePagination>({
Expand Down
2 changes: 2 additions & 0 deletions src/components/ui/dialog/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ withDefaults(
confirmLoading?: boolean;
confirmType?: BasicType;
className?: string;
appendToBody?: boolean;
}>(),
{
top: '15vh',
Expand Down Expand Up @@ -44,6 +45,7 @@ defineOptions({ name: 'ClDialog' });
:custom-class="
['cl-dialog', className, visible ? 'visible' : 'hidden'].join(' ')
"
:append-to-body="appendToBody"
:modal-class="modalClass"
:before-close="onClose"
:model-value="visible"
Expand Down
33 changes: 29 additions & 4 deletions src/components/ui/table/TableEditCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {
ElAutocomplete,
type AutocompleteFetchSuggestions,
} from 'element-plus';
import dayjs from 'dayjs';
import JsonEditorVue from 'json-editor-vue';
import { ClIcon } from '@/components';
import { translate } from '@/utils';
import dayjs from 'dayjs';
const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -114,12 +115,17 @@ const labelValue = computed<string>(() => {
case 'date':
return dayjs(modelValue).format('YYYY-MM-DD');
case 'object':
case 'array':
return JSON.stringify(props.modelValue);
default:
return modelValue;
}
});
const isJson = computed(() => {
return ['object', 'array'].includes(props.dataType || '');
});
defineOptions({ name: 'ClTableEditCell' });
</script>

Expand All @@ -134,8 +140,8 @@ defineOptions({ name: 'ClTableEditCell' });
].join(' ')
"
>
<template v-if="!isEdit">
<span class="display-value" @click.stop="onEdit">
<template v-if="!isEdit || isJson">
<span class="display-value" @click.stop="onEdit" :title="labelValue">
<template v-if="modelValue">
<template v-if="$slots.default">
<slot name="default" />
Expand Down Expand Up @@ -203,7 +209,11 @@ defineOptions({ name: 'ClTableEditCell' });
value-format="YYYY-MM-DD hh:mm:ss"
@keyup.enter="onCheck"
@change="onCheck"
@blur="onCancel"
@visible-change="(visible: boolean) => {
if (!visible) {
onCheck();
}
}"
/>
<el-input
v-else-if="dataType === 'number'"
Expand Down Expand Up @@ -240,6 +250,17 @@ defineOptions({ name: 'ClTableEditCell' });
/>
</div>
</div>

<template v-if="isEdit && dataType && ['object', 'array'].includes(dataType)">
<cl-dialog
:visible="isEdit"
append-to-body
@confirm="onCheck"
@close="onCancel"
>
<JsonEditorVue v-model="internalValue" />
</cl-dialog>
</template>
</template>

<style scoped>
Expand All @@ -251,6 +272,10 @@ defineOptions({ name: 'ClTableEditCell' });
.display-value {
margin: 0 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: calc(100% - 24px);
&:hover {
cursor: pointer;
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/components/ui/table/Table.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export declare global {
className?: string;
proOnly?: boolean;
noPadding?: boolean;
header?: JSX.Element | VNode;
header?: JSX.Element | (() => JSX.Element) | (() => VNode) | VNode;
}

type TableColumns<T = any> = TableColumn<T>[];
Expand Down
17 changes: 14 additions & 3 deletions src/utils/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,18 @@ export const getDataType = (type: string): DatabaseDataType => {
}

// MongoDB-specific types
if (/^(objectid|long|decimal128)$/.test(lowerType)) {
return 'object'; // Handled by MongoDB drivers
if (/^(objectid|long|decimal128|object|isodate)$/.test(lowerType)) {
switch (lowerType) {
case 'long':
case 'decimal128':
return 'number';
case 'objectid':
return 'string';
case 'object':
return 'object';
case 'isodate':
return 'datetime';
}
}

// Default case
Expand All @@ -228,7 +238,8 @@ export const normalizeDataType = (value: any, type: string) => {
case 'string':
return String(value);
case 'date':
return value ? new Date(value) : null; // Convert to Date object
case 'datetime':
return value;
case 'array':
return Array.isArray(value) ? value : [value];
case 'object':
Expand Down

0 comments on commit 5ab8327

Please sign in to comment.