Browse Source

feat: 修改窗体设计

DESKTOP-USV654P\pc 8 months ago
parent
commit
7cd679f10a

+ 1 - 0
apps/web-baicai/src/api/system/table.ts

@@ -18,6 +18,7 @@ export namespace TableApi {
     };
     databaseQueryId: number;
     menuId: number;
+    databaseQueryCode?: string;
   }
 
   export interface RecordItem extends BasicRecordItem {

+ 19 - 5
apps/web-baicai/src/components/form/components/input-code/input-code-modal-ignore.vue

@@ -1,5 +1,5 @@
 <script lang="ts" setup>
-import { nextTick, reactive, ref } from 'vue';
+import { nextTick, reactive, ref, toRaw, unref } from 'vue';
 
 import { useVbenModal } from '@vben/common-ui';
 import { isString } from '@vben/utils';
@@ -16,11 +16,15 @@ defineOptions({
 });
 const emit = defineEmits(['success']);
 const state = reactive<{
+  language: string;
   title: string;
 }>({
   title: '脚本编辑',
+  language: '',
 });
 
+const isPreview = ref(false);
+
 // eslint-disable-next-line no-restricted-globals
 self.MonacoEnvironment = {
   getWorker: (_: string, label: string) => {
@@ -73,13 +77,20 @@ const initMonacoEditor = () => {
 const [Modal, { close, setState, getData }] = useVbenModal({
   draggable: true,
   fullscreen: false,
+  onClosed: () => {
+    monacoEditor.dispose();
+    monacoEditor = null;
+  },
   onConfirm: async () => {
     try {
       setState({ confirmLoading: true });
-      const scriptCode = monacoEditor.getValue();
-
+      const scriptCode = toRaw(monacoEditor).getValue();
       close();
-      emit('success', scriptCode);
+      if (['javascript', 'json'].includes(state.language)) {
+        emit('success', JSON.parse(scriptCode));
+      } else {
+        emit('success', scriptCode);
+      }
     } finally {
       setState({ confirmLoading: false });
     }
@@ -88,6 +99,8 @@ const [Modal, { close, setState, getData }] = useVbenModal({
     if (isOpen) {
       setState({ loading: true });
       const data = getData<Record<string, any>>();
+      isPreview.value = !!data?.isPreview;
+      setState({ footer: !unref(isPreview) });
       if (monacoEditor === null) {
         nextTick(() => {
           initMonacoEditor();
@@ -96,7 +109,7 @@ const [Modal, { close, setState, getData }] = useVbenModal({
       nextTick(() => {
         const { scriptCode, name, language } = data.baseData;
         state.title = name;
-
+        state.language = language;
         if (language) {
           monaco.editor.setModelLanguage(monacoEditor.getModel()!, language);
         }
@@ -107,6 +120,7 @@ const [Modal, { close, setState, getData }] = useVbenModal({
           } else {
             monacoEditor.setValue(JSON.stringify(scriptCode));
           }
+          monacoEditor.getAction('editor.action.formatDocument').run();
         }
         setState({ loading: false });
       });

+ 175 - 146
apps/web-baicai/src/views/form/design/components/setting/form/index.vue

@@ -19,177 +19,192 @@ const handleUpdateConfig = (record: any) => {
   formConfig.value.schemas[findIndex] = data;
 };
 
-const [Form, { setValues, resetForm }] = useVbenForm({
-  showDefaultActions: false,
-  commonConfig: {
-    componentProps: {
-      class: 'w-full',
-    },
-  },
-  handleValuesChange: (values: Record<string, any>) => {
-    handleUpdateConfig(values);
-  },
-  schema: [
-    {
-      component: 'Input',
+const [Form, { setValues, resetForm, updateSchema, setFieldValue }] =
+  useVbenForm({
+    showDefaultActions: false,
+    commonConfig: {
       componentProps: {
-        placeholder: '输入控件类型',
-        disabled: true,
+        class: 'w-full',
       },
-      fieldName: 'component',
-      label: '控件类型',
     },
-    {
-      component: 'Input',
-      componentProps: {
-        placeholder: '输入标题',
-      },
-      fieldName: 'label',
-      label: '标题',
+    handleValuesChange: (values: Record<string, any>) => {
+      handleUpdateConfig(values);
     },
-    {
-      component: 'Input',
-      componentProps: {
-        placeholder: '请输入占位提示',
-      },
-      fieldName: 'componentProps.placeholder',
-      label: '占位提示',
-      dependencies: {
-        triggerFields: ['label'],
-        trigger(values) {
-          if (values?.componentProps?.placeholder) {
-            values.componentProps.placeholder = `请输入${values?.label}`;
-          }
+    schema: [
+      {
+        component: 'Input',
+        componentProps: {
+          placeholder: '输入控件类型',
+          disabled: true,
         },
+        fieldName: 'component',
+        label: '控件类型',
       },
-    },
-    {
-      component: 'Input',
-      componentProps: {
-        placeholder: '请输入绑定字段',
-      },
-      fieldName: 'fieldName',
-      label: '绑定字段',
-    },
-    {
-      component: 'Input',
-      componentProps: {
-        placeholder: '请输入前置标签',
-      },
-      fieldName: 'componentProps.prefix',
-      label: '前置标签',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['Input'].includes(values.component);
+      {
+        component: 'Input',
+        componentProps: {
+          placeholder: '输入标题',
         },
+        fieldName: 'label',
+        label: '标题',
       },
-    },
-    {
-      component: 'Input',
-      componentProps: {
-        placeholder: '请输入后置标签',
-      },
-      fieldName: 'componentProps.suffix',
-      label: '后置标签',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['Input'].includes(values.component);
+      {
+        component: 'Input',
+        componentProps: {
+          placeholder: '请输入占位提示',
         },
-      },
-    },
-    {
-      component: 'Switch',
-      componentProps: {
-        placeholder: '请输入显示字数',
-        class: 'w-auto',
-      },
-      fieldName: 'componentProps.showCount',
-      label: '显示字数',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['Input', 'Textarea'].includes(values.component);
+        fieldName: 'componentProps.placeholder',
+        label: '占位提示',
+        dependencies: {
+          triggerFields: ['label'],
+          trigger(values) {
+            if (values?.componentProps?.placeholder) {
+              values.componentProps.placeholder = `请输入${values?.label}`;
+            }
+          },
         },
       },
-    },
-    {
-      component: 'InputNumber',
-      componentProps: {
-        placeholder: '请输入最大长度',
+      {
+        component: 'Select',
+        componentProps: {
+          placeholder: '请输入绑定字段',
+          options: [],
+          showSearch: true,
+          filterOption(
+            input: string,
+            option: { label: string; value: string },
+          ) {
+            return (
+              option.value.toLowerCase().includes(input.toLowerCase()) ||
+              option.label.toLowerCase().includes(input.toLowerCase())
+            );
+          },
+          onSelect: (_, option: any) => {
+            setFieldValue('label', option.label);
+          },
+        },
+        fieldName: 'fieldName',
+        label: '绑定字段',
       },
-      fieldName: 'componentProps.maxlength',
-      label: '最大长度',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['Input', 'Textarea'].includes(values.component);
+      {
+        component: 'Input',
+        componentProps: {
+          placeholder: '请输入前置标签',
+        },
+        fieldName: 'componentProps.prefix',
+        label: '前置标签',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['Input'].includes(values.component);
+          },
         },
       },
-    },
-    {
-      component: 'InputNumber',
-      componentProps: {
-        placeholder: '请输入最大值',
+      {
+        component: 'Input',
+        componentProps: {
+          placeholder: '请输入后置标签',
+        },
+        fieldName: 'componentProps.suffix',
+        label: '后置标签',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['Input'].includes(values.component);
+          },
+        },
       },
-      fieldName: 'componentProps.max',
-      label: '最大值',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['InputNumber'].includes(values.component);
+      {
+        component: 'Switch',
+        componentProps: {
+          placeholder: '请输入显示字数',
+          class: 'w-auto',
+        },
+        fieldName: 'componentProps.showCount',
+        label: '显示字数',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['Input', 'Textarea'].includes(values.component);
+          },
         },
       },
-    },
-    {
-      component: 'InputNumber',
-      componentProps: {
-        placeholder: '请输入最小值',
+      {
+        component: 'InputNumber',
+        componentProps: {
+          placeholder: '请输入最大长度',
+        },
+        fieldName: 'componentProps.maxlength',
+        label: '最大长度',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['Input', 'Textarea'].includes(values.component);
+          },
+        },
       },
-      fieldName: 'componentProps.min',
-      label: '最小值',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['InputNumber'].includes(values.component);
+      {
+        component: 'InputNumber',
+        componentProps: {
+          placeholder: '请输入最大值',
+        },
+        fieldName: 'componentProps.max',
+        label: '最大值',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['InputNumber'].includes(values.component);
+          },
         },
       },
-    },
-    {
-      component: 'InputNumber',
-      componentProps: {
-        placeholder: '请输入步长',
+      {
+        component: 'InputNumber',
+        componentProps: {
+          placeholder: '请输入最小值',
+        },
+        fieldName: 'componentProps.min',
+        label: '最小值',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['InputNumber'].includes(values.component);
+          },
+        },
       },
-      fieldName: 'componentProps.step',
-      label: '步长',
-      dependencies: {
-        triggerFields: ['component'],
-        show(values) {
-          return ['InputNumber'].includes(values.component);
+      {
+        component: 'InputNumber',
+        componentProps: {
+          placeholder: '请输入步长',
+        },
+        fieldName: 'componentProps.step',
+        label: '步长',
+        dependencies: {
+          triggerFields: ['component'],
+          show(values) {
+            return ['InputNumber'].includes(values.component);
+          },
         },
       },
-    },
-    {
-      component: 'Switch',
-      componentProps: {
-        placeholder: '请输入隐藏标签',
-        class: 'w-auto',
+      {
+        component: 'Switch',
+        componentProps: {
+          placeholder: '请输入隐藏标签',
+          class: 'w-auto',
+        },
+        fieldName: 'hideLabel',
+        label: '隐藏标签',
       },
-      fieldName: 'hideLabel',
-      label: '隐藏标签',
-    },
-    {
-      component: 'InputCode',
-      componentProps: {
-        placeholder: '请输入验证规则',
+      {
+        component: 'InputCode',
+        componentProps: {
+          placeholder: '请输入验证规则',
+        },
+        fieldName: 'rules',
+        label: '验证规则',
       },
-      fieldName: 'rules',
-      label: '验证规则',
-    },
-  ],
-  wrapperClass: 'grid-cols-1',
-});
+    ],
+    wrapperClass: 'grid-cols-1',
+  });
 
 watch(
   () => formConfig.value.selectSchema,
@@ -201,6 +216,20 @@ watch(
   },
   { deep: true, immediate: false },
 );
+
+watch(
+  () => formConfig.value.columns,
+  async (value) => {
+    if (value) {
+      updateSchema([
+        { fieldName: 'fieldName', componentProps: { options: value } },
+      ]);
+    }
+  },
+  {
+    immediate: true,
+  },
+);
 </script>
 <template>
   <div>

+ 2 - 2
apps/web-baicai/src/views/form/design/components/setting/index.vue

@@ -7,10 +7,10 @@ import WidgetConfig from './widget/index.vue';
 
 <template>
   <Tabs :centered="true">
-    <Tabs.TabPane key="1" tab="组件属性">
+    <Tabs.TabPane key="1" tab="组件属性" class="px-4">
       <FormConfig />
     </Tabs.TabPane>
-    <Tabs.TabPane key="2" tab="表单属性">
+    <Tabs.TabPane key="2" tab="表单属性" class="px-4">
       <WidgetConfig />
     </Tabs.TabPane>
   </Tabs>

+ 0 - 9
apps/web-baicai/src/views/form/design/components/setting/widget/index.vue

@@ -45,15 +45,6 @@ const [Form, { setValues }] = useVbenForm({
       fieldName: 'commonConfig.labelWidth',
       label: '标签宽度',
     },
-    {
-      component: 'Switch',
-      componentProps: {
-        placeholder: '请输入显示操作按钮',
-        class: 'w-auto',
-      },
-      fieldName: 'showDefaultActions',
-      label: '显示操作按钮',
-    },
   ],
   wrapperClass: 'grid-cols-1',
 });

+ 41 - 5
apps/web-baicai/src/views/form/design/components/tools/index.vue

@@ -1,13 +1,18 @@
 <script setup lang="ts">
 import type { IVFormComponent } from '../../types';
 
+import { unref } from 'vue';
+
 import { useVbenModal } from '@vben/common-ui';
+import { cloneDeep } from '@vben/utils';
 
 import InputCode from '#/components/form/components/input-code/input-code-modal-ignore.vue';
 import { Icon } from '#/components/icon';
 
 import { useFormDesignState } from '../../hooks/useFormDesignState';
 
+const emit = defineEmits(['save']);
+
 const [InputCodeModal, inputCodeApi] = useVbenModal({
   connectedComponent: InputCode,
 });
@@ -20,8 +25,9 @@ const handleClear = () => {
 };
 
 const handleSuccess = (scriptCode: any) => {
-  // eslint-disable-next-line no-console
-  console.log('scriptCode', scriptCode);
+  formConfig.value.config = { ...scriptCode.config };
+  formConfig.value.schemas = [...(scriptCode.schemas || [])];
+  formDesignMethods.setSelectSchema({ component: '' } as IVFormComponent);
 };
 
 const handleImport = () => {
@@ -44,6 +50,25 @@ const handleImport = () => {
     })
     .open();
 };
+
+const handleExport = () => {
+  const scriptCode = cloneDeep(unref(formConfig));
+  delete scriptCode.selectSchema;
+  inputCodeApi
+    .setData({
+      baseData: {
+        scriptCode,
+        name: '导出JSON',
+        language: 'json',
+      },
+      isPreview: true,
+    })
+    .open();
+};
+
+const handleSave = () => {
+  emit('save');
+};
 </script>
 <template>
   <div class="flex items-center justify-between border-b px-4">
@@ -51,19 +76,30 @@ const handleImport = () => {
     <h3 class="text-lg">设计区域</h3>
     <div class="flex flex-wrap gap-3">
       <Icon
-        icon="carbon:tag-import"
+        icon="tabler:package-import"
         size="24"
         class="cursor-pointer"
         @click="handleImport"
       />
-      <Icon icon="carbon:tag-export" size="24" class="cursor-pointer" />
+      <Icon
+        icon="tabler:package-export"
+        size="24"
+        class="cursor-pointer"
+        @click="handleExport"
+      />
       <Icon
         icon="carbon:clean"
         size="24"
         class="cursor-pointer"
         @click="handleClear"
       />
-      <Icon icon="carbon:view-filled" size="24" class="cursor-pointer" />
+      <Icon icon="hugeicons:view" size="24" class="cursor-pointer" />
+      <Icon
+        icon="fluent:save-multiple-24-filled"
+        size="24"
+        class="cursor-pointer"
+        @click="handleSave"
+      />
     </div>
   </div>
 </template>

+ 19 - 5
apps/web-baicai/src/views/form/design/index.vue

@@ -7,6 +7,9 @@ import { computed, onMounted, provide, ref } from 'vue';
 import { useRoute } from 'vue-router';
 
 import { BcPage, VbenScrollbar } from '@vben/common-ui';
+import { useTabs } from '@vben/hooks';
+
+import { QueryApi } from '#/api';
 
 import FormEdit from './components/form/index.vue';
 import FormSetting from './components/setting/index.vue';
@@ -18,21 +21,22 @@ defineOptions({
 });
 
 const route = useRoute();
+const { closeCurrentTab } = useTabs();
 
 const keyValue = computed(() => {
-  return route.params?.id ?? -1;
+  return route.params?.id ?? '';
 });
 
 const formConfig = ref<IFormConfig>({
   config: {
     wrapperClass: 'grid-cols-1',
-    showDefaultActions: false,
     commonConfig: {
       labelWidth: 100,
     },
   },
   schemas: [],
   selectSchema: { component: '' } as IVFormComponent,
+  columns: [],
 });
 
 const setSelectSchema = (schema: IVFormComponent) => {
@@ -45,9 +49,19 @@ provide<IFormDesignMethods>('formDesignMethods', {
   setSelectSchema,
 });
 
+const handleSave = () => {
+  // if (keyValue.value) {
+  // }
+  closeCurrentTab();
+};
+
 onMounted(async () => {
-  // eslint-disable-next-line no-console
-  console.log('keyValue', keyValue.value);
+  if (keyValue.value) {
+    const columns = await QueryApi.getColumns(keyValue.value as string);
+    formConfig.value.columns = columns.map((item: any) => {
+      return { ...item, value: item.alias, label: item.remark };
+    });
+  }
 });
 </script>
 <template>
@@ -59,7 +73,7 @@ onMounted(async () => {
       <FormSetting />
     </template>
     <div class="relative h-full">
-      <Tools class="h-[46px]" />
+      <Tools class="h-[46px]" @save="handleSave" />
       <VbenScrollbar class="h-[calc(100%-46px)]">
         <FormEdit />
       </VbenScrollbar>

+ 1 - 1
apps/web-baicai/src/views/form/design/types/index.ts

@@ -26,10 +26,10 @@ export interface IFormConfig {
   schemas: IVFormComponent[];
   config: {
     commonConfig?: IAnyObject;
-    showDefaultActions: boolean;
     wrapperClass: string;
   };
   selectSchema?: IVFormComponent;
+  columns?: IAnyObject[];
 }
 
 /**

+ 1 - 1
apps/web-baicai/src/views/system/design/table/index.vue

@@ -82,7 +82,7 @@ const handleActionClick = async ({
       router.push({
         name: 'formDesign',
         params: {
-          id: row.id,
+          id: row.databaseQueryCode,
         },
       });
       break;