Explorar o código

表单构建优化

ys hai 2 semanas
pai
achega
2070b0b0e9

+ 47 - 47
yushu-uivue3/src/utils/generator/config.js

@@ -14,7 +14,7 @@ export const formConf = {
 export const inputComponents = [
   {
     label: '单行文本',
-    tag: 'el-input',
+    tag: 'a-input',
     tagIcon: 'input',
     type: 'text',
     placeholder: '请输入',
@@ -34,11 +34,11 @@ export const inputComponents = [
     required: true,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/input',
+    document: 'https://antdv.com/components/input-cn',
   },
   {
     label: '多行文本',
-    tag: 'el-input',
+    tag: 'a-input',
     tagIcon: 'textarea',
     type: 'textarea',
     placeholder: '请输入',
@@ -57,11 +57,11 @@ export const inputComponents = [
     required: true,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/input',
+    document: 'https://antdv.com/components/input-cn',
   },
   {
     label: '密码',
-    tag: 'el-input',
+    tag: 'a-input',
     tagIcon: 'password',
     type: 'password',
     placeholder: '请输入',
@@ -82,11 +82,11 @@ export const inputComponents = [
     required: true,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/input',
+    document: 'https://antdv.com/components/input-cn',
   },
   {
     label: '计数器',
-    tag: 'el-input-number',
+    tag: 'a-input-number',
     tagIcon: 'number',
     placeholder: '',
     defaultValue: undefined,
@@ -102,14 +102,14 @@ export const inputComponents = [
     required: true,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/input-number',
+    document: 'https://antdv.com/components/input-number-cn',
   },
 ]
 
 export const selectComponents = [
   {
     label: '下拉选择',
-    tag: 'el-select',
+    tag: 'a-select',
     tagIcon: 'select',
     placeholder: '请选择',
     defaultValue: undefined,
@@ -133,11 +133,11 @@ export const selectComponents = [
     ],
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/select',
+    document: 'https://antdv.com/components/select-cn',
   },
   {
     label: '级联选择',
-    tag: 'el-cascader',
+    tag: 'a-cascader',
     tagIcon: 'cascader',
     placeholder: '请选择',
     defaultValue: [],
@@ -175,11 +175,11 @@ export const selectComponents = [
     separator: '/',
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/cascader',
+    document: 'https://antdv.com/components/cascader-cn',
   },
   {
     label: '单选框组',
-    tag: 'el-radio-group',
+    tag: 'a-radio-group',
     tagIcon: 'radio',
     defaultValue: 0,
     span: 24,
@@ -202,11 +202,11 @@ export const selectComponents = [
     ],
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/radio',
+    document: 'https://antdv.com/components/radio-cn',
   },
   {
     label: '多选框组',
-    tag: 'el-checkbox-group',
+    tag: 'a-checkbox-group',
     tagIcon: 'checkbox',
     defaultValue: [],
     span: 24,
@@ -229,11 +229,11 @@ export const selectComponents = [
     ],
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/checkbox',
+    document: 'https://antdv.com/components/checkbox-cn',
   },
   {
     label: '开关',
-    tag: 'el-switch',
+    tag: 'a-switch',
     tagIcon: 'switch',
     defaultValue: false,
     span: 24,
@@ -249,11 +249,11 @@ export const selectComponents = [
     'inactive-value': false,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/switch',
+    document: 'https://antdv.com/components/switch-cn',
   },
   {
     label: '滑块',
-    tag: 'el-slider',
+    tag: 'a-slider',
     tagIcon: 'slider',
     defaultValue: null,
     span: 24,
@@ -267,11 +267,11 @@ export const selectComponents = [
     range: false,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/slider',
+    document: 'https://antdv.com/components/slider-cn',
   },
   {
     label: '时间选择',
-    tag: 'el-time-picker',
+    tag: 'a-time-picker',
     tagIcon: 'time',
     placeholder: '请选择',
     defaultValue: '',
@@ -285,11 +285,11 @@ export const selectComponents = [
     'value-format': 'HH:mm:ss',
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/time-picker',
+    document: 'https://antdv.com/components/time-picker-cn',
   },
   {
     label: '时间范围',
-    tag: 'el-time-picker',
+    tag: 'a-time-picker',
     tagIcon: 'time-range',
     defaultValue: null,
     span: 24,
@@ -306,11 +306,11 @@ export const selectComponents = [
     'value-format': 'HH:mm:ss',
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/time-picker',
+    document: 'https://antdv.com/components/time-picker-cn',
   },
   {
     label: '日期选择',
-    tag: 'el-date-picker',
+    tag: 'a-date-picker',
     tagIcon: 'date',
     placeholder: '请选择',
     defaultValue: null,
@@ -326,11 +326,11 @@ export const selectComponents = [
     readonly: false,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/date-picker',
+    document: 'https://antdv.com/components/date-picker-cn',
   },
   {
     label: '日期范围',
-    tag: 'el-date-picker',
+    tag: 'a-date-picker',
     tagIcon: 'date-range',
     defaultValue: null,
     span: 24,
@@ -348,11 +348,11 @@ export const selectComponents = [
     readonly: false,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/date-picker',
+    document: 'https://antdv.com/components/date-picker-cn',
   },
   {
     label: '评分',
-    tag: 'el-rate',
+    tag: 'a-rate',
     tagIcon: 'rate',
     defaultValue: 0,
     span: 24,
@@ -366,11 +366,11 @@ export const selectComponents = [
     required: true,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/rate',
+    document: 'https://antdv.com/components/rate-cn',
   },
   {
     label: '颜色选择',
-    tag: 'el-color-picker',
+    tag: 'a-color-picker',
     tagIcon: 'color',
     defaultValue: null,
     labelWidth: null,
@@ -381,11 +381,11 @@ export const selectComponents = [
     size: 'default',
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/color-picker',
+    document: 'https://antdv.com/components/color-picker-cn',
   },
   {
     label: '上传',
-    tag: 'el-upload',
+    tag: 'a-upload',
     tagIcon: 'upload',
     action: 'https://jsonplaceholder.typicode.com/posts/',
     defaultValue: null,
@@ -403,7 +403,7 @@ export const selectComponents = [
     multiple: false,
     regList: [],
     changeTag: true,
-    document: 'https://element-plus.org/zh-CN/component/upload',
+    document: 'https://antdv.com/components/upload-cn',
     tip: '只能上传不超过 2MB 的文件',
     style: { width: '100%' },
   },
@@ -419,34 +419,34 @@ export const layoutComponents = [
     label: '行容器',
     layoutTree: true,
     children: [],
-    document: 'https://element-plus.org/zh-CN/component/layout',
+    document: 'https://antdv.com/components/grid-cn',
   },
   {
     layout: 'colFormItem',
     label: '按钮',
     changeTag: true,
     labelWidth: null,
-    tag: 'el-button',
+    tag: 'a-button',
     tagIcon: 'button',
     span: 24,
     default: '主要按钮',
     type: 'primary',
-    icon: 'Search',
+    icon: 'SearchOutlined',
     size: 'default',
     disabled: false,
-    document: 'https://element-plus.org/zh-CN/component/button',
+    document: 'https://antdv.com/components/button-cn',
   },
 ]
 
 // 组件rule的触发方式,无触发方式的组件不生成rule
 export const trigger = {
-  'el-input': 'blur',
-  'el-input-number': 'blur',
-  'el-select': 'change',
-  'el-radio-group': 'change',
-  'el-checkbox-group': 'change',
-  'el-cascader': 'change',
-  'el-time-picker': 'change',
-  'el-date-picker': 'change',
-  'el-rate': 'change',
+  'a-input': 'blur',
+  'a-input-number': 'blur',
+  'a-select': 'change',
+  'a-radio-group': 'change',
+  'a-checkbox-group': 'change',
+  'a-cascader': 'change',
+  'a-time-picker': 'change',
+  'a-date-picker': 'change',
+  'a-rate': 'change',
 }

+ 1 - 1
yushu-uivue3/src/utils/generator/drawingDefault.js

@@ -8,7 +8,7 @@ export function initDrawingDefaultValue() {
       label: '手机号',
       vModel: 'mobile',
       formId: 6,
-      tag: 'el-input',
+      tag: 'a-input',
       placeholder: '请输入手机号',
       defaultValue: '',
       span: 24,

+ 113 - 112
yushu-uivue3/src/utils/generator/html.js

@@ -5,13 +5,9 @@ let confGlobal
 let someSpanIsNot24
 
 export function dialogWrapper(str) {
-  return `<el-dialog v-model="dialogVisible"  @open="onOpen" @close="onClose" title="Dialog Titile">
+  return `<a-modal v-model:open="dialogVisible" @ok="handelConfirm" @cancel="close" title="Dialog Titile">
     ${str}
-    <template #footer>
-      <el-button @click="close">取消</el-button>
-	  <el-button type="primary" @click="handelConfirm">确定</el-button>
-    </template>
-  </el-dialog>`
+  </a-modal>`
 }
 
 export function vueTemplate(str) {
@@ -35,19 +31,21 @@ export function cssStyle(cssStr) {
 }
 
 function buildFormTemplate(conf, child, type) {
-  let labelPosition = ''
-  if (conf.labelPosition !== 'right') {
-    labelPosition = `label-position="${conf.labelPosition}"`
+  let labelAlign = ''
+  if (conf.labelPosition === 'left') {
+    labelAlign = `label-align="left"`
+  } else if (conf.labelPosition === 'right') {
+    labelAlign = `label-align="right"`
   }
   const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
-  let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
+  let str = `<a-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} :label-col="{ style: { width: '${conf.labelWidth}px' } }" ${labelAlign}>
       ${child}
       ${buildFromBtns(conf, type)}
-    </el-form>`
+    </a-form>`
   if (someSpanIsNot24) {
-    str = `<el-row :gutter="${conf.gutter}">
+    str = `<a-row :gutter="${conf.gutter}">
         ${str}
-      </el-row>`
+      </a-row>`
   }
   return str
 }
@@ -55,40 +53,40 @@ function buildFormTemplate(conf, child, type) {
 function buildFromBtns(conf, type) {
   let str = ''
   if (conf.formBtns && type === 'file') {
-    str = `<el-form-item>
-          <el-button type="primary" @click="submitForm">提交</el-button>
-          <el-button @click="resetForm">重置</el-button>
-        </el-form-item>`
+    str = `<a-form-item>
+          <a-button type="primary" @click="submitForm">提交</a-button>
+          <a-button @click="resetForm">重置</a-button>
+        </a-form-item>`
     if (someSpanIsNot24) {
-      str = `<el-col :span="24">
+      str = `<a-col :span="24">
           ${str}
-        </el-col>`
+        </a-col>`
     }
   }
   return str
 }
 
-// span不为24的用el-col包裹
+// span不为24的用a-col包裹
 function colWrapper(element, str) {
   if (someSpanIsNot24 || element.span !== 24) {
-    return `<el-col :span="${element.span}">
+    return `<a-col :span="${element.span}">
       ${str}
-    </el-col>`
+    </a-col>`
   }
   return str
 }
 
 const layouts = {
   colFormItem(element) {
-    let labelWidth = ''
+    let labelCol = ''
     if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
-      labelWidth = `label-width="${element.labelWidth}px"`
+      labelCol = `:label-col="{ style: { width: '${element.labelWidth}px' } }"`
     }
     const required = !trigger[element.tag] && element.required ? 'required' : ''
     const tagDom = tags[element.tag] ? tags[element.tag](element) : null
-    let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
+    let str = `<a-form-item ${labelCol} label="${element.label}" name="${element.vModel}" ${required}>
         ${tagDom}
-      </el-form-item>`
+      </a-form-item>`
     str = colWrapper(element, str)
     return str
   },
@@ -96,148 +94,146 @@ const layouts = {
     const type = element.type === 'default' ? '' : `type="${element.type}"`
     const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
     const align = element.type === 'default' ? '' : `align="${element.align}"`
-    const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
+    const gutter = element.gutter ? `:gutter="${element.gutter}"` : ''
     const children = element.children.map(el => layouts[el.layout](el))
-    let str = `<el-row ${type} ${justify} ${align} ${gutter}>
+    let str = `<a-row ${type} ${justify} ${align} ${gutter}>
       ${children.join('\n')}
-    </el-row>`
+    </a-row>`
     str = colWrapper(element, str)
     return str
   }
 }
 
 const tags = {
-  'el-button': el => {
+  'a-button': el => {
     const {
       tag, disabled
     } = attrBuilder(el)
     const type = el.type ? `type="${el.type}"` : ''
-    const icon = el.icon ? `icon="${el.icon}"` : ''
     const size = el.size ? `size="${el.size}"` : ''
     let child = buildElButtonChild(el)
+    const icon = el.icon ? `\n<template #icon><${el.icon} /></template>` : ''
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`
+    return `<a-button ${type} ${size} ${disabled}>${icon}${child}</a-button>`
   },
-  'el-input': el => {
+  'a-input': el => {
     const {
       disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
-    const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
+    const showCount = el['show-word-limit'] ? 'show-count' : ''
     const readonly = el.readonly ? 'readonly' : ''
-    const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
-    const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
-    const showPassword = el['show-password'] ? 'show-password' : ''
-    const type = el.type ? `type="${el.type}"` : ''
+    const prefix = el['prefix-icon'] ? `prefix='${el['prefix-icon']}'` : ''
+    const suffix = el['suffix-icon'] ? `suffix='${el['suffix-icon']}'` : ''
+    const showPassword = el['show-password'] ? 'type="password"' : ''
+    const type = el.type && !el['show-password'] ? `type="${el.type}"` : ''
     const autosize = el.autosize && el.autosize.minRows
-      ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
+      ? `:auto-size="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
       : ''
     let child = buildElInputChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
+    const finalType = showPassword || type
+    return `<a-input ${vModel} ${finalType} ${placeholder} ${maxlength} ${showCount} ${readonly} ${disabled} ${clearable} ${prefix} ${suffix} ${autosize} ${width}>${child}</a-input>`
   },
-  'el-input-number': el => {
+  'a-input-number': el => {
     const { disabled, vModel, placeholder } = attrBuilder(el)
-    const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
-    const min = el.min ? `:min='${el.min}'` : ''
-    const max = el.max ? `:max='${el.max}'` : ''
-    const step = el.step ? `:step='${el.step}'` : ''
-    const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
-    const precision = el.precision ? `:precision='${el.precision}'` : ''
+    const controlsPosition = el['controls-position'] ? `controls-position="${el['controls-position']}"` : ''
+    const min = el.min !== undefined ? `:min="${el.min}"` : ''
+    const max = el.max !== undefined ? `:max="${el.max}"` : ''
+    const step = el.step ? `:step="${el.step}"` : ''
+    const precision = el.precision !== undefined ? `:precision="${el.precision}"` : ''
 
-    return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
+    return `<a-input-number ${vModel} ${placeholder} ${step} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></a-input-number>`
   },
-  'el-select': el => {
+  'a-select': el => {
     const {
       disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
-    const filterable = el.filterable ? 'filterable' : ''
-    const multiple = el.multiple ? 'multiple' : ''
+    const filterable = el.filterable ? 'show-search' : ''
+    const multiple = el.multiple ? 'mode="multiple"' : ''
     let child = buildElSelectChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
+    return `<a-select ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</a-select>`
   },
-  'el-radio-group': el => {
+  'a-radio-group': el => {
     const { disabled, vModel } = attrBuilder(el)
     const size = `size="${el.size}"`
     let child = buildElRadioGroupChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
+    return `<a-radio-group ${vModel} ${size} ${disabled}>${child}</a-radio-group>`
   },
-  'el-checkbox-group': el => {
+  'a-checkbox-group': el => {
     const { disabled, vModel } = attrBuilder(el)
-    const size = `size="${el.size}"`
-    const min = el.min ? `:min="${el.min}"` : ''
-    const max = el.max ? `:max="${el.max}"` : ''
     let child = buildElCheckboxGroupChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
+    return `<a-checkbox-group ${vModel} ${disabled}>${child}</a-checkbox-group>`
   },
-  'el-switch': el => {
+  'a-switch': el => {
     const { disabled, vModel } = attrBuilder(el)
-    const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
-    const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
-    const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
-    const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
-    const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
-    const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
+    const checkedChildren = el['active-text'] ? `checked-children="${el['active-text']}"` : ''
+    const unCheckedChildren = el['inactive-text'] ? `un-checked-children="${el['inactive-text']}"` : ''
+    const checkedValue = el['active-value'] !== true ? `:checked-value='${JSON.stringify(el['active-value'])}'` : ''
+    const unCheckedValue = el['inactive-value'] !== false ? `:un-checked-value='${JSON.stringify(el['inactive-value'])}'` : ''
 
-    return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
+    return `<a-switch ${vModel} ${checkedChildren} ${unCheckedChildren} ${checkedValue} ${unCheckedValue} ${disabled}></a-switch>`
   },
-  'el-cascader': el => {
+  'a-cascader': el => {
     const {
       disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const options = el.options ? `:options="${el.vModel}Options"` : ''
-    const props = el.props ? `:props="${el.vModel}Props"` : ''
-    const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
-    const filterable = el.filterable ? 'filterable' : ''
-    const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
+    const fieldNames = el.props ? `:field-names="${el.vModel}Props"` : ''
+    const showAllLevels = el['show-all-levels'] ? '' : ':show-search="false"'
+    const showSearch = el.filterable ? 'show-search' : ''
 
-    return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
+    return `<a-cascader ${vModel} ${options} ${fieldNames} ${width} ${placeholder} ${showSearch} ${clearable} ${disabled}></a-cascader>`
   },
-  'el-slider': el => {
+  'a-slider': el => {
     const { disabled, vModel } = attrBuilder(el)
-    const min = el.min ? `:min='${el.min}'` : ''
-    const max = el.max ? `:max='${el.max}'` : ''
-    const step = el.step ? `:step='${el.step}'` : ''
+    const min = el.min !== undefined ? `:min="${el.min}"` : ''
+    const max = el.max !== undefined ? `:max="${el.max}"` : ''
+    const step = el.step ? `:step="${el.step}"` : ''
     const range = el.range ? 'range' : ''
-    const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
+    const marks = el['show-stops'] ? ':marks="marks"' : ''
 
-    return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
+    return `<a-slider ${min} ${max} ${step} ${vModel} ${range} ${marks} ${disabled}></a-slider>`
   },
-  'el-time-picker': el => {
+  'a-time-picker': el => {
     const {
       disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
-    const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
-    const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
-    const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
-    const isRange = el['is-range'] ? 'is-range' : ''
     const format = el.format ? `format="${el.format}"` : ''
     const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
-    const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
-
-    return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
+    
+    if (el['is-range']) {
+      return `<a-time-range-picker ${vModel} ${format} ${valueFormat} ${width} ${clearable} ${disabled}></a-time-range-picker>`
+    }
+    return `<a-time-picker ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${clearable} ${disabled}></a-time-picker>`
   },
-  'el-date-picker': el => {
+  'a-date-picker': el => {
     const {
       disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
-    const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
-    const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
-    const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
     const format = el.format ? `format="${el.format}"` : ''
     const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
-    const type = el.type === 'date' ? '' : `type="${el.type}"`
     const readonly = el.readonly ? 'readonly' : ''
-
-    return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
+    
+    // 根据type选择不同的组件
+    if (el.type === 'daterange' || el.type === 'datetimerange') {
+      return `<a-range-picker ${vModel} ${format} ${valueFormat} ${width} ${clearable} ${readonly} ${disabled}></a-range-picker>`
+    } else if (el.type === 'month') {
+      return `<a-month-picker ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${clearable} ${readonly} ${disabled}></a-month-picker>`
+    } else if (el.type === 'year') {
+      return `<a-date-picker ${vModel} picker="year" ${format} ${valueFormat} ${width} ${placeholder} ${clearable} ${readonly} ${disabled}></a-date-picker>`
+    } else if (el.type === 'datetime') {
+      return `<a-date-picker ${vModel} show-time ${format} ${valueFormat} ${width} ${placeholder} ${clearable} ${readonly} ${disabled}></a-date-picker>`
+    }
+    return `<a-date-picker ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${clearable} ${readonly} ${disabled}></a-date-picker>`
   },
   'el-rate': el => {
     const { disabled, vModel } = attrBuilder(el)
@@ -248,29 +244,27 @@ const tags = {
 
     return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
   },
-  'el-color-picker': el => {
+  'a-color-picker': el => {
     const { disabled, vModel } = attrBuilder(el)
     const size = `size="${el.size}"`
     const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
-    const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
 
-    return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
+    return `<a-color-picker ${vModel} ${size} ${showAlpha} ${disabled}></a-color-picker>`
   },
-  'el-upload': el => {
-    const disabled = el.disabled ? ':disabled=\'true\'' : ''
+  'a-upload': el => {
+    const disabled = el.disabled ? ':disabled="true"' : ''
     const action = el.action ? `:action="${el.vModel}Action"` : ''
     const multiple = el.multiple ? 'multiple' : ''
     const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
     const accept = el.accept ? `accept="${el.accept}"` : ''
     const name = el.name !== 'file' ? `name="${el.name}"` : ''
-    const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
     const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
-    const fileList = `:file-list="${el.vModel}fileList"`
+    const fileList = `v-model:file-list="${el.vModel}fileList"`
     const ref = `ref="${el.vModel}"`
     let child = buildElUploadChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
+    return `<a-upload ${ref} ${fileList} ${action} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</a-upload>`
   }
 }
 
@@ -308,7 +302,7 @@ function buildElInputChild(conf) {
 function buildElSelectChild(conf) {
   const children = []
   if (conf.options && conf.options.length) {
-    children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
+    children.push(`<a-select-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled">{{item.label}}</a-select-option>`)
   }
   return children.join('\n')
 }
@@ -316,9 +310,8 @@ function buildElSelectChild(conf) {
 function buildElRadioGroupChild(conf) {
   const children = []
   if (conf.options && conf.options.length) {
-    const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
-    const border = conf.border ? 'border' : ''
-    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+    const tag = conf.optionType === 'button' ? 'a-radio-button' : 'a-radio'
+    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled">{{item.label}}</${tag}>`)
   }
   return children.join('\n')
 }
@@ -326,18 +319,26 @@ function buildElRadioGroupChild(conf) {
 function buildElCheckboxGroupChild(conf) {
   const children = []
   if (conf.options && conf.options.length) {
-    const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
-    const border = conf.border ? 'border' : ''
-    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :value="item.label" :disabled="item.disabled" ${border} />`)
+    const tag = 'a-checkbox'
+    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled">{{item.label}}</${tag}>`)
   }
   return children.join('\n')
 }
 
 function buildElUploadChild(conf) {
   const list = []
-  if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
-  else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
-  if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
+  if (conf['list-type'] === 'picture-card') {
+    list.push(`<div v-if="${conf.vModel}fileList.length < 8">
+      <plus-outlined />
+      <div style="margin-top: 8px">上传</div>
+    </div>`)
+  } else {
+    list.push(`<a-button type="primary">
+      <template #icon><upload-outlined /></template>
+      ${conf.buttonText}
+    </a-button>`)
+  }
+  if (conf.showTip) list.push(`<div class="ant-upload__tip" style="color: #999; font-size: 12px; margin-top: 8px;">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
   return list.join('\n')
 }
 

+ 1 - 1
yushu-uivue3/src/utils/generator/js.js

@@ -77,7 +77,7 @@ function buildAttributes(
     buildProps(el, propsList)
   }
 
-  if (el.action && el.tag === 'el-upload') {
+  if (el.action && el.tag === 'a-upload') {
     uploadVarList.push(
       `
       // 上传请求路径

+ 280 - 68
yushu-uivue3/src/utils/generator/render.js

@@ -1,5 +1,11 @@
-import { defineComponent, h } from 'vue'
+import { defineComponent, h, resolveComponent } from 'vue'
 import { makeMap } from '@/utils/index'
+import { 
+  Input, InputNumber, Select, Radio, Checkbox, Switch, 
+  Slider, DatePicker, TimePicker, Rate, 
+  Upload, Cascader, Button, Textarea
+} from 'ant-design-vue'
+import { PlusOutlined, UploadOutlined } from '@ant-design/icons-vue'
 
 const isAttr = makeMap(
   'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' +
@@ -14,96 +20,137 @@ const isAttr = makeMap(
   'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' +
   'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' +
   'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' +
-  'target,title,type,usemap,value,width,wrap' + 'prefix-icon'
+  'target,title,type,usemap,value,width,wrap,prefix-icon,suffix-icon'
 )
 const isNotProps = makeMap(
-  'layout,prepend,regList,tag,document,changeTag,defaultValue'
+  'layout,prepend,append,regList,tag,document,changeTag,defaultValue,tagIcon'
 )
 
-function useVModel(props, emit) {
-  return {
-    modelValue: props.defaultValue,
-    'onUpdate:modelValue': (val) => emit('update:modelValue', val),
-  }
+// Ant Design Vue 组件映射
+const componentMap = {
+  'a-input': Input,
+  'a-input-number': InputNumber,
+  'a-select': Select,
+  'a-option': Select.Option,
+  'a-radio-group': Radio.Group,
+  'a-radio': Radio,
+  'a-radio-button': Radio.Button,
+  'a-checkbox-group': Checkbox.Group,
+  'a-checkbox': Checkbox,
+  'a-switch': Switch,
+  'a-slider': Slider,
+  'a-date-picker': DatePicker,
+  'a-time-picker': TimePicker,
+  'a-rate': Rate,
+  'a-color-picker': Input, // 暂时使用 Input 替代
+  'a-upload': Upload,
+  'a-cascader': Cascader,
+  'a-button': Button
 }
+
 const componentChild = {
-  'el-button': {
+  'a-button': {
     default(h, conf, key) {
       return conf[key]
     },
   },
-  'el-select': {
+  'a-select': {
     options(h, conf, key) {
-      return conf.options.map(item => h(resolveComponent('el-option'), {
+      return conf.options.map(item => h(Select.Option, {
         label: item.label,
         value: item.value,
-      }))
+      }, () => item.label))
     }
   },
-  'el-radio-group': {
+  'a-radio-group': {
     options(h, conf, key) {
-      return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), {
-        label: item.value,
-      }, () => item.label)) : conf.options.map(item => h(resolveComponent('el-radio'), {
-        label: item.value,
-        border: conf.border,
-      }, () => item.label))
+      return conf.optionType === 'button' 
+        ? conf.options.map(item => h(Radio.Button, {
+            value: item.value,
+          }, () => item.label)) 
+        : conf.options.map(item => h(Radio, {
+            value: item.value,
+          }, () => item.label))
     }
   },
-  'el-checkbox-group': {
+  'a-checkbox-group': {
     options(h, conf, key) {
-      return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), {
-        label: item.value,
-      }, () => item.label)) : conf.options.map(item => h(resolveComponent('el-checkbox'), {
-        label: item.value,
-        border: conf.border,
+      return conf.options.map(item => h(Checkbox, {
+        value: item.value,
       }, () => item.label))
     }
   },
-  'el-upload': {
+  'a-upload': {
     'list-type': (h, conf, key) => {
-      const option = {}
-      // if (conf.showTip) {
-      //   tip = h('div', {
-      //     class: "el-upload__tip"
-      //   }, () => '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件')
-      // }
       if (conf['list-type'] === 'picture-card') {
-        return h(resolveComponent('el-icon'), option, () => h(resolveComponent('Plus')))
+        return h('div', {}, [
+          h(PlusOutlined),
+          h('div', { style: { marginTop: '8px' } }, '上传')
+        ])
       } else {
-        // option.size = "small"
-        option.type = "primary"
-        option.icon = "Upload"
-        return h(resolveComponent('el-button'), option, () => conf.buttonText)
+        return h(Button, {
+          type: "primary",
+        }, {
+          default: () => conf.buttonText,
+          icon: () => h(UploadOutlined)
+        })
       }
     },
-
   }
 }
+
 const componentSlot = {
-  'el-upload': {
+  'a-upload': {
     'tip': (h, conf, key) => {
       if (conf.showTip) {
         return () => h('div', {
-          class: "el-upload__tip"
+          class: "ant-upload__tip",
+          style: { color: '#999', fontSize: '12px', marginTop: '8px' }
         }, '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件')
       }
     },
   }
 }
-export default defineComponent({
 
+export default defineComponent({
   // 使用 render 函数
   render() {
-    const dataObject = {
-      attrs: {},
-      props: {},
-      on: {},
-      style: {}
-    }
     const confClone = JSON.parse(JSON.stringify(this.conf))
     const children = []
     const slot = {}
+    
+    // 特殊处理颜色选择器
+    if (confClone.tag === 'a-color-picker') {
+      return h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
+        h('input', {
+          type: 'color',
+          value: this.$attrs.modelValue || '#409EFF',
+          style: { width: '40px', height: '32px', border: '1px solid #d9d9d9', borderRadius: '4px', cursor: 'pointer' },
+          onInput: (e) => this.$emit('update:modelValue', e.target.value)
+        }),
+        h(Input, {
+          value: this.$attrs.modelValue || '#409EFF',
+          placeholder: confClone.placeholder || '请输入颜色值',
+          style: { flex: 1 },
+          onInput: (e) => this.$emit('update:modelValue', e.target.value)
+        })
+      ])
+    }
+    
+    // 获取对应的 Ant Design Vue 组件
+    let component = componentMap[confClone.tag]
+    
+    // 特殊处理 textarea
+    if (confClone.tag === 'a-input' && confClone.type === 'textarea') {
+      component = Input.TextArea
+    }
+    
+    if (!component) {
+      console.warn(`Component ${confClone.tag} not found in componentMap`)
+      return h('div', {}, `组件 ${confClone.tag} 未找到`)
+    }
+
+    // 构建子组件
     const childObjs = componentChild[confClone.tag]
     if (childObjs) {
       Object.keys(childObjs).forEach(key => {
@@ -113,6 +160,8 @@ export default defineComponent({
         }
       })
     }
+
+    // 构建插槽
     const slotObjs = componentSlot[confClone.tag]
     if (slotObjs) {
       Object.keys(slotObjs).forEach(key => {
@@ -122,35 +171,198 @@ export default defineComponent({
         }
       })
     }
+
+    if (children.length > 0) {
+      slot.default = () => children
+    }
+
+    // 构建 props
+    const props = {
+      value: this.$attrs.modelValue,
+      'onUpdate:value': (val) => this.$emit('update:modelValue', val)
+    }
+
+    // 处理特殊属性映射
     Object.keys(confClone).forEach(key => {
+      if (isNotProps(key)) return
+      
       const val = confClone[key]
-      if (dataObject[key]) {
-        dataObject[key] = val
-      } else if (isAttr(key)) {
-        dataObject.attrs[key] = val
-      } else if (!isNotProps(key)) {
-        dataObject.props[key] = val
+      
+      // 属性映射
+      switch(key) {
+        case 'type':
+          // 处理 input 类型
+          if (confClone.tag === 'a-input') {
+            if (confClone['show-password']) {
+              props.type = 'password'
+            } else if (val === 'textarea') {
+              // textarea 在 Ant Design Vue 中使用 a-textarea 组件,这里特殊处理
+              props.type = val
+            } else {
+              props.type = val || 'text'
+            }
+          }
+          break
+        case 'show-password':
+          // 已在 type 中处理
+          break
+        case 'autosize':
+          if (confClone.tag === 'a-input' && confClone.type === 'textarea') {
+            props.autoSize = val
+          }
+          break
+        case 'prefix-icon':
+          if (val) props.prefix = val
+          break
+        case 'suffix-icon':
+          if (val) props.suffix = val
+          break
+        case 'show-word-limit':
+          props.showCount = val
+          break
+        case 'maxlength':
+          if (val) props.maxlength = val
+          break
+        case 'placeholder':
+          if (val) props.placeholder = val
+          break
+        case 'clearable':
+          if (val) props.allowClear = val
+          break
+        case 'disabled':
+          props.disabled = val
+          break
+        case 'readonly':
+          props.readonly = val
+          break
+        case 'multiple':
+          if (confClone.tag === 'a-select') {
+            props.mode = val ? 'multiple' : undefined
+          } else {
+            props.multiple = val
+          }
+          break
+        case 'filterable':
+          if (confClone.tag === 'a-select' || confClone.tag === 'a-cascader') {
+            props.showSearch = val
+          }
+          break
+        case 'controls-position':
+          props.controlsPosition = val
+          break
+        case 'step-strictly':
+          props.stepStrictly = val
+          break
+        case 'min':
+          if (val !== undefined) props.min = val
+          break
+        case 'max':
+          if (val !== undefined) props.max = val
+          break
+        case 'step':
+          if (val !== undefined) props.step = val
+          break
+        case 'precision':
+          if (val !== undefined) props.precision = val
+          break
+        case 'active-text':
+          props.checkedChildren = val
+          break
+        case 'inactive-text':
+          props.unCheckedChildren = val
+          break
+        case 'active-value':
+          props.checkedValue = val
+          break
+        case 'inactive-value':
+          props.unCheckedValue = val
+          break
+        case 'active-color':
+          props.checkedColor = val
+          break
+        case 'inactive-color':
+          props.unCheckedColor = val
+          break
+        case 'allow-half':
+          props.allowHalf = val
+          break
+        case 'show-text':
+          props.showText = val
+          break
+        case 'show-score':
+          props.showScore = val
+          break
+        case 'show-stops':
+          props.showStops = val
+          break
+        case 'show-all-levels':
+          props.showAllLevels = val
+          break
+        case 'range-separator':
+          props.separator = val
+          break
+        case 'start-placeholder':
+          props.placeholder = [val, confClone['end-placeholder'] || '']
+          break
+        case 'end-placeholder':
+          // 已在 start-placeholder 中处理
+          break
+        case 'picker-options':
+          // Ant Design Vue 使用不同的 API
+          break
+        case 'list-type':
+          props.listType = val
+          break
+        case 'auto-upload':
+          props.beforeUpload = val ? undefined : () => false
+          break
+        case 'accept':
+          if (val) props.accept = val
+          break
+        case 'name':
+          if (val) props.name = val
+          break
+        case 'action':
+          if (val) props.action = val
+          break
+        case 'color-format':
+          // 颜色格式暂时不处理,因为使用 Input 替代
+          break
+        case 'show-alpha':
+          // 暂时不处理
+          break
+        case 'size':
+          if (val) props.size = val
+          break
+        case 'border':
+          // Ant Design Vue 的 Radio/Checkbox 没有 border 属性
+          break
+        case 'optionType':
+          // 这是内部配置,不传递给组件
+          break
+        case 'options':
+          // options 通过子组件渲染,不直接传递
+          break
+        default:
+          // 其他未处理的属性,如果不在 isAttr 列表中,则直接传递
+          if (!isAttr(key) && val !== undefined && val !== null && val !== '') {
+            props[key] = val
+          }
       }
     })
-    if(children.length > 0){
-      slot.default = () => children
+
+    // 处理样式
+    if (confClone.style) {
+      props.style = confClone.style
     }
-    
-    return h(resolveComponent(this.conf.tag),
-      {
-        modelValue: this.$attrs.modelValue,
-        ...dataObject.props,
-        ...dataObject.attrs,
-        style: {
-          ...dataObject.style
-        },
-      }
-      , slot ?? null)
+
+    return h(component, props, slot.default ? slot : null)
   },
   props: {
     conf: {
       type: Object,
       required: true,
     },
-  }
-})
+  },
+  emits: ['update:modelValue']
+})

+ 23 - 23
yushu-uivue3/src/views/tool/build/CodeTypeDialog.vue

@@ -1,23 +1,18 @@
 <template>
-  <el-dialog v-model="open" width="500px" title="选择生成类型" @open="onOpen" @close="onClose">
-    <el-form ref="codeTypeForm" :model="formData" :rules="rules" label-width="100px">
-      <el-form-item label="生成类型" prop="type">
-        <el-radio-group v-model="formData.type">
-          <el-radio-button v-for="(item, index) in typeOptions" :key="index" :label="item.value">
+  <a-modal v-model:open="open" title="选择生成类型" width="500px" @ok="handelConfirm" @cancel="onClose">
+    <a-form ref="codeTypeFormRef" :model="formData" :rules="rules" :label-col="{ style: { width: '100px' } }">
+      <a-form-item label="生成类型" name="type">
+        <a-radio-group v-model:value="formData.type">
+          <a-radio-button v-for="(item, index) in typeOptions" :key="index" :value="item.value">
             {{ item.label }}
-          </el-radio-button>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item v-if="showFileName" label="文件名" prop="fileName">
-        <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
-      </el-form-item>
-    </el-form>
-
-    <template #footer>
-      <el-button @click="onClose">取消</el-button>
-      <el-button type="primary" @click="handelConfirm">确定</el-button>
-    </template>
-  </el-dialog>
+          </a-radio-button>
+        </a-radio-group>
+      </a-form-item>
+      <a-form-item v-if="showFileName" label="文件名" name="fileName">
+        <a-input v-model:value="formData.fileName" placeholder="请输入文件名" allow-clear />
+      </a-form-item>
+    </a-form>
+  </a-modal>
 </template>
 
 <script setup>
@@ -30,7 +25,7 @@ const formData = ref({
   fileName: undefined,
   type: 'file'
 })
-const codeTypeForm = ref()
+const codeTypeFormRef = ref()
 const rules = {
   fileName: [{
     required: true,
@@ -62,10 +57,15 @@ function onClose() {
   open.value = false
 }
 function handelConfirm() {
-  codeTypeForm.value.validate(valid => {
-    if (!valid) return
+  codeTypeFormRef.value.validateFields().then(() => {
     emit('confirm', { ...formData.value })
     onClose()
-  })
+  }).catch(() => {})
 }
-</script>
+
+watch(open, (val) => {
+  if (val) {
+    onOpen()
+  }
+})
+</script>

+ 15 - 10
yushu-uivue3/src/views/tool/build/DraggableItem.vue

@@ -1,10 +1,14 @@
 <template>
-  <el-col :span="element.span" :class="className" @click.stop="activeItem(element)">
-    <el-form-item :label="element.label" :label-width="element.labelWidth ? element.labelWidth + 'px' : null"
-      :required="element.required" v-if="element.layout === 'colFormItem'">
+  <a-col :span="element.span" :class="className" @click.stop="activeItem(element)">
+    <a-form-item 
+      :label="element.label" 
+      :label-col="{ style: { width: element.labelWidth ? element.labelWidth + 'px' : undefined } }"
+      :required="element.required" 
+      v-if="element.layout === 'colFormItem'"
+    >
       <render :key="element.tag" :conf="element" v-model="element.defaultValue" />
-    </el-form-item>
-    <el-row :gutter="element.gutter" :class="element.class" @click.stop="activeItem(element)" v-else>
+    </a-form-item>
+    <a-row :gutter="element.gutter" :class="element.class" @click.stop="activeItem(element)" v-else>
       <span class="component-name"> {{ element.componentName }} </span>
       <draggable group="componentsGroup" :animation="340" :list="element.children" class="drag-wrapper" item-key="label"
         ref="draggableItemRef" :component-data="getComponentData()">
@@ -15,18 +19,19 @@
             @deleteItem="deleteItem(scoped.index, element.children)" />
         </template>
       </draggable>
-    </el-row>
+    </a-row>
     <span class="drawing-item-copy" title="复制" @click.stop="copyItem(element)">
-      <el-icon><CopyDocument /></el-icon>
+      <CopyOutlined />
     </span>
     <span class="drawing-item-delete" title="删除" @click.stop="deleteItem(index)">
-      <el-icon><Delete /></el-icon>
+      <DeleteOutlined />
     </span>
-  </el-col>
+  </a-col>
 </template>
 <script setup name="DraggableItem">
 import draggable from "vuedraggable/dist/vuedraggable.common"
 import render from '@/utils/generator/render'
+import { CopyOutlined, DeleteOutlined } from '@ant-design/icons-vue'
 
 const props = defineProps({
   element: Object,
@@ -65,4 +70,4 @@ watch(() => props.activeId, (val) => {
     className.value += ' unfocus-bordered'
   }
 }, { immediate: true })
-</script>
+</script>

+ 405 - 450
yushu-uivue3/src/views/tool/build/RightPanel.vue

@@ -1,461 +1,446 @@
 <template>
   <div class="right-board">
-    <el-tabs v-model="currentTab" stretch class="center-tabs">
-      <el-tab-pane label="组件属性" name="field" />
-      <el-tab-pane label="表单属性" name="form" />
-    </el-tabs>
+    <a-tabs v-model:activeKey="currentTab" class="center-tabs">
+      <a-tab-pane key="field" tab="组件属性" />
+      <a-tab-pane key="form" tab="表单属性" />
+    </a-tabs>
     <div class="field-box">
       <a class="document-link" target="_blank" :href="documentLink" title="查看组件文档">
-        <el-icon>
-          <Link />
-        </el-icon>
+        <LinkOutlined />
       </a>
-      <el-scrollbar class="right-scrollbar">
+      <div class="right-scrollbar" style="overflow: auto; height: 100%;">
         <!-- 组件属性 -->
-        <el-form v-show="currentTab === 'field' && showField" size="default" label-width="90px" label-position="top"
-          style="">
-          <el-form-item v-if="activeData.changeTag" label="组件类型">
-            <el-select v-model="activeData.tagIcon" placeholder="请选择组件类型" :style="{ width: '100%' }" @change="tagChange">
-              <el-option-group v-for="group in tagList" :key="group.label" :label="group.label">
-                <el-option v-for="item in group.options" :key="item.label" :label="item.label" :value="item.tagIcon">
+        <a-form v-show="currentTab === 'field' && showField" :label-col="{ style: { width: '90px' } }" layout="vertical" size="default">
+          <a-form-item v-if="activeData.changeTag" label="组件类型">
+            <a-select v-model:value="activeData.tagIcon" placeholder="请选择组件类型" style="width: 100%" @change="tagChange">
+              <a-select-opt-group v-for="group in tagList" :key="group.label" :label="group.label">
+                <a-select-option v-for="item in group.options" :key="item.label" :value="item.tagIcon">
                   <svg-icon class="node-icon" :icon-class="item.tagIcon" style="margin-right: 10px;" />
                   <span> {{ item.label }}</span>
-                </el-option>
-              </el-option-group>
-            </el-select>
-          </el-form-item>
-          <el-form-item v-if="activeData.vModel !== undefined" label="字段名">
-            <el-input v-model="activeData.vModel" placeholder="请输入字段名(v-model)" />
-          </el-form-item>
-          <el-form-item v-if="activeData.componentName !== undefined" label="组件名">
+                </a-select-option>
+              </a-select-opt-group>
+            </a-select>
+          </a-form-item>
+          <a-form-item v-if="activeData.vModel !== undefined" label="字段名">
+            <a-input v-model:value="activeData.vModel" placeholder="请输入字段名(v-model)" />
+          </a-form-item>
+          <a-form-item v-if="activeData.componentName !== undefined" label="组件名">
             {{ activeData.componentName }}
-          </el-form-item>
-          <el-form-item v-if="activeData.label !== undefined" label="标题">
-            <el-input v-model="activeData.label" placeholder="请输入标题" />
-          </el-form-item>
-          <el-form-item v-if="activeData.placeholder !== undefined" label="占位提示">
-            <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" />
-          </el-form-item>
-          <el-form-item v-if="activeData['start-placeholder'] !== undefined" label="开始占位">
-            <el-input v-model="activeData['start-placeholder']" placeholder="请输入占位提示" />
-          </el-form-item>
-          <el-form-item v-if="activeData['end-placeholder'] !== undefined" label="结束占位">
-            <el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" />
-          </el-form-item>
-          <el-form-item v-if="activeData.span !== undefined" label="表单栅格">
-            <el-slider v-model="activeData.span" :max="24" :min="1" :marks="{ 12: '' }" @change="spanChange" />
-          </el-form-item>
-          <el-form-item v-if="activeData.layout === 'rowFormItem'" label="栅格间隔">
-            <el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" />
-          </el-form-item>
-
-          <el-form-item v-if="activeData.justify !== undefined" label="水平排列">
-            <el-select v-model="activeData.justify" placeholder="请选择水平排列" :style="{ width: '100%' }">
-              <el-option v-for="(item, index) in justifyOptions" :key="index" :label="item.label" :value="item.value" />
-            </el-select>
-          </el-form-item>
-          <el-form-item v-if="activeData.align !== undefined" label="垂直排列">
-            <el-radio-group v-model="activeData.align">
-              <el-radio-button label="top" />
-              <el-radio-button label="middle" />
-              <el-radio-button label="bottom" />
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item v-if="activeData.labelWidth !== undefined" label="标签宽度">
-            <el-input v-model.number="activeData.labelWidth" type="number" placeholder="请输入标签宽度" />
-          </el-form-item>
-          <el-form-item v-if="activeData.style && activeData.style.width !== undefined" label="组件宽度">
-            <el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable />
-          </el-form-item>
-          <el-form-item v-if="activeData.vModel !== undefined" label="默认值">
-            <el-input :value="setDefaultValue(activeData.defaultValue)" placeholder="请输入默认值"
+          </a-form-item>
+          <a-form-item v-if="activeData.label !== undefined" label="标题">
+            <a-input v-model:value="activeData.label" placeholder="请输入标题" />
+          </a-form-item>
+          <a-form-item v-if="activeData.placeholder !== undefined" label="占位提示">
+            <a-input v-model:value="activeData.placeholder" placeholder="请输入占位提示" />
+          </a-form-item>
+          <a-form-item v-if="activeData['start-placeholder'] !== undefined" label="开始占位">
+            <a-input v-model:value="activeData['start-placeholder']" placeholder="请输入占位提示" />
+          </a-form-item>
+          <a-form-item v-if="activeData['end-placeholder'] !== undefined" label="结束占位">
+            <a-input v-model:value="activeData['end-placeholder']" placeholder="请输入占位提示" />
+          </a-form-item>
+          <a-form-item v-if="activeData.span !== undefined" label="表单栅格">
+            <a-slider v-model:value="activeData.span" :max="24" :min="1" :marks="{ 12: '' }" @change="spanChange" />
+          </a-form-item>
+          <a-form-item v-if="activeData.layout === 'rowFormItem'" label="栅格间隔">
+            <a-input-number v-model:value="activeData.gutter" :min="0" placeholder="栅格间隔" />
+          </a-form-item>
+
+          <a-form-item v-if="activeData.justify !== undefined" label="水平排列">
+            <a-select v-model:value="activeData.justify" placeholder="请选择水平排列" style="width: 100%">
+              <a-select-option v-for="(item, index) in justifyOptions" :key="index" :value="item.value">{{ item.label }}</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item v-if="activeData.align !== undefined" label="垂直排列">
+            <a-radio-group v-model:value="activeData.align">
+              <a-radio-button value="top">top</a-radio-button>
+              <a-radio-button value="middle">middle</a-radio-button>
+              <a-radio-button value="bottom">bottom</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="activeData.labelWidth !== undefined" label="标签宽度">
+            <a-input-number v-model:value="activeData.labelWidth" placeholder="请输入标签宽度" />
+          </a-form-item>
+          <a-form-item v-if="activeData.style && activeData.style.width !== undefined" label="组件宽度">
+            <a-input v-model:value="activeData.style.width" placeholder="请输入组件宽度" allow-clear />
+          </a-form-item>
+          <a-form-item v-if="activeData.vModel !== undefined" label="默认值">
+            <a-input :value="setDefaultValue(activeData.defaultValue)" placeholder="请输入默认值"
               @input="onDefaultValueInput" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-checkbox-group'" label="至少应选">
-            <el-input-number :value="activeData.min" :min="0" placeholder="至少应选"
-              @input="$set(activeData, 'min', $event ? $event : undefined)" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-checkbox-group'" label="最多可选">
-            <el-input-number :value="activeData.max" :min="0" placeholder="最多可选"
-              @input="$set(activeData, 'max', $event ? $event : undefined)" />
-          </el-form-item>
-          <el-form-item v-if="activeData.prepend !== undefined" label="前缀">
-            <el-input v-model="activeData.prepend" placeholder="请输入前缀" />
-          </el-form-item>
-          <el-form-item v-if="activeData.append !== undefined" label="后缀">
-            <el-input v-model="activeData.append" placeholder="请输入后缀" />
-          </el-form-item>
-          <el-form-item v-if="activeData['prefix-icon'] !== undefined" label="前图标">
-            <el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称">
-              <template #append>
-                <el-button icon="Pointer" @click="openIconsDialog('prefix-icon')">
-                  选择
-                </el-button>
-              </template>
-            </el-input>
-          </el-form-item>
-          <el-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标">
-            <el-input v-model="activeData['suffix-icon']" placeholder="请输入后图标名称">
-              <template #append>
-                <el-button icon="Pointer" @click="openIconsDialog('suffix-icon')">
-                  选择
-                </el-button>
-              </template>
-            </el-input>
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-cascader'" label="选项分隔符">
-            <el-input v-model="activeData.separator" placeholder="请输入选项分隔符" />
-          </el-form-item>
-          <el-form-item v-if="activeData.autosize !== undefined" label="最小行数">
-            <el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="最小行数" />
-          </el-form-item>
-          <el-form-item v-if="activeData.autosize !== undefined" label="最大行数">
-            <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
-          </el-form-item>
-          <el-form-item v-if="activeData.min !== undefined" label="最小值">
-            <el-input-number v-model="activeData.min" placeholder="最小值" />
-          </el-form-item>
-          <el-form-item v-if="activeData.max !== undefined" label="最大值">
-            <el-input-number v-model="activeData.max" placeholder="最大值" />
-          </el-form-item>
-          <el-form-item v-if="activeData.step !== undefined" label="步长">
-            <el-input-number v-model="activeData.step" placeholder="步数" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-input-number'" label="精度">
-            <el-input-number v-model="activeData.precision" :min="0" placeholder="精度" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-input-number'" label="按钮位置">
-            <el-radio-group v-model="activeData['controls-position']">
-              <el-radio-button label="">
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-checkbox-group'" label="至少应选">
+            <a-input-number :value="activeData.min" :min="0" placeholder="至少应选"
+              @update:value="(val) => { activeData.min = val ? val : undefined }" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-checkbox-group'" label="最多可选">
+            <a-input-number :value="activeData.max" :min="0" placeholder="最多可选"
+              @update:value="(val) => { activeData.max = val ? val : undefined }" />
+          </a-form-item>
+          <a-form-item v-if="activeData.prepend !== undefined" label="前缀">
+            <a-input v-model:value="activeData.prepend" placeholder="请输入前缀" />
+          </a-form-item>
+          <a-form-item v-if="activeData.append !== undefined" label="后缀">
+            <a-input v-model:value="activeData.append" placeholder="请输入后缀" />
+          </a-form-item>
+          <a-form-item v-if="activeData['prefix-icon'] !== undefined" label="前图标">
+            <a-input-group compact>
+              <a-input v-model:value="activeData['prefix-icon']" placeholder="请输入前图标名称" style="width: calc(100% - 80px)" />
+              <a-button @click="openIconsDialog('prefix-icon')">
+                选择
+              </a-button>
+            </a-input-group>
+          </a-form-item>
+          <a-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标">
+            <a-input-group compact>
+              <a-input v-model:value="activeData['suffix-icon']" placeholder="请输入后图标名称" style="width: calc(100% - 80px)" />
+              <a-button @click="openIconsDialog('suffix-icon')">
+                选择
+              </a-button>
+            </a-input-group>
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-cascader'" label="选项分隔符">
+            <a-input v-model:value="activeData.separator" placeholder="请输入选项分隔符" />
+          </a-form-item>
+          <a-form-item v-if="activeData.autosize !== undefined" label="最小行数">
+            <a-input-number v-model:value="activeData.autosize.minRows" :min="1" placeholder="最小行数" />
+          </a-form-item>
+          <a-form-item v-if="activeData.autosize !== undefined" label="最大行数">
+            <a-input-number v-model:value="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
+          </a-form-item>
+          <a-form-item v-if="activeData.min !== undefined" label="最小值">
+            <a-input-number v-model:value="activeData.min" placeholder="最小值" />
+          </a-form-item>
+          <a-form-item v-if="activeData.max !== undefined" label="最大值">
+            <a-input-number v-model:value="activeData.max" placeholder="最大值" />
+          </a-form-item>
+          <a-form-item v-if="activeData.step !== undefined" label="步长">
+            <a-input-number v-model:value="activeData.step" placeholder="步数" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-input-number'" label="精度">
+            <a-input-number v-model:value="activeData.precision" :min="0" placeholder="精度" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-input-number'" label="按钮位置">
+            <a-radio-group v-model:value="activeData['controls-position']">
+              <a-radio-button value="">
                 默认
-              </el-radio-button>
-              <el-radio-button label="right">
+              </a-radio-button>
+              <a-radio-button value="right">
                 右侧
-              </el-radio-button>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item v-if="activeData.maxlength !== undefined" label="最多输入">
-            <el-input v-model="activeData.maxlength" placeholder="请输入字符长度">
-              <template slot="append">
-                个字符
-              </template>
-            </el-input>
-          </el-form-item>
-          <el-form-item v-if="activeData['active-text'] !== undefined" label="开启提示">
-            <el-input v-model="activeData['active-text']" placeholder="请输入开启提示" />
-          </el-form-item>
-          <el-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示">
-            <el-input v-model="activeData['inactive-text']" placeholder="请输入关闭提示" />
-          </el-form-item>
-          <el-form-item v-if="activeData['active-value'] !== undefined" label="开启值">
-            <el-input :value="setDefaultValue(activeData['active-value'])" placeholder="请输入开启值"
+              </a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="activeData.maxlength !== undefined" label="最多输入">
+            <a-input-group compact>
+              <a-input v-model:value="activeData.maxlength" placeholder="请输入字符长度" style="width: calc(100% - 60px)" />
+              <a-button disabled>个字符</a-button>
+            </a-input-group>
+          </a-form-item>
+          <a-form-item v-if="activeData['active-text'] !== undefined" label="开启提示">
+            <a-input v-model:value="activeData['active-text']" placeholder="请输入开启提示" />
+          </a-form-item>
+          <a-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示">
+            <a-input v-model:value="activeData['inactive-text']" placeholder="请输入关闭提示" />
+          </a-form-item>
+          <a-form-item v-if="activeData['active-value'] !== undefined" label="开启值">
+            <a-input :value="setDefaultValue(activeData['active-value'])" placeholder="请输入开启值"
               @input="onSwitchValueInput($event, 'active-value')" />
-          </el-form-item>
-          <el-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值">
-            <el-input :value="setDefaultValue(activeData['inactive-value'])" placeholder="请输入关闭值"
+          </a-form-item>
+          <a-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值">
+            <a-input :value="setDefaultValue(activeData['inactive-value'])" placeholder="请输入关闭值"
               @input="onSwitchValueInput($event, 'inactive-value')" />
-          </el-form-item>
-          <el-form-item v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag" label="时间类型">
-            <el-select v-model="activeData.type" placeholder="请选择时间类型" :style="{ width: '100%' }"
+          </a-form-item>
+          <a-form-item v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag" label="时间类型">
+            <a-select v-model:value="activeData.type" placeholder="请选择时间类型" style="width: 100%"
               @change="dateTypeChange">
-              <el-option v-for="(item, index) in dateOptions" :key="index" :label="item.label" :value="item.value" />
-            </el-select>
-          </el-form-item>
-          <el-form-item v-if="activeData.name !== undefined" label="文件字段名">
-            <el-input v-model="activeData.name" placeholder="请输入上传文件字段名" />
-          </el-form-item>
-          <el-form-item v-if="activeData.accept !== undefined" label="文件类型">
-            <el-select v-model="activeData.accept" placeholder="请选择文件类型" :style="{ width: '100%' }" clearable>
-              <el-option label="图片" value="image/*" />
-              <el-option label="视频" value="video/*" />
-              <el-option label="音频" value="audio/*" />
-              <el-option label="excel" value=".xls,.xlsx" />
-              <el-option label="word" value=".doc,.docx" />
-              <el-option label="pdf" value=".pdf" />
-              <el-option label="txt" value=".txt" />
-            </el-select>
-          </el-form-item>
-          <el-form-item v-if="activeData.fileSize !== undefined" label="文件大小">
-            <el-input v-model.number="activeData.fileSize" placeholder="请输入文件大小">
-              <el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }">
-                <el-option label="KB" value="KB" />
-                <el-option label="MB" value="MB" />
-                <el-option label="GB" value="GB" />
-              </el-select>
-            </el-input>
-          </el-form-item>
-          <el-form-item v-if="activeData.action !== undefined" label="上传地址">
-            <el-input v-model="activeData.action" placeholder="请输入上传地址" clearable />
-          </el-form-item>
-          <el-form-item v-if="activeData['list-type'] !== undefined" label="列表类型">
-            <el-radio-group v-model="activeData['list-type']" size="small">
-              <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 v-if="activeData.buttonText !== undefined" v-show="'picture-card' !== activeData['list-type']"
+              <a-select-option v-for="(item, index) in dateOptions" :key="index" :value="item.value">{{ item.label }}</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item v-if="activeData.name !== undefined" label="文件字段名">
+            <a-input v-model:value="activeData.name" placeholder="请输入上传文件字段名" />
+          </a-form-item>
+          <a-form-item v-if="activeData.accept !== undefined" label="文件类型">
+            <a-select v-model:value="activeData.accept" placeholder="请选择文件类型" style="width: 100%" allow-clear>
+              <a-select-option value="image/*">图片</a-select-option>
+              <a-select-option value="video/*">视频</a-select-option>
+              <a-select-option value="audio/*">音频</a-select-option>
+              <a-select-option value=".xls,.xlsx">excel</a-select-option>
+              <a-select-option value=".doc,.docx">word</a-select-option>
+              <a-select-option value=".pdf">pdf</a-select-option>
+              <a-select-option value=".txt">txt</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item v-if="activeData.fileSize !== undefined" label="文件大小">
+            <a-input-group compact>
+              <a-input-number v-model:value="activeData.fileSize" placeholder="请输入文件大小" style="width: calc(100% - 66px)" />
+              <a-select v-model:value="activeData.sizeUnit" style="width: 66px">
+                <a-select-option value="KB">KB</a-select-option>
+                <a-select-option value="MB">MB</a-select-option>
+                <a-select-option value="GB">GB</a-select-option>
+              </a-select>
+            </a-input-group>
+          </a-form-item>
+          <a-form-item v-if="activeData.action !== undefined" label="上传地址">
+            <a-input v-model:value="activeData.action" placeholder="请输入上传地址" allow-clear />
+          </a-form-item>
+          <a-form-item v-if="activeData['list-type'] !== undefined" label="列表类型">
+            <a-radio-group v-model:value="activeData['list-type']" size="small">
+              <a-radio-button value="text">text</a-radio-button>
+              <a-radio-button value="picture">picture</a-radio-button>
+              <a-radio-button value="picture-card">picture-card</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="activeData.buttonText !== undefined" v-show="'picture-card' !== activeData['list-type']"
             label="按钮文字">
-            <el-input v-model="activeData.buttonText" placeholder="请输入按钮文字" />
-          </el-form-item>
-          <el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
-            <el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" />
-          </el-form-item>
-          <el-form-item v-if="activeData['picker-options'] !== undefined" label="时间段">
-            <el-input v-model="activeData['picker-options'].selectableRange" placeholder="请输入时间段" />
-          </el-form-item>
-          <el-form-item v-if="activeData.format !== undefined" label="时间格式">
-            <el-input :value="activeData.format" placeholder="请输入时间格式" @input="setTimeValue($event)" />
-          </el-form-item>
+            <a-input v-model:value="activeData.buttonText" placeholder="请输入按钮文字" />
+          </a-form-item>
+          <a-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
+            <a-input v-model:value="activeData['range-separator']" placeholder="请输入分隔符" />
+          </a-form-item>
+          <a-form-item v-if="activeData['picker-options'] !== undefined" label="时间段">
+            <a-input v-model:value="activeData['picker-options'].selectableRange" placeholder="请输入时间段" />
+          </a-form-item>
+          <a-form-item v-if="activeData.format !== undefined" label="时间格式">
+            <a-input :value="activeData.format" placeholder="请输入时间格式" @input="setTimeValue($event)" />
+          </a-form-item>
           <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1">
-            <el-divider>选项</el-divider>
+            <a-divider>选项</a-divider>
             <draggable :list="activeData.options" :animation="340" group="selectItem" handle=".option-drag"
               item-key="label">
               <template #item="{ element, index }">
                 <div :key="index" class="select-item">
                   <div class="select-line-icon option-drag">
-                    <i class="el-icon-s-operation" />
+                    <MenuOutlined />
                   </div>
-                  <el-input v-model="element.label" placeholder="选项名" size="small" />
-                  <el-input placeholder="选项值" size="small" :value="element.value"
+                  <a-input v-model:value="element.label" placeholder="选项名" size="small" />
+                  <a-input placeholder="选项值" size="small" :value="element.value"
                     @input="setOptionValue(element, $event)" />
                   <div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)">
-                    <el-icon>
-                      <Remove />
-                    </el-icon>
+                    <CloseOutlined />
                   </div>
                 </div>
               </template>
             </draggable>
             <div>
-              <el-button icon="CirclePlus" style="margin-left: 8px; margin-top: 10px;" text bg type="primary"
+              <a-button type="link" style="margin-left: 8px; margin-top: 10px;"
                 @click="addSelectItem">
+                <template #icon><PlusOutlined /></template>
                 添加选项
-              </el-button>
+              </a-button>
             </div>
-            <el-divider />
+            <a-divider />
           </template>
 
           <template v-if="['el-cascader'].indexOf(activeData.tag) > -1">
-            <el-divider>选项</el-divider>
-            <el-form-item label="数据类型">
-              <el-radio-group v-model="activeData.dataType" size="small">
-                <el-radio-button label="dynamic">
-                  动态数据
-                </el-radio-button>
-                <el-radio-button label="static">
-                  静态数据
-                </el-radio-button>
-              </el-radio-group>
-            </el-form-item>
+            <a-divider>选项</a-divider>
+            <a-form-item label="数据类型">
+              <a-radio-group v-model:value="activeData.dataType" size="small">
+                <a-radio-button value="dynamic">动态数据</a-radio-button>
+                <a-radio-button value="static">静态数据</a-radio-button>
+              </a-radio-group>
+            </a-form-item>
 
             <template v-if="activeData.dataType === 'dynamic'">
-              <el-form-item label="标签键名">
-                <el-input v-model="activeData.labelKey" placeholder="请输入标签键名" />
-              </el-form-item>
-              <el-form-item label="值键名">
-                <el-input v-model="activeData.valueKey" placeholder="请输入值键名" />
-              </el-form-item>
-              <el-form-item label="子级键名">
-                <el-input v-model="activeData.childrenKey" placeholder="请输入子级键名" />
-              </el-form-item>
+              <a-form-item label="标签键名">
+                <a-input v-model:value="activeData.labelKey" placeholder="请输入标签键名" />
+              </a-form-item>
+              <a-form-item label="值键名">
+                <a-input v-model:value="activeData.valueKey" placeholder="请输入值键名" />
+              </a-form-item>
+              <a-form-item label="子级键名">
+                <a-input v-model:value="activeData.childrenKey" placeholder="请输入子级键名" />
+              </a-form-item>
             </template>
 
-            <el-tree v-if="activeData.dataType === 'static'" draggable :data="activeData.options" node-key="id"
-              :expand-on-click-node="false" :render-content="renderContent" />
+            <a-tree v-if="activeData.dataType === 'static'" draggable :tree-data="activeData.options" :field-names="{ key: 'id', title: 'label', children: 'children' }"
+              :default-expand-all="true">
+              <template #title="{ data }">
+                <span>{{ data.label }}</span>
+                <span class="node-operation">
+                  <a-button type="link" size="small" @click="append(data)">
+                    <template #icon><PlusOutlined /></template>
+                  </a-button>
+                  <a-button type="link" danger size="small" style="margin-left: 5px;" @click="removeFromTree(data)">
+                    <template #icon><DeleteOutlined /></template>
+                  </a-button>
+                </span>
+              </template>
+            </a-tree>
             <div v-if="activeData.dataType === 'static'">
-              <el-button icon="CirclePlus" style="margin-left: 0; margin-top: 10px;" type="primary" text bg
+              <a-button type="link" style="margin-left: 0; margin-top: 10px;"
                 @click="addTreeItem">
+                <template #icon><PlusOutlined /></template>
                 添加父级
-              </el-button>
+              </a-button>
             </div>
-            <el-divider />
+            <a-divider />
           </template>
 
-          <el-form-item v-if="activeData.optionType !== undefined" label="选项样式">
-            <el-radio-group v-model="activeData.optionType">
-              <el-radio-button label="default">
-                默认
-              </el-radio-button>
-              <el-radio-button label="button">
-                按钮
-              </el-radio-button>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色">
-            <el-color-picker v-model="activeData['active-color']" />
-          </el-form-item>
-          <el-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色">
-            <el-color-picker v-model="activeData['inactive-color']" />
-          </el-form-item>
-
-          <el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
-            <el-switch v-model="activeData['allow-half']" />
-          </el-form-item>
-          <el-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字">
-            <el-switch v-model="activeData['show-text']" @change="rateTextChange" />
-          </el-form-item>
-          <el-form-item v-if="activeData['show-score'] !== undefined" label="显示分数">
-            <el-switch v-model="activeData['show-score']" @change="rateScoreChange" />
-          </el-form-item>
-          <el-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点">
-            <el-switch v-model="activeData['show-stops']" />
-          </el-form-item>
-          <el-form-item v-if="activeData.range !== undefined" label="范围选择">
-            <el-switch v-model="activeData.range" @change="rangeChange" />
-          </el-form-item>
-          <el-form-item v-if="activeData.border !== undefined && activeData.optionType === 'default'" label="是否带边框">
-            <el-switch v-model="activeData.border" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-color-picker'" label="颜色格式">
-            <el-select v-model="activeData['color-format']" placeholder="请选择颜色格式" :style="{ width: '100%' }"
+          <a-form-item v-if="activeData.optionType !== undefined" label="选项样式">
+            <a-radio-group v-model:value="activeData.optionType">
+              <a-radio-button value="default">默认</a-radio-button>
+              <a-radio-button value="button">按钮</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色">
+            <a-color-picker v-model:value="activeData['active-color']" />
+          </a-form-item>
+          <a-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色">
+            <a-color-picker v-model:value="activeData['inactive-color']" />
+          </a-form-item>
+
+          <a-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
+            <a-switch v-model:checked="activeData['allow-half']" />
+          </a-form-item>
+          <a-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字">
+            <a-switch v-model:checked="activeData['show-text']" @change="rateTextChange" />
+          </a-form-item>
+          <a-form-item v-if="activeData['show-score'] !== undefined" label="显示分数">
+            <a-switch v-model:checked="activeData['show-score']" @change="rateScoreChange" />
+          </a-form-item>
+          <a-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点">
+            <a-switch v-model:checked="activeData['show-stops']" />
+          </a-form-item>
+          <a-form-item v-if="activeData.range !== undefined" label="范围选择">
+            <a-switch v-model:checked="activeData.range" @change="rangeChange" />
+          </a-form-item>
+          <a-form-item v-if="activeData.border !== undefined && activeData.optionType === 'default'" label="是否带边框">
+            <a-switch v-model:checked="activeData.border" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-color-picker'" label="颜色格式">
+            <a-select v-model:value="activeData['color-format']" placeholder="请选择颜色格式" style="width: 100%"
               @change="colorFormatChange">
-              <el-option v-for="(item, index) in colorFormatOptions" :key="index" :label="item.label"
-                :value="item.value" />
-            </el-select>
-          </el-form-item>
-          <el-form-item v-if="activeData.size !== undefined &&
+              <a-select-option v-for="(item, index) in colorFormatOptions" :key="index" :value="item.value">{{ item.label }}</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item v-if="activeData.size !== undefined &&
             (activeData.optionType === 'button' ||
               activeData.border ||
-              activeData.tag === 'el-color-picker')" label="选项尺寸">
-            <el-radio-group v-model="activeData.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 v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
-            <el-switch v-model="activeData['show-word-limit']" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-input-number'" label="严格步数">
-            <el-switch v-model="activeData['step-strictly']" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-cascader'" label="是否多选">
-            <el-switch v-model="activeData.props.props.multiple" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-cascader'" label="展示全路径">
-            <el-switch v-model="activeData['show-all-levels']" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-cascader'" label="可否筛选">
-            <el-switch v-model="activeData.filterable" />
-          </el-form-item>
-          <el-form-item v-if="activeData.clearable !== undefined" label="能否清空">
-            <el-switch v-model="activeData.clearable" />
-          </el-form-item>
-          <el-form-item v-if="activeData.showTip !== undefined" label="显示提示">
-            <el-switch v-model="activeData.showTip" />
-          </el-form-item>
-          <el-form-item v-if="activeData.multiple !== undefined" label="多选文件">
-            <el-switch v-model="activeData.multiple" />
-          </el-form-item>
-          <el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
-            <el-switch v-model="activeData['auto-upload']" />
-          </el-form-item>
-          <el-form-item v-if="activeData.readonly !== undefined" label="是否只读">
-            <el-switch v-model="activeData.readonly" />
-          </el-form-item>
-          <el-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
-            <el-switch v-model="activeData.disabled" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-select'" label="是否可搜索">
-            <el-switch v-model="activeData.filterable" />
-          </el-form-item>
-          <el-form-item v-if="activeData.tag === 'el-select'" label="是否多选">
-            <el-switch v-model="activeData.multiple" @change="multipleChange" />
-          </el-form-item>
-          <el-form-item v-if="activeData.required !== undefined" label="是否必填">
-            <el-switch v-model="activeData.required" />
-          </el-form-item>
+              activeData.tag === 'a-color-picker')" label="选项尺寸">
+            <a-radio-group v-model:value="activeData.size">
+              <a-radio-button value="large">较大</a-radio-button>
+              <a-radio-button value="default">默认</a-radio-button>
+              <a-radio-button value="small">较小</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
+            <a-switch v-model:checked="activeData['show-word-limit']" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-input-number'" label="严格步数">
+            <a-switch v-model:checked="activeData['step-strictly']" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-cascader'" label="是否多选">
+            <a-switch v-model:checked="activeData.props.props.multiple" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-cascader'" label="展示全路径">
+            <a-switch v-model:checked="activeData['show-all-levels']" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-cascader'" label="可否筛选">
+            <a-switch v-model:checked="activeData.filterable" />
+          </a-form-item>
+          <a-form-item v-if="activeData.clearable !== undefined" label="能否清空">
+            <a-switch v-model:checked="activeData.clearable" />
+          </a-form-item>
+          <a-form-item v-if="activeData.showTip !== undefined" label="显示提示">
+            <a-switch v-model:checked="activeData.showTip" />
+          </a-form-item>
+          <a-form-item v-if="activeData.multiple !== undefined" label="多选文件">
+            <a-switch v-model:checked="activeData.multiple" />
+          </a-form-item>
+          <a-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
+            <a-switch v-model:checked="activeData['auto-upload']" />
+          </a-form-item>
+          <a-form-item v-if="activeData.readonly !== undefined" label="是否只读">
+            <a-switch v-model:checked="activeData.readonly" />
+          </a-form-item>
+          <a-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
+            <a-switch v-model:checked="activeData.disabled" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-select'" label="是否可搜索">
+            <a-switch v-model:checked="activeData.filterable" />
+          </a-form-item>
+          <a-form-item v-if="activeData.tag === 'a-select'" label="是否多选">
+            <a-switch v-model:checked="activeData.multiple" @change="multipleChange" />
+          </a-form-item>
+          <a-form-item v-if="activeData.required !== undefined" label="是否必填">
+            <a-switch v-model:checked="activeData.required" />
+          </a-form-item>
 
           <template v-if="activeData.layoutTree">
-            <el-divider>布局结构树</el-divider>
-            <el-tree :data="[activeData]" :props="layoutTreeProps" node-key="renderKey" default-expand-all draggable>
-              <template #default="{ node, data }">
+            <a-divider>布局结构树</a-divider>
+            <a-tree :tree-data="[activeData]" :field-names="{ key: 'renderKey', title: (data) => data.componentName || `${data.label}: ${data.vModel}`, children: 'children' }" :default-expand-all="true" draggable>
+              <template #title="{ data }">
                 <span class="node-label">
                   <svg-icon class="node-icon" :icon-class="data.tagIcon" style="margin-right: 5px;" />
-                  {{ node.label }}
+                  {{ data.componentName || `${data.label}: ${data.vModel}` }}
                 </span>
               </template>
-            </el-tree>
+            </a-tree>
           </template>
 
           <template v-if="activeData.layout === 'colFormItem'">
-            <el-divider>正则校验</el-divider>
+            <a-divider>正则校验</a-divider>
             <div v-for="(item, index) in activeData.regList" :key="index" class="reg-item">
               <span class="close-btn" @click="activeData.regList.splice(index, 1)">
-                <el-icon>
-                  <Close />
-                </el-icon>
+                <CloseOutlined />
               </span>
-              <el-form-item label="表达式">
-                <el-input v-model="item.pattern" placeholder="请输入正则" />
-              </el-form-item>
-              <el-form-item label="错误提示" style="margin-bottom:0">
-                <el-input v-model="item.message" placeholder="请输入错误提示" />
-              </el-form-item>
+              <a-form-item label="表达式">
+                <a-input v-model:value="item.pattern" placeholder="请输入正则" />
+              </a-form-item>
+              <a-form-item label="错误提示" style="margin-bottom:0">
+                <a-input v-model:value="item.message" placeholder="请输入错误提示" />
+              </a-form-item>
             </div>
             <div>
-              <el-button icon="CirclePlus" style="margin-left: 0; margin-top: 10px;" type="primary" text bg
+              <a-button type="link" style="margin-left: 0; margin-top: 10px;"
                 @click="addReg">
+                <template #icon><PlusOutlined /></template>
                 添加规则
-              </el-button>
+              </a-button>
             </div>
           </template>
-        </el-form>
+        </a-form>
         <!-- 表单属性 -->
-        <el-form v-show="currentTab === 'form'" label-width="90px" label-position="top">
-          <el-form-item label="表单名">
-            <el-input v-model="formConf.formRef" placeholder="请输入表单名(ref)" />
-          </el-form-item>
-          <el-form-item label="表单模型">
-            <el-input v-model="formConf.formModel" placeholder="请输入数据模型" />
-          </el-form-item>
-          <el-form-item label="校验模型">
-            <el-input v-model="formConf.formRules" placeholder="请输入校验模型" />
-          </el-form-item>
-          <el-form-item label="表单尺寸">
-            <el-radio-group v-model="formConf.size">
-              <el-radio-button label="large" value="较大" />
-              <el-radio-button label="default" value="默认" />
-              <el-radio-button label="small" value="较小" />
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="标签对齐">
-            <el-radio-group v-model="formConf.labelPosition">
-              <el-radio-button label="left" value="左对齐" />
-              <el-radio-button label="right" value="右对齐" />
-              <el-radio-button label="top" value="顶部对齐" />
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="标签宽度">
-            <el-input-number v-model="formConf.labelWidth" placeholder="标签宽度" />
-          </el-form-item>
-          <el-form-item label="栅格间隔">
-            <el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" />
-          </el-form-item>
-          <el-form-item label="禁用表单">
-            <el-switch v-model="formConf.disabled" />
-          </el-form-item>
-          <el-form-item label="表单按钮">
-            <el-switch v-model="formConf.formBtns" />
-          </el-form-item>
-          <el-form-item label="显示未选中组件边框">
-            <el-switch v-model="formConf.unFocusedComponentBorder" />
-          </el-form-item>
-        </el-form>
-      </el-scrollbar>
+        <a-form v-show="currentTab === 'form'" :label-col="{ style: { width: '90px' } }" layout="vertical">
+          <a-form-item label="表单名">
+            <a-input v-model:value="formConf.formRef" placeholder="请输入表单名(ref)" />
+          </a-form-item>
+          <a-form-item label="表单模型">
+            <a-input v-model:value="formConf.formModel" placeholder="请输入数据模型" />
+          </a-form-item>
+          <a-form-item label="校验模型">
+            <a-input v-model:value="formConf.formRules" placeholder="请输入校验模型" />
+          </a-form-item>
+          <a-form-item label="表单尺寸">
+            <a-radio-group v-model:value="formConf.size">
+              <a-radio-button value="large">较大</a-radio-button>
+              <a-radio-button value="default">默认</a-radio-button>
+              <a-radio-button value="small">较小</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item label="标签对齐">
+            <a-radio-group v-model:value="formConf.labelPosition">
+              <a-radio-button value="left">左对齐</a-radio-button>
+              <a-radio-button value="right">右对齐</a-radio-button>
+              <a-radio-button value="top">顶部对齐</a-radio-button>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item label="标签宽度">
+            <a-input-number v-model:value="formConf.labelWidth" placeholder="标签宽度" />
+          </a-form-item>
+          <a-form-item label="栅格间隔">
+            <a-input-number v-model:value="formConf.gutter" :min="0" placeholder="栅格间隔" />
+          </a-form-item>
+          <a-form-item label="禁用表单">
+            <a-switch v-model:checked="formConf.disabled" />
+          </a-form-item>
+          <a-form-item label="表单按钮">
+            <a-switch v-model:checked="formConf.formBtns" />
+          </a-form-item>
+          <a-form-item label="显示未选中组件边框">
+            <a-switch v-model:checked="formConf.unFocusedComponentBorder" />
+          </a-form-item>
+        </a-form>
+      </div>
     </div>
     <icons-dialog v-model="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" />
     <treeNode-dialog v-model="dialogVisible" @commit="addNode" />
@@ -469,6 +454,7 @@ import { isNumberStr } from '@/utils/index'
 import IconsDialog from './IconsDialog'
 import TreeNodeDialog from './TreeNodeDialog'
 import { inputComponents, selectComponents } from '@/utils/generator/config'
+import { LinkOutlined, MenuOutlined, CloseOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'
 
 const { proxy } = getCurrentInstance()
 const dateTimeFormat = {
@@ -582,10 +568,10 @@ const data = reactive({
 
 const { currentTab, currentNode, dialogVisible, iconsVisible, currentIconModel, dateTypeOptions, dateRangeTypeOptions, colorFormatOptions, justifyOptions, layoutTreeProps } = toRefs(data)
 
-const documentLink = computed(() => props.activeData.document || 'https://element-plus.org/zh-CN/guide/installation')
+const documentLink = computed(() => props.activeData.document || 'https://antdv.com/components/overview-cn')
 
 const dateOptions = computed(() => {
-  if (props.activeData.type !== undefined && props.activeData.tag === 'el-date-picker') {
+  if (props.activeData.type !== undefined && props.activeData.tag === 'a-date-picker') {
     if (props.activeData['start-placeholder'] === undefined) {
       return dateTypeOptions.value
     }
@@ -626,35 +612,6 @@ function addTreeItem() {
   currentNode.value = props.activeData.options
 }
 
-function renderContent(h, { node, data, store }) {
-  return h('div', {
-    class: "custom-tree-node"
-  }, [
-    h('span', node.label),
-    h('span', {
-      class: "node-operation"
-    }, [
-      h(resolveComponent('el-link'), {
-        type: "primary",
-        icon: "Plus",
-        underline: false,
-        onClick: () => {
-          append(data)
-
-        }
-      }),
-      h(resolveComponent('el-link'), {
-        type: "danger",
-        icon: "Delete",
-        underline: false,
-        style: "margin-left: 5px;",
-        onClick: () => {
-          remove(node, data)
-        }
-      })
-    ])
-  ])
-}
 function append(data) {
   if (!data.children) {
     data.children = []
@@ -662,11 +619,23 @@ function append(data) {
   dialogVisible.value = true
   currentNode.value = data.children
 }
-function remove(node, data) {
-  const { parent } = node
-  const children = parent.data.children || parent.data
-  const index = children.findIndex(d => d.id === data.id)
-  children.splice(index, 1)
+
+function removeFromTree(data) {
+  // 从 activeData.options 中递归查找并删除
+  function removeRecursive(items, target) {
+    const index = items.findIndex(d => d.id === target.id)
+    if (index > -1) {
+      items.splice(index, 1)
+      return true
+    }
+    for (const item of items) {
+      if (item.children && removeRecursive(item.children, target)) {
+        return true
+      }
+    }
+    return false
+  }
+  removeRecursive(props.activeData.options, data)
 }
 function addNode(data) {
   currentNode.value.push(data)
@@ -770,14 +739,12 @@ function tagChange(tagIcon) {
   top: 0;
   padding-top: 3px;
 
-  &:deep() {
-    .el-tabs__header {
-      margin: 0;
-    }
+  :deep(.ant-tabs-nav) {
+    margin: 0;
+  }
 
-    .el-input-group__append .el-button {
-      display: inline-flex;
-    }
+  :deep(.ant-input-group-addon .ant-btn) {
+    display: inline-flex;
   }
 
   .field-box {
@@ -787,21 +754,15 @@ function tagChange(tagIcon) {
     overflow: hidden;
   }
 
-  .el-scrollbar {
+  .right-scrollbar {
     height: 100%;
-
-    &:deep() {
-      .el-scrollbar__view {
-        padding: 30px 20px;
-      }
-
-    }
+    padding: 30px 20px;
   }
 }
 
 .reg-item {
   padding: 12px 6px;
-  background: var(--el-border-color-extra-light);
+  background: #f5f5f5;
   position: relative;
   border-radius: 4px;
 
@@ -834,7 +795,7 @@ function tagChange(tagIcon) {
     color: #f56c6c;
   }
 
-  & .el-input+.el-input {
+  & .ant-input+.ant-input {
     margin-left: 4px;
   }
 }
@@ -859,15 +820,9 @@ function tagChange(tagIcon) {
 }
 
 .time-range {
-  .el-date-editor {
+  :deep(.ant-picker) {
     width: 227px;
   }
-
-  :deep() {
-    .el-icon-time {
-      display: none;
-    }
-  }
 }
 
 .document-link {

+ 33 - 36
yushu-uivue3/src/views/tool/build/TreeNodeDialog.vue

@@ -1,39 +1,31 @@
 <template>
-  <div>
-    <el-dialog title="添加选项" v-model="open" width="800px" :close-on-click-modal="false" :modal-append-to-body="false"
-      @open="onOpen" @close="onClose">
-      <el-form ref="treeNodeForm" :model="formData" :rules="rules" label-width="100px">
-        <el-col :span="24">
-          <el-form-item label="选项名" prop="label">
-            <el-input v-model="formData.label" placeholder="请输入选项名" clearable />
-          </el-form-item>
-        </el-col>
-        <el-col :span="24">
-          <el-form-item label="选项值" prop="value">
-            <el-input v-model="formData.value" placeholder="请输入选项值" clearable>
-              <template #append>
-                <el-select v-model="dataType" :style="{ width: '100px' }">
-                  <el-option v-for="(item, index) in dataTypeOptions" :key="index" :label="item.label" :value="item.value"
-                    :disabled="item.disabled" />
-                </el-select>
-              </template>
-
-            </el-input>
-          </el-form-item>
-        </el-col>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="handelConfirm">确 定</el-button>
-          <el-button @click="onClose">取 消</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
+  <a-modal v-model:open="open" title="添加选项" width="800px" @ok="handelConfirm" @cancel="onClose">
+    <a-form ref="treeNodeFormRef" :model="formData" :rules="rules" :label-col="{ style: { width: '100px' } }">
+      <a-row>
+        <a-col :span="24">
+          <a-form-item label="选项名" name="label">
+            <a-input v-model:value="formData.label" placeholder="请输入选项名" allow-clear />
+          </a-form-item>
+        </a-col>
+        <a-col :span="24">
+          <a-form-item label="选项值" name="value">
+            <a-input-group compact>
+              <a-input v-model:value="formData.value" placeholder="请输入选项值" allow-clear style="width: calc(100% - 100px)" />
+              <a-select v-model:value="dataType" style="width: 100px">
+                <a-select-option v-for="(item, index) in dataTypeOptions" :key="index" :value="item.value" :disabled="item.disabled">
+                  {{ item.label }}
+                </a-select-option>
+              </a-select>
+            </a-input-group>
+          </a-form-item>
+        </a-col>
+      </a-row>
+    </a-form>
+  </a-modal>
 </template>
 <script setup>
 const open = defineModel()
-const emit = defineEmits(['confirm'])
+const emit = defineEmits(['commit'])
 const formData = ref({
   label: undefined,
   value: undefined
@@ -66,7 +58,7 @@ const dataTypeOptions = ref([
   }
 ])
 const id = ref(100)
-const treeNodeForm = ref()
+const treeNodeFormRef = ref()
 
 function onOpen() {
   formData.value = {
@@ -80,14 +72,19 @@ function onClose() {
 }
 
 function handelConfirm() {
-  treeNodeForm.value.validate(valid => {
-    if (!valid) return
+  treeNodeFormRef.value.validateFields().then(() => {
     if (dataType.value === 'number') {
       formData.value.value = parseFloat(formData.value.value)
     }
     formData.value.id = id.value++
     emit('commit', formData.value)
     onClose()
-  })
+  }).catch(() => {})
 }
+
+watch(open, (val) => {
+  if (val) {
+    onOpen()
+  }
+})
 </script>

+ 70 - 76
yushu-uivue3/src/views/tool/build/index.vue

@@ -110,7 +110,7 @@ import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/
 import { makeUpJs } from '@/utils/generator/js'
 import { makeUpCss } from '@/utils/generator/css'
 import Download from '@/plugins/download'
-import { notification } from 'ant-design-vue'
+import { notification, Modal, message } from 'ant-design-vue'
 import { DownloadOutlined, CopyOutlined, DeleteOutlined } from '@ant-design/icons-vue'
 import DraggableItem from './DraggableItem'
 import RightPanel from './RightPanel'
@@ -148,12 +148,15 @@ function download() {
   operationType.value = 'download'
 }
 function empty() {
-  proxy.$modal.confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(() => {
+  Modal.confirm({
+    title: '提示',
+    content: '确定要清空所有组件吗?',
+    onOk() {
       idGlobal.value = 100
       drawingList.value = []
       cleanDrawingDefaultValue()
     }
-  )
+  })
 }
 
 function onEnd(obj, a) {
@@ -309,7 +312,7 @@ onMounted(() => {
     }
   })
   clipboard.on('error', e => {
-    proxy.$modal.msgError('代码复制失败')
+    message.error('代码复制失败')
   })
 })
 onUnmounted(() => {
@@ -323,7 +326,7 @@ $lighterBlue: #409EFF;
 .container {
   position: relative;
   width: 100%;
-  background-color: var(--el-bg-color-overlay);
+  background-color: #f5f5f5;
   height: calc(100vh - 50px - 40px);
   overflow: hidden;
 
@@ -337,7 +340,7 @@ $lighterBlue: #409EFF;
     .logo-wrapper {
       position: relative;
       height: 42px;
-      border-bottom: 1px solid var(--el-border-color-extra-light);
+      border-bottom: 1px solid #e8e8e8;
       box-sizing: border-box;
 
       .logo {
@@ -369,64 +372,57 @@ $lighterBlue: #409EFF;
     }
 
     .left-scrollbar {
-      .el-scrollbar__wrap {
+      box-sizing: border-box;
+      overflow-x: hidden !important;
+      overflow-y: auto;
+
+      .components-list {
+        padding: 8px;
         box-sizing: border-box;
-        overflow-x: hidden !important;
-        margin-bottom: 0 !important;
-
-        .components-list {
-          padding: 8px;
-          box-sizing: border-box;
-          height: 100%;
-
-          .components-title {
-            font-size: 14px;
-            // color: #222;
-            margin: 6px 2px;
-
-            .svg-icon {
-              // color: #666;
-              font-size: 18px;
-              margin-right: 5px;
-            }
+        height: 100%;
+
+        .components-title {
+          font-size: 14px;
+          margin: 6px 2px;
+
+          .svg-icon {
+            font-size: 18px;
+            margin-right: 5px;
           }
+        }
 
-          .components-draggable {
-            padding-bottom: 20px;
+        .components-draggable {
+          padding-bottom: 20px;
 
-            .components-item {
-              display: inline-block;
-              width: 48%;
-              margin: 1%;
-              transition: transform 0ms !important;
-
-              .components-body {
-                padding: 8px 10px;
-                background: var(--el-border-color-extra-light);
-                font-size: 12px;
-                cursor: move;
-                border: 1px dashed var(--el-border-color-extra-light);
-                border-radius: 3px;
+          .components-item {
+            display: inline-block;
+            width: 48%;
+            margin: 1%;
+            transition: transform 0ms !important;
 
-                .svg-icon {
-                  // color: #777;
-                  font-size: 15px;
-                  margin-right: 5px;
-                }
+            .components-body {
+              padding: 8px 10px;
+              background: #f0f0f0;
+              font-size: 12px;
+              cursor: move;
+              border: 1px dashed #d9d9d9;
+              border-radius: 3px;
 
-                &:hover {
-                  border: 1px dashed #787be8;
-                  color: #787be8;
+              .svg-icon {
+                font-size: 15px;
+                margin-right: 5px;
+              }
 
-                  .svg-icon {
-                    color: #787be8;
-                  }
+              &:hover {
+                border: 1px dashed #787be8;
+                color: #787be8;
+
+                .svg-icon {
+                  color: #787be8;
                 }
               }
             }
           }
-
-
         }
       }
     }
@@ -443,15 +439,15 @@ $lighterBlue: #409EFF;
       height: 42px;
       padding: 0 15px;
       box-sizing: border-box;
-      ;
-      border: 1px solid var(--el-border-color-extra-light);
+      border: 1px solid #e8e8e8;
       border-top: none;
       border-left: none;
       display: flex;
       align-items: center;
       justify-content: flex-end;
+      gap: 8px;
 
-      u .delete-btn {
+      .delete-btn {
         color: #F56C6C;
       }
     }
@@ -459,25 +455,23 @@ $lighterBlue: #409EFF;
     .center-scrollbar {
       height: calc(100vh - 50px - 40px - 42px);
       overflow: hidden;
-      border-left: 1px solid var(--el-border-color-extra-light);
-      border-right: 1px solid var(--el-border-color-extra-light);
+      border-left: 1px solid #e8e8e8;
+      border-right: 1px solid #e8e8e8;
       box-sizing: border-box;
-
-      .el-scrollbar__view {
-        overflow-x: hidden;
-      }
+      overflow-x: hidden;
+      overflow-y: auto;
 
       .center-board-row {
         padding: 12px 12px 15px 12px;
         box-sizing: border-box;
 
-        &>.el-form {
+        &>.ant-form {
           // 69 = 12+15+42
-          height: calc(100vh - 50px - 40px - 69px);
+          min-height: calc(100vh - 50px - 40px - 69px);
           flex: 1;
 
           .drawing-board {
-            height: 100%;
+            min-height: 100%;
             position: relative;
 
             .components-body {
@@ -506,12 +500,12 @@ $lighterBlue: #409EFF;
             .components-item.sortable-ghost {
               width: 100%;
               height: 60px;
-              background: var(--el-border-color-extra-light);
+              background: #f0f0f0;
             }
 
             .active-from-item {
-              &>.el-form-item {
-                background: var(--el-border-color-extra-light);
+              &>.ant-form-item {
+                background: #f0f0f0;
                 border-radius: 6px;
               }
 
@@ -524,12 +518,12 @@ $lighterBlue: #409EFF;
                 color: $lighterBlue;
               }
 
-              .el-input__wrapper {
-                box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset;
+              .ant-input {
+                border-color: #409eff;
               }
             }
 
-            .el-form-item {
+            .ant-form-item {
               margin-bottom: 15px;
             }
           }
@@ -542,7 +536,7 @@ $lighterBlue: #409EFF;
               border: 1px dashed #ccc;
             }
 
-            .el-form-item {
+            .ant-form-item {
               padding: 12px 10px;
             }
           }
@@ -560,11 +554,11 @@ $lighterBlue: #409EFF;
               margin-bottom: 2px;
             }
 
-            .el-col {
+            .ant-col {
               margin-top: 22px;
             }
 
-            .el-form-item {
+            .ant-form-item {
               margin-bottom: 0;
             }
 
@@ -593,8 +587,8 @@ $lighterBlue: #409EFF;
           .drawing-item,
           .drawing-row-item {
             &:hover {
-              &>.el-form-item {
-                background: var(--el-border-color-extra-light);
+              &>.ant-form-item {
+                background: #f0f0f0;
                 border-radius: 6px;
               }
 

+ 35 - 29
yushu-uivue3/src/views/tool/gen/basicInfoForm.vue

@@ -1,33 +1,33 @@
 <template>
-  <el-form ref="basicInfoForm" :model="info" :rules="rules" label-width="150px">
-    <el-row>
-      <el-col :span="12">
-        <el-form-item label="表名称" prop="tableName">
-          <el-input placeholder="请输入仓库名称" v-model="info.tableName" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="表描述" prop="tableComment">
-          <el-input placeholder="请输入" v-model="info.tableComment" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="实体类名称" prop="className">
-          <el-input placeholder="请输入" v-model="info.className" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="作者" prop="functionAuthor">
-          <el-input placeholder="请输入" v-model="info.functionAuthor" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="24">
-        <el-form-item label="备注" prop="remark">
-          <el-input type="textarea" :rows="3" v-model="info.remark"></el-input>
-        </el-form-item>
-      </el-col>
-    </el-row>
-  </el-form>
+  <a-form ref="basicInfoFormRef" :model="info" :rules="rules" :label-col="{ style: { width: '150px' } }">
+    <a-row>
+      <a-col :span="12">
+        <a-form-item label="表名称" name="tableName">
+          <a-input placeholder="请输入仓库名称" v-model:value="info.tableName" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item label="表描述" name="tableComment">
+          <a-input placeholder="请输入" v-model:value="info.tableComment" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item label="实体类名称" name="className">
+          <a-input placeholder="请输入" v-model:value="info.className" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item label="作者" name="functionAuthor">
+          <a-input placeholder="请输入" v-model:value="info.functionAuthor" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="24">
+        <a-form-item label="备注" name="remark">
+          <a-textarea :rows="3" v-model:value="info.remark"></a-textarea>
+        </a-form-item>
+      </a-col>
+    </a-row>
+  </a-form>
 </template>
 
 <script setup>
@@ -38,6 +38,8 @@ defineProps({
   }
 })
 
+const basicInfoFormRef = ref()
+
 // 表单校验
 const rules = ref({
   tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }],
@@ -45,4 +47,8 @@ const rules = ref({
   className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }],
   functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }]
 })
+
+defineExpose({
+  basicInfoForm: basicInfoFormRef
+})
 </script>

+ 256 - 132
yushu-uivue3/src/views/tool/gen/editTable.vue

@@ -1,142 +1,254 @@
 <template>
-  <el-card>
-    <el-tabs v-model="activeName">
-      <el-tab-pane label="基本信息" name="basic">
+  <a-card>
+    <a-tabs v-model:activeKey="activeName">
+      <a-tab-pane key="basic" tab="基本信息">
         <basic-info-form ref="basicInfo" :info="info" />
-      </el-tab-pane>
-      <el-tab-pane label="字段信息" name="columnInfo">
-        <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
-          <el-table-column label="序号" type="index" min-width="5%" class-name="allowDrag"/>
-          <el-table-column label="字段列名" prop="columnName" min-width="10%" :show-overflow-tooltip="true" class-name="allowDrag"/>
-          <el-table-column label="字段描述" min-width="10%">
-            <template #default="scope">
-              <el-input v-model="scope.row.columnComment"></el-input>
-            </template>
-          </el-table-column>
-          <el-table-column
-            label="物理类型"
-            prop="columnType"
-            min-width="10%"
-            :show-overflow-tooltip="true"
-          />
-          <el-table-column label="Java类型" min-width="11%">
-            <template #default="scope">
-              <el-select v-model="scope.row.javaType">
-                <el-option label="Long" value="Long" />
-                <el-option label="String" value="String" />
-                <el-option label="Integer" value="Integer" />
-                <el-option label="Double" value="Double" />
-                <el-option label="BigDecimal" value="BigDecimal" />
-                <el-option label="Date" value="Date" />
-                <el-option label="Boolean" value="Boolean" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="java属性" min-width="10%">
-            <template #default="scope">
-              <el-input v-model="scope.row.javaField"></el-input>
-            </template>
-          </el-table-column>
-
-          <el-table-column label="插入" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-value="1" false-value="0" v-model="scope.row.isInsert"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="编辑" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-value="1" false-value="0" v-model="scope.row.isEdit"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="列表" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-value="1" false-value="0" v-model="scope.row.isList"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="查询" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-value="1" false-value="0" v-model="scope.row.isQuery"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="查询方式" min-width="10%">
-            <template #default="scope">
-              <el-select v-model="scope.row.queryType">
-                <el-option label="=" value="EQ" />
-                <el-option label="!=" value="NE" />
-                <el-option label=">" value="GT" />
-                <el-option label=">=" value="GTE" />
-                <el-option label="<" value="LT" />
-                <el-option label="<=" value="LTE" />
-                <el-option label="LIKE" value="LIKE" />
-                <el-option label="BETWEEN" value="BETWEEN" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="必填" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-value="1" false-value="0" v-model="scope.row.isRequired"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="显示类型" min-width="12%">
-            <template #default="scope">
-              <el-select v-model="scope.row.htmlType">
-                <el-option label="文本框" value="input" />
-                <el-option label="文本域" value="textarea" />
-                <el-option label="下拉框" value="select" />
-                <el-option label="单选框" value="radio" />
-                <el-option label="复选框" value="checkbox" />
-                <el-option label="日期控件" value="datetime" />
-                <el-option label="图片上传" value="imageUpload" />
-                <el-option label="文件上传" value="fileUpload" />
-                <el-option label="富文本控件" value="editor" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="字典类型" min-width="12%">
-            <template #default="scope">
-              <el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
-                <el-option
-                  v-for="dict in dictOptions"
-                  :key="dict.dictType"
-                  :label="dict.dictName"
-                  :value="dict.dictType">
-                  <span style="float: left">{{ dict.dictName }}</span>
-                  <span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
-              </el-option>
-              </el-select>
-            </template>
-          </el-table-column>
-        </el-table>
-      </el-tab-pane>
-      <el-tab-pane label="生成信息" name="genInfo">
+      </a-tab-pane>
+      <a-tab-pane key="columnInfo" tab="字段信息">
+        <a-table 
+          ref="dragTableRef"
+          :dataSource="columns" 
+          :columns="tableColumns" 
+          :pagination="false"
+          :scroll="{ y: tableHeight }"
+          row-key="columnId"
+          size="small"
+        />
+      </a-tab-pane>
+      <a-tab-pane key="genInfo" tab="生成信息">
         <gen-info-form ref="genInfo" :info="info" :tables="tables" />
-      </el-tab-pane>
-    </el-tabs>
-    <el-form label-width="100px">
-      <div style="text-align: center;margin-left:-100px;margin-top:10px;">
-        <el-button type="primary" @click="submitForm()">提交</el-button>
-        <el-button @click="close()">返回</el-button>
-      </div>
-    </el-form>
-  </el-card>
+      </a-tab-pane>
+    </a-tabs>
+    <div style="text-align: center; margin-top: 20px;">
+      <a-button type="primary" @click="submitForm()">提交</a-button>
+      <a-button @click="close()" style="margin-left: 8px">返回</a-button>
+    </div>
+  </a-card>
 </template>
 
 <script setup name="GenEdit">
+import { h } from 'vue'
 import { getGenTable, updateGenTable } from "@/api/tool/gen"
 import { optionselect as getDictOptionselect } from "@/api/system/dict/type"
 import basicInfoForm from "./basicInfoForm"
 import genInfoForm from "./genInfoForm"
 import Sortable from 'sortablejs'
+import { message, Modal } from 'ant-design-vue'
 
 const route = useRoute()
 const { proxy } = getCurrentInstance()
 
 const activeName = ref("columnInfo")
-const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px")
+const tableHeight = ref(document.documentElement.scrollHeight - 245)
 const tables = ref([])
 const columns = ref([])
 const dictOptions = ref([])
 const info = ref({})
+const dragTableRef = ref()
+
+// 表格列配置
+const tableColumns = [
+  {
+    title: '序号',
+    key: 'index',
+    width: '5%',
+    customRender: ({ index }) => index + 1,
+    className: 'allowDrag'
+  },
+  {
+    title: '字段列名',
+    dataIndex: 'columnName',
+    key: 'columnName',
+    width: '10%',
+    ellipsis: true,
+    className: 'allowDrag'
+  },
+  {
+    title: '字段描述',
+    key: 'columnComment',
+    width: '10%',
+    customRender: ({ record }) => {
+      return h('a-input', {
+        value: record.columnComment,
+        'onUpdate:value': (val) => { record.columnComment = val }
+      })
+    }
+  },
+  {
+    title: '物理类型',
+    dataIndex: 'columnType',
+    key: 'columnType',
+    width: '10%',
+    ellipsis: true
+  },
+  {
+    title: 'Java类型',
+    key: 'javaType',
+    width: '11%',
+    customRender: ({ record }) => {
+      return h('a-select', {
+        value: record.javaType,
+        'onUpdate:value': (val) => { record.javaType = val },
+        style: { width: '100%' }
+      }, {
+        default: () => [
+          h('a-select-option', { value: 'Long' }, 'Long'),
+          h('a-select-option', { value: 'String' }, 'String'),
+          h('a-select-option', { value: 'Integer' }, 'Integer'),
+          h('a-select-option', { value: 'Double' }, 'Double'),
+          h('a-select-option', { value: 'BigDecimal' }, 'BigDecimal'),
+          h('a-select-option', { value: 'Date' }, 'Date'),
+          h('a-select-option', { value: 'Boolean' }, 'Boolean')
+        ]
+      })
+    }
+  },
+  {
+    title: 'java属性',
+    key: 'javaField',
+    width: '10%',
+    customRender: ({ record }) => {
+      return h('a-input', {
+        value: record.javaField,
+        'onUpdate:value': (val) => { record.javaField = val }
+      })
+    }
+  },
+  {
+    title: '插入',
+    key: 'isInsert',
+    width: '5%',
+    align: 'center',
+    customRender: ({ record }) => {
+      return h('a-checkbox', {
+        checked: record.isInsert == '1',
+        'onUpdate:checked': (val) => { record.isInsert = val ? '1' : '0' }
+      })
+    }
+  },
+  {
+    title: '编辑',
+    key: 'isEdit',
+    width: '5%',
+    align: 'center',
+    customRender: ({ record }) => {
+      return h('a-checkbox', {
+        checked: record.isEdit == '1',
+        'onUpdate:checked': (val) => { record.isEdit = val ? '1' : '0' }
+      })
+    }
+  },
+  {
+    title: '列表',
+    key: 'isList',
+    width: '5%',
+    align: 'center',
+    customRender: ({ record }) => {
+      return h('a-checkbox', {
+        checked: record.isList == '1',
+        'onUpdate:checked': (val) => { record.isList = val ? '1' : '0' }
+      })
+    }
+  },
+  {
+    title: '查询',
+    key: 'isQuery',
+    width: '5%',
+    align: 'center',
+    customRender: ({ record }) => {
+      return h('a-checkbox', {
+        checked: record.isQuery == '1',
+        'onUpdate:checked': (val) => { record.isQuery = val ? '1' : '0' }
+      })
+    }
+  },
+  {
+    title: '查询方式',
+    key: 'queryType',
+    width: '10%',
+    customRender: ({ record }) => {
+      return h('a-select', {
+        value: record.queryType,
+        'onUpdate:value': (val) => { record.queryType = val },
+        style: { width: '100%' }
+      }, {
+        default: () => [
+          h('a-select-option', { value: 'EQ' }, '='),
+          h('a-select-option', { value: 'NE' }, '!='),
+          h('a-select-option', { value: 'GT' }, '>'),
+          h('a-select-option', { value: 'GTE' }, '>='),
+          h('a-select-option', { value: 'LT' }, '<'),
+          h('a-select-option', { value: 'LTE' }, '<='),
+          h('a-select-option', { value: 'LIKE' }, 'LIKE'),
+          h('a-select-option', { value: 'BETWEEN' }, 'BETWEEN')
+        ]
+      })
+    }
+  },
+  {
+    title: '必填',
+    key: 'isRequired',
+    width: '5%',
+    align: 'center',
+    customRender: ({ record }) => {
+      return h('a-checkbox', {
+        checked: record.isRequired == '1',
+        'onUpdate:checked': (val) => { record.isRequired = val ? '1' : '0' }
+      })
+    }
+  },
+  {
+    title: '显示类型',
+    key: 'htmlType',
+    width: '12%',
+    customRender: ({ record }) => {
+      return h('a-select', {
+        value: record.htmlType,
+        'onUpdate:value': (val) => { record.htmlType = val },
+        style: { width: '100%' }
+      }, {
+        default: () => [
+          h('a-select-option', { value: 'input' }, '文本框'),
+          h('a-select-option', { value: 'textarea' }, '文本域'),
+          h('a-select-option', { value: 'select' }, '下拉框'),
+          h('a-select-option', { value: 'radio' }, '单选框'),
+          h('a-select-option', { value: 'checkbox' }, '复选框'),
+          h('a-select-option', { value: 'datetime' }, '日期控件'),
+          h('a-select-option', { value: 'imageUpload' }, '图片上传'),
+          h('a-select-option', { value: 'fileUpload' }, '文件上传'),
+          h('a-select-option', { value: 'editor' }, '富文本控件')
+        ]
+      })
+    }
+  },
+  {
+    title: '字典类型',
+    key: 'dictType',
+    width: '12%',
+    customRender: ({ record }) => {
+      return h('a-select', {
+        value: record.dictType,
+        'onUpdate:value': (val) => { record.dictType = val },
+        allowClear: true,
+        showSearch: true,
+        placeholder: '请选择',
+        style: { width: '100%' }
+      }, {
+        default: () => dictOptions.value.map(dict => 
+          h('a-select-option', { 
+            key: dict.dictType, 
+            value: dict.dictType 
+          }, {
+            default: () => [
+              h('span', { style: { float: 'left' } }, dict.dictName),
+              h('span', { style: { float: 'right', color: '#8492a6', fontSize: '13px' } }, dict.dictType)
+            ]
+          })
+        )
+      })
+    }
+  }
+]
 
 /** 提交按钮 */
 function submitForm() {
@@ -154,21 +266,23 @@ function submitForm() {
         parentMenuId: info.value.parentMenuId
       }
       updateGenTable(genTable).then(res => {
-        proxy.$modal.msgSuccess(res.msg)
+        message.success(res.msg)
         if (res.code === 200) {
           close()
         }
       })
     } else {
-      proxy.$modal.msgError("表单校验未通过,请重新检查提交内容")
+      message.error("表单校验未通过,请重新检查提交内容")
     }
   })
 }
 
 function getFormPromise(form) {
   return new Promise(resolve => {
-    form.validate(res => {
-      resolve(res)
+    form.validateFields().then(() => {
+      resolve(true)
+    }).catch(() => {
+      resolve(false)
     })
   })
 }
@@ -196,16 +310,26 @@ function close() {
 
 // 拖动排序
 onMounted(() => {
-  const element = document.querySelector('.el-table__body > tbody')
-  Sortable.create(element, {
-    handle: ".allowDrag",
-    onEnd: (evt) => {
-      const targetRow = columns.value.splice(evt.oldIndex, 1)[0]
-      columns.value.splice(evt.newIndex, 0, targetRow)
-      for (const index in columns.value) {
-        columns.value[index].sort = parseInt(index) + 1
-      }
+  nextTick(() => {
+    const element = document.querySelector('.ant-table-tbody')
+    if (element) {
+      Sortable.create(element, {
+        handle: ".allowDrag",
+        onEnd: (evt) => {
+          const targetRow = columns.value.splice(evt.oldIndex, 1)[0]
+          columns.value.splice(evt.newIndex, 0, targetRow)
+          for (const index in columns.value) {
+            columns.value[index].sort = parseInt(index) + 1
+          }
+        }
+      })
     }
   })
 })
 </script>
+
+<style lang="scss" scoped>
+:deep(.allowDrag) {
+  cursor: move;
+}
+</style>

+ 184 - 175
yushu-uivue3/src/views/tool/gen/genInfoForm.vue

@@ -1,245 +1,242 @@
 <template>
-  <el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px">
-    <el-row>
-      <el-col :span="12">
-        <el-form-item prop="tplCategory">
-          <template #label>生成模板</template>
-          <el-select v-model="info.tplCategory" @change="tplSelectChange">
-            <el-option label="单表(增删改查)" value="crud" />
-            <el-option label="树表(增删改查)" value="tree" />
-            <el-option label="主子表(增删改查)" value="sub" />
-          </el-select>
-        </el-form-item>
-      </el-col>
+  <a-form ref="genInfoFormRef" :model="info" :rules="rules" :label-col="{ style: { width: '150px' } }">
+    <a-row>
+      <a-col :span="12">
+        <a-form-item name="tplCategory" label="生成模板">
+          <a-select v-model:value="info.tplCategory" @change="tplSelectChange">
+            <a-select-option value="crud">单表(增删改查)</a-select-option>
+            <a-select-option value="tree">树表(增删改查)</a-select-option>
+            <a-select-option value="sub">主子表(增删改查)</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="tplWebType">
-          <template #label>前端类型</template>
-          <el-select v-model="info.tplWebType">
-            <el-option label="Vue2 Element UI 模版" value="element-ui" />
-            <el-option label="Vue3 Element Plus 模版" value="element-plus" />
-          </el-select>
-        </el-form-item>
-      </el-col>
+      <a-col :span="12">
+        <a-form-item name="tplWebType" label="前端类型">
+          <a-select v-model:value="info.tplWebType">
+            <a-select-option value="element-ui">Vue2 Element UI 模版</a-select-option>
+            <a-select-option value="element-plus">Vue3 Element Plus 模版</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="packageName">
+      <a-col :span="12">
+        <a-form-item name="packageName" label="生成包路径">
           <template #label>
-            生成包路径
-            <el-tooltip content="生成在哪个java包下,例如 com.yushu.system" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>生成包路径</span>
+            <a-tooltip title="生成在哪个java包下,例如 com.yushu.system" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-input v-model="info.packageName" />
-        </el-form-item>
-      </el-col>
+          <a-input v-model:value="info.packageName" />
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="moduleName">
+      <a-col :span="12">
+        <a-form-item name="moduleName" label="生成模块名">
           <template #label>
-            生成模块名
-            <el-tooltip content="可理解为子系统名,例如 system" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>生成模块名</span>
+            <a-tooltip title="可理解为子系统名,例如 system" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-input v-model="info.moduleName" />
-        </el-form-item>
-      </el-col>
+          <a-input v-model:value="info.moduleName" />
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="businessName">
+      <a-col :span="12">
+        <a-form-item name="businessName" label="生成业务名">
           <template #label>
-            生成业务名
-            <el-tooltip content="可理解为功能英文名,例如 user" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>生成业务名</span>
+            <a-tooltip title="可理解为功能英文名,例如 user" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-input v-model="info.businessName" />
-        </el-form-item>
-      </el-col>
+          <a-input v-model:value="info.businessName" />
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="functionName">
+      <a-col :span="12">
+        <a-form-item name="functionName" label="生成功能名">
           <template #label>
-            生成功能名
-            <el-tooltip content="用作类描述,例如 用户" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>生成功能名</span>
+            <a-tooltip title="用作类描述,例如 用户" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-input v-model="info.functionName" />
-        </el-form-item>
-      </el-col>
+          <a-input v-model:value="info.functionName" />
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item prop="genType">
+      <a-col :span="12">
+        <a-form-item name="genType" label="生成代码方式">
           <template #label>
-            生成代码方式
-            <el-tooltip content="默认为zip压缩包下载,也可以自定义生成路径" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>生成代码方式</span>
+            <a-tooltip title="默认为zip压缩包下载,也可以自定义生成路径" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-radio v-model="info.genType" value="0">zip压缩包</el-radio>
-          <el-radio v-model="info.genType" value="1">自定义路径</el-radio>
-        </el-form-item>
-      </el-col>
+          <a-radio-group v-model:value="info.genType">
+            <a-radio value="0">zip压缩包</a-radio>
+            <a-radio value="1">自定义路径</a-radio>
+          </a-radio-group>
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="12">
-        <el-form-item>
+      <a-col :span="12">
+        <a-form-item label="上级菜单">
           <template #label>
-            上级菜单
-            <el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>上级菜单</span>
+            <a-tooltip title="分配到指定菜单下,例如 系统管理" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-tree-select
-            v-model="info.parentMenuId"
-            :data="menuOptions"
-            :props="{ value: 'menuId', label: 'menuName', children: 'children' }"
-            value-key="menuId"
+          <a-tree-select
+            v-model:value="info.parentMenuId"
+            :tree-data="menuOptions"
+            :field-names="{ value: 'menuId', label: 'menuName', children: 'children' }"
             placeholder="请选择系统菜单"
-            check-strictly
+            :tree-checkable="false"
+            :show-checked-strategy="false"
+            tree-default-expand-all
           />
-        </el-form-item>
-      </el-col>
+        </a-form-item>
+      </a-col>
 
-      <el-col :span="24" v-if="info.genType == '1'">
-        <el-form-item prop="genPath">
+      <a-col :span="24" v-if="info.genType == '1'">
+        <a-form-item name="genPath" label="自定义路径">
           <template #label>
-            自定义路径
-            <el-tooltip content="填写磁盘绝对路径,若不填写,则生成到当前Web项目下" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
+            <span>自定义路径</span>
+            <a-tooltip title="填写磁盘绝对路径,若不填写,则生成到当前Web项目下" placement="top">
+              <QuestionCircleOutlined style="margin-left: 4px" />
+            </a-tooltip>
           </template>
-          <el-input v-model="info.genPath">
-            <template #append>
-              <el-dropdown>
-                <el-button type="primary">
-                  最近路径快速选择
-                  <i class="el-icon-arrow-down el-icon--right"></i>
-                </el-button>
-                <template #dropdown>
-                  <el-dropdown-menu>
-                    <el-dropdown-item @click="info.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item>
-                  </el-dropdown-menu>
-                </template>
-              </el-dropdown>
-            </template>
-          </el-input>
-        </el-form-item>
-      </el-col>
-    </el-row>
+          <a-input-group compact>
+            <a-input v-model:value="info.genPath" style="width: calc(100% - 150px)" />
+            <a-dropdown>
+              <a-button type="primary">
+                最近路径快速选择
+                <DownOutlined />
+              </a-button>
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item @click="info.genPath = '/'">恢复默认的生成基础路径</a-menu-item>
+                </a-menu>
+              </template>
+            </a-dropdown>
+          </a-input-group>
+        </a-form-item>
+      </a-col>
+    </a-row>
     
     <template v-if="info.tplCategory == 'tree'">
       <h4 class="form-header">其他信息</h4>
-      <el-row v-show="info.tplCategory == 'tree'">
-        <el-col :span="12">
-          <el-form-item>
+      <a-row v-show="info.tplCategory == 'tree'">
+        <a-col :span="12">
+          <a-form-item label="树编码字段">
             <template #label>
-              树编码字段
-              <el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
+              <span>树编码字段</span>
+              <a-tooltip title="树显示的编码字段名, 如:dept_id" placement="top">
+                <QuestionCircleOutlined style="margin-left: 4px" />
+              </a-tooltip>
             </template>
-            <el-select v-model="info.treeCode" placeholder="请选择">
-              <el-option
+            <a-select v-model:value="info.treeCode" placeholder="请选择">
+              <a-select-option
                 v-for="(column, index) in info.columns"
                 :key="index"
-                :label="column.columnName + ':' + column.columnComment"
                 :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
+              >{{ column.columnName + ':' + column.columnComment }}</a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-item label="树父编码字段">
             <template #label>
-              树父编码字段
-              <el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
+              <span>树父编码字段</span>
+              <a-tooltip title="树显示的父编码字段名, 如:parent_Id" placement="top">
+                <QuestionCircleOutlined style="margin-left: 4px" />
+              </a-tooltip>
             </template>
-            <el-select v-model="info.treeParentCode" placeholder="请选择">
-              <el-option
+            <a-select v-model:value="info.treeParentCode" placeholder="请选择">
+              <a-select-option
                 v-for="(column, index) in info.columns"
                 :key="index"
-                :label="column.columnName + ':' + column.columnComment"
                 :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
+              >{{ column.columnName + ':' + column.columnComment }}</a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-item label="树名称字段">
             <template #label>
-              树名称字段
-              <el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
+              <span>树名称字段</span>
+              <a-tooltip title="树节点的显示名称字段名, 如:dept_name" placement="top">
+                <QuestionCircleOutlined style="margin-left: 4px" />
+              </a-tooltip>
             </template>
-            <el-select v-model="info.treeName" placeholder="请选择">
-              <el-option
+            <a-select v-model:value="info.treeName" placeholder="请选择">
+              <a-select-option
                 v-for="(column, index) in info.columns"
                 :key="index"
-                :label="column.columnName + ':' + column.columnComment"
                 :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-      </el-row>
+              >{{ column.columnName + ':' + column.columnComment }}</a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-col>
+      </a-row>
     </template>
 
     <template v-if="info.tplCategory == 'sub'">
       <h4 class="form-header">关联信息</h4>
-      <el-row>
-        <el-col :span="12">
-          <el-form-item>
+      <a-row>
+        <a-col :span="12">
+          <a-form-item label="关联子表的表名">
             <template #label>
-              关联子表的表名
-              <el-tooltip content="关联子表的表名, 如:sys_user" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
+              <span>关联子表的表名</span>
+              <a-tooltip title="关联子表的表名, 如:sys_user" placement="top">
+                <QuestionCircleOutlined style="margin-left: 4px" />
+              </a-tooltip>
             </template>
-            <el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange">
-              <el-option
+            <a-select v-model:value="info.subTableName" placeholder="请选择" @change="subSelectChange">
+              <a-select-option
                 v-for="(table, index) in tables"
                 :key="index"
-                :label="table.tableName + ':' + table.tableComment"
                 :value="table.tableName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
+              >{{ table.tableName + ':' + table.tableComment }}</a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-item label="子表关联的外键名">
             <template #label>
-              子表关联的外键名
-              <el-tooltip content="子表关联的外键名, 如:user_id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
+              <span>子表关联的外键名</span>
+              <a-tooltip title="子表关联的外键名, 如:user_id" placement="top">
+                <QuestionCircleOutlined style="margin-left: 4px" />
+              </a-tooltip>
             </template>
-            <el-select v-model="info.subTableFkName" placeholder="请选择">
-              <el-option
+            <a-select v-model:value="info.subTableFkName" placeholder="请选择">
+              <a-select-option
                 v-for="(column, index) in subColumns"
                 :key="index"
-                :label="column.columnName + ':' + column.columnComment"
                 :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-      </el-row>
+              >{{ column.columnName + ':' + column.columnComment }}</a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-col>
+      </a-row>
     </template>
 
-  </el-form>
+  </a-form>
 </template>
 
 <script setup>
 import { listMenu } from "@/api/system/menu"
+import { QuestionCircleOutlined, DownOutlined } from '@ant-design/icons-vue'
 
 const subColumns = ref([])
 const menuOptions = ref([])
 const { proxy } = getCurrentInstance()
+const genInfoFormRef = ref()
 
 const props = defineProps({
   info: {
@@ -302,4 +299,16 @@ watch(() => props.info.tplWebType, val => {
     props.info.tplWebType = "element-plus"
   }
 })
+
+defineExpose({
+  genInfoForm: genInfoFormRef
+})
 </script>
+
+<style lang="scss" scoped>
+.form-header {
+  margin: 20px 0 10px 0;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e8e8e8;
+}
+</style>