├── docs ├── index.md ├── .vitepress │ └── config.ts └── introduction.md ├── .eslintrc ├── src ├── index.d.ts ├── assets │ └── logo.png ├── vueCustomProperties.d.ts ├── env.d.ts ├── components │ ├── Content │ │ ├── components │ │ │ ├── DraggableItem.vue │ │ │ └── Drop.vue │ │ └── Content.vue │ ├── LeftSider │ │ └── Sider.vue │ ├── RightSider │ │ ├── components │ │ │ ├── FormItemConfig │ │ │ │ ├── SwitchFormItemConfig.vue │ │ │ │ ├── TreeSelectFormItemConfig.vue │ │ │ │ ├── DividerConfig.vue │ │ │ │ ├── RateFormItemConfig.vue │ │ │ │ ├── InputFormItemConfig.vue │ │ │ │ ├── InputNumberFormItemConfig.vue │ │ │ │ ├── ColorPickerFormItemConfig.vue │ │ │ │ ├── CheckBoxFormItemConfig.vue │ │ │ │ ├── TimePickerFormItemConfig.vue │ │ │ │ ├── SliderFormItemConfig.vue │ │ │ │ ├── RadioFormItemConfig.vue │ │ │ │ ├── SelectFormItemConfig.vue │ │ │ │ ├── DatePickerFormItemConfig.vue │ │ │ │ └── UploadFormItemConfig.vue │ │ │ └── FormConfig │ │ │ │ └── FormConfig.vue │ │ └── Sider.vue │ └── Layout │ │ ├── components │ │ └── SetGenerateCodeModal │ │ │ └── SetGenerateCodeModal.vue │ │ └── Layout.vue ├── main.ts ├── utils │ ├── hook │ │ └── useConfig.ts │ ├── i18n │ │ ├── index.ts │ │ └── const.ts │ └── index.ts ├── App.vue ├── const │ └── const.ts └── store │ └── index.ts ├── .gitattributes ├── .vscode └── extensions.json ├── .husky └── pre-commit ├── lib ├── index.ts └── components │ └── proForm │ ├── types │ └── props.ts │ └── index.tsx ├── .gitignore ├── public └── favicon.ico ├── vite.config.ts ├── tsconfig.node.json ├── index.html ├── README.zh-CN.md ├── tsconfig.json ├── vite.config.lib.ts ├── README.md └── package.json /docs/index.md: -------------------------------------------------------------------------------- 1 | 111 -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@antfu" 3 | } -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | $message: any; 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import ProFrom from './components/proForm/index' 2 | 3 | export { ProFrom } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | yarn.lock 7 | es 8 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doom-9-zz/naive-ui-form-creator/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doom-9-zz/naive-ui-form-creator/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/vueCustomProperties.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@vue/runtime-core' { 2 | export interface ComponentCustomProperties { 3 | $t: (target: string) => string; 4 | } 5 | } 6 | 7 | export {}; 8 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | }) 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts","vite.config.lib.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Content/components/DraggableItem.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'VitePress111', 3 | description: 'Just playing around.', 4 | themeConfig: { 5 | sidebar: [ 6 | { 7 | text: 'Guide', 8 | items: [ 9 | { text: 'Introduction', link: '/introduction' }, 10 | ], 11 | }, 12 | ], 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | naive-ui-form-creator 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import naive from 'naive-ui' 3 | import i18n from 'vue3-i18n-plugin' 4 | import { store } from './store/index' 5 | import App from './App.vue' 6 | import i18nConfig from './utils/i18n/const' 7 | 8 | createApp(App) 9 | .use(store) 10 | .use(naive) 11 | .use(i18n, { 12 | initial: 'zh', 13 | translationConfiguration: i18nConfig, 14 | }) 15 | .mount('#app') 16 | -------------------------------------------------------------------------------- /src/utils/hook/useConfig.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | import { ref, watch } from 'vue' 3 | 4 | import { store } from '../../store/index' 5 | 6 | export const useConfig = >(option: { sync?: boolean }): Ref => { 7 | const Config = ref({ ...store.getters.formItemConfig }) 8 | 9 | if (option.sync === undefined || option.sync) { 10 | watch( 11 | Config, 12 | () => { 13 | store.commit('changeSelectedFormItemConfig', Config.value) 14 | }, 15 | { 16 | deep: true, 17 | }, 18 | ) 19 | } 20 | 21 | return Config 22 | } 23 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | [English](https://github.com/doom-9/naive-ui-form-creator/blob/main/README.md) 2 | 3 | ## 简介 4 | 5 | `naive-ui-form-creator`是一个基于 `naive-ui` 组件库的**可视化表单生成器**,如果你有经常**制作`naive-ui`表单**的需求,可以使用此工具。`naive-ui-form-creator`是**可视化的**,**支持拖拽排序的**,它能够及时呈现和反馈表单的内容。 6 | 7 | 在线预览:[地址](https://naive-create-form.vercel.app/) 8 | 9 | ## 如何使用 10 | 11 | 你可以 Fock 项目,自己部署访问,或者直接访问上面已经部署好的地址。 12 | 13 | 当你制作完成表单后,点击右上角的**生成组件代码**,你就能得到一份 Vue3 的模版代码,然后拷贝到你的项目中使用。 14 | 15 | ## 提示 16 | 17 | 使用过程中如果遇到问题或者 bug,欢迎提交 issue,会及时修复的。 18 | 19 | ## Todo 20 | 21 | - [ ] 增加更多组件。 22 | - [x] 支持校验规则。 23 | - [ ] 优化拖拽动画。 24 | - [ ] 优化操作逻辑。 25 | - [x] 国际化 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "jsxFactory": "h", 10 | "jsxFragmentFactory": "Fragment", 11 | "sourceMap": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "esModuleInterop": true, 15 | "lib": ["esnext", "dom"], 16 | "skipLibCheck": true 17 | }, 18 | "include": [ 19 | "src/**/*.ts", 20 | "src/**/*.d.ts", 21 | "src/**/*.tsx", 22 | "src/**/*.vue", 23 | "lib/**/*.tsx", 24 | "lib/**/*.ts" 25 | ], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /vite.config.lib.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import { defineConfig } from 'vite' 3 | import vue from '@vitejs/plugin-vue' 4 | import vueJsx from '@vitejs/plugin-vue-jsx' 5 | import dts from 'vite-plugin-dts' 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | vue(), 11 | vueJsx({ 12 | // options are passed on to @vue/babel-plugin-jsx 13 | }), 14 | dts({ 15 | include: ['lib/**/*.tsx', 'lib/**/*.ts'], 16 | insertTypesEntry: true, 17 | }), 18 | ], 19 | build: { 20 | lib: { 21 | entry: resolve(__dirname, 'lib/index.ts'), 22 | formats: ['es', 'cjs'], 23 | fileName: 'index', 24 | }, 25 | rollupOptions: { 26 | // 确保外部化处理那些你不想打包进库的依赖 27 | external: ['vue', 'naive-ui'], 28 | output: { 29 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 30 | globals: { 31 | vue: 'Vue', 32 | }, 33 | preserveModules: true, 34 | }, 35 | }, 36 | outDir: 'dist', 37 | }, 38 | }) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [中文](https://github.com/doom-9/naive-ui-form-creator/blob/main/README.zh-CN.md) 2 | 3 | ## Introduce 4 | 5 | `Naive UI form creator` is a **visual form generator** based on the `naive UI` component library. You can use this tool if you often need to **create `naive UI` forms**. Naive UI form creator is **Visual** and **Support drag-and-drop sorting**,which can present and feed back the contents of the form in time. 6 | 7 | Online preview: [Address](https://naive-create-form.vercel.app/) 8 | 9 | ## How to use 10 | 11 | You can either deploy your own access to the Fork project or directly access the address already deployed above. 12 | 13 | When you have finished making the form, click **Generate Component Code** in the upper right corner, and you will get a template code for Vue3 and copy it to use in your project. 14 | 15 | ## Tips 16 | 17 | If you encounter problems or bugs during use, you are welcome to submit issues. 18 | 19 | ## Todo 20 | 21 | - [ ] Add more components. 22 | - [x] Supports checking rules. 23 | - [ ] Optimize drag animation. 24 | - [ ] Optimize operation logic. 25 | - [x] Internationalization 26 | -------------------------------------------------------------------------------- /src/utils/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import { ref } from 'vue' 3 | 4 | interface I18nOptions { 5 | initial: string 6 | i18nConfig: Record 7 | } 8 | 9 | const local = ref(undefined) 10 | 11 | const getOptionsValue = (target: string, options: Record): string => { 12 | const targetSplitArray = target.split('.') 13 | if (local.value === undefined) 14 | return '' 15 | targetSplitArray.unshift(local.value) 16 | let current = options 17 | while (targetSplitArray.length > 0) { 18 | current = current[targetSplitArray[0]] 19 | if (current === undefined) 20 | return '' 21 | 22 | targetSplitArray.shift() 23 | } 24 | if (typeof current === 'string') 25 | return current 26 | 27 | return '' 28 | } 29 | 30 | export const changeLocal = (value: string): void => { 31 | local.value = value 32 | } 33 | 34 | export default { 35 | install: (app: App, options: I18nOptions) => { 36 | local.value = options.initial 37 | app.config.globalProperties.$t = (target: string): string => { 38 | return getOptionsValue(target, options.i18nConfig) 39 | } 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /src/components/LeftSider/Sider.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/SwitchFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/TreeSelectFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/DividerConfig.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/components/Layout/components/SetGenerateCodeModal/SetGenerateCodeModal.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/RateFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "naive-create-form", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vue-tsc --noEmit && vite build", 7 | "lib-build": "vite build -c vite.config.lib.ts", 8 | "serve": "vite preview", 9 | "lint": "eslint .", 10 | "check": "npx taze -w", 11 | "docs:dev": "vitepress dev docs", 12 | "docs:build": "vitepress build docs", 13 | "docs:serve": "vitepress serve docs", 14 | "prepare": "husky install" 15 | }, 16 | "dependencies": { 17 | "@types/uuid": "^8.3.4", 18 | "@vicons/antd": "^0.11.0", 19 | "highlight.js": "^11.6.0", 20 | "naive-ui": "^2.31.0", 21 | "uuid": "^8.3.2", 22 | "vue": "^3.0.0", 23 | "vue3-i18n-plugin": "^1.0.9", 24 | "vuex": "^4.0.2" 25 | }, 26 | "devDependencies": { 27 | "@antfu/eslint-config": "^0.25.2", 28 | "@types/node": "^18.0.4", 29 | "@vicons/fluent": "^0.12.0", 30 | "@vitejs/plugin-vue": "^2.3.3", 31 | "@vitejs/plugin-vue-jsx": "^1.3.10", 32 | "eslint": "^8.19.0", 33 | "husky": "^7.0.4", 34 | "lint-staged": "^13.0.3", 35 | "typescript": "^4.7.4", 36 | "vite": "^3.0.0", 37 | "vite-plugin-dts": "^1.3.0", 38 | "vitepress": "^1.0.0-draft.8", 39 | "vue-tsc": "^0.3.0" 40 | }, 41 | "peerDependencies": { 42 | "naive-ui": "^2.31.0", 43 | "vue": "^3.0.0" 44 | }, 45 | "lint-staged": { 46 | "*.ts": [ 47 | "prettier --write", 48 | "eslint --fix" 49 | ], 50 | "*.vue": [ 51 | "prettier --write", 52 | "eslint --fix" 53 | ], 54 | "*.tsx": [ 55 | "prettier --write", 56 | "eslint --fix" 57 | ] 58 | }, 59 | "license": "MIT", 60 | "files": [ 61 | "es", 62 | "README.md" 63 | ], 64 | "types": "dist/index.d.ts", 65 | "main": "dist/index.js", 66 | "module": "dist/index.mjs" 67 | } 68 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 58 | 59 | 64 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/InputFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/InputNumberFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/ColorPickerFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/CheckBoxFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/TimePickerFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/SliderFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/RadioFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /lib/components/proForm/types/props.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CheckboxGroupProps, 3 | CheckboxProps, 4 | ColorPickerProps, 5 | DatePickerProps, 6 | FormItemProps, 7 | InputNumberProps, 8 | InputProps, 9 | RadioGroupProps, 10 | RadioProps, 11 | RateProps, 12 | SelectProps, 13 | SliderProps, 14 | SwitchProps, 15 | TimePickerProps, 16 | UploadProps, 17 | } from 'naive-ui' 18 | // import type { FileInfo } from 'naive-ui/es/upload/src/interface' 19 | 20 | type addCommonProps> = T & { 21 | formItemProps?: Omit 22 | label: string 23 | key: string 24 | show?: boolean 25 | tooltipConfig?: { 26 | show: boolean 27 | text: string 28 | } 29 | } 30 | 31 | export type ProFormItem = 32 | | addCommonProps<{ 33 | type: 'input' 34 | props?: Omit 35 | }> 36 | | addCommonProps<{ 37 | type: 'inputNumber' 38 | props?: Omit 39 | }> 40 | | addCommonProps<{ 41 | type: 'radio' 42 | props?: 43 | | Omit 44 | | Omit 45 | valueEnum: { 46 | label: string 47 | value: string | number 48 | }[] 49 | group?: boolean 50 | }> 51 | | addCommonProps<{ 52 | type: 'select' 53 | props?: Omit 54 | valueEnum: { 55 | label: string 56 | value: string | number 57 | disabled: boolean 58 | }[] 59 | }> 60 | | addCommonProps<{ 61 | type: 'rate' 62 | props?: Omit 63 | }> 64 | | addCommonProps<{ 65 | type: 'switch' 66 | props?: Omit 67 | }> 68 | | addCommonProps<{ 69 | type: 'timePicker' 70 | props?: Omit 71 | }> 72 | | addCommonProps<{ 73 | type: 'datePicker' 74 | props?: Omit 75 | }> 76 | | addCommonProps<{ 77 | type: 'colorPicker' 78 | props?: Omit 79 | }> 80 | | addCommonProps<{ 81 | type: 'checkbox' 82 | props?: 83 | | Omit< 84 | CheckboxProps, 85 | 'onUpdateChecked' | 'value' | 'label' | 'disabled' 86 | > 87 | | Omit 88 | valueEnum: { 89 | label: string 90 | value: string | number 91 | disabled: boolean 92 | }[] 93 | }> 94 | | addCommonProps<{ 95 | type: 'upload' 96 | props?: Omit 97 | buttonText?: string 98 | }> 99 | | addCommonProps<{ 100 | type: 'slider' 101 | props?: Omit 102 | }> 103 | | { 104 | type: 'divider' 105 | dashed?: boolean 106 | titlePlacement?: 'left' | 'right' | 'center' 107 | vertical?: boolean 108 | text: string 109 | } 110 | 111 | export type ProFormItemType = 'input' | 'radio' 112 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormConfig/FormConfig.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | 138 | 139 | # Docs 140 | 141 | This is a .md using a custom component 142 | 143 | 144 | 145 | ## More docs 146 | 147 | ... 148 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/SelectFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/components/RightSider/Sider.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/DatePickerFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/components/Content/components/Drop.vue: -------------------------------------------------------------------------------- 1 | 128 | 129 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/const/const.ts: -------------------------------------------------------------------------------- 1 | import type { State } from '../store' 2 | 3 | type selectItem = Array<{ 4 | value: string 5 | formItemConfig: { 6 | [key: string]: any 7 | } 8 | }> 9 | 10 | export const options: selectItem = [ 11 | { 12 | value: '0', 13 | formItemConfig: { 14 | label: 'input', 15 | name: undefined, 16 | rules: [], 17 | clearable: false, 18 | maxlength: undefined, 19 | minlength: undefined, 20 | type: 'text', 21 | size: 'medium', 22 | }, 23 | }, 24 | { 25 | value: '1', 26 | formItemConfig: { 27 | label: 'inputNumber', 28 | name: undefined, 29 | rules: [], 30 | clearable: false, 31 | max: undefined, 32 | min: undefined, 33 | type: 'text', 34 | size: 'medium', 35 | step: 1, 36 | showButton: true, 37 | }, 38 | }, 39 | { 40 | value: '2', 41 | formItemConfig: { 42 | label: 'radio', 43 | name: undefined, 44 | rules: [], 45 | size: 'medium', 46 | options: [], 47 | }, 48 | }, 49 | { 50 | value: '3', 51 | formItemConfig: { 52 | label: 'rate', 53 | name: undefined, 54 | rules: [], 55 | size: 'medium', 56 | count: 5, 57 | allowHalf: false, 58 | }, 59 | }, 60 | { 61 | value: '4', 62 | formItemConfig: { 63 | label: 'select', 64 | name: undefined, 65 | rules: [], 66 | size: 'medium', 67 | multiple: false, 68 | placeholder: '请选择', 69 | clearable: false, 70 | options: [], 71 | }, 72 | }, 73 | { 74 | value: '5', 75 | formItemConfig: { 76 | label: 'slider', 77 | name: undefined, 78 | rules: [], 79 | max: 100, 80 | min: 0, 81 | step: 1, 82 | range: false, 83 | reverse: false, 84 | vertical: false, 85 | tooltip: true, 86 | }, 87 | }, 88 | { 89 | value: '6', 90 | formItemConfig: { 91 | label: 'switch', 92 | name: undefined, 93 | rules: [], 94 | round: true, 95 | size: 'medium', 96 | }, 97 | }, 98 | { 99 | value: '7', 100 | formItemConfig: { 101 | label: 'timePicker', 102 | name: undefined, 103 | rules: [], 104 | actions: [], 105 | clearable: false, 106 | format: 'HH:mm:ss', 107 | size: 'medium', 108 | placeholder: undefined, 109 | use12Hours: false, 110 | }, 111 | }, 112 | { 113 | value: '12', 114 | formItemConfig: { 115 | label: 'datePicker', 116 | name: undefined, 117 | rules: [], 118 | actions: [], 119 | clearable: false, 120 | format: '', 121 | size: 'medium', 122 | placeholder: undefined, 123 | type: 'date', 124 | startPlaceholder: undefined, 125 | endPlaceholder: undefined, 126 | separator: undefined, 127 | }, 128 | }, 129 | { 130 | value: '8', 131 | formItemConfig: { 132 | label: 'treeSelect', 133 | name: undefined, 134 | rules: [], 135 | size: 'medium', 136 | clearable: false, 137 | }, 138 | }, 139 | { 140 | value: '9', 141 | formItemConfig: { 142 | label: 'upload', 143 | name: undefined, 144 | rules: [], 145 | accept: undefined, 146 | action: undefined, 147 | defaultUpload: true, 148 | data: [], 149 | headers: [], 150 | listType: 'text', 151 | max: undefined, 152 | method: 'POST', 153 | multiple: false, 154 | fileName: 'file', 155 | withCredentials: false, 156 | showCancelButton: true, 157 | showDownloadButton: false, 158 | showRemoveButton: true, 159 | showRetryButton: true, 160 | showFileList: true, 161 | }, 162 | }, 163 | { 164 | value: '10', 165 | formItemConfig: { 166 | label: 'colorPicker', 167 | name: undefined, 168 | rules: [], 169 | size: 'medium', 170 | modes: [], 171 | showAlpha: true, 172 | actions: [], 173 | }, 174 | }, 175 | { 176 | value: '11', 177 | formItemConfig: { 178 | label: 'checkbox', 179 | name: undefined, 180 | rules: [], 181 | max: undefined, 182 | min: undefined, 183 | options: [], 184 | }, 185 | }, 186 | ] 187 | 188 | export const LayoutOptions: selectItem = [ 189 | { 190 | value: '13', 191 | formItemConfig: { 192 | label: 'divider', 193 | name: 'divider', 194 | dashed: false, 195 | vertical: false, 196 | titlePlacement: 'center', 197 | }, 198 | }, 199 | ] 200 | 201 | export const initialFormState: State['formConfig'] = { 202 | labelPlacement: 'left', 203 | labelWidth: '80', 204 | labelAlign: 'left', 205 | showRequireMark: false, 206 | size: 'medium', 207 | inline: false, 208 | showFeedback: true, 209 | showLabel: true, 210 | requireMarkPlacement: 'right', 211 | model: 'formValue', 212 | } 213 | 214 | export const UI_NAME = 'naive-ui' 215 | export const PREFIX = 'n' 216 | 217 | export const getItemConfig = ( 218 | value: string, 219 | ): { 220 | [key: string]: any 221 | } => { 222 | const mergeOptions = [...options, ...LayoutOptions] 223 | const config = mergeOptions.find(item => item.value === value) 224 | if (config !== undefined) 225 | return config.formItemConfig 226 | 227 | return {} 228 | } 229 | 230 | export const ruleOptions: Array<{ 231 | value: string 232 | label: string 233 | }> = [ 234 | { 235 | label: 'Required verification', 236 | value: '0', 237 | }, 238 | { 239 | label: 'Mobile phone number verification', 240 | value: '1', 241 | }, 242 | { 243 | label: 'ID card verification', 244 | value: '2', 245 | }, 246 | ] 247 | -------------------------------------------------------------------------------- /src/utils/i18n/const.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | zh: { 3 | input: '文本输入', 4 | inputNumber: '数字输入', 5 | radio: '单选', 6 | rate: '评分', 7 | select: '选择器', 8 | slider: '滑动选择', 9 | switch: '开关', 10 | timePicker: '时间选择器', 11 | datePicker: '日期选择器', 12 | treeSelect: '树形选择', 13 | upload: '上传', 14 | colorPicker: '颜色选择器', 15 | checkbox: '复选框', 16 | divider: '分割线', 17 | formComponent: '表单组件', 18 | layoutComponent: '布局组件', 19 | dark: '暗色', 20 | light: '亮色', 21 | preview: '预览', 22 | generateCodeSettings: '生成代码设置', 23 | generateComponentCode: '生成组件代码', 24 | tip: '使用过程中如果遇到问题或者 bug,欢迎提交 issue。', 25 | setUp: '设置', 26 | formItemConfiguration: '表单项配置', 27 | formConfiguration: '表单配置', 28 | add: '试着添加一个', 29 | addFormItem: '从左侧添加表单项', 30 | addFormItemAndSelect: '试着添加一个并且选中它', 31 | model: '收集到的值的对象标识', 32 | size: '尺寸', 33 | inline: '行内', 34 | labelPlacement: '标签位置', 35 | labelWidth: '标签宽度', 36 | labelAlign: '标签对齐', 37 | showFeedback: '显示反馈', 38 | showLabel: '显示标签', 39 | showRequireMark: '显示必填标记', 40 | requireMarkPlacement: '必填标记位置', 41 | reset: '重置', 42 | id: '标识', 43 | name: '名称', 44 | rules: '校验规则', 45 | maxSelect: '可被勾选的 checkbox 的最大数量', 46 | minSelect: '可被勾选的 checkbox 的最小数量', 47 | addOptions: '添加选项', 48 | apply: '应用', 49 | showAlpha: '显示透明度', 50 | modes: '模式', 51 | actions: '操作', 52 | type: '类型', 53 | format: '格式', 54 | clearable: '可清空', 55 | startPlaceholder: '开始时的占位符', 56 | endPlaceholder: '结束时的占位符', 57 | separator: '分隔符', 58 | placeholder: '占位符', 59 | maxlength: '最大长度', 60 | minlength: '最小长度', 61 | max: '最大值', 62 | min: '最小值', 63 | step: '步长', 64 | showButton: '显示按钮', 65 | count: '数量', 66 | allowHalf: '允许半选', 67 | multiple: '多选', 68 | range: '范围', 69 | reverse: '反向', 70 | vertical: '垂直', 71 | tooltip: '提示', 72 | round: '圆角', 73 | use12Hours: '12 小时制', 74 | accept: '接受文件类型', 75 | action: '请求提交的地址', 76 | uploadData: '需要附加的数据', 77 | headers: '请求头', 78 | defaultUpload: '默认上传', 79 | listType: '列表类型', 80 | uploadMax: '最大上传数量', 81 | method: '请求方法', 82 | fileName: '文件名', 83 | withCredentials: '携带cookie', 84 | showCancelButton: '显示取消按钮', 85 | showDownloadButton: '显示下载按钮', 86 | showRemoveButton: '显示移除按钮', 87 | showRetryButton: '显示重试按钮', 88 | showFileList: '显示文件列表', 89 | dashed: '虚线', 90 | titlePlacement: '标题位置', 91 | yes: '是', 92 | no: '否', 93 | import: '自动添加import', 94 | confirmAndCancelBtn: '确认和取消按钮', 95 | confirm: '确认', 96 | cancel: '取消', 97 | modalTitle: '选项', 98 | copy: '复制', 99 | redo: '重做', 100 | }, 101 | en: { 102 | input: 'Input', 103 | inputNumber: 'InputNumber', 104 | radio: 'Radio', 105 | rate: 'Rate', 106 | select: 'Select', 107 | slider: 'Slider', 108 | switch: 'Switch', 109 | timePicker: 'TimePicker', 110 | datePicker: 'DatePicker', 111 | treeSelect: 'TreeSelect', 112 | upload: 'Upload', 113 | colorPicker: 'ColorPicker', 114 | checkbox: 'Checkbox', 115 | divider: 'Divider', 116 | formComponent: 'Form Component', 117 | layoutComponent: 'Layout Component', 118 | dark: 'Dark', 119 | light: 'Light', 120 | preview: 'Preview', 121 | generateCodeSettings: 'Generate Code Settings', 122 | generateComponentCode: 'Generate Component Code', 123 | tip: 'If you encounter problems or bugs during use, you are welcome to submit issue.', 124 | setUp: 'Set Up', 125 | formItemConfiguration: 'Form Item Configuration', 126 | formConfiguration: 'Form Configuration', 127 | add: 'Try adding one', 128 | addFormItem: 'Add form items from left', 129 | addFormItemAndSelect: 'Try adding one and select it', 130 | model: 'Model', 131 | size: 'Size', 132 | inline: 'Inline', 133 | labelPlacement: 'Label Placement', 134 | labelWidth: 'Label Width', 135 | labelAlign: 'Label Align', 136 | showFeedback: 'Show Feedback', 137 | showLabel: 'Show Label', 138 | showRequireMark: 'Show Require Mark', 139 | requireMarkPlacement: 'Require Mark Placement', 140 | reset: 'Reset', 141 | id: 'ID', 142 | name: 'Name', 143 | rules: 'Rules', 144 | maxSelect: 'Maximum number of checkboxes that can be checked', 145 | minSelect: 'Minimum number of checkboxes that can be checked', 146 | addOptions: 'Add Options', 147 | apply: 'Apply', 148 | showAlpha: 'Show Alpha', 149 | modes: 'Modes', 150 | actions: 'Actions', 151 | type: 'Type', 152 | format: 'Format', 153 | clearable: 'Clearable', 154 | startPlaceholder: 'Start Placeholder', 155 | endPlaceholder: 'End Placeholder', 156 | separator: 'Separator', 157 | placeholder: 'Placeholder', 158 | maxlength: 'Maximum Length', 159 | minlength: 'Minimum Length', 160 | max: 'Maximum', 161 | min: 'Minimum', 162 | step: 'Step', 163 | showButton: 'Show Button', 164 | count: 'Count', 165 | allowHalf: 'Allow Half', 166 | multiple: 'Multiple', 167 | range: 'Range', 168 | reverse: 'Reverse', 169 | vertical: 'Vertical', 170 | tooltip: 'Tooltip', 171 | round: 'Round', 172 | use12Hours: 'Use 12 Hours', 173 | accept: 'Accept File Type', 174 | action: 'Action', 175 | uploadData: 'Upload Data', 176 | headers: 'Headers', 177 | defaultUpload: 'Default Upload', 178 | listType: 'List Type', 179 | uploadMax: 'Maximum Upload', 180 | method: 'Method', 181 | fileName: 'File Name', 182 | withCredentials: 'With Credentials', 183 | showCancelButton: 'Show Cancel Button', 184 | showDownloadButton: 'Show Download Button', 185 | showRemoveButton: 'Show Remove Button', 186 | showRetryButton: 'Show Retry Button', 187 | showFileList: 'Show File List', 188 | dashed: 'Dashed', 189 | titlePlacement: 'Title Placement', 190 | yes: 'Yes', 191 | no: 'No', 192 | import: 'Auto add import', 193 | confirmAndCancelBtn: 'Confirm and Cancel Button', 194 | confirm: 'Confirm', 195 | cancel: 'Cancel', 196 | modalTitle: 'Options', 197 | copy: 'Copy', 198 | redo: 'Redo', 199 | }, 200 | } 201 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex' 2 | import { v4 as uuidv4 } from 'uuid' 3 | import { getItemConfig, initialFormState } from '../const/const' 4 | 5 | export interface formItemType { 6 | value: string 7 | id: string 8 | formItemConfig: { 9 | [key: string]: any 10 | } 11 | } 12 | 13 | export interface State { 14 | formItemArray: formItemType[] 15 | selectedFormItem: string 16 | selectedFormItemType: string 17 | autoAddImport: boolean 18 | confirmAndCancelBtn: boolean 19 | formConfig: { 20 | labelPlacement: 'left' | 'top' 21 | labelWidth: string 22 | labelAlign: 'left' | 'right' 23 | size: 'small' | 'medium' | 'large' 24 | inline: boolean 25 | showFeedback: boolean 26 | showLabel: boolean 27 | showRequireMark: boolean 28 | requireMarkPlacement: 'left' | 'right' 29 | model: string | undefined 30 | } 31 | tabsValue: 'form' | 'formItem' 32 | local: string 33 | } 34 | 35 | export const store = createStore({ 36 | strict: true, 37 | state() { 38 | return { 39 | formItemArray: [], 40 | autoAddImport: false, 41 | confirmAndCancelBtn: false, 42 | formConfig: initialFormState, 43 | selectedFormItem: '', 44 | selectedFormItemType: '', 45 | tabsValue: 'formItem', 46 | local: 'zh', 47 | } 48 | }, 49 | mutations: { 50 | add(state: State, payload: Omit): void { 51 | const id = uuidv4() 52 | state.formItemArray.push({ 53 | id, 54 | formItemConfig: getItemConfig(payload.value), 55 | ...payload, 56 | }) 57 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 58 | }, 59 | addAndSelect(state: State, payload: Omit): void { 60 | const id = uuidv4() 61 | state.formItemArray.push({ 62 | id, 63 | formItemConfig: getItemConfig(payload.value), 64 | ...payload, 65 | }) 66 | store.commit('changeSelectedFormItem', id) 67 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 68 | }, 69 | remove(state: State, payload: string): void { 70 | state.formItemArray.splice( 71 | state.formItemArray.findIndex(item => item.id === payload), 72 | 1, 73 | ) 74 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 75 | }, 76 | clear(state: State): void { 77 | state.formItemArray = [] 78 | }, 79 | copy(state: State, payload: string): void { 80 | const index = state.formItemArray.findIndex(item => item.id === payload) 81 | if (index !== -1) { 82 | const newItem = { ...state.formItemArray[index] } 83 | newItem.id = uuidv4() 84 | state.formItemArray.splice(index + 1, 0, newItem) 85 | } 86 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 87 | }, 88 | up(state: State, payload: string) { 89 | const index = state.formItemArray.findIndex(item => item.id === payload) 90 | if (index > 0) { 91 | const item = state.formItemArray[index] 92 | state.formItemArray.splice(index, 1) 93 | state.formItemArray.splice(index - 1, 0, item) 94 | } 95 | else { 96 | window.$message.warning(state.local === 'zh' ? '已经是第一个了' : 'Already the first') 97 | } 98 | }, 99 | down(state: State, payload: string) { 100 | const index = state.formItemArray.findIndex(item => item.id === payload) 101 | if (index < state.formItemArray.length - 1) { 102 | const item = state.formItemArray[index] 103 | state.formItemArray.splice(index, 1) 104 | state.formItemArray.splice(index + 1, 0, item) 105 | } 106 | else { 107 | window.$message.warning(state.local === 'zh' ? '已经是最后一个了' : 'Already the last') 108 | } 109 | }, 110 | exchange(state: State, payload: { id1: string; id2: string }) { 111 | const index1 = state.formItemArray.findIndex(item => item.id === payload.id1) 112 | const index2 = state.formItemArray.findIndex(item => item.id === payload.id2) 113 | if (index1 !== -1 && index2 !== -1) { 114 | const item1 = state.formItemArray[index1] 115 | const item2 = state.formItemArray[index2] 116 | state.formItemArray.splice(index1, 1, item2) 117 | state.formItemArray.splice(index2, 1, item1) 118 | } 119 | }, 120 | insertDrop(state: State, payload: { from: number; to: number }) { 121 | if (payload.from !== -1 && payload.to !== -1) { 122 | if (payload.from < payload.to) { 123 | const item1 = state.formItemArray[payload.from] 124 | state.formItemArray.splice(payload.from, 1) 125 | state.formItemArray.splice(payload.to, 0, item1) 126 | } 127 | if (payload.from > payload.to) { 128 | const item1 = state.formItemArray[payload.from] 129 | state.formItemArray.splice(payload.from, 1) 130 | state.formItemArray.splice(payload.to, 0, item1) 131 | } 132 | 133 | // dom diff 134 | state.formItemArray = state.formItemArray.map((item) => { 135 | item.id = uuidv4() 136 | return item 137 | }) 138 | } 139 | }, 140 | changeAutoAddImport(state: State, payload: boolean) { 141 | state.autoAddImport = payload 142 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 143 | }, 144 | changeFormConfig(state: State, payload: Partial) { 145 | state.formConfig = { 146 | ...state.formConfig, 147 | ...payload, 148 | } 149 | window.$message.success(state.local === 'zh' ? '操作成功' : 'Success') 150 | }, 151 | changeSelectedFormItem(state: State, payload: string) { 152 | state.selectedFormItem = payload 153 | const index = state.formItemArray.findIndex(item => item.id === payload) 154 | if (index !== -1) 155 | state.selectedFormItemType = state.formItemArray[index].value 156 | }, 157 | changeSelectedFormItemConfig(state: State, payload: formItemType['formItemConfig']) { 158 | const index = state.formItemArray.findIndex(item => item.id === state.selectedFormItem) 159 | if (index !== -1) { 160 | state.formItemArray[index].formItemConfig = { 161 | ...state.formItemArray[index].formItemConfig, 162 | ...payload, 163 | } 164 | } 165 | else { 166 | window.$message.warning( 167 | state.local === 'zh' ? '请先选择一个表单项' : 'Please select a form item', 168 | ) 169 | } 170 | }, 171 | changeTabsValue(state: State, payload: State['tabsValue']) { 172 | state.tabsValue = payload 173 | }, 174 | changeConfirmAndCancelBtn(state: State, payload: boolean) { 175 | state.confirmAndCancelBtn = payload 176 | }, 177 | changeLocal(state: State, payload: State['local']) { 178 | state.local = payload 179 | }, 180 | }, 181 | getters: { 182 | formItemArrayLength(state) { 183 | return state.formItemArray.length 184 | }, 185 | formItemConfig(state) { 186 | return state.formItemArray.find(item => item.id === state.selectedFormItem)?.formItemConfig 187 | }, 188 | selectedFormItem(state) { 189 | const index = state.formItemArray.findIndex(item => item.id === state.selectedFormItem) 190 | if (index !== -1) 191 | return state.formItemArray[index] 192 | 193 | return null 194 | }, 195 | }, 196 | }) 197 | -------------------------------------------------------------------------------- /src/components/RightSider/components/FormItemConfig/UploadFormItemConfig.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/components/Content/Content.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 249 | 250 | 258 | -------------------------------------------------------------------------------- /lib/components/proForm/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-string-refs */ 2 | import type { ComputedRef, PropType } from 'vue' 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | import { Fragment, computed, defineComponent, h, ref, watchEffect } from 'vue' 5 | import type { 6 | CheckboxGroupProps, 7 | FormInst, 8 | FormProps, 9 | RadioGroupProps, 10 | } from 'naive-ui' 11 | import { 12 | NButton, 13 | NCheckbox, 14 | NCheckboxGroup, 15 | NColorPicker, 16 | NDatePicker, 17 | NDivider, 18 | NForm, 19 | NFormItem, 20 | NIcon, 21 | NInput, 22 | NInputNumber, 23 | NRadio, 24 | NRadioGroup, 25 | NRate, 26 | NSelect, 27 | NSlider, 28 | NSpace, 29 | NSwitch, 30 | NTimePicker, 31 | NTooltip, 32 | NUpload, 33 | } from 'naive-ui' 34 | import type { FormValidateCallback } from 'naive-ui/es/form/src/interface' 35 | import type { FileInfo } from 'naive-ui/es/upload/src/interface' 36 | import { QuestionCircle48Regular } from '@vicons/fluent' 37 | import type { ProFormItem } from './types/props' 38 | 39 | const ProFormProps = { 40 | formProps: Object as PropType>, 41 | formItems: Array as PropType, 42 | resetButton: Boolean, 43 | validateButton: Boolean, 44 | submitButton: { 45 | type: Boolean, 46 | default: true, 47 | }, 48 | onReset: Function as PropType<() => void>, 49 | onFinish: Function as PropType<() => void>, 50 | onError: Function as PropType, 51 | onValidate: Function as PropType<() => void>, 52 | title: String, 53 | isKeyPressSubmit: Boolean, 54 | } 55 | 56 | export default defineComponent({ 57 | name: 'ProForm', 58 | props: ProFormProps, 59 | setup(props) { 60 | const getObj = (data: ProFormItem[] | undefined) => { 61 | if (!data) 62 | return {} 63 | 64 | const obj: Record = {} 65 | 66 | data.forEach((item) => { 67 | if (item.type === 'divider') 68 | return 69 | obj[item.key] = null 70 | }) 71 | 72 | return obj 73 | } 74 | 75 | const modalData = ref>(getObj(props.formItems)) 76 | 77 | const formRef = ref(null) 78 | 79 | const handleValidateClick = () => { 80 | formRef.value?.validate((errors) => { 81 | if (!errors) 82 | props?.onValidate && props.onValidate() 83 | else props?.onError && props.onError(errors) 84 | }) 85 | } 86 | 87 | const handleResetClick = () => { 88 | for (const key in modalData.value) { 89 | if (Object.prototype.hasOwnProperty.call(modalData.value, key)) 90 | modalData.value[key] = null 91 | } 92 | props?.onReset && props.onReset() 93 | } 94 | 95 | const handleSubmitClick = () => { 96 | formRef.value?.validate((errors) => { 97 | if (!errors) { 98 | props?.onFinish && props.onFinish() 99 | console.log(modalData.value) 100 | } 101 | else { 102 | props?.onError && props.onError(errors) 103 | } 104 | }) 105 | } 106 | 107 | const handleInputUpdateValue = ( 108 | val: string | number | null | (string | number)[] | Required[], 109 | key: string, 110 | ) => { 111 | modalData.value[key] = val 112 | } 113 | 114 | const handleRadioUpdateChecked = ( 115 | val: boolean, 116 | key: string, 117 | flag: string | number, 118 | ) => { 119 | if (val) 120 | modalData.value[key] = flag 121 | } 122 | 123 | const keyDownHandler = (e: KeyboardEvent) => { 124 | if (e.key === 'Enter') 125 | handleSubmitClick() 126 | } 127 | 128 | watchEffect(() => { 129 | if (props.isKeyPressSubmit) 130 | window.addEventListener('keydown', keyDownHandler) 131 | else window.removeEventListener('keydown', keyDownHandler) 132 | }) 133 | 134 | const getNTooltipVnode = (item: ProFormItem) => { 135 | if (item.type === 'divider') 136 | return 137 | return item.tooltipConfig?.show 138 | ? ( 139 | 140 | {{ 141 | trigger: () => ( 142 | 143 | 144 | 145 | ), 146 | default: () => item.tooltipConfig?.text, 147 | }} 148 | 149 | ) 150 | : null 151 | } 152 | 153 | const getNFormItemVnode: ( 154 | item: ProFormItem 155 | ) => JSX.Element | JSX.Element[] | undefined = (item) => { 156 | switch (item.type) { 157 | case 'input': 158 | return ( 159 | { 163 | handleInputUpdateValue(value, item.key) 164 | }} 165 | /> 166 | ) 167 | case 'inputNumber': 168 | return ( 169 | { 173 | handleInputUpdateValue(value, item.key) 174 | }} 175 | /> 176 | ) 177 | 178 | case 'radio': 179 | return item.valueEnum.length > 1 180 | ? ( 181 | { 185 | handleInputUpdateValue(value, item.key) 186 | }} 187 | > 188 | {item.valueEnum.map(valueItem => ( 189 | 190 | ))} 191 | 192 | ) 193 | : ( 194 | item.valueEnum.map(valueItem => ( 195 | { 201 | handleRadioUpdateChecked(value, item.key, valueItem.value) 202 | }} 203 | /> 204 | )) 205 | ) 206 | 207 | case 'select': 208 | return ( 209 | { 214 | handleInputUpdateValue(value, item.key) 215 | }} 216 | /> 217 | ) 218 | 219 | case 'rate': 220 | return ( 221 | { 225 | handleInputUpdateValue(value, item.key) 226 | }} 227 | /> 228 | ) 229 | 230 | case 'switch': 231 | return ( 232 | { 236 | handleInputUpdateValue(value, item.key) 237 | }} 238 | /> 239 | ) 240 | 241 | case 'timePicker': 242 | return ( 243 | { 247 | handleInputUpdateValue(value, item.key) 248 | }} 249 | /> 250 | ) 251 | 252 | case 'datePicker': 253 | return ( 254 | { 257 | handleInputUpdateValue(value, item.key) 258 | }} 259 | value={modalData.value[item.key]} 260 | /> 261 | ) 262 | 263 | case 'colorPicker': 264 | return ( 265 | { 268 | handleInputUpdateValue(value, item.key) 269 | }} 270 | value={modalData.value[item.key]} 271 | /> 272 | ) 273 | 274 | case 'checkbox': 275 | return item.valueEnum.length > 1 276 | ? ( 277 | )} 282 | onUpdateValue={(value) => { 283 | handleInputUpdateValue(value, item.key) 284 | }} 285 | value={modalData.value[item.key]} 286 | > 287 | {item.valueEnum.map(valueItem => ( 288 | 289 | ))} 290 | 291 | ) 292 | : ( 293 | item.valueEnum.map(valueItem => ( 294 | { 299 | handleInputUpdateValue(value, item.key) 300 | }} 301 | value={modalData.value[item.key]} 302 | /> 303 | )) 304 | ) 305 | 306 | case 'upload': 307 | return ( 308 | { 312 | handleInputUpdateValue(value, item.key) 313 | }} 314 | > 315 | {item.buttonText} 316 | 317 | ) 318 | 319 | case 'slider': 320 | return ( 321 | { 323 | handleInputUpdateValue(value, item.key) 324 | }} 325 | value={modalData.value[item.key]} 326 | /> 327 | ) 328 | 329 | default: 330 | return undefined 331 | } 332 | } 333 | 334 | const Vnode: ComputedRef = computed(() => { 335 | const { formItems } = props 336 | return formItems?.map((item) => { 337 | if (item.type === 'divider') { 338 | return ( 339 | 344 | {item.text} 345 | 346 | ) 347 | } 348 | else { 349 | return ( 350 | 356 | {getNFormItemVnode(item)} 357 | {getNTooltipVnode(item)} 358 | 359 | ) 360 | } 361 | }) 362 | }) 363 | 364 | return { 365 | modalData, 366 | formRef, 367 | handleValidateClick, 368 | handleInputUpdateValue, 369 | handleRadioUpdateChecked, 370 | handleResetClick, 371 | handleSubmitClick, 372 | Vnode, 373 | } 374 | }, 375 | render() { 376 | const { 377 | formProps, 378 | modalData, 379 | handleValidateClick, 380 | handleResetClick, 381 | handleSubmitClick, 382 | resetButton, 383 | validateButton, 384 | submitButton, 385 | title, 386 | Vnode, 387 | } = this 388 | 389 | return ( 390 | 391 | {title ? {title} : null} 392 | 393 | {Vnode} 394 | 395 | 396 | {validateButton === true 397 | ? ( 398 | 399 | 验证 400 | 401 | ) 402 | : null} 403 | {resetButton === true 404 | ? ( 405 | 406 | 重置 407 | 408 | ) 409 | : null} 410 | {submitButton === true 411 | ? ( 412 | 413 | 提交 414 | 415 | ) 416 | : null} 417 | 418 | 419 | ) 420 | }, 421 | }) 422 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { PREFIX, UI_NAME } from '../const/const' 2 | import type { formItemType } from '../store' 3 | import { store } from '../store' 4 | 5 | interface bindConfig { 6 | name: string 7 | val: any 8 | } 9 | 10 | const typeToImport: Record = { 11 | 0: ['Input'], 12 | 1: ['InputNumber'], 13 | 2: ['Radio', 'RadioGroup', 'Space'], 14 | 3: ['Rate'], 15 | 4: ['Select'], 16 | 5: ['Slider'], 17 | 6: ['Switch'], 18 | 7: ['TimePicker'], 19 | 8: ['TreeSelect'], 20 | 9: ['Upload'], 21 | 10: ['ColorPicker'], 22 | 11: ['Checkbox', 'CheckboxGroup', 'Space'], 23 | 12: ['DatePicker'], 24 | 13: ['Divider'], 25 | } 26 | 27 | // bind 28 | 29 | const combineNameAndValue = ( 30 | name: string, 31 | val: any, 32 | ): { 33 | name: string 34 | val: any 35 | } => { 36 | return { 37 | name, 38 | val, 39 | } 40 | } 41 | 42 | const bindBooleanAndNumberConfig = (config: bindConfig): string => { 43 | return `${ 44 | config.val !== undefined 45 | ? `:${String(config.name)}="${String(config.val)}"` 46 | : '' 47 | }` 48 | } 49 | 50 | const bindStringConfig = (config: bindConfig): string => { 51 | return `${ 52 | config.val !== undefined 53 | ? `${String(config.name)}="${String(config.val)}"` 54 | : '' 55 | }` 56 | } 57 | 58 | const bindValueConfig = (config: bindConfig): string => { 59 | return `${ 60 | config.val !== undefined 61 | ? `v-model="${ 62 | store.state.formConfig.model !== undefined 63 | ? `${store.state.formConfig.model}.` 64 | : '' 65 | }${String(config.val)}"` 66 | : '' 67 | }` 68 | } 69 | 70 | const bindFileListConfig = (config: bindConfig): string => { 71 | return `${ 72 | config.val !== undefined ? `v-model:file-list="${String(config.val)}"` : '' 73 | }` 74 | } 75 | 76 | const replaceOptions = (str: string): string => { 77 | return str.replace(/"label"/g, 'label').replace(/"value"/g, 'value') 78 | } 79 | 80 | // formItemConfig 81 | 82 | const getInputFormItemContentConfig = (item: { 83 | [key: string]: any 84 | }): string => { 85 | const { name, clearable, maxlength, minlength, type, size } = item 86 | return `${bindValueConfig( 87 | combineNameAndValue('name', name), 88 | )} ${bindBooleanAndNumberConfig( 89 | combineNameAndValue('clearable', clearable), 90 | )} ${bindBooleanAndNumberConfig( 91 | combineNameAndValue('maxlength', maxlength), 92 | )} ${bindBooleanAndNumberConfig( 93 | combineNameAndValue('minlength', minlength), 94 | )} ${bindStringConfig(combineNameAndValue('type', type))} ${bindStringConfig( 95 | combineNameAndValue('size', size), 96 | )}` 97 | } 98 | 99 | const getInputNumberFormItemContentConfig = (item: { 100 | [key: string]: any 101 | }): string => { 102 | const { name, clearable, max, min, size, step, showButton } = item 103 | return `${bindValueConfig( 104 | combineNameAndValue('name', name), 105 | )} ${bindBooleanAndNumberConfig( 106 | combineNameAndValue('clearable', clearable), 107 | )} ${bindBooleanAndNumberConfig( 108 | combineNameAndValue('max', max), 109 | )} ${bindBooleanAndNumberConfig( 110 | combineNameAndValue('min', min), 111 | )} ${bindBooleanAndNumberConfig( 112 | combineNameAndValue('step', step), 113 | )} ${bindStringConfig( 114 | combineNameAndValue('size', size), 115 | )} ${bindBooleanAndNumberConfig( 116 | combineNameAndValue('show-button', showButton), 117 | )}` 118 | } 119 | 120 | const getRadioFormItemContentConfig = (item: { 121 | [key: string]: any 122 | }): string => { 123 | const { name, size } = item 124 | return `${bindValueConfig( 125 | combineNameAndValue('name', name), 126 | )} ${bindStringConfig(combineNameAndValue('size', size))}` 127 | } 128 | 129 | const getRateFormItemContentConfig = (item: { [key: string]: any }): string => { 130 | const { name, size, count, allowHalf } = item 131 | return `${bindValueConfig( 132 | combineNameAndValue('name', name), 133 | )} ${bindStringConfig( 134 | combineNameAndValue('size', size), 135 | )} ${bindBooleanAndNumberConfig( 136 | combineNameAndValue('count', count), 137 | )} ${bindBooleanAndNumberConfig( 138 | combineNameAndValue('allow-half', allowHalf), 139 | )}` 140 | } 141 | 142 | const getSelectFormItemContentConfig = (item: { 143 | [key: string]: any 144 | }): string => { 145 | const { name, size, multiple, placeholder, clearable, options } = item 146 | return `${bindValueConfig( 147 | combineNameAndValue('name', name), 148 | )} ${bindStringConfig(combineNameAndValue('size', size))} ${bindStringConfig( 149 | combineNameAndValue('placeholder', placeholder), 150 | )} ${bindBooleanAndNumberConfig( 151 | combineNameAndValue('multiple', multiple), 152 | )} ${bindBooleanAndNumberConfig( 153 | combineNameAndValue('clearable', clearable), 154 | )} ${bindBooleanAndNumberConfig( 155 | combineNameAndValue('options', replaceOptions(JSON.stringify(options))), 156 | )}` 157 | } 158 | 159 | const getSliderFormItemContentConfig = (item: { 160 | [key: string]: any 161 | }): string => { 162 | const { name, max, min, step, range, reverse, vertical, tooltip } = item 163 | return `${bindValueConfig( 164 | combineNameAndValue('name', name), 165 | )} ${bindBooleanAndNumberConfig( 166 | combineNameAndValue('max', max), 167 | )} ${bindBooleanAndNumberConfig( 168 | combineNameAndValue('min', min), 169 | )} ${bindBooleanAndNumberConfig( 170 | combineNameAndValue('step', step), 171 | )} ${bindBooleanAndNumberConfig( 172 | combineNameAndValue('range', range), 173 | )} ${bindBooleanAndNumberConfig( 174 | combineNameAndValue('reverse', reverse), 175 | )} ${bindBooleanAndNumberConfig( 176 | combineNameAndValue('vertical', vertical), 177 | )} ${bindBooleanAndNumberConfig(combineNameAndValue('tooltip', tooltip))}` 178 | } 179 | 180 | const getSwitchFormItemContentConfig = (item: { 181 | [key: string]: any 182 | }): string => { 183 | const { name, round, size } = item 184 | return `${bindValueConfig( 185 | combineNameAndValue('name', name), 186 | )} ${bindBooleanAndNumberConfig( 187 | combineNameAndValue('round', round), 188 | )} ${bindStringConfig(combineNameAndValue('size', size))}` 189 | } 190 | 191 | const getTimePickerFormItemContentConfig = (item: { 192 | [key: string]: any 193 | }): string => { 194 | const { name, size, actions, clearable, format, placeholder, use12Hours } 195 | = item 196 | return `${bindValueConfig( 197 | combineNameAndValue('name', name), 198 | )} ${bindBooleanAndNumberConfig( 199 | combineNameAndValue('clearable', clearable), 200 | )} ${bindBooleanAndNumberConfig( 201 | combineNameAndValue('use-12-hours', use12Hours), 202 | )} ${bindBooleanAndNumberConfig( 203 | combineNameAndValue('actions', JSON.stringify(actions)), 204 | )} ${bindStringConfig( 205 | combineNameAndValue('format', format), 206 | )} ${bindStringConfig( 207 | combineNameAndValue('placeholder', placeholder), 208 | )} ${bindStringConfig(combineNameAndValue('size', size))}` 209 | } 210 | 211 | const getTreeSelectFormItemContentConfig = (item: { 212 | [key: string]: any 213 | }): string => { 214 | const { name, size, clearable } = item 215 | return `${bindValueConfig( 216 | combineNameAndValue('name', name), 217 | )} ${bindBooleanAndNumberConfig( 218 | combineNameAndValue('clearable', clearable), 219 | )} ${bindStringConfig(combineNameAndValue('size', size))}` 220 | } 221 | 222 | const getUploadFormItemContentConfig = (item: { 223 | [key: string]: any 224 | }): string => { 225 | const { 226 | name, 227 | accept, 228 | action, 229 | defaultUpload, 230 | data, 231 | headers, 232 | listType, 233 | max, 234 | method, 235 | multiple, 236 | fileName, 237 | withCredentials, 238 | showCancelButton, 239 | showDownloadButton, 240 | showRemoveButton, 241 | showRetryButton, 242 | showFileList, 243 | } = item 244 | const handledDate: Record = {} 245 | const handledHeaders: Record = {}; 246 | ( 247 | data as Array<{ 248 | key: string 249 | value: string 250 | }> 251 | ).map((item) => { 252 | return (handledDate[item.key] = item.value) 253 | }); 254 | ( 255 | headers as Array<{ 256 | key: string 257 | value: string 258 | }> 259 | ).map((item) => { 260 | return (handledHeaders[item.key] = item.value) 261 | }) 262 | return `${bindFileListConfig( 263 | combineNameAndValue('name', name), 264 | )} ${bindBooleanAndNumberConfig( 265 | combineNameAndValue('default-upload', defaultUpload), 266 | )} ${bindBooleanAndNumberConfig( 267 | combineNameAndValue('data', JSON.stringify(handledDate)), 268 | )} ${bindBooleanAndNumberConfig( 269 | combineNameAndValue('headers', JSON.stringify(handledHeaders)), 270 | )} ${bindBooleanAndNumberConfig( 271 | combineNameAndValue('show-cancel-button', showCancelButton), 272 | )} ${bindBooleanAndNumberConfig( 273 | combineNameAndValue('show-download-button', showDownloadButton), 274 | )} ${bindBooleanAndNumberConfig( 275 | combineNameAndValue('show-remove-button', showRemoveButton), 276 | )} ${bindBooleanAndNumberConfig( 277 | combineNameAndValue('show-retry-button', showRetryButton), 278 | )} ${bindBooleanAndNumberConfig( 279 | combineNameAndValue('show-file-list', showFileList), 280 | )} ${bindStringConfig( 281 | combineNameAndValue('accept', accept), 282 | )} ${bindStringConfig( 283 | combineNameAndValue('action', action), 284 | )} ${bindStringConfig( 285 | combineNameAndValue('list-type', listType), 286 | )} ${bindBooleanAndNumberConfig( 287 | combineNameAndValue('max', max), 288 | )} ${bindStringConfig( 289 | combineNameAndValue('method', method), 290 | )} ${bindBooleanAndNumberConfig( 291 | combineNameAndValue('multiple', multiple), 292 | )} ${bindStringConfig( 293 | combineNameAndValue('name', fileName), 294 | )} ${bindBooleanAndNumberConfig( 295 | combineNameAndValue('with-credentials', withCredentials), 296 | )}` 297 | } 298 | 299 | const getCheckBoxFormItemContentConfig = (item: { 300 | [key: string]: any 301 | }): string => { 302 | const { name, max, min } = item 303 | return `${bindValueConfig( 304 | combineNameAndValue('name', name), 305 | )} ${bindBooleanAndNumberConfig( 306 | combineNameAndValue('max', max), 307 | )} ${bindBooleanAndNumberConfig(combineNameAndValue('min', min))}` 308 | } 309 | 310 | const getColorPickerFormItemContentConfig = (item: { 311 | [key: string]: any 312 | }): string => { 313 | const { name, size, modes, showAlpha, actions } = item 314 | return `${bindValueConfig( 315 | combineNameAndValue('name', name), 316 | )} ${bindBooleanAndNumberConfig( 317 | combineNameAndValue('modes', JSON.stringify(modes)), 318 | )} ${bindBooleanAndNumberConfig( 319 | combineNameAndValue('showAlpha', showAlpha), 320 | )} ${bindBooleanAndNumberConfig( 321 | combineNameAndValue('actions', JSON.stringify(actions)), 322 | )} ${bindStringConfig(combineNameAndValue('size', size))}` 323 | } 324 | 325 | const getDividerContentConfig = (item: { [key: string]: any }): string => { 326 | const { dashed, vertical, titlePlacement } = item 327 | return `${bindBooleanAndNumberConfig( 328 | combineNameAndValue('dashed', dashed), 329 | )} ${bindBooleanAndNumberConfig( 330 | combineNameAndValue('vertical', vertical), 331 | )} ${bindStringConfig( 332 | combineNameAndValue('title-placement', titlePlacement), 333 | )}` 334 | } 335 | 336 | const getDatePickerFormItemContentConfig = (item: { 337 | [key: string]: any 338 | }): string => { 339 | const { 340 | name, 341 | clearable, 342 | format, 343 | size, 344 | actions, 345 | placeholder, 346 | type, 347 | startPlaceholder, 348 | endPlaceholder, 349 | separator, 350 | } = item 351 | return `${bindValueConfig( 352 | combineNameAndValue('name', name), 353 | )} ${bindBooleanAndNumberConfig( 354 | combineNameAndValue('actions', JSON.stringify(actions)), 355 | )} ${bindBooleanAndNumberConfig( 356 | combineNameAndValue('clearable', clearable), 357 | )} ${bindStringConfig( 358 | combineNameAndValue('format', format), 359 | )} ${bindStringConfig(combineNameAndValue('size', size))} ${bindStringConfig( 360 | combineNameAndValue('placeholder', placeholder), 361 | )} ${bindStringConfig(combineNameAndValue('type', type))} ${bindStringConfig( 362 | combineNameAndValue('start-placeholder', startPlaceholder), 363 | )} ${bindStringConfig( 364 | combineNameAndValue('end-placeholder', endPlaceholder), 365 | )} ${bindStringConfig(combineNameAndValue('separator', separator))}` 366 | } 367 | 368 | const getFormItemConfig = (item: formItemType): string => { 369 | return `label="${item.formItemConfig.label as string}" path="${ 370 | item.formItemConfig.name as string 371 | }"` 372 | } 373 | 374 | const getFormItemContentConfig = ( 375 | item: { [key: string]: any }, 376 | type: string, 377 | ): string => { 378 | if (Object.keys(item).length === 0) 379 | return '' 380 | 381 | switch (type) { 382 | case '0': 383 | return getInputFormItemContentConfig(item) 384 | case '1': 385 | return getInputNumberFormItemContentConfig(item) 386 | case '2': 387 | return getRadioFormItemContentConfig(item) 388 | case '3': 389 | return getRateFormItemContentConfig(item) 390 | case '4': 391 | return getSelectFormItemContentConfig(item) 392 | case '5': 393 | return getSliderFormItemContentConfig(item) 394 | case '6': 395 | return getSwitchFormItemContentConfig(item) 396 | case '7': 397 | return getTimePickerFormItemContentConfig(item) 398 | case '8': 399 | return getTreeSelectFormItemContentConfig(item) 400 | case '9': 401 | return getUploadFormItemContentConfig(item) 402 | case '10': 403 | return getColorPickerFormItemContentConfig(item) 404 | case '11': 405 | return getCheckBoxFormItemContentConfig(item) 406 | case '12': 407 | return getDatePickerFormItemContentConfig(item) 408 | case '13': 409 | return getDividerContentConfig(item) 410 | default: 411 | return '' 412 | } 413 | } 414 | 415 | const getTypeToFormItem = (item: formItemType): string => { 416 | const type = item.value 417 | const formItemConfig = getFormItemConfig(item) 418 | const formItemContentConfig = getFormItemContentConfig( 419 | item.formItemConfig, 420 | type, 421 | ) 422 | switch (type) { 423 | case '0': 424 | return ` 425 | <${PREFIX}-form-item ${formItemConfig}> 426 | <${PREFIX}-input ${formItemContentConfig}/> 427 | ` 428 | case '1': 429 | return ` 430 | <${PREFIX}-form-item ${formItemConfig}> 431 | <${PREFIX}-input-number ${formItemContentConfig}/> 432 | ` 433 | case '2': 434 | return ` 435 | <${PREFIX}-form-item ${formItemConfig}> 436 | <${PREFIX}-radio-group ${formItemContentConfig}> 437 | <${PREFIX}-space> 438 | ${ 439 | item.formItemConfig.options !== undefined 440 | ? ( 441 | item.formItemConfig.options as Array<{ 442 | label: string 443 | value: string 444 | }> 445 | ) 446 | .map( 447 | (option: { value: string; label: string }) => 448 | `<${PREFIX}-radio value="${option.value}">${option.label}`, 449 | ) 450 | .join('') 451 | : '' 452 | } 453 | 454 | 455 | ` 456 | case '3': 457 | return ` 458 | <${PREFIX}-form-item ${formItemConfig}> 459 | <${PREFIX}-rate ${formItemContentConfig}/> 460 | ` 461 | case '4': 462 | return ` 463 | <${PREFIX}-form-item ${formItemConfig}> 464 | <${PREFIX}-select ${formItemContentConfig} /> 465 | ` 466 | case '5': 467 | return ` 468 | <${PREFIX}-form-item ${formItemConfig}> 469 | <${PREFIX}-slider ${formItemContentConfig}/> 470 | ` 471 | case '6': 472 | return ` 473 | <${PREFIX}-form-item ${formItemConfig}> 474 | <${PREFIX}-switch ${formItemContentConfig}/> 475 | ` 476 | case '7': 477 | return ` 478 | <${PREFIX}-form-item ${formItemConfig}> 479 | <${PREFIX}-time-picker ${formItemContentConfig}/> 480 | ` 481 | case '8': 482 | return ` 483 | <${PREFIX}-form-item ${formItemConfig}> 484 | <${PREFIX}-tree-select ${formItemContentConfig}/> 485 | ` 486 | case '9': 487 | return ` 488 | <${PREFIX}-form-item ${formItemConfig}> 489 | <${PREFIX}-upload ${formItemContentConfig}> 490 | <${PREFIX}-button>上传文件 491 | 492 | ` 493 | case '10': 494 | return ` 495 | <${PREFIX}-form-item ${formItemConfig}> 496 | <${PREFIX}-color-picker ${formItemContentConfig}/> 497 | ` 498 | case '11': 499 | return ` 500 | <${PREFIX}-form-item ${formItemConfig}> 501 | <${PREFIX}-checkbox-group ${formItemContentConfig}> 502 | <${PREFIX}-space item-style="display: flex;"> 503 | ${ 504 | item.formItemConfig.options !== undefined 505 | ? ( 506 | item.formItemConfig.options as Array<{ 507 | label: string 508 | value: string 509 | }> 510 | ) 511 | .map( 512 | (option: { value: string; label: string }) => 513 | `<${PREFIX}-checkbox value="${option.value}" label="${option.label}"/>`, 514 | ) 515 | .join('') 516 | : '' 517 | } 518 | 519 | 520 | ` 521 | case '13': 522 | return ` 523 | <${PREFIX}-divider ${formItemContentConfig}>${ 524 | (item.formItemConfig.name as string) ?? '' 525 | }` 526 | case '12': 527 | return ` 528 | <${PREFIX}-form-item ${formItemConfig}> 529 | <${PREFIX}-date-picker ${formItemContentConfig}/> 530 | ` 531 | default: 532 | return '' 533 | } 534 | } 535 | 536 | const getFormItemImport = (data: formItemType[]): string => { 537 | const prefix = PREFIX.toUpperCase() 538 | const { confirmAndCancelBtn } = store.state 539 | const array: string[] = [] 540 | data.forEach((item) => { 541 | return typeToImport[item.value].forEach((importItem) => { 542 | array.push(`${prefix}${importItem} ,`) 543 | }) 544 | }) 545 | // button 546 | if (confirmAndCancelBtn) { 547 | ['Button', 'Space'].forEach((item) => { 548 | array.push(`${prefix}${item} ,`) 549 | }) 550 | } 551 | return `${Array.from(new Set(array)).join('')}` 552 | } 553 | 554 | // formConfig 555 | 556 | const getFormConfig = (): string => { 557 | const { 558 | size, 559 | inline, 560 | labelWidth, 561 | labelAlign, 562 | labelPlacement, 563 | showFeedback, 564 | showLabel, 565 | showRequireMark, 566 | requireMarkPlacement, 567 | model, 568 | } = store.state.formConfig 569 | 570 | return `${bindBooleanAndNumberConfig( 571 | combineNameAndValue('model', model), 572 | )} ${bindStringConfig( 573 | combineNameAndValue('size', size), 574 | )} ${bindBooleanAndNumberConfig( 575 | combineNameAndValue('inline', inline), 576 | )} ${bindBooleanAndNumberConfig( 577 | combineNameAndValue('label-width', labelWidth), 578 | )} ${bindStringConfig( 579 | combineNameAndValue('label-align', labelAlign), 580 | )} ${bindStringConfig( 581 | combineNameAndValue('label-placement', labelPlacement), 582 | )} ${bindBooleanAndNumberConfig( 583 | combineNameAndValue('show-feedback', showFeedback), 584 | )} ${bindBooleanAndNumberConfig( 585 | combineNameAndValue('show-label', showLabel), 586 | )} ${bindBooleanAndNumberConfig( 587 | combineNameAndValue('show-require-mark', showRequireMark), 588 | )} ${bindBooleanAndNumberConfig( 589 | combineNameAndValue('rules', 'rules'), 590 | )} ${bindStringConfig( 591 | combineNameAndValue('require-mark-placement', requireMarkPlacement), 592 | )}` 593 | } 594 | 595 | // import 596 | 597 | const getImport = (data: formItemType[]): string => { 598 | const prefix = PREFIX.toUpperCase() 599 | if (store.state.autoAddImport) { 600 | const importStr = ` 601 | 606 | ` 607 | return importStr 608 | } 609 | 610 | return '' 611 | } 612 | 613 | // confirmAndCancelButton 614 | 615 | const getConfirmAndCancelButton = (): string => { 616 | const { confirmAndCancelBtn } = store.state 617 | if (confirmAndCancelBtn) { 618 | return ` 619 | <${PREFIX}-form-item> 620 | <${PREFIX}-space justify="center"> 621 | <${PREFIX}-button @click="handleValidateClick">确认 622 | <${PREFIX}-button>取消 623 | 624 | ` 625 | } 626 | return '' 627 | } 628 | 629 | // rules 630 | 631 | const getRulesObject = (data: formItemType[]): string => { 632 | const rulesArray: string[] = [] 633 | 634 | for (let i = 0; i < data.length; i++) { 635 | const item = data[i] 636 | if (item.formItemConfig.name === undefined) 637 | continue 638 | const rules = data[i].formItemConfig.rules as string[] 639 | const itemRulesArray: string[] = [] 640 | 641 | for (let i = 0; i < rules.length; i++) { 642 | const element = rules[i] 643 | switch (element) { 644 | case '0': 645 | itemRulesArray.push( 646 | `{ required: true, message: '请输入${ 647 | item.formItemConfig.label as string 648 | }', trigger: 'blur' },`, 649 | ) 650 | break 651 | case '1': 652 | itemRulesArray.push( 653 | `{ validator: (rule,value)=>{ 654 | let reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/; 655 | if(!reg.test(value)){ 656 | return new Error('请输入正确的${ 657 | item.formItemConfig.label as string 658 | }'); 659 | } 660 | return true; 661 | }, message: '请输入正确的${ 662 | item.formItemConfig.label as string 663 | }', trigger: 'blur' },`, 664 | ) 665 | break 666 | case '2': 667 | itemRulesArray.push( 668 | `{ validator: (rule,value)=>{ 669 | let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; 670 | if(!reg.test(value)){ 671 | return new Error('请输入正确的${ 672 | item.formItemConfig.label as string 673 | }'); 674 | } 675 | return true; 676 | }, message: '请输入正确的${ 677 | item.formItemConfig.label as string 678 | }', trigger: 'blur' },`, 679 | ) 680 | break 681 | default: 682 | break 683 | } 684 | } 685 | 686 | rulesArray.push(`${item.formItemConfig.name as string}: [ 687 | ${itemRulesArray.join('')} 688 | ],`) 689 | } 690 | 691 | if (rulesArray.length === 0) 692 | return '' 693 | 694 | return ` 695 | const rules = { 696 | ${rulesArray.join('')} 697 | } 698 | ` 699 | } 700 | 701 | // entry 702 | 703 | export const generateCode = (data: formItemType[]): string => { 704 | const Code = ` 705 | 711 | 715 | ` 716 | 717 | return Code 718 | } 719 | 720 | // normal 721 | 722 | export const copy = (value: string): void => { 723 | const textarea = document.createElement('textarea') 724 | textarea.value = value 725 | document.body.appendChild(textarea) 726 | textarea.select() 727 | // 暂时没有可用的替代方法。 728 | // 确切的说这个 API 本来也不是标准 API,而是一个 IE 的私有 API,在 IE9 时被引入,后续的若干年里陆续被 Chrome / Firefix / Opera 等浏览器也做了兼容支持,但始终没有形成标准。 729 | // 这个 API 被废弃的主要原因第一个就是安全问题,在用户未经授权的情况下就可以执行一些敏感操作,这就很恐怖了;第二个问题是因为这是一个同步方法,而且操作了 DOM 对象,会阻塞页面渲染和脚本执行,因当初还没 Promise,所以没设计成异步,挖坑了。新设计的 API 肯定是要解决这两个问题。 730 | // 不过 W3C 也正在拟草案,大概率以后会引入一个叫 Clipboard 的类型(Chrome 66.0 开始已经有这个类型了,不过还不能用,相关 API 仅存在于文档中),用来处理跟剪贴版相关的操作,不过之后肯定会是像现在获取地理位置啊、麦克风啊什么的,浏览器先会弹出一个对话框让用户授权,你才能读写剪贴板了。 731 | document.execCommand('copy') 732 | document.body.removeChild(textarea) 733 | } 734 | 735 | export function copyPropertyValue( 736 | obj: T, 737 | target: T, 738 | key: K, 739 | ): void { 740 | obj[key] = target[key] 741 | } 742 | 743 | export function getParentElement( 744 | element: HTMLElement | null, 745 | ): HTMLElement | null { 746 | if (element === null) 747 | return null 748 | 749 | let currElement: HTMLElement | null = element 750 | while (currElement != null) { 751 | if (currElement.dataset.drag === 'doom') 752 | return currElement 753 | 754 | currElement = currElement.parentElement 755 | } 756 | return null 757 | } 758 | --------------------------------------------------------------------------------