Explorar o código

feat: 自定义表单h5页面预览

huangziyang hai 3 semanas
pai
achega
77625b6b45

+ 1 - 0
.env.dev

@@ -0,0 +1 @@
+VITE_BASE_URL = 'https://openwork.dfwy.tech'

+ 1 - 0
.env.prod

@@ -0,0 +1 @@
+VITE_BASE_URL = 'https://api.kailin.com.cn'

+ 1 - 0
.env.release

@@ -0,0 +1 @@
+VITE_BASE_URL = 'https://openwork.dfwy.tech'

BIN=BIN
form.zip


+ 8 - 8
form/index.html

@@ -1,9 +1,9 @@
 <!DOCTYPE html><html lang=""><head>
     <meta charset="UTF-8">
-    <link rel="icon" href="/saas/form/favicon.ico">
+    <link rel="icon" href="/favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Vite App</title>
-    <script crossorigin="">import('/saas/form/static/index.DyY_YUf0.js').finally(() => {
+    <script crossorigin="">import('/static/index.35ei16Lw.js').finally(() => {
             
     const qiankunLifeCycle = window.moudleQiankunAppLifeCycles && window.moudleQiankunAppLifeCycles['curstom-form'];
     if (qiankunLifeCycle) {
@@ -14,12 +14,12 @@
     }
   
           })</script>
-    <link rel="modulepreload" crossorigin="" href="/saas/form/static/.pnpm.ZhcTz5Wm.js">
-    <link rel="modulepreload" crossorigin="" href="/saas/form/static/lodash-es.C-xrcuFl.js">
-    <link rel="modulepreload" crossorigin="" href="/saas/form/static/element-plus.K6A-vihL.js">
-    <link rel="modulepreload" crossorigin="" href="/saas/form/static/@form-create/designer.Bw0zwQqi.js">
-    <link rel="stylesheet" crossorigin="" href="/saas/form/static/element-plus.D92rDM1h.css">
-    <link rel="stylesheet" crossorigin="" href="/saas/form/static/index.BSjPPZKB.css">
+    <link rel="modulepreload" crossorigin="" href="/static/.pnpm.ZhcTz5Wm.js">
+    <link rel="modulepreload" crossorigin="" href="/static/lodash-es.C-xrcuFl.js">
+    <link rel="modulepreload" crossorigin="" href="/static/element-plus.K6A-vihL.js">
+    <link rel="modulepreload" crossorigin="" href="/static/@form-create/designer.Bw0zwQqi.js">
+    <link rel="stylesheet" crossorigin="" href="/static/element-plus.D92rDM1h.css">
+    <link rel="stylesheet" crossorigin="" href="/static/index.BSjPPZKB.css">
   </head>
   <body>
     <div id="app"></div>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
form/static/index.35ei16Lw.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
form/static/index.DyY_YUf0.js


+ 2 - 2
package.json

@@ -4,9 +4,9 @@
   "private": true,
   "type": "module",
   "scripts": {
-    "dev": "vite",
+    "dev": "vite --mode dev",
     "build": "vite build --mode release",
-    "build:prod": "vite build --mode production",
+    "build:prod": "vite build --mode prod",
     "preview": "vite preview",
     "type-check": "vue-tsc --build",
     "lint": "eslint . --fix",

+ 0 - 3
src/components/404/no_found.vue

@@ -1,3 +0,0 @@
-<template>
-  <el-result title="404" icon="error" sub-title="抱歉,您访问的页面不存在">1 </el-result>
-</template>

+ 61 - 0
src/components/H5-form/H5-form.tsx

@@ -0,0 +1,61 @@
+import { ElCheckbox, ElCheckboxGroup, ElOption, ElRadio, ElSelect } from 'element-plus'
+import type { Component } from 'vue'
+
+export interface CustomerOptions {
+  title: string
+  cover: string
+  form_field: {
+    field_name: string
+    default_text: string
+    is_required: 0 | 1
+    field_type: 'input' | 'select' | 'checkbox' | 'radio' | 'textarea'
+    props?: {
+      type: string
+    }
+    synchronize_field?: {
+      field_name: string
+      synchrinize_to: string
+      synchrinize_type: string
+    }
+    field_options?: {
+      count: number
+      option_name: string
+      option_img_src: string
+      tags: string[]
+    }[]
+    render: (
+      filed: CustomerOptions['form_field'][number],
+      defaultValue: Record<string, string | string[]>,
+    ) => Component
+  }[]
+}
+
+export const renderMap: Record<string, CustomerOptions['form_field'][number]['render']> = {
+  select: (field, defaultValue) => {
+    return (
+      <ElSelect v-model={defaultValue[field.field_name]}>
+        {field.field_options?.map((options) => {
+          return <ElOption value={options.option_name} label={options.option_name} />
+        })}
+      </ElSelect>
+    )
+  },
+  radio: (field, defaultValue) => {
+    return (
+      <ElRadio.RadioGroup v-model={defaultValue[field.field_name]}>
+        {field.field_options?.map((options) => {
+          return <ElRadio label={options.option_name}>{options.option_name}</ElRadio>
+        })}
+      </ElRadio.RadioGroup>
+    )
+  },
+  checkbox: (field, defaultValue) => {
+    return (
+      <ElCheckboxGroup v-model={defaultValue[field.field_name]}>
+        {field.field_options?.map((options) => {
+          return <ElCheckbox value={options.option_name} label={options.option_name} />
+        })}
+      </ElCheckboxGroup>
+    )
+  },
+}

+ 147 - 0
src/components/H5-form/H5-form.vue

@@ -0,0 +1,147 @@
+<!-- eslint-disable @typescript-eslint/ban-ts-comment -->
+<script setup lang="ts">
+import { request } from '@/utils/request'
+import { onMounted, ref } from 'vue'
+import { renderMap, type CustomerOptions } from './H5-form'
+import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
+
+const customerOptions = ref<CustomerOptions>({} as CustomerOptions)
+const ruleFormRef = ref<FormInstance>()
+const defaultValue = ref<Record<string, string | string[]>>({})
+const rules = ref<Record<string, FormRules>>({})
+const loading = ref(false)
+
+const hasOptions = (type: string) => type === 'select' || type === 'checkbox' || type === 'radio'
+
+const onSubmit = async (formEl: FormInstance | undefined) => {
+  // 预览不需要提交
+  console.log(formEl, ElMessage)
+
+  // loading.value = true
+  // if (!formEl) return
+  // const result = []
+  // for (const field_name in defaultValue.value) {
+  //   if (field_name in rules.value) {
+  //     if (defaultValue.value[field_name] === '' || defaultValue.value[field_name] === undefined) {
+  //       loading.value = false
+  //       ElMessage.error('请填写' + field_name)
+  //       return
+  //     }
+  //   }
+  //   const item = customerOptions.value.form_field.find((item) => item.field_name === field_name)
+  //   result.push({
+  //     field_name,
+  //     field_content: defaultValue.value[field_name],
+  //     field_type: item?.props?.type || item?.field_type,
+  //   })
+  // }
+  // request({
+  //   method: 'post',
+  //   url: '/customer_form/submit_data',
+  //   data: {
+  //     submit_data: result,
+  //   },
+  // })
+  // loading.value = false
+}
+
+onMounted(async () => {
+  const id = location.search?.substring(1).split('=')[1] || ''
+  if (!id) return
+  const res = await request<CustomerOptions>({
+    url: '/customer_form/get_detail',
+    method: 'post',
+    data: {
+      id,
+    },
+  })
+  res.data.form_field = res.data.form_field.map((item) => {
+    // 将文本域改为输入框
+    if (item.field_type === 'textarea') {
+      item.field_type = 'input'
+      item.props = {
+        type: 'textarea',
+      }
+    }
+    // 自定义组件
+    if (hasOptions(item.field_type)) {
+      item.render = renderMap[item.field_type]
+    }
+    // 设置校验规则
+    if (item.is_required) {
+      // @ts-ignore
+      rules.value[item.field_name] = [
+        {
+          required: true,
+          message: item.field_name + '是必须的',
+          trigger: 'blur',
+        },
+      ]
+    }
+    if (!item.default_text) return item
+    // 设置默认值
+    if (item.field_type === 'checkbox') {
+      defaultValue.value[item.field_name] = [item.default_text]
+    } else {
+      defaultValue.value[item.field_name] = item.default_text
+    }
+    return item
+  })
+  customerOptions.value = res.data
+})
+</script>
+<template>
+  <el-scrollbar height="100%" class="contariner">
+    <img :src="customerOptions.cover" width="100%" alt="" />
+    <div class="title">{{ customerOptions.title }}</div>
+    <div class="form" v-if="customerOptions.form_field">
+      <el-form ref="ruleFormRef" :model="defaultValue" label-width="auto" label-position="top">
+        <el-form-item
+          :label="`${index + 1}、${field.field_name} ${field.is_required ? '(必填)' : ''}`"
+          v-for="(field, index) in customerOptions.form_field"
+          :key="field.field_name"
+          :props="field.field_name"
+          class="form-item"
+        >
+          <component
+            v-if="!hasOptions(field.field_type)"
+            :is="`el-${field.field_type}`"
+            v-model="defaultValue[field.field_name]"
+          />
+          <component v-else :is="field.render?.(field, defaultValue)" />
+        </el-form-item>
+      </el-form>
+      <el-form-item>
+        <el-button
+          type="primary"
+          style="width: 100%"
+          @click="onSubmit(ruleFormRef)"
+          :loading="loading"
+          >提交</el-button
+        >
+      </el-form-item>
+    </div>
+  </el-scrollbar>
+</template>
+<style>
+body {
+  background-color: #fff;
+  margin: 0;
+  padding: 0;
+  font-family: Arial, sans-serif;
+  color: #333;
+  font-size: 16px;
+  box-sizing: border-box;
+}
+
+.title {
+  font-size: 24px;
+  font-weight: bolder;
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.form {
+  padding: 0 10px;
+}
+</style>

+ 5 - 1
src/form.config.ts

@@ -191,7 +191,7 @@ const formatOptions = (result: any) => {
       .map((item: any) => {
         const formContentValue = {
           field_name: item.title, // 字段名称
-          field_type: item.type.substring(3), // rc- 前缀
+          field_type: item.type.startsWith('rc-') ? item.type.substring(3) : item.type, // rc- 前缀
           default_text: item.value, // 默认文本
           is_required: item.$required ? 1 : 0, // 是否必填
           field_options: item.options?.map((op: any) => ({
@@ -202,6 +202,10 @@ const formatOptions = (result: any) => {
           synchronize_field: item.synchronize_field,
         }
 
+        if (item.props?.type === 'textarea') {
+          formContentValue.field_type = 'textarea'
+        }
+
         // 处理选项类型
         if (!hasOptionsType.includes(formContentValue.field_type!)) {
           delete formContentValue.field_options

+ 3 - 2
src/main.ts

@@ -13,7 +13,7 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
 //封装弹窗方法
 import setupComponent from './components'
-import No_found from './components/404/no_found.vue'
+import H5_Form from '@/components/H5-form/H5-form.vue'
 
 let app: AppType | null = null
 //导入方法
@@ -67,5 +67,6 @@ if (qiankunWindow.__POWERED_BY_QIANKUN__) {
   initQianKun()
 } else {
   // 独立运行时,直接挂载应用
-  bootstrapVue3(No_found)
+
+  bootstrapVue3(H5_Form)
 }

+ 29 - 0
src/utils/request.ts

@@ -0,0 +1,29 @@
+const BASE_URL = import.meta.env.VITE_BASE_URL + '/openwork'
+
+interface Response<T> {
+  code: number
+  data: T
+  message: string
+}
+
+export const request = async <T = Record<string, string>>({
+  url,
+  data,
+  method = 'GET',
+  ...options
+}: RequestInit & { url: string; data?: Record<string, unknown> }): Promise<Response<T>> => {
+  const formatDate = JSON.stringify(data)
+  const r = await fetch(BASE_URL + url, {
+    ...options,
+    body: formatDate,
+    method,
+    mode: 'cors',
+    cache: 'no-cache',
+    headers: {
+      'Content-Type': 'application/json',
+      ...options.headers,
+    },
+  })
+  const res = await r.json()
+  return res
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio