Ver Fonte

feat: 自定义表单接口对接完成

huangziyang há 4 semanas atrás
pai
commit
f90144f150

+ 0 - 44
form/index.html

@@ -1,44 +0,0 @@
-<!DOCTYPE html><html lang=""><head>
-    <meta charset="UTF-8">
-    <link rel="icon" href="/saas/form/favicon.ico">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Vite App</title>
-    <script crossorigin="">import('/saas/form/static/index.DYYWzmnX.js').finally(() => {
-            
-    const qiankunLifeCycle = window.moudleQiankunAppLifeCycles && window.moudleQiankunAppLifeCycles['curstom-form'];
-    if (qiankunLifeCycle) {
-      window.proxy.vitemount((props) => qiankunLifeCycle.mount(props));
-      window.proxy.viteunmount((props) => qiankunLifeCycle.unmount(props));
-      window.proxy.vitebootstrap(() => qiankunLifeCycle.bootstrap());
-      window.proxy.viteupdate((props) => qiankunLifeCycle.update(props));
-    }
-  
-          })</script>
-    <link rel="stylesheet" crossorigin="" href="/saas/form/static/index.BePiSQRJ.css">
-  </head>
-  <body>
-    <div id="app"></div>
-  
-
-<script>
-  const createDeffer = (hookName) => {
-    const d = new Promise((resolve, reject) => {
-      window.proxy && (window.proxy[`vite${hookName}`] = resolve)
-    })
-    return props => d.then(fn => fn(props));
-  }
-  const bootstrap = createDeffer('bootstrap');
-  const mount = createDeffer('mount');
-  const unmount = createDeffer('unmount');
-  const update = createDeffer('update');
-
-  ;(global => {
-    global.qiankunName = 'curstom-form';
-    global['curstom-form'] = {
-      bootstrap,
-      mount,
-      unmount,
-      update
-    };
-  })(window);
-</script></body></html>

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
form/static/index.BePiSQRJ.css


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
form/static/index.DYYWzmnX.js


+ 3 - 1
package.json

@@ -6,8 +6,10 @@
   "scripts": {
     "dev": "vite",
     "build": "run-p type-check \"build-only {@}\" --",
+    "build:prod": "run-p type-check \"build-only:prod {@}\" --",
     "preview": "vite preview",
-    "build-only": "vite build",
+    "build-only": "vite build --mode release",
+    "build-only:prod": "vite build --mode production",
     "type-check": "vue-tsc --build",
     "lint": "eslint . --fix",
     "format": "prettier --write src/"

+ 33 - 15
src/App.vue

@@ -2,7 +2,7 @@
 <script setup lang="ts">
 import { ref, onMounted } from 'vue'
 import type { Config, Handle } from '@form-create/designer'
-import { formConfig } from './form.config.ts'
+import { formConfig, formSubmitSave } from './form.config.ts'
 import FcDesigner from '@form-create/designer'
 // 组件
 import { RcSelect } from './plugins/components/rc-select.config.ts'
@@ -12,7 +12,8 @@ import { RcCheckbox } from './plugins/components/rc-checkbox.config.ts'
 import { MenuOptions } from './plugins/menu/options.ts'
 
 import type { FcDesignerInstance } from '@form-create/designer' // 注册组件
-import type { Rule } from '@form-create/element-ui'
+import type { Options, Rule } from '@form-create/element-ui'
+import type { Option } from './components/rc-options/createOptions.ts'
 
 /**
  * @see https://view.form-create.com/methods
@@ -25,7 +26,7 @@ declare global {
     } // 事件中心
     __QIANKUN__REQUSET__: any // 请求API接口
     __QIANKUN__GUID__: () => string // 获取唯一标识
-    __QIANKUN__OPEN__TAG__: () => void // 打开标签页
+    __QIANKUN__OPEN__TAG__: (tags: Option['tags']) => void // 打开标签页
     __QIANKUN__QUITE__: () => void // 返回页面
   }
 }
@@ -39,26 +40,33 @@ const config = ref<Config>(formConfig)
 // 顶部更多操作按钮
 const handle = ref<Handle>([])
 
+let id = ''
+
 const onSave = async (config: { options: string; rule: string }) => {
   const rule = config.rule
   const options = config.options
-  // 接收从主应用过来的事件
-  window.__QIANKUN__EVENT__?.emit('save', {
+
+  formSubmitSave({
     rule: JSON.parse(rule),
     options: JSON.parse(options),
     original: config,
+    id,
+    onSuccess() {
+      window.__QIANKUN__QUITE__()
+    },
   })
 }
 
-const rule = ref(
-  '[{"type":"rc-checkbox","title":"多选框","props":{"clearable":true,"options":[{"key":"cw_1iqn0n3e6mgc1vgp7di8as15s55","label":"123421","value":"123421","validate":false,"tags":[],"default":""}]},"_fc_id":"id_Fsbmmaeu7rtlabc","name":"ref_Finhmaeu7rtlacc","field":"Ft20maeu7rtladc","display":true,"hidden":false,"_fc_drag_tag":"rc-checkbox","options":[{"key":"cw_1iqn0n3e6mgc1vgp7di8as15s55","label":"123421","value":"123421","validate":false,"tags":[],"default":""}]},{"type":"rc-select","title":"下拉框","props":{"placeholder":"请选择","clearable":true,"options":[{"key":"cw_1iqn0n6gm1uos105812fafne1mqj6","label":"223","value":"223","validate":false,"tags":[],"default":""}]},"_fc_id":"id_Fuf6maeu7skxaec","name":"ref_Fgfdmaeu7skxafc","field":"Fgckmaeu7skxagc","display":true,"hidden":false,"_fc_drag_tag":"rc-select","options":[{"key":"cw_1iqn0n6gm1uos105812fafne1mqj6","label":"223","value":"223","validate":false,"tags":[],"default":""}],"synchronize_field":{"name":"备注名"}}]',
-)
-
 window.__QIANKUN__EVENT__.on('formCoverChange', (cover: string) => {
-  const newRule = JSON.parse(rule.value)
+  const newRule = designer.value!.getRule() as Rule[]
+  if (!cover) {
+    newRule.shift()
+    designer.value?.setRule(newRule)
+    return
+  }
   const firstType = newRule[0]?.type
   if (firstType === 'rc-image') {
-    newRule[0].props.cover = cover
+    newRule[0]!.props!.cover = cover
   } else {
     newRule.unshift({
       type: 'rc-image',
@@ -68,19 +76,29 @@ window.__QIANKUN__EVENT__.on('formCoverChange', (cover: string) => {
     } as Rule)
   }
 
-  rule.value = JSON.stringify(newRule)
-  designer.value?.setRule(rule.value) // 重新赋值
+  designer.value?.setRule(JSON.stringify(newRule)) // 重新赋值
 })
 
 onMounted(async () => {
+  console.log(window.__QIANKUN__EVENT__)
+
   // 注册拖拽规则
   designer.value?.addMenu(MenuOptions)
   designer.value?.addComponent(RcSelect)
   designer.value?.addComponent(RcRadio)
   designer.value?.addComponent(RcCheckbox)
 
-  // 设置拖拽规则
-  designer.value?.setRule(rule.value)
+  id = location.search?.substring(1).split('=')[1] || ''
+  if (!id) return
+
+  window.__QIANKUN__REQUSET__.CustomrForm._getDetail({ id }).then(
+    (res: { data: { rule: string; options: string } }) => {
+      if (!res) return
+      // 设置拖拽规则
+      designer.value?.setRule(res.data.rule)
+      designer.value?.setOptions(res.data.options as Options) // 重新赋值
+    },
+  )
 })
 </script>
 

+ 3 - 3
src/components/rc-options/rc-options.vue

@@ -40,9 +40,9 @@ const onRemoveClick = (index: number) => {
 }
 
 // 打开标签弹窗
-const onOpenTagsDialog = (index: number) => {
+const onOpenTagsDialog = (index: number, tags: Option['tags']) => {
   currentIndex = index
-  window.__QIANKUN__OPEN__TAG__?.()
+  window.__QIANKUN__OPEN__TAG__?.(tags)
 }
 
 const validate = (value: string, currentItem: Option) => {
@@ -106,7 +106,7 @@ onMounted(() => {
             tag.tag_name
           }}</el-tag>
         </div>
-        <div style="width: 40%" @click="onOpenTagsDialog(index)">
+        <div style="width: 40%" @click="onOpenTagsDialog(index, item.tags)">
           <el-button :icon="Plus">添加标签</el-button>
         </div>
       </div>

+ 38 - 16
src/components/rc-switch/rc-switch.vue

@@ -1,24 +1,26 @@
 <script lang="ts" name="rc-select" setup>
 import type { FormCreateProps } from '@form-create/element-ui'
 import { nextTick, onMounted, ref } from 'vue'
-
+interface Item {
+  value: string
+  label: string
+}
 const props = defineProps<{
   formCreateInject: FormCreateProps
 }>()
 
 const seniority = ref(false)
-const selectTag = ref('')
+const selectTag = ref<Item>({ value: '', label: '' })
 const centerDialogVisible = ref(false)
-const radio1 = ref('')
+const radio1 = ref<string>('')
+const radioList = ref<Item[]>([])
 // props.formCreateInject?.api?.activeRule
 const onSwitchChange = (val: boolean) => {
   // 设置客户画像字段
   if (val) {
     props.formCreateInject!.api!.activeRule.synchronize_field = {
-      field_name: selectTag.value,
-      // TODO
-      synchronize_to: '',
-      synchronize_type: '',
+      field_name: selectTag.value.label,
+      synchronize_to: selectTag.value.value,
     }
   } else {
     props.formCreateInject!.api!.activeRule.synchronize_field = null
@@ -26,22 +28,42 @@ const onSwitchChange = (val: boolean) => {
 }
 
 const onBeforeClose = (done: () => void) => {
-  radio1.value = props.formCreateInject?.api?.activeRule?.synchronize_field?.field_name || ''
+  radio1.value = selectTag.value.value || ''
   done()
 }
 
 const onSave = () => {
   centerDialogVisible.value = false
-  selectTag.value = radio1.value
+  radioList.value.find((item) => item.value === radio1.value)
+  selectTag.value = radioList.value.find((item) => item.value === radio1.value)!
+  onSwitchChange(true)
 }
 
 onMounted(() => {
+  window.__QIANKUN__REQUSET__.CustomrForm._getUserPortrait().then(
+    (res: { data: Record<string, string> }) => {
+      if (!res.data) return
+      const list = []
+      for (const key in res.data) {
+        list.push({
+          value: key,
+          label: res.data[key],
+        })
+      }
+      radioList.value = list
+    },
+  )
   nextTick(() => {
     // 这里是为了获取到父组件传递过来的值
-    if (!props.formCreateInject?.api?.activeRule) return
-
-    seniority.value = !!props.formCreateInject?.api?.activeRule?.synchronize_field
-    selectTag.value = props.formCreateInject?.api?.activeRule?.synchronize_field?.field_name || ''
+    const activeRule = props.formCreateInject?.api?.activeRule
+    if (!activeRule) return
+    seniority.value = !!activeRule?.synchronize_field
+    if (!seniority.value) return
+    selectTag.value = {
+      label: activeRule?.synchronize_field?.field_name,
+      value: activeRule?.synchronize_field?.synchronize_to,
+    }
+    radio1.value = selectTag.value.value
   })
 })
 </script>
@@ -52,7 +74,7 @@ onMounted(() => {
       <span>自动同步至客户画像</span>
     </div>
     <div class="aaatip" v-if="seniority">
-      <span>开启后,将同步至【客户画像-{{ selectTag }}】</span
+      <span>开启后,将同步至【客户画像-{{ selectTag.label }}】</span
       ><el-text
         class="mx-1"
         type="primary"
@@ -81,8 +103,8 @@ onMounted(() => {
       <div class="tags">
         <div class="title">基本信息</div>
         <el-radio-group v-model="radio1">
-          <div class="el-radio" v-for="item in 10" :key="item">
-            <el-radio :value="item" size="small" border>Option {{ item }}</el-radio>
+          <div class="el-radio" v-for="item in radioList" :key="item.value">
+            <el-radio :value="item.value" size="small" border>{{ item.label }}</el-radio>
           </div>
         </el-radio-group>
       </div>

+ 93 - 0
src/form.config.ts

@@ -1,4 +1,7 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import type { Config } from '@form-create/designer'
+import { debounce } from './utils/debouce'
+import { ElMessage } from 'element-plus'
 
 // 配置项
 export const formConfig: Config = {
@@ -101,6 +104,29 @@ export const formConfig: Config = {
               },
             },
           },
+        ]
+      },
+    },
+    input: {
+      append: true,
+      rule(rule) {
+        return [
+          {
+            type: 'rc-switch',
+            field: 'synchronize_field',
+            label: '高级功能',
+            title: '高级功能',
+            props: {
+              rule,
+            },
+          },
+        ]
+      },
+    },
+    textarea: {
+      append: true,
+      rule(rule) {
+        return [
           {
             type: 'rc-switch',
             field: 'synchronize_field',
@@ -140,8 +166,75 @@ export const formConfig: Config = {
             file.url = res.data.url
             window.__QIANKUN__EVENT__.emit('formCoverChange', res.data.url)
           },
+          onRemove() {
+            window.__QIANKUN__EVENT__.emit('formCoverChange')
+          },
         },
       },
     ]
   },
 }
+
+// 提交表单
+
+// 格式化参数
+const formatOptions = (result: any) => {
+  const hasOptionsType = ['radio', 'checkbox', 'select']
+  return {
+    id: result.id,
+    title: result.options.form.formName,
+    name: result.options.form.formName,
+    cover: result.options.form.cover,
+    rule: result.original.rule,
+    options: result.original.options,
+    form_content: result.rule
+      .map((item: any) => {
+        const formContentValue = {
+          field_name: item.title, // 字段名称
+          field_type: item.type.substring(3), // rc- 前缀
+          default_text: item.value, // 默认文本
+          is_required: item.$required ? 1 : 0, // 是否必填
+          field_options: item.options?.map((op: any) => ({
+            count: 0,
+            option_name: op.label,
+            tags: op.tags.map((i: any) => i.tag_id),
+          })),
+          synchronize_field: item.synchronize_field,
+        }
+
+        // 处理选项类型
+        if (!hasOptionsType.includes(formContentValue.field_type!)) {
+          delete formContentValue.field_options
+        }
+        return formContentValue
+      })
+      .filter((i: any) => i.field_type !== 'image'),
+  }
+}
+
+export const formSubmitSave = debounce((result: any) => {
+  const formated = formatOptions(result)
+  if (!formated.name) {
+    ElMessage({
+      message: '表单标题不能为空',
+      type: 'error',
+    })
+    return
+  }
+  const requestMethod = result.id
+    ? window.__QIANKUN__REQUSET__.CustomrForm._edit
+    : window.__QIANKUN__REQUSET__.CustomrForm._add
+
+  requestMethod
+    .bind(window.__QIANKUN__REQUSET__.CustomrForm)(formated)
+    .then((res: any) => {
+      if (!res) return
+      ElMessage({
+        message: result.id ? '修改成功' : '保存成功',
+        type: 'success',
+        onClose: () => {
+          result.onSuccess?.()
+        },
+      })
+    })
+}, 300)

+ 40 - 33
vite.config.ts

@@ -6,41 +6,48 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
 import qiankun from 'vite-plugin-qiankun'
 
 // https://vite.dev/config/
-export default defineConfig({
-  plugins: [
-    vue(),
-    vueJsx(),
-    qiankun('curstom-form', {
-      useDevMode: true, // 开发模式下使用热更新
-    }),
-  ],
-  resolve: {
-    alias: {
-      '@': fileURLToPath(new URL('./src', import.meta.url)),
-    },
-  },
-  server: {
-    port: 5173,
-    cors: true,
-  },
-  base: '/saas/form/',
-  build: {
-    target: 'esnext',
-    outDir: 'form',
-    chunkSizeWarningLimit: 1000,
-    terserOptions: {
-      compress: {
-        drop_console: true,
-        drop_debugger: true,
+export default defineConfig(({ mode }) => {
+  // 根据环境变量设置 base
+  const base = mode === 'release' ? '/saas/form/' : '/'
+  return {
+    plugins: [
+      vue(),
+      vueJsx(),
+      qiankun('curstom-form', {
+        useDevMode: true, // 开发模式下使用热更新
+      }),
+    ],
+    resolve: {
+      alias: {
+        '@': fileURLToPath(new URL('./src', import.meta.url)),
       },
     },
-
-    rollupOptions: {
-      output: {
-        assetFileNames: 'static/[name].[hash].[ext]', // 确保静态资源路径正确
-        chunkFileNames: 'static/[name].[hash].js',
-        entryFileNames: 'static/[name].[hash].js',
+    server: {
+      port: 5173,
+      cors: true,
+    },
+    base,
+    build: {
+      target: 'esnext',
+      outDir: 'form',
+      chunkSizeWarningLimit: 1000,
+      terserOptions: {
+        compress: {
+          drop_console: true,
+          drop_debugger: true,
+        },
+      },
+      rollupOptions: {
+        output: {
+          sourcemap: mode !== 'production',
+          assetFileNames: 'static/[name].[hash].[ext]', // 确保静态资源路径正确
+          chunkFileNames: () => {
+            return 'static/[name].[hash].js'
+          },
+          entryFileNames: 'static/[name].[hash].js',
+          cssChunkFileNames: 'static/[name].[hash].css',
+        },
       },
     },
-  },
+  }
 })

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff