Procházet zdrojové kódy

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper

Min před 1 rokem
rodič
revize
c5a526b228
67 změnil soubory, kde provedl 8008 přidání a 14 odebrání
  1. 8 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.eslintrc
  2. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.github/FUNDING.yml
  3. 40 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.github/workflows/release.yml
  4. 25 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.gitignore
  5. 10 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.stylelintrc.json
  6. 14 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/.vscode/settings.json
  7. 117 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/README.md
  8. 123 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/build/README.md
  9. 75 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/build/package.json
  10. 12 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/netlify.toml
  11. 74 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/package.json
  12. 13 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/index.html
  13. 10 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/package.json
  14. binární
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/public/favicon.ico
  15. 5 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/shims.d.ts
  16. 17 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/App.vue
  17. 38 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/components.d.ts
  18. 4 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/main.ts
  19. 19 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/tsconfig.json
  20. 6 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/vite.config.ts
  21. 4489 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/pnpm-lock.yaml
  22. 2 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/pnpm-workspace.yaml
  23. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/cascader.svg
  24. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/checkbox.svg
  25. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/clearable.svg
  26. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/color.svg
  27. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/date.svg
  28. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/divider.svg
  29. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/download.svg
  30. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/generate-code.svg
  31. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/generate-json.svg
  32. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/grid.svg
  33. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/img-upload.svg
  34. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/input.svg
  35. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/insert.svg
  36. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/item.svg
  37. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/number.svg
  38. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/password.svg
  39. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/preview.svg
  40. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/radio.svg
  41. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/rate.svg
  42. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/richtext-editor.svg
  43. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/select.svg
  44. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/slider.svg
  45. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/switch.svg
  46. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/table.svg
  47. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/text.svg
  48. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/textarea.svg
  49. 1 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/time.svg
  50. 232 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/auto-imports.d.ts
  51. 37 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components.d.ts
  52. 51 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components/CodeEditor.vue
  53. 47 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components/ComponentGroup.vue
  54. 388 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/config/index.ts
  55. 214 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/DesignForm.vue
  56. 46 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/FormConfig.vue
  57. 753 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetConfig.vue
  58. 66 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetForm.vue
  59. 336 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetFormItem.vue
  60. 158 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateForm.vue
  61. 385 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateFormItem.vue
  62. 5 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/index.ts
  63. 18 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/shims.d.ts
  64. 27 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/tsconfig.json
  65. 102 0
      fhKeeper/formulahousekeeper/plugIn/form-design-master/vite.config.ts
  66. 11 11
      fhKeeper/formulahousekeeper/timesheet/config/index.js
  67. 3 3
      fhKeeper/formulahousekeeper/timesheet_h5/vue.config.js

+ 8 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.eslintrc

@@ -0,0 +1,8 @@
+{
+  "extends": [
+    "@antfu"
+  ],
+  "rules": {
+    "vue/no-mutating-props": "off"
+  }
+}

+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.github/FUNDING.yml

@@ -0,0 +1 @@
+github: [zhiyuanzmj]

+ 40 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.github/workflows/release.yml

@@ -0,0 +1,40 @@
+name: Release
+
+on:
+  push:
+    tags:
+      - 'v*'
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - name: Install pnpm
+        uses: pnpm/action-setup@v2.2.1
+
+      - name: Use Node.js v16
+        uses: actions/setup-node@v2
+        with:
+          node-version: v16
+          registry-url: https://registry.npmjs.org/
+          cache: pnpm
+
+      - run: npx conventional-github-releaser -p angular
+        continue-on-error: true
+        env:
+          CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}
+
+      - name: Install Dependencies
+        run: pnpm install
+
+      - name: PNPM build
+        run: pnpm run build
+
+      - name: Publish to NPM
+        run: pnpm publish --access public --no-git-checks
+        env:
+          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

+ 25 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.gitignore

@@ -0,0 +1,25 @@
+.DS_Store
+node_modules
+dist
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+pnpm-debug.log*
+pnpm-debug.log*
+pnpm-error.log*
+
+# Editor directories and files
+.project
+.idea
+# .vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*
+
+# Local Netlify folder
+.netlify

+ 10 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.stylelintrc.json

@@ -0,0 +1,10 @@
+{
+  "extends":[
+    "stylelint-config-standard-scss",
+    "stylelint-config-recess-order",
+    "stylelint-config-standard-vue/scss"
+  ],
+  "rules": {
+    "selector-class-pattern": "^[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$|^el-.*$"
+  }
+}

+ 14 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/.vscode/settings.json

@@ -0,0 +1,14 @@
+{
+  "prettier.enable": false,
+  "typescript.tsdk": "node_modules/typescript/lib",
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true,
+    "source.fixAll.stylelint": true
+  },
+  "files.associations": {
+    "*.css": "postcss"
+  },
+  "css.validate": false,
+  "scss.validate": false,
+  "stylelint.validate": ["css", "scss", "postcss", "vue"]
+}

+ 117 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/README.md

@@ -0,0 +1,117 @@
+# @zmjs/form-create
+
+基于 Vue3.0 + vite + element-plus + TS 的自定义表单生成器。
+
+![预览](https://user-images.githubusercontent.com/32807958/163714729-de4ce227-0651-4637-8cd0-b0491179c449.png)
+
+### 支持功能
+
+- 远端数据获取
+- 图片上传
+- 栅格布局
+- 生成 JSON
+
+### 演示地址
+
+[https://zmjs-form-design.netlify.app/](https://zmjs-form-design.netlify.app/)
+
+### npm
+
+[https://www.npmjs.com/package/@zmjs/form-design](https://www.npmjs.com/package/@zmjs/form-design)
+
+## 1 安装
+
+```shell
+$ pnpm install @zmjs/form-design
+```
+
+```shell
+$ yarn add @zmjs/form-design
+```
+
+### 表单设计器(DesignForm)
+
+#### 示例
+
+```html
+<script setup lang="ts">
+import { DesignForm } from '@zmjs/form-design'
+</script>
+
+<template>
+  <DesignForm ref="designForm" />
+</template>
+```
+
+#### API
+
+##### Props
+
+|     参数      |          说明          |  类型   | 默认值 |
+| :-----------: | :--------------------: | :-----: | :----: |
+|    preview    |   设计器预览操作按钮   | boolean |  true  |
+| generateJson  |  设计器生成 Json 按钮  | boolean |  true  |
+|  uploadJson   |  设计器导入 JSON 按钮  | boolean |  true  |
+|   clearable   |     设计器清空按钮     | boolean |  true  |
+|  basicFields  | 设计器左侧基础字段配置 |  array  |   -    |
+| advanceFields | 设计器左侧高级字段配置 |  array  |   -    |
+| layoutFields  | 设计器左侧布局字段配置 |  array  |   -    |
+
+##### 方法
+
+通过 ref 可以获取到实例并调用实例方法
+
+|      方法名       |                说明                |             参数              |
+| :---------------: | :--------------------------------: | :---------------------------: |
+|     getJson()     |     获取设计器配置的 JSON 数据     |               -               |
+|  setJson(value)   |        设置设计器的配置信息        |    通过 getJson 获取的数据    |
+|      clear()      |             清空设计器             |                               |
+
+### 表单生成器(GenerateForm)
+
+#### 示例
+
+```html
+<script setup lang="ts">
+import { GenerateForm } from '@zmjs/form-design'
+</script>
+
+<template>
+  <GenerateForm ref="generateForm" />
+</template>
+```
+
+#### API
+
+##### Props
+
+|   参数   |                     说明                      |  类型   | 默认值 |
+| :------: | :-------------------------------------------: | :-----: | :----: |
+|   data   | 表单 json 配置数据(从表单设计器获取的 json) | object  |   -    |
+|  value   |     表单数据(从表单生成器获取的 value)      | object  |   -    |
+| disabled |                   是否禁用                    | boolean | false  |
+
+##### 方法
+
+通过 ref 可以获取到实例并调用实例方法
+
+|  方法名   |             说明             |          参数           |
+| :-------: | :--------------------------: | :---------------------: |
+| getData() | 获取表单数据(返回 Promise) |            -            |
+|  reset()  |         重置表单数据         | 通过 getJson 获取的数据 |
+
+## 3 功能说明
+
+### 远端数据
+
+单选框,多选框,下拉选择框、级联选择器等选择项需要通过数据生成,这时可以配置远端数据。
+
+设置远端方法地址与返回值。
+
+![](https://ftp.bmp.ovh/imgs/2021/04/63b3a638b9a9d251.png)
+
+### 文件上传
+
+填写服务器上传地址、参数名等配置信息。
+
+![](https://ftp.bmp.ovh/imgs/2021/04/91624bc0a32bad63.png)

+ 123 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/build/README.md

@@ -0,0 +1,123 @@
+# @zmjs/form-create
+
+基于 Vue3.0 + vite + element-plus + TS 的自定义表单生成器。
+
+![预览](https://user-images.githubusercontent.com/32807958/163714729-de4ce227-0651-4637-8cd0-b0491179c449.png)
+
+### 支持功能
+
+- 远端数据获取
+- 图片上传
+- 栅格布局
+- 生成 JSON
+
+### 演示地址
+
+[https://zmjs-form-design.netlify.app/](https://zmjs-form-design.netlify.app/)
+
+### npm
+
+[https://www.npmjs.com/package/@zmjs/form-design](https://www.npmjs.com/package/@zmjs/form-design)
+
+## 1 安装
+
+```shell
+$ pnpm install @zmjs/form-design
+```
+
+```shell
+$ yarn add @zmjs/form-design
+```
+### 示例
+
+**npm 引入**
+
+```javascript
+import { createApp } from 'vue'
+import App from './App.vue'
+import {DesignForm} from '@zmjs/form-design'
+
+createApp(App)
+  .use(DesignForm)
+  .mount('#app')
+```
+## 2 组件说明
+
+### 表单设计器(DesignForm)
+
+#### 示例
+
+```html
+<template>
+  <DesignForm ref="designForm" />
+</template>
+```
+
+#### API
+
+##### Props
+
+|     参数      |          说明          |  类型   | 默认值 |
+| :-----------: | :--------------------: | :-----: | :----: |
+|    preview    |   设计器预览操作按钮   | boolean |  true  |
+| generateJson  |  设计器生成 Json 按钮  | boolean |  true  |
+|  uploadJson   |  设计器导入 JSON 按钮  | boolean |  true  |
+|   clearable   |     设计器清空按钮     | boolean |  true  |
+|  basicFields  | 设计器左侧基础字段配置 |  array  |   -    |
+| advanceFields | 设计器左侧高级字段配置 |  array  |   -    |
+| layoutFields  | 设计器左侧布局字段配置 |  array  |   -    |
+
+##### 方法
+
+通过 ref 可以获取到实例并调用实例方法
+
+|      方法名       |                说明                |             参数              |
+| :---------------: | :--------------------------------: | :---------------------------: |
+|     getJson()     |     获取设计器配置的 JSON 数据     |               -               |
+|  setJson(value)   |        设置设计器的配置信息        |    通过 getJson 获取的数据    |
+|      clear()      |             清空设计器             |                               |
+
+### 表单生成器(GenerateForm)
+
+#### 示例
+
+```html
+<template>
+  <GenerateForm ref="generateForm" />
+</template>
+```
+
+#### API
+
+##### Props
+
+|   参数   |                     说明                      |  类型   | 默认值 |
+| :------: | :-------------------------------------------: | :-----: | :----: |
+|   data   | 表单 json 配置数据(从表单设计器获取的 json) | object  |   -    |
+|  value   |     表单数据(从表单生成器获取的 value)      | object  |   -    |
+| disabled |                   是否禁用                    | boolean | false  |
+
+##### 方法
+
+通过 ref 可以获取到实例并调用实例方法
+
+|  方法名   |             说明             |          参数           |
+| :-------: | :--------------------------: | :---------------------: |
+| getData() | 获取表单数据(返回 Promise) |            -            |
+|  reset()  |         重置表单数据         | 通过 getJson 获取的数据 |
+
+## 3 功能说明
+
+### 远端数据
+
+单选框,多选框,下拉选择框、级联选择器等选择项需要通过数据生成,这时可以配置远端数据。
+
+设置远端方法地址与返回值。
+
+![](https://ftp.bmp.ovh/imgs/2021/04/63b3a638b9a9d251.png)
+
+### 文件上传
+
+填写服务器上传地址、参数名等配置信息。
+
+![](https://ftp.bmp.ovh/imgs/2021/04/91624bc0a32bad63.png)

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 75 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/build/package.json


+ 12 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/netlify.toml

@@ -0,0 +1,12 @@
+[build.environment]
+  NPM_FLAGS = "--version"
+  NODE_VERSION = "16"
+
+[build]
+  publish = "playground/dist"
+  command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm build && npx pnpm run play:build"
+
+[[redirects]]
+  from = "/*"
+  to = "/index.html"
+  status = 200

+ 74 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/package.json

@@ -0,0 +1,74 @@
+{
+  "name": "@zmjs/form-design",
+  "version": "0.1.16",
+  "description": "表单设计器",
+  "packageManager": "pnpm@6.32.3",
+  "keywords": [
+    "form-design"
+  ],
+  "homepage": "https://github.com/zhiyuanzmj/form-design#readme",
+  "bugs": {
+    "url": "https://github.com/zhiyuanzmj/form-design/issues"
+  },
+  "license": "MIT",
+  "author": "zhiyuanzmj <260480378@qq.com>",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/zhiyuanzmj/form-design.git"
+  },
+  "funding": "https://github.com/sponsors/zhiyuanzmj",
+  "main": "dist/index.umd.js",
+  "module": "dist/index.es.js",
+  "types": "dist/index.d.ts",
+  "files": [
+    "dist"
+  ],
+  "scripts": {
+    "dev": "vue-tsc & vite build -w",
+    "build": "vite build && vue-tsc",
+    "play": "vite playground --open",
+    "play:build": "vite build playground",
+    "release": "npx bumpp --commit --tag --push"
+  },
+  "dependencies": {
+    "@vueuse/core": "^8.2.3",
+    "ace-builds": "^1.4.14",
+    "element-plus": "^2.1.10",
+    "lodash-es": "^4.17.21",
+    "prismjs": "^1.28.0",
+    "vue": "^3.2.31",
+    "vue-prism-editor": "^2.0.0-alpha.2",
+    "vuedraggable": "^4.1.0"
+  },
+  "devDependencies": {
+    "@antfu/eslint-config": "^0.18.9",
+    "@iconify/json": "^2.1.31",
+    "@iconify/utils": "^1.0.32",
+    "@types/lodash-es": "^4.17.6",
+    "@types/node": "^17.0.23",
+    "@types/prismjs": "^1.26.0",
+    "@vitejs/plugin-vue": "^2.3.1",
+    "@vitejs/plugin-vue-jsx": "^1.3.9",
+    "eslint": "^8.12.0",
+    "postcss": "^8.4.12",
+    "postcss-html": "^1.3.0",
+    "postcss-scss": "^4.0.3",
+    "rollup": "^2.70.1",
+    "sass": "^1.49.11",
+    "stylelint": "^14.6.1",
+    "stylelint-config-recess-order": "^3.0.0",
+    "stylelint-config-standard-scss": "^3.0.0",
+    "stylelint-config-standard-vue": "^1.0.0",
+    "typescript": "^4.6.3",
+    "unocss": "^0.30.11",
+    "unplugin-auto-import": "^0.6.9",
+    "unplugin-vue-components": "^0.18.5",
+    "unplugin-vue-define-options": "^0.5.0",
+    "vite": "^2.9.1",
+    "vite-plugin-libcss": "^1.0.5",
+    "vue-tsc": "^0.34.7"
+  },
+  "engines": {
+    "node": ">=14"
+  }
+}

+ 13 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Form Design</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 10 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/package.json

@@ -0,0 +1,10 @@
+{
+  "private": true,
+  "scripts": {
+    "build": "vite build",
+    "dev": "vite --port 3333 --open"
+  },
+  "dependencies": {
+    "@zmjs/form-design":"workspace:*"
+  }
+}

binární
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/public/favicon.ico


+ 5 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/shims.d.ts

@@ -0,0 +1,5 @@
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

+ 17 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/App.vue

@@ -0,0 +1,17 @@
+<template>
+  <DesignForm :preview="true" />
+</template>
+
+<script lang="ts" setup>
+import { DesignForm } from '@zmjs/form-design'
+</script>
+
+<style>
+html,
+body,
+#app {
+  height: 100%;
+  padding: 0;
+  margin: 0;
+}
+</style>

+ 38 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/components.d.ts

@@ -0,0 +1,38 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/vue-next/pull/3399
+
+declare module 'vue' {
+  export interface GlobalComponents {
+    ElAlert: typeof import('element-plus/es')['ElAlert']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
+    ElCol: typeof import('element-plus/es')['ElCol']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElImage: typeof import('element-plus/es')['ElImage']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElRadio: typeof import('element-plus/es')['ElRadio']
+    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
+    ElRate: typeof import('element-plus/es')['ElRate']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElSpace: typeof import('element-plus/es')['ElSpace']
+    ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
+  }
+}
+
+export { }

+ 4 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/src/main.ts

@@ -0,0 +1,4 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+
+createApp(App).mount('#app')

+ 19 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/tsconfig.json

@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "esnext",
+    "baseUrl": ".",
+    "lib": ["esnext", "DOM"],
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "strict": true,
+    "strictNullChecks": true,
+    "resolveJsonModule": true,
+    "skipLibCheck": true,
+    "jsx": "preserve",
+    "skipDefaultLibCheck": true,
+    "types": [
+      "vite/client",
+    ]
+  }
+}

+ 6 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/playground/vite.config.ts

@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import Vue from '@vitejs/plugin-vue'
+
+export default defineConfig({
+  plugins: [Vue()],
+})

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 4489 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/pnpm-lock.yaml


+ 2 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/pnpm-workspace.yaml

@@ -0,0 +1,2 @@
+packages:
+  - playground

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/cascader.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/checkbox.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/clearable.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/color.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/date.svg


+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/divider.svg

@@ -0,0 +1 @@
+<svg t="1648866429160" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2244" width="200" height="200"><path d="M904 560H120c-27.2 0-48-20.8-48-48s20.8-48 48-48h784c27.2 0 48 20.8 48 48s-20.8 48-48 48z" p-id="2245"></path></svg>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/download.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/generate-code.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/generate-json.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/grid.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/img-upload.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/input.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/insert.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/item.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/number.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/password.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/preview.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/radio.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/rate.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/richtext-editor.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/select.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/slider.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/switch.svg


+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/table.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ba633cb8=""><path fill="currentColor" d="M640 384v256H384V384h256zm64 0h192v256H704V384zm-64 512H384V704h256v192zm64 0V704h192v192H704zm-64-768v192H384V128h256zm64 0h192v192H704V128zM320 384v256H128V384h192zm0 512H128V704h192v192zm0-768v192H128V128h192z"></path></svg>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/text.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/textarea.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/assets/icons/time.svg


+ 232 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/auto-imports.d.ts

@@ -0,0 +1,232 @@
+// Generated by 'unplugin-auto-import'
+// We suggest you to commit this file into source control
+declare global {
+  const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
+  const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
+  const computed: typeof import('vue')['computed']
+  const computedAsync: typeof import('@vueuse/core')['computedAsync']
+  const computedEager: typeof import('@vueuse/core')['computedEager']
+  const computedInject: typeof import('@vueuse/core')['computedInject']
+  const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
+  const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
+  const controlledRef: typeof import('@vueuse/core')['controlledRef']
+  const createApp: typeof import('vue')['createApp']
+  const createEventHook: typeof import('@vueuse/core')['createEventHook']
+  const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
+  const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
+  const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
+  const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
+  const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
+  const customRef: typeof import('vue')['customRef']
+  const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
+  const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
+  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+  const defineComponent: typeof import('vue')['defineComponent']
+  const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
+  const effectScope: typeof import('vue')['effectScope']
+  const EffectScope: typeof import('vue')['EffectScope']
+  const extendRef: typeof import('@vueuse/core')['extendRef']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const h: typeof import('vue')['h']
+  const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
+  const inject: typeof import('vue')['inject']
+  const isDefined: typeof import('@vueuse/core')['isDefined']
+  const isReadonly: typeof import('vue')['isReadonly']
+  const isRef: typeof import('vue')['isRef']
+  const logicAnd: typeof import('@vueuse/core')['logicAnd']
+  const logicNot: typeof import('@vueuse/core')['logicNot']
+  const logicOr: typeof import('@vueuse/core')['logicOr']
+  const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
+  const markRaw: typeof import('vue')['markRaw']
+  const nextTick: typeof import('vue')['nextTick']
+  const onActivated: typeof import('vue')['onActivated']
+  const onBeforeMount: typeof import('vue')['onBeforeMount']
+  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+  const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
+  const onDeactivated: typeof import('vue')['onDeactivated']
+  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+  const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
+  const onLongPress: typeof import('@vueuse/core')['onLongPress']
+  const onMounted: typeof import('vue')['onMounted']
+  const onRenderTracked: typeof import('vue')['onRenderTracked']
+  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+  const onScopeDispose: typeof import('vue')['onScopeDispose']
+  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+  const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
+  const onUnmounted: typeof import('vue')['onUnmounted']
+  const onUpdated: typeof import('vue')['onUpdated']
+  const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
+  const provide: typeof import('vue')['provide']
+  const reactify: typeof import('@vueuse/core')['reactify']
+  const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
+  const reactive: typeof import('vue')['reactive']
+  const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
+  const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
+  const reactivePick: typeof import('@vueuse/core')['reactivePick']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
+  const refDebounced: typeof import('@vueuse/core')['refDebounced']
+  const refDefault: typeof import('@vueuse/core')['refDefault']
+  const refThrottled: typeof import('@vueuse/core')['refThrottled']
+  const refWithControl: typeof import('@vueuse/core')['refWithControl']
+  const resolveComponent: typeof import('vue')['resolveComponent']
+  const shallowReactive: typeof import('vue')['shallowReactive']
+  const shallowReadonly: typeof import('vue')['shallowReadonly']
+  const shallowRef: typeof import('vue')['shallowRef']
+  const syncRef: typeof import('@vueuse/core')['syncRef']
+  const syncRefs: typeof import('@vueuse/core')['syncRefs']
+  const templateRef: typeof import('@vueuse/core')['templateRef']
+  const throttledRef: typeof import('@vueuse/core')['throttledRef']
+  const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
+  const toRaw: typeof import('vue')['toRaw']
+  const toReactive: typeof import('@vueuse/core')['toReactive']
+  const toRef: typeof import('vue')['toRef']
+  const toRefs: typeof import('vue')['toRefs']
+  const triggerRef: typeof import('vue')['triggerRef']
+  const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
+  const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
+  const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
+  const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
+  const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
+  const unref: typeof import('vue')['unref']
+  const unrefElement: typeof import('@vueuse/core')['unrefElement']
+  const until: typeof import('@vueuse/core')['until']
+  const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
+  const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
+  const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
+  const useAttrs: typeof import('vue')['useAttrs']
+  const useBase64: typeof import('@vueuse/core')['useBase64']
+  const useBattery: typeof import('@vueuse/core')['useBattery']
+  const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
+  const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
+  const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
+  const useCached: typeof import('@vueuse/core')['useCached']
+  const useClamp: typeof import('@vueuse/core')['useClamp']
+  const useClipboard: typeof import('@vueuse/core')['useClipboard']
+  const useColorMode: typeof import('@vueuse/core')['useColorMode']
+  const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
+  const useCounter: typeof import('@vueuse/core')['useCounter']
+  const useCssModule: typeof import('vue')['useCssModule']
+  const useCssVar: typeof import('@vueuse/core')['useCssVar']
+  const useCssVars: typeof import('vue')['useCssVars']
+  const useCycleList: typeof import('@vueuse/core')['useCycleList']
+  const useDark: typeof import('@vueuse/core')['useDark']
+  const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
+  const useDebounce: typeof import('@vueuse/core')['useDebounce']
+  const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
+  const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
+  const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
+  const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
+  const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
+  const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
+  const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
+  const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
+  const useDraggable: typeof import('@vueuse/core')['useDraggable']
+  const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
+  const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
+  const useElementHover: typeof import('@vueuse/core')['useElementHover']
+  const useElementSize: typeof import('@vueuse/core')['useElementSize']
+  const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
+  const useEventBus: typeof import('@vueuse/core')['useEventBus']
+  const useEventListener: typeof import('@vueuse/core')['useEventListener']
+  const useEventSource: typeof import('@vueuse/core')['useEventSource']
+  const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
+  const useFavicon: typeof import('@vueuse/core')['useFavicon']
+  const useFetch: typeof import('@vueuse/core')['useFetch']
+  const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
+  const useFocus: typeof import('@vueuse/core')['useFocus']
+  const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
+  const useFps: typeof import('@vueuse/core')['useFps']
+  const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
+  const useGamepad: typeof import('@vueuse/core')['useGamepad']
+  const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
+  const useIdle: typeof import('@vueuse/core')['useIdle']
+  const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
+  const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
+  const useInterval: typeof import('@vueuse/core')['useInterval']
+  const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
+  const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
+  const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
+  const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
+  const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
+  const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
+  const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
+  const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
+  const useMemoize: typeof import('@vueuse/core')['useMemoize']
+  const useMemory: typeof import('@vueuse/core')['useMemory']
+  const useMounted: typeof import('@vueuse/core')['useMounted']
+  const useMouse: typeof import('@vueuse/core')['useMouse']
+  const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
+  const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
+  const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
+  const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
+  const useNetwork: typeof import('@vueuse/core')['useNetwork']
+  const useNow: typeof import('@vueuse/core')['useNow']
+  const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
+  const useOnline: typeof import('@vueuse/core')['useOnline']
+  const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
+  const useParallax: typeof import('@vueuse/core')['useParallax']
+  const usePermission: typeof import('@vueuse/core')['usePermission']
+  const usePointer: typeof import('@vueuse/core')['usePointer']
+  const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
+  const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
+  const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
+  const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
+  const useRafFn: typeof import('@vueuse/core')['useRafFn']
+  const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
+  const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
+  const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
+  const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
+  const useScroll: typeof import('@vueuse/core')['useScroll']
+  const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
+  const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
+  const useShare: typeof import('@vueuse/core')['useShare']
+  const useSlots: typeof import('vue')['useSlots']
+  const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
+  const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
+  const useStorage: typeof import('@vueuse/core')['useStorage']
+  const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
+  const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
+  const useSwipe: typeof import('@vueuse/core')['useSwipe']
+  const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
+  const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
+  const useThrottle: typeof import('@vueuse/core')['useThrottle']
+  const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
+  const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
+  const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
+  const useTimeout: typeof import('@vueuse/core')['useTimeout']
+  const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
+  const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
+  const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
+  const useTitle: typeof import('@vueuse/core')['useTitle']
+  const useToggle: typeof import('@vueuse/core')['useToggle']
+  const useTransition: typeof import('@vueuse/core')['useTransition']
+  const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
+  const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
+  const useVibrate: typeof import('@vueuse/core')['useVibrate']
+  const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
+  const useVModel: typeof import('@vueuse/core')['useVModel']
+  const useVModels: typeof import('@vueuse/core')['useVModels']
+  const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
+  const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
+  const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
+  const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
+  const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
+  const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
+  const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
+  const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
+  const watch: typeof import('vue')['watch']
+  const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
+  const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
+  const watchEffect: typeof import('vue')['watchEffect']
+  const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
+  const watchOnce: typeof import('@vueuse/core')['watchOnce']
+  const watchPausable: typeof import('@vueuse/core')['watchPausable']
+  const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
+  const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
+  const whenever: typeof import('@vueuse/core')['whenever']
+}
+export {}

+ 37 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components.d.ts

@@ -0,0 +1,37 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/vue-next/pull/3399
+
+declare module 'vue' {
+  export interface GlobalComponents {
+    CodeEditor: typeof import('./components/CodeEditor.vue')['default']
+    ComponentGroup: typeof import('./components/ComponentGroup.vue')['default']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElImage: typeof import('element-plus/es')['ElImage']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElRadio: typeof import('element-plus/es')['ElRadio']
+    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
+    ElRate: typeof import('element-plus/es')['ElRate']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElSpace: typeof import('element-plus/es')['ElSpace']
+    ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
+  }
+}
+
+export { }

+ 51 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components/CodeEditor.vue

@@ -0,0 +1,51 @@
+<template>
+  <PrismEditor v-model="code" max-h-xl class="form-design-editor" :highlight="(code:any)=>{return highlight(code,languages[language])}" :readonly="readonly" line-numbers />
+</template>
+
+<script lang="ts" setup>
+import { PrismEditor } from 'vue-prism-editor'
+import 'vue-prism-editor/dist/prismeditor.min.css'
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-expect-error
+import { highlight, languages } from 'prismjs/components/prism-core'
+import 'prismjs/components/prism-clike'
+import 'prismjs/components/prism-json'
+import 'prismjs/themes/prism-tomorrow.css'
+
+const { language = 'json', modelValue = '', readonly = false } = defineProps<{
+  modelValue: string
+  language?: string
+  readonly?: boolean
+}>()
+const emit = defineEmits(['update:modelValue'])
+
+const code = $computed<string>({
+  get() { return modelValue },
+  set(val) { emit('update:modelValue', val) },
+})
+</script>
+<style lang="scss">
+.form-design-editor {
+  box-sizing: border-box;
+  padding: 10px;
+
+  /* you must provide font-family font-size line-height. Example: */
+  font-family: "Fira code", "Fira Mono", Consolas, Menlo, Courier, monospace;
+  font-size: 14px;
+  line-height: 1.5;
+  color: #ccc;
+
+  /* we dont use `language-` classes anymore so thats why we need to add background and text color manually */
+  background: #2d2d2d;
+
+  /* optional class for removing the outline */
+  .prism-editor__textarea:focus {
+    outline: none;
+  }
+
+  .prism-editor__line-numbers {
+    user-select: none;
+  }
+}
+</style>

+ 47 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/components/ComponentGroup.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="px-3 py-2 text-sm text-gray-700">
+    {{ title }}
+  </div>
+  <Draggable
+    tag="ul"
+    class="m-0 p-3 pt-0 grid grid-cols-2 gap-1"
+    item-key="type"
+    :group="{ name: 'form-design', pull: 'clone', put: false }"
+    :clone="cloneWidget"
+    :sort="false"
+    :list="list"
+  >
+    <template #item="{ element }">
+      <li
+        v-if="fields.includes(element.type)"
+        class="bg-gray-100 text-xs text-gray-700 hover:text-blue-500 leading-8 cursor-move -outline-offset-1 hover:outline-dashed outline-1 outline-blue-500 list-none flex items-center"
+      >
+        <i :class="'custom:'+element.type" class="ml-2 mr-1" />
+        <span>{{ element.label }}</span>
+      </li>
+    </template>
+  </Draggable>
+</template>
+<script lang="ts" setup>
+import Draggable from 'vuedraggable'
+import { cloneDeep } from 'lodash-es'
+
+defineProps<{ fields: any[]; title: string; list: any[] }>()
+</script>
+
+<script lang="ts">
+export function cloneWidget(params: any) {
+  const key = Math.random().toString(36).substring(2, 9)
+  params = cloneDeep({
+    ...params,
+    key,
+    model: `${params.type}_${key}`,
+  })
+  if (params.type === 'grid') {
+    params.columns.forEach((i: any) => {
+      i.list = i.list.map(cloneWidget)
+    })
+  }
+  return params
+}
+</script>

+ 388 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/config/index.ts

@@ -0,0 +1,388 @@
+export interface Rules {
+  trigger: string
+  enum: string
+  len?: number
+  max?: number
+  message: string
+  min?: number
+  pattern: string
+  required: boolean
+  type: string
+}
+
+export interface WidgetForm {
+  list: any[]
+  config: {
+    size: '' | 'default' | 'small' | 'large'
+    hideRequiredAsterisk: boolean
+    labelWidth: number
+    labelPosition: string
+  }
+}
+
+const rules: Rules = {
+  trigger: 'blur',
+  enum: '',
+  len: undefined,
+  max: undefined,
+  message: '',
+  min: undefined,
+  pattern: '',
+  required: false,
+  type: 'any',
+}
+
+export const getWidgetForm = (): WidgetForm => ({
+  list: [],
+  config: {
+    size: 'default',
+    hideRequiredAsterisk: false,
+    labelWidth: 100,
+    labelPosition: 'right',
+  },
+})
+
+export const basicComponents = [
+  {
+    label: '单行文本',
+    type: 'input' as const,
+    options: {
+      width: '100%',
+      defaultValue: '',
+      placeholder: '',
+      maxlength: null,
+      prefix: '',
+      suffix: '',
+      prepend: '',
+      append: '',
+      disabled: false,
+      clearable: false,
+      readonly: false,
+      rules,
+    },
+  },
+  {
+    label: '密码框',
+    type: 'password' as const,
+    options: {
+      width: '100%',
+      defaultValue: '',
+      placeholder: '',
+      maxlength: null,
+      prefix: '',
+      suffix: '',
+      prepend: '',
+      append: '',
+      showPassword: true,
+      disabled: false,
+      clearable: false,
+      readonly: false,
+      rules,
+    },
+  },
+  {
+    label: '多行文本',
+    type: 'textarea' as const,
+    options: {
+      width: '100%',
+      defaultValue: '',
+      placeholder: '',
+      maxlength: null,
+      rows: 4,
+      autosize: false,
+      showWordLimit: false,
+      disabled: false,
+      clearable: false,
+      readonly: false,
+      rules,
+    },
+  },
+  {
+    label: '计数器',
+    type: 'number' as const,
+    options: {
+      width: '',
+      defaultValue: 0,
+      min: 0,
+      max: 100,
+      step: 1,
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '单选框组',
+    type: 'radio' as const,
+    options: {
+      defaultValue: '',
+      width: '',
+      inline: true,
+      remote: false,
+      showLabel: true,
+      remoteFunc:
+        'https://raw.githubusercontent.com/fuchengwei/vue-form-create/master/mock/mock.json',
+      options: [
+        {
+          value: 'Option 1',
+          label: 'Option 1',
+        },
+        {
+          value: 'Option 2',
+          label: 'Option 2',
+        },
+        {
+          value: 'Option 3',
+          label: 'Option 3',
+        },
+      ],
+      remoteOptions: [],
+      props: {
+        value: 'value',
+        label: 'label',
+      },
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '多选框组',
+    type: 'checkbox' as const,
+    options: {
+      defaultValue: [],
+      width: '',
+      inline: true,
+      remote: false,
+      showLabel: true,
+      remoteFunc:
+        'https://raw.githubusercontent.com/fuchengwei/vue-form-create/master/mock/mock.json',
+      options: [
+        {
+          label: 'Option 1',
+          value: 'Option 1',
+        },
+        {
+          label: 'Option 2',
+          value: 'Option 2',
+        },
+        {
+          label: 'Option 3',
+          value: 'Option 3',
+        },
+      ],
+      remoteOptions: [],
+      props: {
+        value: 'value',
+        label: 'label',
+      },
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '时间选择器',
+    type: 'time' as const,
+    options: {
+      defaultValue: '',
+      width: '',
+      placeholder: '请选择时间',
+      format: 'HH:mm:ss',
+      valueFormat: 'HH:mm:ss',
+      readonly: false,
+      editable: true,
+      clearable: true,
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '日期选择器',
+    type: 'date' as const,
+    options: {
+      defaultValue: '',
+      width: '',
+      placeholder: '请选择时间',
+      format: 'YYYY-MM-DD',
+      readonly: false,
+      editable: true,
+      clearable: true,
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '评分',
+    type: 'rate' as const,
+    options: {
+      defaultValue: 0,
+      max: 5,
+      allowHalf: false,
+      disabled: false,
+      rules,
+    },
+  },
+  {
+    label: '下拉选择框',
+    type: 'select' as const,
+    options: {
+      defaultValue: '',
+      width: '200px',
+      multiple: false,
+      placeholder: '',
+      remote: false,
+      showLabel: true,
+      filterable: false,
+      clearable: false,
+      disabled: false,
+      props: {
+        label: 'label',
+        value: 'value',
+      },
+      options: [
+        {
+          label: 'Option 1',
+          value: 'Option 1',
+        },
+        {
+          label: 'Option 2',
+          value: 'Option 2',
+        },
+        {
+          label: 'Option 3',
+          value: 'Option 3',
+        },
+      ],
+      remoteOptions: [],
+      remoteFunc:
+        'https://raw.githubusercontent.com/fuchengwei/vue-form-create/master/mock/mock.json',
+      rules,
+    },
+  },
+  {
+    label: '开关',
+    type: 'switch' as const,
+    options: {
+      defaultValue: false,
+      disabled: false,
+      activeText: '',
+      inactiveText: '',
+      rules,
+    },
+  },
+  {
+    label: '滑块',
+    type: 'slider' as const,
+    options: {
+      defaultValue: 0,
+      width: '',
+      min: 0,
+      max: 100,
+      step: 1,
+      disabled: false,
+      range: false,
+      rules,
+    },
+  },
+  {
+    label: '文字',
+    type: 'text' as const,
+    options: {
+      defaultValue: 'This is a text',
+    },
+  },
+]
+
+export const basicFields = (basicComponents).map(i => i.type)
+
+export const advanceComponents = [
+  {
+    label: '图片',
+    type: 'img-upload' as const,
+    options: {
+      defaultValue: [],
+      name: 'file',
+      action: 'http://example.com/upload',
+      method: 'post',
+      listType: 'text',
+      accept: 'image/*',
+      limit: 1,
+      multiple: false,
+      disabled: true,
+      rules,
+    },
+  },
+  {
+    label: '下载',
+    type: 'download' as const,
+    labelWidth: 'auto',
+    options: {
+      defaultValue: '',
+      name: 'file',
+    },
+  },
+  {
+    label: '级联选择器',
+    type: 'cascader' as const,
+    options: {
+      defaultValue: [],
+      width: '200px',
+      placeholder: '',
+      disabled: false,
+      clearable: false,
+      filterable: false,
+      remote: true,
+      remoteOptions: [],
+      props: {
+        label: 'label',
+        value: 'value',
+        children: 'children',
+      },
+      remoteFunc:
+        'https://raw.githubusercontent.com/fuchengwei/vue-form-create/master/mock/mock.json',
+      rules,
+    },
+  },
+]
+
+export const advanceFields = (advanceComponents).map(i => i.type)
+
+export const layoutComponents = [
+  {
+    label: '栅格布局',
+    type: 'grid',
+    columns: [
+      {
+        span: 12,
+        list: [],
+      },
+      {
+        span: 12,
+        list: [],
+      },
+    ],
+    options: {
+      gutter: 0,
+      align: 'initial',
+    },
+  },
+  {
+    label: '数据表格',
+    type: 'table',
+    columns: [
+      { label: '名称', prop: 'name' },
+      { label: '创建时间', prop: 'createTime' },
+    ],
+    options: {
+      defaultValue: [],
+      disabled: false,
+      size: '',
+      align: 'center',
+    },
+  },
+  {
+    label: '分割线',
+    type: 'divider',
+    options: {},
+  },
+]
+
+export const layoutFields = (layoutComponents).map(i => i.type)

+ 214 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/DesignForm.vue

@@ -0,0 +1,214 @@
+<template>
+  <section h-full bg-white flex>
+    <aside pt-2 w-62>
+      <ComponentGroup
+        title="基础字段"
+        :fields="basicFieldsList"
+        :list="basicComponents"
+      />
+      <ComponentGroup
+        title="高级字段"
+        :fields="advanceFieldsList"
+        :list="advanceComponents"
+      />
+      <ComponentGroup
+        title="布局字段"
+        :fields="layoutFieldsList"
+        :list="layoutComponents"
+      />
+    </aside>
+
+    <main border="0 x solid gray-200" flex="~ 1 col nowrap">
+      <header border="0 b solid gray-200" flex gap-2 items-center justify-end h-11 py-0 px-2>
+        <slot />
+        <el-button v-if="generateJson" type="text" @click="handleGenerateJson">
+          <template #icon>
+            <i class="ep:edit" />
+          </template>修改
+        </el-button>
+        <el-button v-if="clearable" type="text" @click="handleClearable">
+          <template #icon>
+            <i class="custom:clearable" />
+          </template>清空
+        </el-button>
+        <el-button v-if="preview" type="text" @click="previewVisible = true">
+          <template #icon>
+            <i class="custom:preview" />
+          </template>预览
+        </el-button>
+      </header>
+
+      <WidgetFormVue
+        ref="widgetFormRef"
+        v-bind="widgetForm"
+        v-model:selectWidget="selectWidget"
+      />
+    </main>
+
+    <aside relative w-75 flex="~ col">
+      <header border="0 b-1 gray-200" grid="~ cols-2" min-h-11 px-2>
+        <div :class="[{active:!tab}, 'config-tab']" @click="tab = 0">
+          字段属性
+        </div>
+        <div :class="[{active:tab}, 'config-tab']" @click="tab = 1">
+          表单属性
+        </div>
+      </header>
+
+      <WidgetConfig
+        v-show="!tab"
+        v-model:select="selectWidget"
+      />
+      <FormConfig
+        v-show="tab"
+        v-model:config="widgetForm.config"
+      />
+    </aside>
+
+    <el-dialog v-model="previewVisible" destroy-on-close title="预览" :z-index="2000" :width="800" @close="dataVisible=false">
+      <CodeEditor v-show="dataVisible" v-model="generateJsonTemplate" />
+      <GenerateForm
+        v-show="!dataVisible"
+        ref="generateFormRef"
+        :request="request"
+        :data="widgetForm"
+      />
+
+      <template #footer>
+        <el-button @click="dataVisible?handleCopyClick(generateJsonTemplate):handleReset()">
+          {{ dataVisible?'复制':'重置' }}
+        </el-button>
+        <el-button type="primary" @click="handleGetData">
+          {{ dataVisible?'返回预览':'获取数据' }}
+        </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="generateJsonVisible" append-to-body title="修改JSON" :z-index="2000" :width="800">
+      <CodeEditor v-model="generateJsonTemplate" />
+
+      <template #footer>
+        <el-button @click="handleCopyClick(generateJsonTemplate)">
+          复制
+        </el-button>
+        <el-button type="primary" @click="handleUploadJson">
+          确认修改
+        </el-button>
+      </template>
+    </el-dialog>
+  </section>
+</template>
+
+<script lang="ts" setup>
+import type { PropType } from 'vue'
+import { ElMessage } from 'element-plus'
+// eslint-disable-next-line @typescript-eslint/consistent-type-imports
+import GenerateForm from '../generate/GenerateForm.vue'
+import WidgetFormVue from './WidgetForm.vue'
+import WidgetConfig from './WidgetConfig.vue'
+import FormConfig from './FormConfig.vue'
+import ComponentGroup from '@/components/ComponentGroup.vue'
+import CodeEditor from '@/components/CodeEditor.vue'
+import type { WidgetForm } from '@/config'
+import { advanceComponents, advanceFields, basicComponents, basicFields, getWidgetForm, layoutComponents, layoutFields } from '@/config'
+
+defineOptions({ name: 'DesignForm' })
+
+defineProps({
+  preview: {
+    type: Boolean,
+    default: true,
+  },
+  generateJson: {
+    type: Boolean,
+    default: true,
+  },
+  clearable: {
+    type: Boolean,
+    default: true,
+  },
+  basicFieldsList: {
+    type: Array as PropType<typeof basicFields>,
+    default: () => basicFields,
+  },
+  advanceFieldsList: {
+    type: Array as PropType<typeof advanceFields>,
+    default: () => advanceFields,
+  },
+  layoutFieldsList: {
+    type: Array as PropType<typeof layoutFields>,
+    default: () => layoutFields,
+  },
+  request: {
+    type: Function,
+  },
+})
+
+let tab = $ref(0)
+let selectWidget = $ref<any>()
+watch(() => selectWidget, (val) => {
+  tab = val ? 0 : 1
+})
+
+let widgetForm = $ref(getWidgetForm())
+let generateJsonVisible = $ref(false)
+let generateJsonTemplate = $ref(JSON.stringify(getWidgetForm(), null, 2))
+const handleUploadJson = () => {
+  try {
+    widgetForm = JSON.parse(generateJsonTemplate)
+    selectWidget = widgetForm.list?.[0]
+    generateJsonVisible = false
+    ElMessage.success('修改成功')
+  }
+  catch (error) {
+    ElMessage.error('修改失败,请检查JSON格式是否正确。')
+  }
+}
+
+const handleGenerateJson = () =>
+  (generateJsonTemplate = JSON.stringify(widgetForm, null, 2))
+  && (generateJsonVisible = true)
+
+const handleClearable = () => {
+  widgetForm = getWidgetForm()
+  selectWidget = undefined
+}
+
+let dataVisible = $ref(false)
+const generateFormRef = $ref<InstanceType<typeof GenerateForm>|null>(null)
+const handleGetData = async() => {
+  dataVisible = !dataVisible
+  if (!dataVisible) return
+  const res = await generateFormRef?.getData()
+  generateJsonTemplate = JSON.stringify(res, null, 2)
+}
+const handleReset = () => generateFormRef?.reset()
+
+const handleCopyClick = (text: string) => {
+  navigator.clipboard.writeText(text)
+  ElMessage.success({ message: 'Copy成功' })
+}
+
+const previewVisible = $ref(false)
+
+defineExpose({
+  getJson: () => $$(widgetForm).value,
+  setJson: (json: WidgetForm) => {
+    widgetForm = json
+    selectWidget = json.list?.[0]
+  },
+  clear: handleClearable,
+})
+
+</script>
+<style lang="scss">
+  .config-tab {
+    @apply cursor-pointer text-sm text-gray-700
+      flex items-center justify-center
+      border-0 border-b-2 border-white;
+
+    &.active {
+      @apply border-blue-500;
+    }
+  }
+</style>

+ 46 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/FormConfig.vue

@@ -0,0 +1,46 @@
+<template>
+  <el-form p-2 label-position="top" @submit.prevent>
+    <el-form-item label="标签对齐方式">
+      <el-radio-group v-model="config.labelPosition">
+        <el-radio-button label="left">
+          左对齐
+        </el-radio-button>
+        <el-radio-button label="right">
+          右对齐
+        </el-radio-button>
+        <el-radio-button label="top">
+          顶部对齐
+        </el-radio-button>
+      </el-radio-group>
+    </el-form-item>
+
+    <el-form-item label="标签宽度">
+      <el-input-number v-model.number="config.labelWidth" :min="0" />
+    </el-form-item>
+
+    <el-form-item label="组件尺寸">
+      <el-radio-group v-model="config.size">
+        <el-radio-button label="large">
+          大
+        </el-radio-button>
+        <el-radio-button label="default">
+          默认
+        </el-radio-button>
+        <el-radio-button label="small">
+          小
+        </el-radio-button>
+      </el-radio-group>
+    </el-form-item>
+
+    <el-form-item label="隐藏必选标记">
+      <el-switch v-model="config.hideRequiredAsterisk" />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import type { WidgetForm } from '@/config'
+defineProps<{
+  config: WidgetForm['config']
+}>()
+</script>

+ 753 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetConfig.vue

@@ -0,0 +1,753 @@
+<template>
+  <el-form v-if="data" :key="data.key" label-position="top" p-2 flex-1 overflow-auto>
+    <el-form-item v-if="!['grid'].includes(data.type)" label="字段标识">
+      <el-input v-model="data.model" />
+    </el-form-item>
+
+    <el-form-item v-if="!['grid', 'table'].includes(data.type)" label="标题">
+      <el-input v-model="data.label" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('width')" label="宽度">
+      <el-input v-model="data.options.width" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('placeholder')" label="占位内容">
+      <el-input v-model="data.options.placeholder" />
+    </el-form-item>
+
+    <el-form-item
+      v-if="
+        hasKey('defaultValue') &&
+          (data.type === 'input' ||
+            data.type === 'password' ||
+            data.type === 'textarea' ||
+            data.type === 'text' ||
+            data.type === 'rate' ||
+            data.type === 'switch' ||
+            data.type === 'slider')
+      "
+      label="默认内容"
+    >
+      <el-input
+        v-if="
+          data.type === 'input' ||
+            data.type === 'password' ||
+            data.type === 'text'
+        "
+        v-model="data.options.defaultValue"
+      />
+      <el-input
+        v-if="data.type === 'textarea'"
+        v-model="data.options.defaultValue"
+        type="textarea"
+      />
+      <el-rate
+        v-if="data.type === 'rate'"
+        v-model="data.options.defaultValue"
+        :max="data.options.max"
+        :allow-half="data.options.allowHalf"
+      />
+      <el-switch
+        v-if="data.type === 'switch'"
+        v-model="data.options.defaultValue"
+      />
+      <template v-if="data.type === 'slider'">
+        <el-input-number
+          v-if="!data.options.range"
+          v-model.number="data.options.defaultValue"
+        />
+        <template v-if="data.options.range">
+          <el-input-number
+            v-model.number="data.options.defaultValue[0]"
+            :max="data.options.max"
+          />
+          <el-input-number
+            v-model.number="data.options.defaultValue[1]"
+            :max="data.options.max"
+          />
+        </template>
+      </template>
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('maxlength')" label="最大长度">
+      <el-input v-model.number="data.options.maxlength" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('max')" label="最大值">
+      <el-input-number v-model.number="data.options.max" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('min')" label="最小值">
+      <el-input-number v-model.number="data.options.min" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('step')" label="步长">
+      <el-input-number v-model.number="data.options.step" :min="0" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('prefix')" label="前缀">
+      <el-input v-model="data.options.prefix" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('suffix')" label="后缀">
+      <el-input v-model="data.options.suffix" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('prepend')" label="前置标签">
+      <el-input v-model="data.options.prepend" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('append')" label="后置标签">
+      <el-input v-model="data.options.append" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('activeText')" label="选中时的内容">
+      <el-input v-model="data.options.activeText" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('inactiveText')" label="非选中时的内容">
+      <el-input v-model="data.options.inactiveText" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('editable')" label="文本框可输入">
+      <el-switch v-model="data.options.editable" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('range')" label="范围选择">
+      <el-switch
+        v-model="data.options.range"
+        @change="handleSliderModeChange"
+      />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('showPassword')" label="是否显示切换按钮">
+      <el-switch v-model="data.options.showPassword" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('showWordLimit')" label="是否显示字数">
+      <el-switch v-model="data.options.showWordLimit" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('autosize')" label="是否自适应内容高度">
+      <el-switch v-model="data.options.autosize" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('rows') && !data.options.autosize" label="行数">
+      <el-input-number v-model="data.options.rows" :min="0" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('allowHalf')" label="是否允许半选">
+      <el-switch v-model="data.options.allowHalf" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('inline')" label="布局方式">
+      <el-radio-group v-model="data.options.inline">
+        <el-radio-button :label="true">
+          行内
+        </el-radio-button>
+        <el-radio-button :label="false">
+          块级
+        </el-radio-button>
+      </el-radio-group>
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('multiple')" label="是否多选">
+      <el-switch
+        v-model="data.options.multiple"
+        @change="handleSelectModeChange"
+      />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('filterable')" label="是否可搜索">
+      <el-switch v-model="data.options.filterable" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('showLabel')" label="是否显示标签">
+      <el-switch v-model="data.options.showLabel" />
+    </el-form-item>
+
+    <el-form-item v-if="hasKey('options')" label="选项">
+      <el-radio-group v-model="data.options.remote">
+        <el-radio-button :label="false">
+          静态数据
+        </el-radio-button>
+        <el-radio-button :label="true">
+          远端数据
+        </el-radio-button>
+      </el-radio-group>
+      <el-space
+        v-if="data.options.remote"
+        alignment="start"
+        direction="vertical"
+        style="margin-top: 10px;"
+      >
+        <el-input v-model="data.options.remoteFunc">
+          <template #prepend>
+            远端方法
+          </template>
+        </el-input>
+        <el-input v-model="data.options.props.label">
+          <template #prepend>
+            标签
+          </template>
+        </el-input>
+        <el-input v-model="data.options.props.value">
+          <template #prepend>
+            值
+          </template>
+        </el-input>
+      </el-space>
+      <template v-else>
+        <template
+          v-if="
+            data.type === 'radio' ||
+              (data.type === 'select' && !data.options.multiple)
+          "
+        >
+          <el-radio-group
+            v-model="data.options.defaultValue"
+            style="margin-top: 8px;"
+          >
+            <Draggable
+              item-key="index"
+              ghost-class="ghost"
+              handle=".drag-item"
+              :group="{ name: 'options' }"
+              :list="data.options.options"
+            >
+              <template #item="{ element, index }">
+                <div class="flex items-center">
+                  <el-radio
+                    :label="element.value"
+                    style="margin-right: 0; margin-bottom: 0;"
+                  >
+                    <el-input
+                      v-model="element.value"
+                      :style="{
+                        width: data.options.showLabel ? '90px' : '180px'
+                      }"
+                    />
+                    <el-input
+                      v-if="data.options.showLabel"
+                      v-model="element.label"
+                      class="ml-1"
+                      :style="{
+                        width: '90px'
+                      }"
+                    />
+                  </el-radio>
+                  <i
+                    style="margin: 0 5px; cursor: move;"
+                    class="drag-item custom:item"
+                  />
+                  <el-button
+                    type="primary"
+                    circle
+                    @click="handleOptionsRemove(index)"
+                  >
+                    <i class="fa6-regular:trash-can" />
+                  </el-button>
+                </div>
+              </template>
+            </Draggable>
+          </el-radio-group>
+        </template>
+
+        <template
+          v-if="
+            data.type === 'checkbox' ||
+              (data.type === 'select' && data.options.multiple)
+          "
+        >
+          <el-checkbox-group
+            v-model="data.options.defaultValue"
+            style="margin-top: 8px;"
+          >
+            <Draggable
+              item-key="index"
+              ghost-class="ghost"
+              handle="[cursor-move]"
+              :group="{ name: 'options' }"
+              :list="data.options.options"
+            >
+              <template #item="{ element, index }">
+                <div class="flex items-center justify-center gap-2 mb-2">
+                  <el-checkbox :label="element.value">
+                    {{ '' }}
+                  </el-checkbox>
+                  <el-input
+                    v-model="element.value"
+                    :style="{
+                      width: data.options.showLabel ? '90px' : '180px'
+                    }"
+                  />
+                  <el-input
+                    v-if="data.options.showLabel"
+                    v-model="element.label"
+                    :style="{ width: '90px' }"
+                  />
+                  <i class="cursor-move text-sm custom:item" />
+                  <el-button
+                    type="primary"
+                    circle
+                    @click="handleOptionsRemove(index)"
+                  >
+                    <i class="fa6-regular:trash-can" />
+                  </el-button>
+                </div>
+              </template>
+            </Draggable>
+          </el-checkbox-group>
+        </template>
+
+        <div style="margin-top: 5px;">
+          <el-button
+            type="text"
+            @click="handleInsertOption"
+          >
+            添加选项
+          </el-button>
+        </div>
+      </template>
+    </el-form-item>
+
+    <template v-if="data.type === 'time'">
+      <el-form-item label="默认值">
+        <el-time-picker
+          v-model="data.options.defaultValue"
+          style="width: 100%;"
+          :format="data.options.format"
+          :placeholder="data.options.placeholder"
+        />
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type === 'date'">
+      <el-form-item label="默认值">
+        <el-date-picker
+          v-model="data.options.defaultValue"
+          style="width: 100%;"
+          :format="data.options.format"
+          :placeholder="data.options.placeholder"
+        />
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type === 'time' || data.type === 'date'">
+      <el-form-item label="格式">
+        <el-input v-model="data.options.format" />
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type === 'download'">
+      <el-form-item label="地址">
+        <el-input v-model="data.options.defaultValue" />
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type === 'img-upload'">
+      <el-form-item label="模式">
+        <el-radio-group v-model="data.options.listType">
+          <el-radio-button label="text">
+            text
+          </el-radio-button>
+          <el-radio-button label="picture">
+            picture
+          </el-radio-button>
+          <el-radio-button label="picture-card">
+            picture-card
+          </el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+
+      <el-form-item label="文件参数名">
+        <el-input v-model="data.options.name" />
+      </el-form-item>
+
+      <el-form-item label="上传地址">
+        <el-input v-model="data.options.action" />
+      </el-form-item>
+
+      <el-form-item label="接受上传的文件类型(多个使用 , 隔开)">
+        <el-input v-model="data.options.accept" />
+      </el-form-item>
+
+      <el-form-item label="最大上传数量">
+        <el-input-number v-model.number="data.options.limit" :min="1" />
+      </el-form-item>
+
+      <el-form-item label="上传请求方法">
+        <el-radio-group v-model="data.options.method">
+          <el-radio-button label="post">
+            POST
+          </el-radio-button>
+          <el-radio-button label="put">
+            PUT
+          </el-radio-button>
+          <el-radio-button label="get">
+            GET
+          </el-radio-button>
+          <el-radio-button label="delete">
+            DELETE
+          </el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+    </template>
+
+    <el-form-item v-if="data.type === 'cascader'" label="远端数据">
+      <el-space direction="vertical" alignment="start">
+        <el-input v-model="data.options.remoteFunc">
+          <template #prepend>
+            远端方法
+          </template>
+        </el-input>
+        <el-input v-model="data.options.props.label">
+          <template #prepend>
+            标签
+          </template>
+        </el-input>
+        <el-input v-model="data.options.props.value">
+          <template #prepend>
+            值
+          </template>
+        </el-input>
+        <el-input v-model="data.options.props.children">
+          <template #prepend>
+            子选项
+          </template>
+        </el-input>
+      </el-space>
+    </el-form-item>
+
+    <template v-if="data.type === 'table'">
+      <el-form-item label="列配置项">
+        <Draggable
+          tag="ul"
+          item-key="index"
+          ghost-class="ghost"
+          handle=".drag-item"
+          :group="{ name: 'options' }"
+          :list="data.columns"
+        >
+          <template #item="{ element, index }">
+            <li style=" display: flex; gap: 5px;margin-bottom: 5px;">
+              <i class="drag-item custom:item" />
+              <el-input v-model="element.label" placeholder="显示名称" />
+              <el-input v-model="element.prop" placeholder="字段字段" />
+              <el-button
+                type="primary"
+                circle
+                style="margin-left: 5px;"
+                @click="data.columns.splice(index, 1)"
+              >
+                <i class="fa6-regular:trash-can" />
+              </el-button>
+            </li>
+          </template>
+        </Draggable>
+
+        <div>
+          <el-button type="text" @click="data.columns.push({})">
+            添加列
+          </el-button>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="对齐方式">
+        <el-radio-group v-model="data.options.align">
+          <el-radio-button label="left">
+            居左对齐
+          </el-radio-button>
+          <el-radio-button label="center">
+            居中对齐
+          </el-radio-button>
+          <el-radio-button label="right">
+            巨右对齐
+          </el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type === 'grid'">
+      <el-form-item label="栅格间隔">
+        <el-input-number v-model.number="data.options.gutter" :min="0" />
+      </el-form-item>
+
+      <el-form-item label="列配置项">
+        <Draggable
+          tag="ul"
+          item-key="index"
+          ghost-class="ghost"
+          handle=".drag-item"
+          :group="{ name: 'options' }"
+          :list="data.columns"
+        >
+          <template #item="{ element, index }">
+            <li style="margin-bottom: 5px;">
+              <i class="drag-item custom:item" />
+              <el-input-number
+                v-model.number="element.span"
+                placeholder="栅格值"
+                :min="0"
+                :max="24"
+              />
+              <el-button
+                type="primary"
+                circle
+                style="margin-left: 5px;"
+                @click="handleOptionsRemove(index)"
+              >
+                <i class="fa6-regular:trash-can" />
+              </el-button>
+            </li>
+          </template>
+        </Draggable>
+
+        <div>
+          <el-button type="text" @click="handleInsertColumn">
+            添加列
+          </el-button>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="垂直对齐方式">
+        <el-radio-group v-model="data.options.align">
+          <el-radio-button label="initial">
+            顶部对齐
+          </el-radio-button>
+          <el-radio-button label="center">
+            居中对齐
+          </el-radio-button>
+          <el-radio-button label="flex-end">
+            底部对齐
+          </el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+    </template>
+
+    <template v-if="data.type !== 'grid'">
+      <el-form-item
+        v-if="
+          hasKey('rules') ||
+            hasKey('readonly') ||
+            hasKey('disabled') ||
+            hasKey('allowClear')
+        "
+        label="操作属性"
+      >
+        <el-checkbox
+          v-if="hasKey('rules')"
+          v-model="data.options.rules.required"
+        >
+          必填
+        </el-checkbox>
+        <el-checkbox
+          v-if="hasKey('readonly')"
+          v-model="data.options.readonly"
+        >
+          只读
+        </el-checkbox>
+        <el-checkbox
+          v-if="hasKey('disabled')"
+          v-model="data.options.disabled"
+        >
+          禁用
+        </el-checkbox>
+        <el-checkbox
+          v-if="hasKey('clearable')"
+          v-model="data.options.clearable"
+        >
+          清除
+        </el-checkbox>
+      </el-form-item>
+
+      <template v-if="hasKey('rules')">
+        <h4 class="pb-2 text-gray-700 border-0 border-b-1 border-gray-200">
+          验证规则
+        </h4>
+
+        <el-form-item label="触发时机">
+          <el-radio-group v-model="data.options.rules.trigger">
+            <el-radio-button label="blur">
+              Blur
+            </el-radio-button>
+            <el-radio-button label="change">
+              Change
+            </el-radio-button>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="枚举类型">
+          <el-input v-model:value="data.options.rules.enum" />
+        </el-form-item>
+
+        <el-form-item label="字段长度">
+          <el-input v-model.number="data.options.rules.len" />
+        </el-form-item>
+
+        <el-form-item label="最大长度">
+          <el-input v-model.number="data.options.rules.max" />
+        </el-form-item>
+
+        <el-form-item label="最小长度">
+          <el-input v-model.number="data.options.rules.min" />
+        </el-form-item>
+
+        <el-form-item label="校验文案">
+          <el-input v-model="data.options.rules.message" />
+        </el-form-item>
+
+        <el-form-item label="正则表达式">
+          <el-input v-model="data.options.rules.pattern" />
+        </el-form-item>
+
+        <el-form-item label="校验类型">
+          <el-select v-model="data.options.rules.type">
+            <el-option value="string">
+              字符串
+            </el-option>
+            <el-option value="number">
+              数字
+            </el-option>
+            <el-option value="boolean">
+              布尔值
+            </el-option>
+            <el-option value="method">
+              方法
+            </el-option>
+            <el-option value="regexp">
+              正则表达式
+            </el-option>
+            <el-option value="integer">
+              整数
+            </el-option>
+            <el-option value="float">
+              浮点数
+            </el-option>
+            <el-option value="array">
+              数组
+            </el-option>
+            <el-option value="object">
+              对象
+            </el-option>
+            <el-option value="enum">
+              枚举
+            </el-option>
+            <el-option value="date">
+              日期
+            </el-option>
+            <el-option value="url">
+              URL地址
+            </el-option>
+            <el-option value="hex">
+              十六进制
+            </el-option>
+            <el-option value="email">
+              邮箱地址
+            </el-option>
+            <el-option value="any">
+              任意类型
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </template>
+    </template>
+  </el-form>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch } from 'vue'
+import Draggable from 'vuedraggable'
+
+export default defineComponent({
+  name: 'ElWidgetConfig',
+  components: {
+    Draggable,
+  },
+  props: {
+    select: {
+      type: Object,
+    },
+  },
+  emits: ['update:select'],
+  setup(props, context) {
+    const data = ref<any>(props.select)
+
+    watch(
+      () => props.select,
+      val => (data.value = val),
+    )
+
+    watch(data, val => context.emit('update:select', val), { deep: true })
+
+    const hasKey = (key: string) =>
+      Object.keys(data.value.options).includes(key)
+
+    const handleInsertColumn = () => {
+      data.value.columns.push({
+        span: 0,
+        list: [],
+      })
+    }
+
+    const handleInsertOption = () => {
+      const index = data.value.options.options.length + 1
+      data.value.options.options.push({
+        label: `Option ${index}`,
+        value: `Option ${index}`,
+      })
+    }
+
+    const handleOptionsRemove = (index: number) => {
+      if (data.value.type === 'grid')
+        data.value.columns.splice(index, 1)
+
+      else
+        data.value.options.options.splice(index, 1)
+    }
+
+    const handleSliderModeChange = (checked: string | number | boolean) => {
+      checked
+        ? (data.value.options.defaultValue = [10, 90])
+        : (data.value.options.defaultValue = 0)
+    }
+
+    const handleSelectModeChange = (val: string | number | boolean) => {
+      if (data.value.type === 'img-upload')
+        return
+
+      if (val) {
+        if (data.value.options.defaultValue) {
+          if (!(data.value.options.defaultValue instanceof Array))
+            data.value.options.defaultValue = [data.value.options.defaultValue]
+        }
+        else {
+          data.value.options.defaultValue = []
+        }
+      }
+      else {
+        data.value.options.defaultValue.length
+          ? (data.value.options.defaultValue
+              = data.value.options.defaultValue[0])
+          : (data.value.options.defaultValue = null)
+      }
+    }
+
+    return {
+      data,
+      hasKey,
+      handleInsertColumn,
+      handleInsertOption,
+      handleOptionsRemove,
+      handleSliderModeChange,
+      handleSelectModeChange,
+    }
+  },
+})
+</script>
+<style>
+  .ghost {
+    @apply outline outline-dashed outline-blue-500;
+  }
+</style>

+ 66 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetForm.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-form
+    label-suffix=":"
+    bg-gray-50 relative flex="~ 1"
+    v-bind="config"
+  >
+    <div v-if="!list?.length" text="gray-400 lg" absolute top-50 left="1/2" translate-x="-1/2">
+      从左侧拖拽来添加字段
+    </div>
+
+    <Draggable
+      class="flex-1 m-2 bg-white shadow p-1"
+      item-key="key"
+      handle="[cursor-move]"
+      :animation="200"
+      group="form-design"
+      :list="list"
+      @add="selectWidget=list[$event.newIndex]"
+    >
+      <template #item="{ element, index }">
+        <WidgetFormItem
+          v-model:selectWidget="selectWidget"
+          :element="element"
+          :index="index"
+          :list="list"
+        />
+      </template>
+    </Draggable>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import Draggable from 'vuedraggable'
+import WidgetFormItem from './WidgetFormItem.vue'
+import type { WidgetForm } from '@/config'
+
+const props = defineProps<{ list: any; config: WidgetForm['config']; selectWidget: any }>()
+defineEmits(['update:selectWidget'])
+const selectWidget = useVModel(props, 'selectWidget')
+</script>
+
+<style lang="scss">
+.widget-view {
+  @apply relative m-0.5 p-1 border border-dashed border-gray-300;
+
+  &.col {
+    @apply grid grid-cols-24;
+  }
+
+  &.active {
+    @apply border-solid border-blue-500 outline outline-2 outline-blue-500;
+
+    &.col {
+      @apply border-yellow-500 outline-yellow-500;
+    }
+  }
+
+  &:hover {
+    @apply border-blue-500 bg-blue-50;
+
+    &.col {
+      @apply border-yellow-500 bg-yellow-50;
+    }
+  }
+}
+</style>

+ 336 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/design/WidgetFormItem.vue

@@ -0,0 +1,336 @@
+<script lang="ts" setup>
+import Draggable from 'vuedraggable'
+import { cloneWidget } from '@/components/ComponentGroup.vue'
+
+const { element, index, list, ...props } = defineProps<{
+  list: any
+  element: any
+  index: number
+  selectWidget: any
+}>()
+defineEmits(['itemClick', 'delete', 'update:selectWidget'])
+
+let selectWidget = $(useVModel(props, 'selectWidget'))
+
+const handleCopyClick = () => {
+  list.splice(index + 1, 0, cloneWidget(list[index]))
+  selectWidget = list[index + 1]
+}
+
+const handleDeleteClick = () => {
+  list.splice(index, 1)
+  selectWidget = list[list.length === index ? (index - 1) : index]
+}
+</script>
+
+<template>
+  <div
+    class="widget-view"
+    :class="{ active: selectWidget?.key === element.key ,col:element.type === 'grid'}"
+    :style="element.type === 'grid'?`gap: ${element.options.gutter}px; align-items: ${element.options.align};`:''"
+    @click.stop="selectWidget=element"
+  >
+    <template v-if="element.type === 'grid'">
+      <Draggable
+        v-for="(col, key) of element.columns"
+        :key="key"
+        class="bg-white min-h-12 border border-dashed border-gray-300"
+        :style="`grid-column: span ${col.span}`"
+        item-key="key"
+        handle="[cursor-move]"
+        :animation="200"
+        group="form-design"
+        :no-transition-on-drag="true"
+        :list="col.list"
+        @add="selectWidget=col.list[$event.newIndex]"
+      >
+        <template #item="{ element: colElement, index: colIndex }">
+          <WidgetFormItem
+            v-model:selectWidget="selectWidget"
+            :element="colElement"
+            :list="col.list"
+            :index="colIndex"
+          />
+        </template>
+      </Draggable>
+    </template>
+
+    <el-table
+      v-else-if="element.type === 'table'"
+      class="w-0 flex-1"
+      border
+      :data="element.options.defaultValue"
+    >
+      <el-table-column
+        v-for="i in element.columns"
+        :key="i.prop"
+        :header-align="element.options.align"
+        :align="element.options.align"
+        v-bind="i"
+      />
+    </el-table>
+
+    <el-divider v-else-if="element.type === 'divider'" class="pb-0" :data="element.options.defaultValue" content-position="left">
+      {{ element.label }}
+    </el-divider>
+
+    <el-form-item
+      v-else-if="element"
+      :key="element.key"
+      :label="element.label"
+      :label-width="element.labelWidth"
+      :rules="element.options.rules"
+    >
+      <template v-if="element.type === 'input'">
+        <el-input
+          readonly
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :placeholder="element.options.placeholder"
+          :maxlength="parseInt(element.options.maxlength)"
+          :clearable="element.options.clearable"
+          :disabled="element.options.disabled"
+        >
+          <template v-if="element.options.prefix" #prefix>
+            {{ element.options.prefix }}
+          </template>
+          <template v-if="element.options.suffix" #suffix>
+            {{ element.options.suffix }}
+          </template>
+          <template v-if="element.options.prepend" #prepend>
+            {{ element.options.prepend }}
+          </template>
+          <template v-if="element.options.append" #append>
+            {{ element.options.append }}
+          </template>
+        </el-input>
+      </template>
+
+      <template v-if="element.type === 'password'">
+        <el-input
+          readonly
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :placeholder="element.options.placeholder"
+          :maxlength="parseInt(element.options.maxlength)"
+          :clearable="element.options.clearable"
+          :disabled="element.options.disabled"
+          :show-password="element.options.showPassword"
+        >
+          <template v-if="element.options.prefix" #prefix>
+            {{ element.options.prefix }}
+          </template>
+          <template v-if="element.options.suffix" #suffix>
+            {{ element.options.suffix }}
+          </template>
+          <template v-if="element.options.prepend" #prepend>
+            {{ element.options.prepend }}
+          </template>
+          <template v-if="element.options.append" #append>
+            {{ element.options.append }}
+          </template>
+        </el-input>
+      </template>
+
+      <template v-if="element.type === 'textarea'">
+        <el-input
+          type="textarea"
+          resize="none"
+          readonly
+          :rows="element.options.rows"
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :placeholder="element.options.placeholder"
+          :maxlength="parseInt(element.options.maxlength)"
+          :show-word-limit="element.options.showWordLimit"
+          :autosize="element.options.autosize"
+          :clearable="element.options.clearable"
+          :disabled="element.options.disabled"
+        />
+      </template>
+
+      <template v-if="element.type === 'number'">
+        <el-input-number
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :max="element.options.max"
+          :min="element.options.min"
+          :disabled="element.options.disabled"
+        />
+      </template>
+
+      <template v-if="element.type === 'radio'">
+        <el-radio-group
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :disabled="element.options.disabled"
+        >
+          <el-radio
+            v-for="item of element.options.options"
+            :key="item.value"
+            :label="item.value"
+            :style="{
+              display: element.options.inline ? 'inline-block' : 'block'
+            }"
+          >
+            {{ element.options.showLabel ? item.label : item.value }}
+          </el-radio>
+        </el-radio-group>
+      </template>
+
+      <template v-if="element.type === 'checkbox'">
+        <el-checkbox-group
+          :model-value="element.options.defaultValue"
+          :style="{ width: element.options.width }"
+          :disabled="element.options.disabled"
+        >
+          <el-checkbox
+            v-for="item of element.options.options"
+            :key="item.value"
+            :label="item.value"
+            :style="{
+              display: element.options.inline ? 'inline-block' : 'block'
+            }"
+          >
+            {{ element.options.showLabel ? item.label : item.value }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </template>
+
+      <template v-if="element.type === 'time'">
+        <el-time-picker
+          :model-value="element.options.defaultValue"
+          :placeholder="element.options.placeholder"
+          :readonly="element.options.readonly"
+          :editable="element.options.editable"
+          :clearable="element.options.clearable"
+          :format="element.options.format"
+          :disabled="element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </template>
+
+      <template v-if="element.type === 'date'">
+        <el-date-picker
+          :model-value="element.options.defaultValue"
+          :placeholder="element.options.placeholder"
+          :readonly="element.options.readonly"
+          :editable="element.options.editable"
+          :clearable="element.options.clearable"
+          :format="element.options.format"
+          :disabled="element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </template>
+
+      <template v-if="element.type === 'rate'">
+        <el-rate
+          :model-value="element.options.defaultValue"
+          :max="element.options.max"
+          :allow-half="element.options.allowHalf"
+          :disabled="element.options.disabled"
+        />
+      </template>
+
+      <template v-if="element.type === 'select'">
+        <el-select
+          :model-value="element.options.defaultValue"
+          :multiple="element.options.multiple"
+          :placeholder="element.options.placeholder"
+          :clearable="element.options.clearable"
+          :filterable="element.options.filterable"
+          :disabled="element.options.disabled"
+          :style="{ width: element.options.width }"
+        >
+          <el-option
+            v-for="item of element.options.options"
+            :key="item.value"
+            :value="item.value"
+            :label="element.options.showLabel ? item.label : item.value"
+          />
+        </el-select>
+      </template>
+
+      <template v-if="element.type === 'switch'">
+        <el-switch
+          :model-value="element.options.defaultValue"
+          :active-text="element.options.activeText"
+          :inactive-text="element.options.inactiveText"
+          :disabled="element.options.disabled"
+        />
+      </template>
+
+      <template v-if="element.type === 'slider'">
+        <el-slider
+          :model-value="element.options.defaultValue"
+          :min="element.options.min"
+          :max="element.options.max"
+          :step="element.options.step"
+          :range="element.options.range"
+          :disabled="element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </template>
+
+      <template v-if="element.type == 'text'">
+        <span>{{ element.options.defaultValue }}</span>
+      </template>
+
+      <template v-if="element.type === 'img-upload'">
+        <el-upload
+          :name="element.options.file"
+          :action="element.options.action"
+          :accept="element.options.accept"
+          :list-type="element.options.listType"
+          :multiple="element.options.multiple"
+          :limit="element.options.limit"
+          :disabled="element.options.disabled"
+        >
+          <template v-if="element.options.listType === 'picture-card'">
+            <el-image
+              v-if="element.options.defaultValue?.length"
+              style="width: 100%; height: 100%;"
+              :preview-src-list="[
+                '/api/sys/common/static/' + element.options.defaultValue
+              ]"
+              :src="'/api/sys/common/static/' + element.options.defaultValue"
+            />
+            <i v-else class="custom:insert" />
+          </template>
+          <el-button v-else>
+            <i icon-class="custom:img-upload" style="margin-right: 10px;" />
+            点击上传
+          </el-button>
+        </el-upload>
+      </template>
+
+      <template v-if="element.type === 'download'">
+        <el-button type="text" style="margin-top: -4px;">
+          下载
+        </el-button>
+      </template>
+
+      <template v-if="element.type === 'cascader'">
+        <el-cascader
+          :model-value="element.options.defaultValue"
+          :options="element.options.remoteOptions"
+          :placeholder="element.options.placeholder"
+          :filterable="element.options.filterable"
+          :clearable="element.options.clearable"
+          :disabled="element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </template>
+    </el-form-item>
+
+    <template v-if="selectWidget?.key === element.key">
+      <div absolute z-10 left-0 top="-.5" bg-blue-500 text-white p=".5 l-0 t-0" cursor-move :class="{'bg-yellow-500':element.type === 'grid'}">
+        <i class="eva:move-outline" text-lg />
+      </div>
+      <div absolute z-10 right-0 bottom="-0.5" bg-blue-500 flex gap-1 p="1 r-.5" text-white cursor-pointer :class="{'bg-yellow-500':element.type === 'grid'}">
+        <i class="fa6-regular:clone" @click.stop="handleCopyClick()" />
+        <i class="fa6-regular:trash-can" @click.stop="handleDeleteClick()" />
+      </div>
+    </template>
+  </div>
+</template>

+ 158 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateForm.vue

@@ -0,0 +1,158 @@
+<template>
+  <el-form
+    ref="generateForm"
+    label-suffix=":"
+    :model="model"
+    :rules="rules"
+    :size="widgetForm.config.size"
+    :label-position="widgetForm.config.labelPosition"
+    :label-width="`${widgetForm.config.labelWidth}px`"
+    :hide-required-asterisk="widgetForm.config.hideRequiredAsterisk"
+  >
+    <template v-for="(element, index) of widgetForm.list" :key="element.key">
+      <GenerateFormItem
+        :model="model"
+        :updated-model="updatedModel"
+        :element="widgetForm.list[index]"
+        :config="data.config"
+        :disabled="disabled"
+        :request="request"
+      />
+    </template>
+  </el-form>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import GenerateFormItem from './GenerateFormItem.vue'
+import { getWidgetForm } from '@/config'
+
+export default defineComponent({
+  name: 'FormGenerate',
+  components: {
+    GenerateFormItem,
+  },
+  props: {
+    data: {
+      type: Object,
+      default: getWidgetForm(),
+    },
+    value: {
+      type: Object,
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    request: {
+      type: Function,
+    },
+  },
+  setup(props) {
+    const state = reactive({
+      generateForm: null as any,
+      model: {} as any,
+      updatedModel: {} as any,
+      rules: {} as any,
+      widgetForm:
+        (props.data && JSON.parse(JSON.stringify(props.data)))
+        ?? getWidgetForm(),
+    })
+
+    const generateModel = (list: any[]) => {
+      for (let index = 0; index < list.length; index++) {
+        const model = list[index].model
+        if (!model)
+          return
+
+        if (list[index].type === 'grid') {
+          list[index].columns.forEach((col: any) => generateModel(col.list))
+        }
+        else {
+          if (props.value && Object.keys(props.value).includes(model))
+            state.model[model] = props.value[model]
+
+          else
+            state.model[model] = list[index].options.defaultValue
+
+          state.rules[model] = list[index].options.rules
+        }
+      }
+    }
+
+    const generateOptions = (list: any[]) => {
+      list.forEach(async(item) => {
+        if (item.type === 'grid') {
+          item.columns.forEach((col: any) => generateOptions(col.list))
+        }
+        else {
+          if (item.options.remote && item.options.remoteFunc) {
+            if (props.request) {
+              const { data } = await props.request({
+                url: item.options.remoteFunc,
+              })
+              item.options.remoteOptions = data.map((i: any) => ({
+                label: i[item.options.props.label],
+                value: i[item.options.props.value],
+                children: i[item.options.props.children],
+              }))
+              return
+            }
+            fetch(item.options.remoteFunc)
+              .then(resp => resp.json())
+              .then((json) => {
+                if (json instanceof Array) {
+                  item.options.remoteOptions = json.map(data => ({
+                    label: data[item.options.props.label],
+                    value: data[item.options.props.value],
+                    children: data[item.options.props.children],
+                  }))
+                }
+              })
+          }
+        }
+      })
+    }
+
+    watch(
+      () => props.data,
+      (val) => {
+        state.widgetForm
+          = (val && JSON.parse(JSON.stringify(val))) ?? getWidgetForm()
+        state.model = {}
+        state.rules = {}
+        generateModel(state.widgetForm.list)
+        generateOptions(state.widgetForm.list)
+      },
+      { deep: true, immediate: true },
+    )
+
+    const getData = () => {
+      return new Promise((resolve, reject) => {
+        state.generateForm
+          .validate()
+          .then((validate: boolean) => {
+            if (validate)
+              resolve(state.updatedModel)
+
+            else
+              ElMessage.error('验证失败')
+          })
+          .catch((error: Error) => {
+            reject(error)
+          })
+      })
+    }
+
+    const reset = () => {
+      state.generateForm.resetFields()
+    }
+    return {
+      ...toRefs(state),
+      getData,
+      reset,
+    }
+  },
+})
+</script>

+ 385 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateFormItem.vue

@@ -0,0 +1,385 @@
+<template>
+  <div
+    v-if="element.type === 'grid'"
+    grid="~ cols-24"
+    :style="`gap: ${element.options.gutter}px; align-items: ${element.options.align};`"
+  >
+    <div
+      v-for="(col, colIndex) of element.columns"
+      :key="colIndex"
+      :style="`grid-column: span ${col.span}`"
+    >
+      <GenerateFormItem
+        v-for="colItem of col.list"
+        :key="colItem.key"
+        :request="request"
+        :model="model"
+        :updated-model="updatedModel"
+        :element="colItem"
+        :config="config"
+        :disabled="disabled"
+      />
+    </div>
+  </div>
+  <el-table
+    v-else-if="element.type === 'table'"
+    border
+    class="mb-4.5"
+    :data="element.options.defaultValue"
+  >
+    <el-table-column
+      v-for="i in element.columns"
+      :key="i.prop"
+      :align="element.options.align"
+      v-bind="i"
+    />
+  </el-table>
+  <el-divider
+    v-else-if="element.type === 'divider'"
+    :data="element.options.defaultValue"
+    content-position="left"
+  >
+    {{ element.label }}
+  </el-divider>
+  <el-form-item
+    v-else-if="element"
+    :key="element.key"
+    :label="element.label"
+    :prop="element.model"
+    :style="
+      element.type === 'download' ? 'display:inline-flex;margin-right:40px' : ''
+    "
+    :label-width="element.labelWidth"
+  >
+    <template v-if="element.type === 'input'">
+      <el-input
+        v-model="data"
+        :style="{ width: element.options.width }"
+        :placeholder="element.options.placeholder"
+        :maxlength="parseInt(element.options.maxlength)"
+        :clearable="element.options.clearable"
+        :readonly="element.options.readonly"
+        :disabled="disabled || element.options.disabled"
+      >
+        <template v-if="element.options.prefix" #prefix>
+          {{
+            element.options.prefix
+          }}
+        </template>
+        <template v-if="element.options.suffix" #suffix>
+          {{
+            element.options.suffix
+          }}
+        </template>
+        <template v-if="element.options.prepend" #prepend>
+          {{
+            element.options.prepend
+          }}
+        </template>
+        <template v-if="element.options.append" #append>
+          {{
+            element.options.append
+          }}
+        </template>
+      </el-input>
+    </template>
+
+    <template v-if="element.type === 'password'">
+      <el-input
+        v-model="data"
+        :style="{ width: element.options.width }"
+        :placeholder="element.options.placeholder"
+        :maxlength="parseInt(element.options.maxlength)"
+        :clearable="element.options.clearable"
+        :disabled="disabled || element.options.disabled"
+        :readonly="element.options.readonly"
+        :show-password="element.options.showPassword"
+      >
+        <template v-if="element.options.prefix" #prefix>
+          {{
+            element.options.prefix
+          }}
+        </template>
+        <template v-if="element.options.suffix" #suffix>
+          {{
+            element.options.suffix
+          }}
+        </template>
+        <template v-if="element.options.prepend" #prepend>
+          {{
+            element.options.prepend
+          }}
+        </template>
+        <template v-if="element.options.append" #append>
+          {{
+            element.options.append
+          }}
+        </template>
+      </el-input>
+    </template>
+
+    <template v-if="element.type === 'textarea'">
+      <el-input
+        v-model="data"
+        type="textarea"
+        resize="none"
+        :rows="element.options.rows"
+        :style="{ width: element.options.width }"
+        :placeholder="element.options.placeholder"
+        :maxlength="parseInt(element.options.maxlength)"
+        :show-word-limit="element.options.showWordLimit"
+        :autosize="element.options.autosize"
+        :clearable="element.options.clearable"
+        :readonly="element.options.readonly"
+        :disabled="disabled || element.options.disabled"
+      />
+    </template>
+
+    <template v-if="element.type === 'number'">
+      <el-input-number
+        v-model="data"
+        :style="{ width: element.options.width }"
+        :max="element.options.max"
+        :min="element.options.min"
+        :disabled="disabled || element.options.disabled"
+      />
+    </template>
+
+    <template v-if="element.type === 'radio'">
+      <el-radio-group
+        v-model="data"
+        :style="{ width: element.options.width }"
+        :disabled="disabled || element.options.disabled"
+      >
+        <el-radio
+          v-for="item of element.options.remote
+            ? element.options.remoteOptions
+            : element.options.options"
+          :key="item.value"
+          :label="item.value"
+          :style="{
+            display: element.options.inline ? 'inline-block' : 'block'
+          }"
+        >
+          {{ element.options.showLabel ? item.label : item.value }}
+        </el-radio>
+      </el-radio-group>
+    </template>
+
+    <template v-if="element.type === 'checkbox'">
+      <el-checkbox-group
+        v-model="data"
+        :style="{ width: element.options.width }"
+        :disabled="disabled || element.options.disabled"
+      >
+        <el-checkbox
+          v-for="item of element.options.remote
+            ? element.options.remoteOptions
+            : element.options.options"
+          :key="item.value"
+          :label="item.value"
+          :style="{
+            display: element.options.inline ? 'inline-block' : 'block'
+          }"
+        >
+          {{ element.options.showLabel ? item.label : item.value }}
+        </el-checkbox>
+      </el-checkbox-group>
+    </template>
+
+    <template v-if="element.type === 'time'">
+      <el-time-picker
+        v-model="data"
+        :placeholder="element.options.placeholder"
+        :readonly="element.options.readonly"
+        :editable="element.options.editable"
+        :clearable="element.options.clearable"
+        :format="element.options.format"
+        :disabled="disabled || element.options.disabled"
+        :style="{ width: element.options.width }"
+      />
+    </template>
+
+    <template v-if="element.type === 'date'">
+      <el-date-picker
+        v-model="data"
+        :placeholder="element.options.placeholder"
+        :readonly="element.options.readonly"
+        :editable="element.options.editable"
+        :clearable="element.options.clearable"
+        :format="element.options.format"
+        :disabled="disabled || element.options.disabled"
+        :style="{ width: element.options.width }"
+      />
+    </template>
+
+    <template v-if="element.type === 'rate'">
+      <el-rate
+        v-model="data"
+        :max="element.options.max"
+        :allow-half="element.options.allowHalf"
+        :disabled="disabled || element.options.disabled"
+      />
+    </template>
+
+    <template v-if="element.type === 'select'">
+      <el-select
+        v-model="data"
+        :multiple="element.options.multiple"
+        :placeholder="element.options.placeholder"
+        :clearable="element.options.clearable"
+        :filterable="element.options.filterable"
+        :disabled="disabled || element.options.disabled"
+        :style="{ width: element.options.width }"
+      >
+        <el-option
+          v-for="item of element.options.remote
+            ? element.options.remoteOptions
+            : element.options.options"
+          :key="item.value"
+          :value="item.value"
+          :label="element.options.showLabel ? item.label : item.value"
+        />
+      </el-select>
+    </template>
+
+    <template v-if="element.type === 'switch'">
+      <el-switch
+        v-model="data"
+        :active-text="element.options.activeText"
+        :inactive-text="element.options.inactiveText"
+        :disabled="disabled || element.options.disabled"
+      />
+    </template>
+
+    <template v-if="element.type === 'slider'">
+      <el-slider
+        v-model="data"
+        :min="element.options.min"
+        :max="element.options.max"
+        :step="element.options.step"
+        :range="element.options.range"
+        :disabled="disabled || element.options.disabled"
+        :style="{ width: element.options.width }"
+      />
+    </template>
+
+    <template v-if="element.type == 'text'">
+      <span>{{ element.options.defaultValue }}</span>
+    </template>
+
+    <template v-if="element.type === 'img-upload'">
+      <el-upload
+        :name="element.options.file"
+        :action="element.options.action"
+        :accept="element.options.accept"
+        :list-type="element.options.listType"
+        :multiple="element.options.multiple"
+        :limit="element.options.limit"
+        :disabled="disabled || element.options.disabled"
+        :on-success="handleUploadSuccess"
+      >
+        <template v-if="element.options.listType === 'picture-card'">
+          <el-image
+            v-if="element.options.defaultValue?.length"
+            style=" width: 100%;height: 100%;"
+            :preview-src-list="[
+              '/api/sys/common/static/' + element.options.defaultValue
+            ]"
+            :src="'/api/sys/common/static/' + element.options.defaultValue"
+          />
+          <i v-else class="custom:insert" />
+        </template>
+        <el-button v-else>
+          <i class="custom:img-upload mr-2" />点击上传
+        </el-button>
+      </el-upload>
+    </template>
+
+    <template v-if="element.type === 'download'" #label="{ label }">
+      <a
+        :href="`/api/sys/common/static/${element.options.defaultValue}?inline=1`"
+        style="color: #606266;"
+        target="_blank"
+      >
+        {{ label }}
+      </a>
+    </template>
+    <template v-if="element.type === 'download'">
+      <el-button
+
+        style="margin-top: -4px;"
+        type="text"
+        @click="download(element.options.defaultValue, element.label)"
+      >
+        下载
+      </el-button>
+    </template>
+
+    <template v-if="element.type === 'cascader'">
+      <el-cascader
+        v-model="data"
+        :options="element.options.remoteOptions"
+        :placeholder="element.options.placeholder"
+        :filterable="element.options.filterable"
+        :clearable="element.options.clearable"
+        :disabled="disabled || element.options.disabled"
+        :style="{ width: element.options.width }"
+      />
+    </template>
+  </el-form-item>
+</template>
+
+<script lang="ts" setup>
+import type { WidgetForm } from '@/config'
+
+const props = defineProps<{
+  config: WidgetForm['config']
+  element: any
+  model: any
+  updatedModel: any
+  disabled: boolean
+  request?: Function
+}>()
+const originData = props.model[props.element.model]
+const data = computed({
+  get: () => props.model[props.element.model],
+  set: (val) => {
+    // eslint-disable-next-line vue/no-mutating-props
+    props.model[props.element.model] = val
+    if (JSON.stringify(originData) === JSON.stringify(val)) {
+      Reflect.deleteProperty(props.updatedModel, props.element.model)
+    }
+    else {
+      // eslint-disable-next-line vue/no-mutating-props
+      props.updatedModel[props.element.model] = val
+    }
+  },
+})
+
+const handleUploadSuccess = (_res: any, _file: any, fileList: any[]) => {
+  data.value = fileList
+}
+
+async function download(defaultValue: string, label: string) {
+  const a = document.createElement('a')
+  if (!props.request) return
+  a.href = await props
+    .request({
+      url: `/sys/common/static/${defaultValue}`,
+      responseType: 'blob',
+    })
+    .then((i: any) => {
+      if (i.size === 0) return ''
+      return URL.createObjectURL(i)
+    })
+  a.download = `${label}.${defaultValue.split('.')[1]}`
+  a.click()
+}
+</script>
+<style scoped>
+:deep(.el-upload--picture-card) {
+  width: 134px;
+  height: 134px;
+}
+</style>

+ 5 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/index.ts

@@ -0,0 +1,5 @@
+import 'element-plus/theme-chalk/el-message.css'
+
+export { basicFields, advanceFields, layoutFields } from './config'
+export { default as DesignForm } from './design/DesignForm.vue'
+export { default as GenerateForm } from './generate/GenerateForm.vue'

+ 18 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/shims.d.ts

@@ -0,0 +1,18 @@
+/// <reference types="vue/macros-global" />
+/// <reference types="unplugin-vue-define-options" />
+// Mocks all files ending in `.vue` showing them as plain Vue instances
+declare interface Window {
+  // extend the window
+}
+
+import type { AttributifyAttributes } from '@unocss/preset-attributify'
+
+declare module '@vue/runtime-dom' {
+  interface HTMLAttributes extends AttributifyAttributes {}
+}
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

+ 27 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/tsconfig.json

@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "target": "es2017",
+    "module": "esnext",
+    "lib": ["DOM","esnext"],
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "strict": true,
+    "jsx": "preserve",
+    "strictNullChecks": true,
+    "resolveJsonModule": true,
+    "declaration": true,
+    "isolatedModules": true,
+    "skipLibCheck": true,
+    "outDir": "dist",
+    "emitDeclarationOnly": true,
+    "paths": {
+      "@/*": ["./src/*"]
+    },
+    "types":["vite/client","unplugin-vue-define-options", "vue/macros-global"]
+  },
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "exclude": [
+    "**/dist/**",
+    "**/node_modules/**"
+  ]
+}

+ 102 - 0
fhKeeper/formulahousekeeper/plugIn/form-design-master/vite.config.ts

@@ -0,0 +1,102 @@
+import path from 'path'
+import { readdirSync } from 'fs'
+import { defineConfig } from 'vite'
+import Vue from '@vitejs/plugin-vue'
+import VueJsx from '@vitejs/plugin-vue-jsx'
+import DefineOptions from 'unplugin-vue-define-options/vite'
+import AutoImport from 'unplugin-auto-import/vite'
+import Unocss from 'unocss/vite'
+import libCss from 'vite-plugin-libcss'
+import { presetAttributify, presetIcons, presetUno, transformerDirectives } from 'unocss'
+import Component from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
+const resolve = p => path.resolve(__dirname, p)
+
+export default defineConfig({
+  resolve: {
+    alias: {
+      '@/': `${resolve('src')}/`,
+    },
+  },
+  plugins: [
+    Vue({
+      reactivityTransform: true,
+    }),
+    DefineOptions(),
+    VueJsx(),
+    libCss(),
+    AutoImport({
+      imports: [
+        'vue',
+        '@vueuse/core',
+      ],
+      dts: resolve('src/auto-imports.d.ts'),
+    }),
+    Component({
+      resolvers: [
+        ElementPlusResolver(),
+      ],
+      dts: resolve('src/components.d.ts'),
+    }),
+    Unocss({
+      mode: 'dist-chunk',
+      theme: {
+        colors: {
+          blue: { 500: '#409EFF' },
+        },
+      },
+      transformers: [
+        transformerDirectives(),
+      ],
+      safelist: [...readdirSync(path.resolve(__dirname, 'src/assets/icons')).map(i => `custom:${i.split('.')[0]}`)],
+      presets: [
+        presetUno(),
+        presetIcons({
+          prefix: '',
+          extraProperties: {
+            'display': 'inline-block',
+            'vertical-align': 'middle',
+            'font-size': '14px',
+          },
+          scale: 1.2,
+          collections: {
+            custom: FileSystemIconLoader(
+              path.resolve(__dirname, 'src/assets/icons'),
+              (svg) => {
+                svg = svg.replace(/^<\?xml.*?<svg/, '<svg')
+                if (svg.includes('fill="#')) return svg
+                return svg.replace(/^<svg /, '<svg fill="currentColor" ')
+              },
+            ),
+          },
+        }),
+        presetAttributify(),
+      ],
+    }),
+  ],
+  build: {
+    lib: {
+      entry: path.resolve(__dirname, 'src/index.ts'),
+      formats: ['es'],
+      fileName: format => `index.${format}.js`,
+    },
+    reportCompressedSize: false,
+    sourcemap: true,
+    cssCodeSplit: true,
+    rollupOptions: {
+      external: [
+        'vue',
+        'element-plus',
+        'vuedraggable',
+      ],
+      output: {
+        globals: {
+          'vue': 'Vue',
+          'element-plus': 'ElementPlus',
+          'vuedraggable': 'vuedraggable',
+        },
+      },
+    },
+  },
+})

+ 11 - 11
fhKeeper/formulahousekeeper/timesheet/config/index.js

@@ -1,20 +1,20 @@
 var path = require('path')
 
 //  var ip = '192.168.2.12'
-//var ip = '47.101.180.183'
+var ip = '47.101.180.183'
 // var ip = '47.100.37.243'
 // var ip = '192.168.10.2'
-// var ip = '192.168.2.13' 
+// var ip = '192.168.2.8' 
 
-var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
-for (var i in ifaces) {
-    for (var j in ifaces[i]) {
-        var val = ifaces[i][j]
-        if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
-            ip = val.address
-        }
-    }
-}
+// var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
+// for (var i in ifaces) {
+//     for (var j in ifaces[i]) {
+//         var val = ifaces[i][j]
+//         if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
+//             ip = val.address
+//         }
+//     }
+// }
 // 1196735749
 module.exports = {
   build: {

+ 3 - 3
fhKeeper/formulahousekeeper/timesheet_h5/vue.config.js

@@ -4,9 +4,9 @@ const path = require('path');
 const themePath = path.resolve(__dirname,'src/assets/style/theme.less');
 const Timestamp = new Date().getTime();
 
-// var ip = '47.101.180.183'
-var ip = '47.100.37.243'
-// var ip = '192.168.2.7'
+var ip = '47.101.180.183'
+// var ip = '47.100.37.243'
+// var ip = '192.168.2.8'
 // var ip = '127.0.0.1'
 
 // var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip