commit
f3ef324096
|
|
@ -26,7 +26,10 @@
|
||||||
"#/*": "./src/*"
|
"#/*": "./src/*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@form-create/ant-design-vue": "catalog:",
|
||||||
|
"@form-create/antd-designer": "catalog:",
|
||||||
"@tinymce/tinymce-vue": "catalog:",
|
"@tinymce/tinymce-vue": "catalog:",
|
||||||
|
"@types/lodash.clonedeep": "catalog:",
|
||||||
"@vben/access": "workspace:*",
|
"@vben/access": "workspace:*",
|
||||||
"@vben/common-ui": "workspace:*",
|
"@vben/common-ui": "workspace:*",
|
||||||
"@vben/constants": "workspace:*",
|
"@vben/constants": "workspace:*",
|
||||||
|
|
@ -47,8 +50,10 @@
|
||||||
"crypto-js": "catalog:",
|
"crypto-js": "catalog:",
|
||||||
"dayjs": "catalog:",
|
"dayjs": "catalog:",
|
||||||
"highlight.js": "catalog:",
|
"highlight.js": "catalog:",
|
||||||
|
"lodash.clonedeep": "catalog:",
|
||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
|
"vue-dompurify-html": "catalog:",
|
||||||
"vue-router": "catalog:"
|
"vue-router": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { createApp, watchEffect } from 'vue';
|
import { createApp, watchEffect } from 'vue';
|
||||||
|
import VueDOMPurifyHTML from 'vue-dompurify-html';
|
||||||
|
|
||||||
import { registerAccessDirective } from '@vben/access';
|
import { registerAccessDirective } from '@vben/access';
|
||||||
import { registerLoadingDirective } from '@vben/common-ui/es/loading';
|
import { registerLoadingDirective } from '@vben/common-ui/es/loading';
|
||||||
|
|
@ -10,6 +11,7 @@ import '@vben/styles/antd';
|
||||||
import { useTitle } from '@vueuse/core';
|
import { useTitle } from '@vueuse/core';
|
||||||
|
|
||||||
import { $t, setupI18n } from '#/locales';
|
import { $t, setupI18n } from '#/locales';
|
||||||
|
import { setupFormCreate } from '#/plugins/form-create';
|
||||||
|
|
||||||
import { initComponentAdapter } from './adapter/component';
|
import { initComponentAdapter } from './adapter/component';
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
|
|
@ -39,7 +41,7 @@ async function bootstrap(namespace: string) {
|
||||||
// 国际化 i18n 配置
|
// 国际化 i18n 配置
|
||||||
await setupI18n(app);
|
await setupI18n(app);
|
||||||
|
|
||||||
// 配置 pinia-tore
|
// 配置 pinia-store
|
||||||
await initStores(app, { namespace });
|
await initStores(app, { namespace });
|
||||||
|
|
||||||
// 安装权限指令
|
// 安装权限指令
|
||||||
|
|
@ -52,6 +54,12 @@ async function bootstrap(namespace: string) {
|
||||||
// 配置路由及路由守卫
|
// 配置路由及路由守卫
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
|
// formCreate
|
||||||
|
setupFormCreate(app);
|
||||||
|
|
||||||
|
// vue-dompurify-html
|
||||||
|
app.use(VueDOMPurifyHTML);
|
||||||
|
|
||||||
// 配置Motion插件
|
// 配置Motion插件
|
||||||
const { MotionPlugin } = await import('@vben/plugins/motion');
|
const { MotionPlugin } = await import('@vben/plugins/motion');
|
||||||
app.use(MotionPlugin);
|
app.use(MotionPlugin);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
<!-- 数据字典 Select 选择器 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { DictSelectProps } from '../typing';
|
||||||
|
|
||||||
|
import { computed, useAttrs } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Select,
|
||||||
|
SelectOption,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getBoolDictOptions,
|
||||||
|
getIntDictOptions,
|
||||||
|
getStrDictOptions,
|
||||||
|
} from '#/utils/dict';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DictSelect' });
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<DictSelectProps>(), {
|
||||||
|
valueType: 'str',
|
||||||
|
selectType: 'select',
|
||||||
|
});
|
||||||
|
|
||||||
|
const attrs = useAttrs();
|
||||||
|
|
||||||
|
// 获得字典配置
|
||||||
|
const getDictOptions = computed(() => {
|
||||||
|
switch (props.valueType) {
|
||||||
|
case 'bool': {
|
||||||
|
return getBoolDictOptions(props.dictType);
|
||||||
|
}
|
||||||
|
case 'int': {
|
||||||
|
return getIntDictOptions(props.dictType);
|
||||||
|
}
|
||||||
|
case 'str': {
|
||||||
|
return getStrDictOptions(props.dictType);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Select v-if="selectType === 'select'" class="w-1/1" v-bind="attrs">
|
||||||
|
<SelectOption
|
||||||
|
v-for="(dict, index) in getDictOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</SelectOption>
|
||||||
|
</Select>
|
||||||
|
<RadioGroup v-if="selectType === 'radio'" class="w-1/1" v-bind="attrs">
|
||||||
|
<Radio
|
||||||
|
v-for="(dict, index) in getDictOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</Radio>
|
||||||
|
</RadioGroup>
|
||||||
|
<CheckboxGroup v-if="selectType === 'checkbox'" class="w-1/1" v-bind="attrs">
|
||||||
|
<Checkbox
|
||||||
|
v-for="(dict, index) in getDictOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</Checkbox>
|
||||||
|
</CheckboxGroup>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,290 @@
|
||||||
|
import type { ApiSelectProps } from '#/components/form-create/typing';
|
||||||
|
|
||||||
|
import { defineComponent, onMounted, ref, useAttrs } from 'vue';
|
||||||
|
|
||||||
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Select,
|
||||||
|
SelectOption,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export const useApiSelect = (option: ApiSelectProps) => {
|
||||||
|
return defineComponent({
|
||||||
|
name: option.name,
|
||||||
|
props: {
|
||||||
|
// 选项标签
|
||||||
|
labelField: {
|
||||||
|
type: String,
|
||||||
|
default: () => option.labelField ?? 'label',
|
||||||
|
},
|
||||||
|
// 选项的值
|
||||||
|
valueField: {
|
||||||
|
type: String,
|
||||||
|
default: () => option.valueField ?? 'value',
|
||||||
|
},
|
||||||
|
// api 接口
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: () => option.url ?? '',
|
||||||
|
},
|
||||||
|
// 请求类型
|
||||||
|
method: {
|
||||||
|
type: String,
|
||||||
|
default: 'GET',
|
||||||
|
},
|
||||||
|
// 选项解析函数
|
||||||
|
parseFunc: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// 请求参数
|
||||||
|
data: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
|
||||||
|
selectType: {
|
||||||
|
type: String,
|
||||||
|
default: 'select',
|
||||||
|
},
|
||||||
|
// 是否多选
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 是否远程搜索
|
||||||
|
remote: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 远程搜索时携带的参数
|
||||||
|
remoteField: {
|
||||||
|
type: String,
|
||||||
|
default: 'label',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const options = ref<any[]>([]); // 下拉数据
|
||||||
|
const loading = ref(false); // 是否正在从远程获取数据
|
||||||
|
const queryParam = ref<any>(); // 当前输入的值
|
||||||
|
const getOptions = async () => {
|
||||||
|
options.value = [];
|
||||||
|
// 接口选择器
|
||||||
|
if (isEmpty(props.url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (props.method) {
|
||||||
|
case 'GET': {
|
||||||
|
let url: string = props.url;
|
||||||
|
if (props.remote && queryParam.value !== undefined) {
|
||||||
|
url = url.includes('?')
|
||||||
|
? `${url}&${props.remoteField}=${queryParam.value}`
|
||||||
|
: `${url}?${props.remoteField}=${queryParam.value}`;
|
||||||
|
}
|
||||||
|
parseOptions(await requestClient.get(url));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'POST': {
|
||||||
|
const data: any = JSON.parse(props.data);
|
||||||
|
if (props.remote) {
|
||||||
|
data[props.remoteField] = queryParam.value;
|
||||||
|
}
|
||||||
|
parseOptions(await requestClient.post(props.url, data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseOptions(data: any) {
|
||||||
|
// 情况一:如果有自定义解析函数优先使用自定义解析
|
||||||
|
if (!isEmpty(props.parseFunc)) {
|
||||||
|
options.value = parseFunc()?.(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 情况二:返回的直接是一个列表
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
parseOptions0(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 情况二:返回的是分页数据,尝试读取 list
|
||||||
|
data = data.list;
|
||||||
|
if (!!data && Array.isArray(data)) {
|
||||||
|
parseOptions0(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 情况三:不是 yudao-vue-pro 标准返回
|
||||||
|
console.warn(
|
||||||
|
`接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOptions0(data: any[]) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
options.value = data.map((item: any) => ({
|
||||||
|
label: parseExpression(item, props.labelField),
|
||||||
|
value: parseExpression(item, props.valueField),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.warn(`接口[${props.url}] 返回结果不是一个数组`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFunc() {
|
||||||
|
let parse: any = null;
|
||||||
|
if (props.parseFunc) {
|
||||||
|
// 解析字符串函数
|
||||||
|
// eslint-disable-next-line no-new-func
|
||||||
|
parse = new Function(`return ${props.parseFunc}`)();
|
||||||
|
}
|
||||||
|
return parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseExpression(data: any, template: string) {
|
||||||
|
// 检测是否使用了表达式
|
||||||
|
if (!template.includes('${')) {
|
||||||
|
return data[template];
|
||||||
|
}
|
||||||
|
// 正则表达式匹配模板字符串中的 ${...}
|
||||||
|
const pattern = /\$\{([^}]*)\}/g;
|
||||||
|
// 使用replace函数配合正则表达式和回调函数来进行替换
|
||||||
|
return template.replaceAll(pattern, (_, expr) => {
|
||||||
|
// expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值
|
||||||
|
const result = data[expr.trim()]; // 去除前后空白,以防用户输入带空格的属性名
|
||||||
|
if (!result) {
|
||||||
|
console.warn(
|
||||||
|
`接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteMethod = async (query: any) => {
|
||||||
|
if (!query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
queryParam.value = query;
|
||||||
|
await getOptions();
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getOptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildSelect = () => {
|
||||||
|
if (props.multiple) {
|
||||||
|
// fix:多写此步是为了解决 multiple 属性问题
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
class="w-1/1"
|
||||||
|
loading={loading.value}
|
||||||
|
mode="multiple"
|
||||||
|
{...attrs}
|
||||||
|
// TODO: remote 对等实现
|
||||||
|
// remote={props.remote}
|
||||||
|
{...(props.remote && { remoteMethod })}
|
||||||
|
>
|
||||||
|
{options.value.map(
|
||||||
|
(item: { label: any; value: any }, index: any) => (
|
||||||
|
<SelectOption key={index} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</SelectOption>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
class="w-1/1"
|
||||||
|
loading={loading.value}
|
||||||
|
{...attrs}
|
||||||
|
// TODO: @dhb52 remote 对等实现, 还是说没作用
|
||||||
|
// remote={props.remote}
|
||||||
|
{...(props.remote && { remoteMethod })}
|
||||||
|
>
|
||||||
|
{options.value.map(
|
||||||
|
(item: { label: any; value: any }, index: any) => (
|
||||||
|
<SelectOption key={index} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</SelectOption>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const buildCheckbox = () => {
|
||||||
|
if (isEmpty(options.value)) {
|
||||||
|
options.value = [
|
||||||
|
{ label: '选项1', value: '选项1' },
|
||||||
|
{ label: '选项2', value: '选项2' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CheckboxGroup class="w-1/1" {...attrs}>
|
||||||
|
{options.value.map(
|
||||||
|
(item: { label: any; value: any }, index: any) => (
|
||||||
|
<Checkbox key={index} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</Checkbox>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</CheckboxGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const buildRadio = () => {
|
||||||
|
if (isEmpty(options.value)) {
|
||||||
|
options.value = [
|
||||||
|
{ label: '选项1', value: '选项1' },
|
||||||
|
{ label: '选项2', value: '选项2' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<RadioGroup class="w-1/1" {...attrs}>
|
||||||
|
{options.value.map(
|
||||||
|
(item: { label: any; value: any }, index: any) => (
|
||||||
|
<Radio key={index} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</Radio>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</RadioGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return () => (
|
||||||
|
<>
|
||||||
|
{(() => {
|
||||||
|
switch (props.selectType) {
|
||||||
|
case 'checkbox': {
|
||||||
|
return buildCheckbox();
|
||||||
|
}
|
||||||
|
case 'radio': {
|
||||||
|
return buildRadio();
|
||||||
|
}
|
||||||
|
case 'select': {
|
||||||
|
return buildSelect();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return buildSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
import ImageUpload from '#/components/upload/image-upload.vue';
|
||||||
|
|
||||||
|
export const useImagesUpload = () => {
|
||||||
|
return defineComponent({
|
||||||
|
props: {
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
maxNumber: {
|
||||||
|
type: Number,
|
||||||
|
default: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
// TODO: @dhb52 其实还是靠 props 默认参数起作用,没能从 formCreate 传递
|
||||||
|
return (props: { maxNumber?: number; multiple?: boolean }) => (
|
||||||
|
<ImageUpload maxNumber={props.maxNumber} multiple={props.multiple} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
name: 'ImagesUpload',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
|
import type { Menu } from '#/components/form-create/typing';
|
||||||
|
|
||||||
|
import { nextTick, onMounted } from 'vue';
|
||||||
|
|
||||||
|
import { apiSelectRule } from '#/components/form-create/rules/data';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useDictSelectRule,
|
||||||
|
useEditorRule,
|
||||||
|
useSelectRule,
|
||||||
|
useUploadFileRule,
|
||||||
|
useUploadImageRule,
|
||||||
|
useUploadImagesRule,
|
||||||
|
} from './rules';
|
||||||
|
|
||||||
|
export function makeRequiredRule() {
|
||||||
|
return {
|
||||||
|
type: 'Required',
|
||||||
|
field: 'formCreate$required',
|
||||||
|
title: '是否必填',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const localeProps = (
|
||||||
|
t: (msg: string) => any,
|
||||||
|
prefix: string,
|
||||||
|
rules: any[],
|
||||||
|
) => {
|
||||||
|
return rules.map((rule: { field: string; title: any }) => {
|
||||||
|
if (rule.field === 'formCreate$required') {
|
||||||
|
rule.title = t('props.required') || rule.title;
|
||||||
|
} else if (rule.field && rule.field !== '_optionType') {
|
||||||
|
rule.title = t(`components.${prefix}.${rule.field}`) || rule.title;
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析表单组件的 field, title 等字段(递归,如果组件包含子组件)
|
||||||
|
*
|
||||||
|
* @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule
|
||||||
|
* @param fields 解析后表单组件字段
|
||||||
|
* @param parentTitle 如果是子表单,子表单的标题,默认为空
|
||||||
|
*/
|
||||||
|
export const parseFormFields = (
|
||||||
|
rule: Record<string, any>,
|
||||||
|
fields: Array<Record<string, any>> = [],
|
||||||
|
parentTitle: string = '',
|
||||||
|
) => {
|
||||||
|
const { type, field, $required, title: tempTitle, children } = rule;
|
||||||
|
if (field && tempTitle) {
|
||||||
|
let title = tempTitle;
|
||||||
|
if (parentTitle) {
|
||||||
|
title = `${parentTitle}.${tempTitle}`;
|
||||||
|
}
|
||||||
|
let required = false;
|
||||||
|
if ($required) {
|
||||||
|
required = true;
|
||||||
|
}
|
||||||
|
fields.push({
|
||||||
|
field,
|
||||||
|
title,
|
||||||
|
type,
|
||||||
|
required,
|
||||||
|
});
|
||||||
|
// TODO 子表单 需要处理子表单字段
|
||||||
|
// if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) {
|
||||||
|
// // 解析子表单的字段
|
||||||
|
// rule.props.rule.forEach((item) => {
|
||||||
|
// parseFields(item, fieldsPermission, title)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if (children && Array.isArray(children)) {
|
||||||
|
children.forEach((rule) => {
|
||||||
|
parseFormFields(rule, fields);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单设计器增强 hook
|
||||||
|
* 新增
|
||||||
|
* - 文件上传
|
||||||
|
* - 单图上传
|
||||||
|
* - 多图上传
|
||||||
|
* - 字典选择器
|
||||||
|
* - 用户选择器
|
||||||
|
* - 部门选择器
|
||||||
|
* - 富文本
|
||||||
|
*/
|
||||||
|
export const useFormCreateDesigner = async (designer: Ref) => {
|
||||||
|
const editorRule = useEditorRule();
|
||||||
|
const uploadFileRule = useUploadFileRule();
|
||||||
|
const uploadImageRule = useUploadImageRule();
|
||||||
|
const uploadImagesRule = useUploadImagesRule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建表单组件
|
||||||
|
*/
|
||||||
|
const buildFormComponents = () => {
|
||||||
|
// 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
|
||||||
|
designer.value?.removeMenuItem('upload');
|
||||||
|
// 移除自带的富文本组件规则,使用 editorRule 替代
|
||||||
|
designer.value?.removeMenuItem('fc-editor');
|
||||||
|
const components = [
|
||||||
|
editorRule,
|
||||||
|
uploadFileRule,
|
||||||
|
uploadImageRule,
|
||||||
|
uploadImagesRule,
|
||||||
|
];
|
||||||
|
components.forEach((component) => {
|
||||||
|
// 插入组件规则
|
||||||
|
designer.value?.addComponent(component);
|
||||||
|
// 插入拖拽按钮到 `main` 分类下
|
||||||
|
designer.value?.appendMenuItem('main', {
|
||||||
|
icon: component.icon,
|
||||||
|
name: component.name,
|
||||||
|
label: component.label,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const userSelectRule = useSelectRule({
|
||||||
|
name: 'UserSelect',
|
||||||
|
label: '用户选择器',
|
||||||
|
icon: 'icon-eye',
|
||||||
|
});
|
||||||
|
const deptSelectRule = useSelectRule({
|
||||||
|
name: 'DeptSelect',
|
||||||
|
label: '部门选择器',
|
||||||
|
icon: 'icon-tree',
|
||||||
|
});
|
||||||
|
const dictSelectRule = useDictSelectRule();
|
||||||
|
const apiSelectRule0 = useSelectRule({
|
||||||
|
name: 'ApiSelect',
|
||||||
|
label: '接口选择器',
|
||||||
|
icon: 'icon-json',
|
||||||
|
props: [...apiSelectRule],
|
||||||
|
event: ['click', 'change', 'visibleChange', 'clear', 'blur', 'focus'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建系统字段菜单
|
||||||
|
*/
|
||||||
|
const buildSystemMenu = () => {
|
||||||
|
// 移除自带的下拉选择器组件,使用 currencySelectRule 替代
|
||||||
|
// designer.value?.removeMenuItem('select')
|
||||||
|
// designer.value?.removeMenuItem('radio')
|
||||||
|
// designer.value?.removeMenuItem('checkbox')
|
||||||
|
const components = [
|
||||||
|
userSelectRule,
|
||||||
|
deptSelectRule,
|
||||||
|
dictSelectRule,
|
||||||
|
apiSelectRule0,
|
||||||
|
];
|
||||||
|
const menu: Menu = {
|
||||||
|
name: 'system',
|
||||||
|
title: '系统字段',
|
||||||
|
list: components.map((component) => {
|
||||||
|
// 插入组件规则
|
||||||
|
designer.value?.addComponent(component);
|
||||||
|
// 插入拖拽按钮到 `system` 分类下
|
||||||
|
return {
|
||||||
|
icon: component.icon,
|
||||||
|
name: component.name,
|
||||||
|
label: component.label,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
designer.value?.addMenu(menu);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
buildFormComponents();
|
||||||
|
buildSystemMenu();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { useApiSelect } from './components/use-api-select';
|
||||||
|
|
||||||
|
export { useFormCreateDesigner } from './helpers';
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
/* eslint-disable no-template-curly-in-string */
|
||||||
|
const selectRule = [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'selectType',
|
||||||
|
title: '选择器类型',
|
||||||
|
value: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '下拉框', value: 'select' },
|
||||||
|
{ label: '单选框', value: 'radio' },
|
||||||
|
{ label: '多选框', value: 'checkbox' },
|
||||||
|
],
|
||||||
|
// 参考 https://www.form-create.com/v3/guide/control 组件联动,单选框和多选框不需要多选属性
|
||||||
|
control: [
|
||||||
|
{
|
||||||
|
value: 'select',
|
||||||
|
condition: '==',
|
||||||
|
method: 'hidden',
|
||||||
|
rule: [
|
||||||
|
'multiple',
|
||||||
|
'clearable',
|
||||||
|
'collapseTags',
|
||||||
|
'multipleLimit',
|
||||||
|
'allowCreate',
|
||||||
|
'filterable',
|
||||||
|
'noMatchText',
|
||||||
|
'remote',
|
||||||
|
'remoteMethod',
|
||||||
|
'reserveKeyword',
|
||||||
|
'defaultFirstOption',
|
||||||
|
'automaticDropdown',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'filterable',
|
||||||
|
title: '是否可搜索',
|
||||||
|
},
|
||||||
|
{ type: 'switch', field: 'multiple', title: '是否多选' },
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'disabled',
|
||||||
|
title: '是否禁用',
|
||||||
|
},
|
||||||
|
{ type: 'switch', field: 'clearable', title: '是否可以清空选项' },
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'collapseTags',
|
||||||
|
title: '多选时是否将选中值按文字的形式展示',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'multipleLimit',
|
||||||
|
title: '多选时用户最多可以选择的项目数,为 0 则不限制',
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'autocomplete',
|
||||||
|
title: 'autocomplete 属性',
|
||||||
|
},
|
||||||
|
{ type: 'input', field: 'placeholder', title: '占位符' },
|
||||||
|
{ type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'noMatchText',
|
||||||
|
title: '搜索条件无匹配时显示的文字',
|
||||||
|
},
|
||||||
|
{ type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'reserveKeyword',
|
||||||
|
title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'defaultFirstOption',
|
||||||
|
title: '在输入框按下回车,选择第一个匹配项',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'popperAppendToBody',
|
||||||
|
title: '是否将弹出框插入至 body 元素',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'automaticDropdown',
|
||||||
|
title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const apiSelectRule = [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'url',
|
||||||
|
title: 'url 地址',
|
||||||
|
props: {
|
||||||
|
placeholder: '/system/user/simple-list',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'method',
|
||||||
|
title: '请求类型',
|
||||||
|
value: 'GET',
|
||||||
|
options: [
|
||||||
|
{ label: 'GET', value: 'GET' },
|
||||||
|
{ label: 'POST', value: 'POST' },
|
||||||
|
],
|
||||||
|
control: [
|
||||||
|
{
|
||||||
|
value: 'GET',
|
||||||
|
condition: '!=',
|
||||||
|
method: 'hidden',
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'data',
|
||||||
|
title: '请求参数 JSON 格式',
|
||||||
|
props: {
|
||||||
|
autosize: true,
|
||||||
|
type: 'textarea',
|
||||||
|
placeholder: '{"type": 1}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'labelField',
|
||||||
|
title: 'label 属性',
|
||||||
|
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}',
|
||||||
|
props: {
|
||||||
|
placeholder: 'nickname',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'valueField',
|
||||||
|
title: 'value 属性',
|
||||||
|
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}',
|
||||||
|
props: {
|
||||||
|
placeholder: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'parseFunc',
|
||||||
|
title: '选项解析函数',
|
||||||
|
info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表
|
||||||
|
(data: any)=>{ label: string; value: any }[]`,
|
||||||
|
props: {
|
||||||
|
autosize: true,
|
||||||
|
rows: { minRows: 2, maxRows: 6 },
|
||||||
|
type: 'textarea',
|
||||||
|
placeholder: `
|
||||||
|
function (data) {
|
||||||
|
console.log(data)
|
||||||
|
return data.list.map(item=> ({label: item.nickname,value: item.id}))
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'remote',
|
||||||
|
info: '是否可搜索',
|
||||||
|
title: '其中的选项是否从服务器远程加载',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'remoteField',
|
||||||
|
title: '请求参数',
|
||||||
|
info: '远程请求时请求携带的参数名称,如:name',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export { apiSelectRule, selectRule };
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export { useDictSelectRule } from './use-dict-select';
|
||||||
|
export { useEditorRule } from './use-editor-rule';
|
||||||
|
export { useSelectRule } from './use-select-rule';
|
||||||
|
export { useUploadFileRule } from './use-upload-file-rule';
|
||||||
|
export { useUploadImageRule } from './use-upload-image-rule';
|
||||||
|
export { useUploadImagesRule } from './use-upload-images-rule';
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
|
|
||||||
|
import * as DictDataApi from '#/api/system/dict/type';
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
import { selectRule } from '#/components/form-create/rules/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule
|
||||||
|
*/
|
||||||
|
export const useDictSelectRule = () => {
|
||||||
|
const label = '字典选择器';
|
||||||
|
const name = 'DictSelect';
|
||||||
|
const rules = cloneDeep(selectRule);
|
||||||
|
const dictOptions = ref<{ label: string; value: string }[]>([]); // 字典类型下拉数据
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await DictDataApi.getSimpleDictTypeList();
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dictOptions.value =
|
||||||
|
data?.map((item: DictDataApi.SystemDictTypeApi.DictType) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.type,
|
||||||
|
})) ?? [];
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
icon: 'icon-descriptions',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'dictType',
|
||||||
|
title: '字典类型',
|
||||||
|
value: '',
|
||||||
|
options: dictOptions.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'valueType',
|
||||||
|
title: '字典值类型',
|
||||||
|
value: 'str',
|
||||||
|
options: [
|
||||||
|
{ label: '数字', value: 'int' },
|
||||||
|
{ label: '字符串', value: 'str' },
|
||||||
|
{ label: '布尔值', value: 'bool' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...rules,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
|
||||||
|
export const useEditorRule = () => {
|
||||||
|
const label = '富文本';
|
||||||
|
const name = 'Tinymce';
|
||||||
|
return {
|
||||||
|
icon: 'icon-editor',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'height',
|
||||||
|
title: '高度',
|
||||||
|
},
|
||||||
|
{ type: 'switch', field: 'readonly', title: '是否只读' },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import type { SelectRuleOption } from '#/components/form-create/typing';
|
||||||
|
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
|
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
import { selectRule } from '#/components/form-create/rules/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用选择器规则 hook
|
||||||
|
*
|
||||||
|
* @param option 规则配置
|
||||||
|
*/
|
||||||
|
export const useSelectRule = (option: SelectRuleOption) => {
|
||||||
|
const label = option.label;
|
||||||
|
const name = option.name;
|
||||||
|
const rules = cloneDeep(selectRule);
|
||||||
|
return {
|
||||||
|
icon: option.icon,
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
event: option.event,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
if (!option.props) {
|
||||||
|
option.props = [];
|
||||||
|
}
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
...option.props,
|
||||||
|
...rules,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
|
||||||
|
export const useUploadFileRule = () => {
|
||||||
|
const label = '文件上传';
|
||||||
|
const name = 'FileUpload';
|
||||||
|
return {
|
||||||
|
icon: 'icon-upload',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'fileType',
|
||||||
|
title: '文件类型',
|
||||||
|
value: ['doc', 'xls', 'ppt', 'txt', 'pdf'],
|
||||||
|
options: [
|
||||||
|
{ label: 'doc', value: 'doc' },
|
||||||
|
{ label: 'xls', value: 'xls' },
|
||||||
|
{ label: 'ppt', value: 'ppt' },
|
||||||
|
{ label: 'txt', value: 'txt' },
|
||||||
|
{ label: 'pdf', value: 'pdf' },
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
multiple: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'autoUpload',
|
||||||
|
title: '是否在选取文件后立即进行上传',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'drag',
|
||||||
|
title: '拖拽上传',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'isShowTip',
|
||||||
|
title: '是否显示提示',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'fileSize',
|
||||||
|
title: '大小限制(MB)',
|
||||||
|
value: 5,
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'limit',
|
||||||
|
title: '数量限制',
|
||||||
|
value: 5,
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'disabled',
|
||||||
|
title: '是否禁用',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
|
||||||
|
export const useUploadImageRule = () => {
|
||||||
|
const label = '单图上传';
|
||||||
|
const name = 'ImageUpload';
|
||||||
|
return {
|
||||||
|
icon: 'icon-image',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'drag',
|
||||||
|
title: '拖拽上传',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'fileType',
|
||||||
|
title: '图片类型限制',
|
||||||
|
value: ['image/jpeg', 'image/png', 'image/gif'],
|
||||||
|
options: [
|
||||||
|
{ label: 'image/apng', value: 'image/apng' },
|
||||||
|
{ label: 'image/bmp', value: 'image/bmp' },
|
||||||
|
{ label: 'image/gif', value: 'image/gif' },
|
||||||
|
{ label: 'image/jpeg', value: 'image/jpeg' },
|
||||||
|
{ label: 'image/pjpeg', value: 'image/pjpeg' },
|
||||||
|
{ label: 'image/svg+xml', value: 'image/svg+xml' },
|
||||||
|
{ label: 'image/tiff', value: 'image/tiff' },
|
||||||
|
{ label: 'image/webp', value: 'image/webp' },
|
||||||
|
{ label: 'image/x-icon', value: 'image/x-icon' },
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
multiple: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'fileSize',
|
||||||
|
title: '大小限制(MB)',
|
||||||
|
value: 5,
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'height',
|
||||||
|
title: '组件高度',
|
||||||
|
value: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'width',
|
||||||
|
title: '组件宽度',
|
||||||
|
value: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'borderradius',
|
||||||
|
title: '组件边框圆角',
|
||||||
|
value: '8px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'disabled',
|
||||||
|
title: '是否显示删除按钮',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'showBtnText',
|
||||||
|
title: '是否显示按钮文字',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { buildUUID } from '@vben/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
localeProps,
|
||||||
|
makeRequiredRule,
|
||||||
|
} from '#/components/form-create/helpers';
|
||||||
|
|
||||||
|
export const useUploadImagesRule = () => {
|
||||||
|
const label = '多图上传';
|
||||||
|
const name = 'ImagesUpload';
|
||||||
|
return {
|
||||||
|
icon: 'icon-image',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: buildUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props(_: any, { t }: any) {
|
||||||
|
return localeProps(t, `${name}.props`, [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'switch',
|
||||||
|
field: 'drag',
|
||||||
|
title: '拖拽上传',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
field: 'fileType',
|
||||||
|
title: '图片类型限制',
|
||||||
|
value: ['image/jpeg', 'image/png', 'image/gif'],
|
||||||
|
options: [
|
||||||
|
{ label: 'image/apng', value: 'image/apng' },
|
||||||
|
{ label: 'image/bmp', value: 'image/bmp' },
|
||||||
|
{ label: 'image/gif', value: 'image/gif' },
|
||||||
|
{ label: 'image/jpeg', value: 'image/jpeg' },
|
||||||
|
{ label: 'image/pjpeg', value: 'image/pjpeg' },
|
||||||
|
{ label: 'image/svg+xml', value: 'image/svg+xml' },
|
||||||
|
{ label: 'image/tiff', value: 'image/tiff' },
|
||||||
|
{ label: 'image/webp', value: 'image/webp' },
|
||||||
|
{ label: 'image/x-icon', value: 'image/x-icon' },
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
multiple: true,
|
||||||
|
maxNumber: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'fileSize',
|
||||||
|
title: '大小限制(MB)',
|
||||||
|
value: 5,
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inputNumber',
|
||||||
|
field: 'limit',
|
||||||
|
title: '数量限制',
|
||||||
|
value: 5,
|
||||||
|
props: { min: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'height',
|
||||||
|
title: '组件高度',
|
||||||
|
value: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'width',
|
||||||
|
title: '组件宽度',
|
||||||
|
value: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'borderradius',
|
||||||
|
title: '组件边框圆角',
|
||||||
|
value: '8px',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import type { Rule } from '@form-create/ant-design-vue';
|
||||||
|
|
||||||
|
// 数据字典 Select 选择器组件 Props 类型
|
||||||
|
export interface DictSelectProps {
|
||||||
|
dictType: string; // 字典类型
|
||||||
|
valueType?: 'bool' | 'int' | 'str'; // 字典值类型
|
||||||
|
selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
|
||||||
|
formCreateInject?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左侧拖拽按钮
|
||||||
|
export interface MenuItem {
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左侧拖拽按钮分类
|
||||||
|
export interface Menu {
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
list: MenuItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MenuList = Array<Menu>;
|
||||||
|
|
||||||
|
// 拖拽组件的规则
|
||||||
|
export interface DragRule {
|
||||||
|
icon: string;
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
children?: string;
|
||||||
|
inside?: true;
|
||||||
|
drag?: string | true;
|
||||||
|
dragBtn?: false;
|
||||||
|
mask?: false;
|
||||||
|
|
||||||
|
rule(): Rule;
|
||||||
|
|
||||||
|
props(v: any, v1: any): Rule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用下拉组件 Props 类型
|
||||||
|
export interface ApiSelectProps {
|
||||||
|
name: string; // 组件名称
|
||||||
|
labelField?: string; // 选项标签
|
||||||
|
valueField?: string; // 选项的值
|
||||||
|
url?: string; // url 接口
|
||||||
|
isDict?: boolean; // 是否字典选择器
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择组件规则配置类型
|
||||||
|
export interface SelectRuleOption {
|
||||||
|
label: string; // label 名称
|
||||||
|
name: string; // 组件名称
|
||||||
|
icon: string; // 组件图标
|
||||||
|
props?: any[]; // 组件规则
|
||||||
|
event?: any[]; // 事件配置
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import {
|
||||||
|
|
||||||
type InitOptions = IPropTypes['init'];
|
type InitOptions = IPropTypes['init'];
|
||||||
|
|
||||||
defineOptions({ inheritAttrs: false });
|
defineOptions({ name: 'Tinymce', inheritAttrs: false });
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
options: {
|
options: {
|
||||||
|
|
@ -157,11 +157,11 @@ const initOptions = computed((): InitOptions => {
|
||||||
const { httpRequest } = useUpload();
|
const { httpRequest } = useUpload();
|
||||||
httpRequest(file)
|
httpRequest(file)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
console.log('tinymce 上传图片成功:', url);
|
// console.log('tinymce 上传图片成功:', url);
|
||||||
resolve(url);
|
resolve(url);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('tinymce 上传图片失败:', error);
|
// console.error('tinymce 上传图片失败:', error);
|
||||||
reject(error.message);
|
reject(error.message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"copy": "Copy",
|
||||||
|
"copySuccess": "Copy Success",
|
||||||
|
"copyError": "Copy Error"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"copy": "复制",
|
||||||
|
"copySuccess": "复制成功",
|
||||||
|
"copyError": "复制失败"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import type { App } from 'vue';
|
||||||
|
|
||||||
|
// import install from '@form-create/ant-design-vue/auto-import';
|
||||||
|
import FcDesigner from '@form-create/antd-designer';
|
||||||
|
import Antd from 'ant-design-vue';
|
||||||
|
|
||||||
|
// ======================= 自定义组件 =======================
|
||||||
|
import { useApiSelect } from '#/components/form-create';
|
||||||
|
import DictSelect from '#/components/form-create/components/dict-select.vue';
|
||||||
|
import { useImagesUpload } from '#/components/form-create/components/use-images-upload';
|
||||||
|
import { Tinymce } from '#/components/tinymce';
|
||||||
|
import { FileUpload, ImageUpload } from '#/components/upload';
|
||||||
|
|
||||||
|
const UserSelect = useApiSelect({
|
||||||
|
name: 'UserSelect',
|
||||||
|
labelField: 'nickname',
|
||||||
|
valueField: 'id',
|
||||||
|
url: '/system/user/simple-list',
|
||||||
|
});
|
||||||
|
const DeptSelect = useApiSelect({
|
||||||
|
name: 'DeptSelect',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
|
url: '/system/dept/simple-list',
|
||||||
|
});
|
||||||
|
const ApiSelect = useApiSelect({
|
||||||
|
name: 'ApiSelect',
|
||||||
|
});
|
||||||
|
const ImagesUpload = useImagesUpload();
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
ImageUpload,
|
||||||
|
ImagesUpload,
|
||||||
|
FileUpload,
|
||||||
|
Tinymce,
|
||||||
|
DictSelect,
|
||||||
|
UserSelect,
|
||||||
|
DeptSelect,
|
||||||
|
ApiSelect,
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: @dhb52 按需导入,而不是app.use(Antd);
|
||||||
|
// 参考 http://www.form-create.com/v3/ant-design-vue/auto-import.html 文档
|
||||||
|
export const setupFormCreate = (app: App) => {
|
||||||
|
components.forEach((component) => {
|
||||||
|
app.component(component.name as string, component);
|
||||||
|
});
|
||||||
|
app.use(Antd);
|
||||||
|
app.use(FcDesigner);
|
||||||
|
app.use(FcDesigner.formCreate);
|
||||||
|
};
|
||||||
|
|
@ -6,7 +6,28 @@ import { isObject } from '@vben/utils';
|
||||||
|
|
||||||
import { useDictStore } from '#/store';
|
import { useDictStore } from '#/store';
|
||||||
|
|
||||||
const dictStore = useDictStore();
|
// TODO @dhb52:top-level 调用 导致:"getActivePinia()" was called but there was no active Pinia
|
||||||
|
// 先临时移入到方法中
|
||||||
|
// const dictStore = useDictStore();
|
||||||
|
|
||||||
|
// TODO @dhb: antd 组件的 color 类型
|
||||||
|
type ColorType = 'error' | 'info' | 'success' | 'warning';
|
||||||
|
|
||||||
|
export interface DictDataType {
|
||||||
|
dictType: string;
|
||||||
|
label: string;
|
||||||
|
value: boolean | number | string;
|
||||||
|
colorType: ColorType;
|
||||||
|
cssClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NumberDictDataType extends DictDataType {
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StringDictDataType extends DictDataType {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取字典标签
|
* 获取字典标签
|
||||||
|
|
@ -16,6 +37,7 @@ const dictStore = useDictStore();
|
||||||
* @returns 字典标签
|
* @returns 字典标签
|
||||||
*/
|
*/
|
||||||
function getDictLabel(dictType: string, value: any) {
|
function getDictLabel(dictType: string, value: any) {
|
||||||
|
const dictStore = useDictStore();
|
||||||
const dictObj = dictStore.getDictData(dictType, value);
|
const dictObj = dictStore.getDictData(dictType, value);
|
||||||
return isObject(dictObj) ? dictObj.label : '';
|
return isObject(dictObj) ? dictObj.label : '';
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +50,7 @@ function getDictLabel(dictType: string, value: any) {
|
||||||
* @returns 字典对象
|
* @returns 字典对象
|
||||||
*/
|
*/
|
||||||
function getDictObj(dictType: string, value: any) {
|
function getDictObj(dictType: string, value: any) {
|
||||||
|
const dictStore = useDictStore();
|
||||||
const dictObj = dictStore.getDictData(dictType, value);
|
const dictObj = dictStore.getDictData(dictType, value);
|
||||||
return isObject(dictObj) ? dictObj : null;
|
return isObject(dictObj) ? dictObj : null;
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +65,7 @@ function getDictOptions(
|
||||||
dictType: string,
|
dictType: string,
|
||||||
valueType: 'boolean' | 'number' | 'string' = 'string',
|
valueType: 'boolean' | 'number' | 'string' = 'string',
|
||||||
) {
|
) {
|
||||||
|
const dictStore = useDictStore();
|
||||||
const dictOpts = dictStore.getDictOptions(dictType);
|
const dictOpts = dictStore.getDictOptions(dictType);
|
||||||
const dictOptions: DefaultOptionType = [];
|
const dictOptions: DefaultOptionType = [];
|
||||||
if (dictOpts.length > 0) {
|
if (dictOpts.length > 0) {
|
||||||
|
|
@ -71,6 +95,48 @@ function getDictOptions(
|
||||||
return dictOptions.length > 0 ? dictOptions : [];
|
return dictOptions.length > 0 ? dictOptions : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getIntDictOptions = (dictType: string): NumberDictDataType[] => {
|
||||||
|
// 获得通用的 DictDataType 列表
|
||||||
|
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
||||||
|
// 转换成 number 类型的 NumberDictDataType 类型
|
||||||
|
// why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警
|
||||||
|
const dictOption: NumberDictDataType[] = [];
|
||||||
|
dictOptions.forEach((dict: DictDataType) => {
|
||||||
|
dictOption.push({
|
||||||
|
...dict,
|
||||||
|
value: Number.parseInt(`${dict.value}`),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dictOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStrDictOptions = (dictType: string) => {
|
||||||
|
// 获得通用的 DictDataType 列表
|
||||||
|
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
||||||
|
// 转换成 string 类型的 StringDictDataType 类型
|
||||||
|
// why 需要特殊转换:避免 IDEA 在 v-for="dict in getStrDictOptions(...)" 时,el-option 的 key 会告警
|
||||||
|
const dictOption: StringDictDataType[] = [];
|
||||||
|
dictOptions.forEach((dict: DictDataType) => {
|
||||||
|
dictOption.push({
|
||||||
|
...dict,
|
||||||
|
value: `${dict.value}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dictOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBoolDictOptions = (dictType: string) => {
|
||||||
|
const dictOption: DictDataType[] = [];
|
||||||
|
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
||||||
|
dictOptions.forEach((dict: DictDataType) => {
|
||||||
|
dictOption.push({
|
||||||
|
...dict,
|
||||||
|
value: `${dict.value}` === 'true',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dictOption;
|
||||||
|
};
|
||||||
|
|
||||||
enum DICT_TYPE {
|
enum DICT_TYPE {
|
||||||
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
||||||
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
<!-- eslint-disable no-useless-escape -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref, unref } from 'vue';
|
||||||
|
|
||||||
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { isString } from '@vben/utils';
|
||||||
|
|
||||||
|
import formCreate from '@form-create/ant-design-vue';
|
||||||
|
import FcDesigner from '@form-create/antd-designer';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
import xml from 'highlight.js/lib/languages/java';
|
||||||
|
import json from 'highlight.js/lib/languages/json';
|
||||||
|
|
||||||
|
import { useFormCreateDesigner } from '#/components/form-create';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import 'highlight.js/styles/github.css';
|
||||||
|
|
||||||
|
defineOptions({ name: 'InfraBuild' });
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal();
|
||||||
|
|
||||||
|
const designer = ref(); // 表单设计器
|
||||||
|
|
||||||
|
// 表单设计器配置
|
||||||
|
const designerConfig = ref({
|
||||||
|
switchType: [], // 是否可以切换组件类型,或者可以相互切换的字段
|
||||||
|
autoActive: true, // 是否自动选中拖入的组件
|
||||||
|
useTemplate: false, // 是否生成vue2语法的模板组件
|
||||||
|
formOptions: {
|
||||||
|
form: {
|
||||||
|
labelWidth: '100px', // 设置默认的 label 宽度为 100px
|
||||||
|
},
|
||||||
|
}, // 定义表单配置默认值
|
||||||
|
fieldReadonly: false, // 配置field是否可以编辑
|
||||||
|
hiddenDragMenu: false, // 隐藏拖拽操作按钮
|
||||||
|
hiddenDragBtn: false, // 隐藏拖拽按钮
|
||||||
|
hiddenMenu: [], // 隐藏部分菜单
|
||||||
|
hiddenItem: [], // 隐藏部分组件
|
||||||
|
hiddenItemConfig: {}, // 隐藏组件的部分配置项
|
||||||
|
disabledItemConfig: {}, // 禁用组件的部分配置项
|
||||||
|
showSaveBtn: false, // 是否显示保存按钮
|
||||||
|
showConfig: true, // 是否显示右侧的配置界面
|
||||||
|
showBaseForm: true, // 是否显示组件的基础配置表单
|
||||||
|
showControl: true, // 是否显示组件联动
|
||||||
|
showPropsForm: true, // 是否显示组件的属性配置表单
|
||||||
|
showEventForm: true, // 是否显示组件的事件配置表单
|
||||||
|
showValidateForm: true, // 是否显示组件的验证配置表单
|
||||||
|
showFormConfig: true, // 是否显示表单配置
|
||||||
|
showInputData: true, // 是否显示录入按钮
|
||||||
|
showDevice: true, // 是否显示多端适配选项
|
||||||
|
appendConfigData: [], // 定义渲染规则所需的formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogVisible = ref(false); // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref(''); // 弹窗的标题
|
||||||
|
const formType = ref(-1); // 表单的类型:0 - 生成 JSON;1 - 生成 Options;2 - 生成组件
|
||||||
|
const formData = ref(''); // 表单数据
|
||||||
|
useFormCreateDesigner(designer); // 表单设计器增强
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const openModel = (title: string) => {
|
||||||
|
dialogVisible.value = true;
|
||||||
|
dialogTitle.value = title;
|
||||||
|
modalApi.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 生成 JSON */
|
||||||
|
const showJson = () => {
|
||||||
|
openModel('生成 JSON');
|
||||||
|
formType.value = 0;
|
||||||
|
formData.value = designer.value.getRule();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 生成 Options */
|
||||||
|
const showOption = () => {
|
||||||
|
openModel('生成 Options');
|
||||||
|
formType.value = 1;
|
||||||
|
formData.value = designer.value.getOption();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 生成组件 */
|
||||||
|
const showTemplate = () => {
|
||||||
|
openModel('生成组件');
|
||||||
|
formType.value = 2;
|
||||||
|
formData.value = makeTemplate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeTemplate = () => {
|
||||||
|
const rule = designer.value.getRule();
|
||||||
|
const opt = designer.value.getOption();
|
||||||
|
return `<template>
|
||||||
|
<form-create
|
||||||
|
v-model:api="fApi"
|
||||||
|
:rule="rule"
|
||||||
|
:option="option"
|
||||||
|
@submit="onSubmit"
|
||||||
|
></form-create>
|
||||||
|
</template>
|
||||||
|
<script setup lang=ts>
|
||||||
|
const faps = ref(null)
|
||||||
|
const rule = ref('')
|
||||||
|
const option = ref('')
|
||||||
|
const init = () => {
|
||||||
|
rule.value = formCreate.parseJson('${formCreate.toJson(rule).replaceAll('\\', '\\\\')}')
|
||||||
|
option.value = formCreate.parseJson('${JSON.stringify(opt, null, 2)}')
|
||||||
|
}
|
||||||
|
const onSubmit = (formData) => {
|
||||||
|
//todo 提交表单
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
<\/script>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 复制 */
|
||||||
|
const copy = async (text: string) => {
|
||||||
|
const textToCopy = JSON.stringify(text, null, 2);
|
||||||
|
const { copy, copied, isSupported } = useClipboard({ source: textToCopy });
|
||||||
|
if (isSupported) {
|
||||||
|
await copy();
|
||||||
|
if (unref(copied)) {
|
||||||
|
message.success($t('common.copySuccess'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error($t('common.copyError'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码高亮
|
||||||
|
*/
|
||||||
|
const highlightedCode = (code: string) => {
|
||||||
|
// 处理语言和代码
|
||||||
|
let language = 'json';
|
||||||
|
if (formType.value === 2) {
|
||||||
|
language = 'xml';
|
||||||
|
}
|
||||||
|
// debugger
|
||||||
|
if (!isString(code)) {
|
||||||
|
code = JSON.stringify(code, null, 2);
|
||||||
|
}
|
||||||
|
// 高亮
|
||||||
|
const result = hljs.highlight(code, { language, ignoreIllegals: true });
|
||||||
|
return result.value || ' ';
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
// 注册代码高亮的各种语言
|
||||||
|
hljs.registerLanguage('xml', xml);
|
||||||
|
hljs.registerLanguage('json', json);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<div class="m-4">
|
||||||
|
<FcDesigner ref="designer" height="100vh" :config="designerConfig">
|
||||||
|
<template #handle>
|
||||||
|
<Button size="small" type="primary" ghost @click="showJson">
|
||||||
|
生成JSON
|
||||||
|
</Button>
|
||||||
|
<Button size="small" type="primary" ghost @click="showOption">
|
||||||
|
生成Options
|
||||||
|
</Button>
|
||||||
|
<Button size="small" type="primary" ghost @click="showTemplate">
|
||||||
|
生成组件
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</FcDesigner>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 弹窗:表单预览 -->
|
||||||
|
<Modal
|
||||||
|
:title="dialogTitle"
|
||||||
|
:footer="false"
|
||||||
|
:fullscreen-button="false"
|
||||||
|
max-height="600"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Button style="float: right" @click="copy(formData)">
|
||||||
|
{{ $t('common.copy') }}
|
||||||
|
</Button>
|
||||||
|
<div>
|
||||||
|
<pre><code v-dompurify-html="highlightedCode(formData)" class="hljs"></code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
167
pnpm-lock.yaml
167
pnpm-lock.yaml
|
|
@ -30,6 +30,12 @@ catalogs:
|
||||||
'@faker-js/faker':
|
'@faker-js/faker':
|
||||||
specifier: ^9.6.0
|
specifier: ^9.6.0
|
||||||
version: 9.7.0
|
version: 9.7.0
|
||||||
|
'@form-create/ant-design-vue':
|
||||||
|
specifier: ^3.2.22
|
||||||
|
version: 3.2.22
|
||||||
|
'@form-create/antd-designer':
|
||||||
|
specifier: ^3.2.11
|
||||||
|
version: 3.2.11
|
||||||
'@iconify/json':
|
'@iconify/json':
|
||||||
specifier: ^2.2.324
|
specifier: ^2.2.324
|
||||||
version: 2.2.330
|
version: 2.2.330
|
||||||
|
|
@ -498,6 +504,9 @@ catalogs:
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^2.1.9
|
specifier: ^2.1.9
|
||||||
version: 2.1.9
|
version: 2.1.9
|
||||||
|
vue-dompurify-html:
|
||||||
|
specifier: ^5.2.0
|
||||||
|
version: 5.2.0
|
||||||
vue-eslint-parser:
|
vue-eslint-parser:
|
||||||
specifier: ^9.4.3
|
specifier: ^9.4.3
|
||||||
version: 9.4.3
|
version: 9.4.3
|
||||||
|
|
@ -665,9 +674,18 @@ importers:
|
||||||
|
|
||||||
apps/web-antd:
|
apps/web-antd:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@form-create/ant-design-vue':
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 3.2.22(vue@3.5.13(typescript@5.8.3))
|
||||||
|
'@form-create/antd-designer':
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 3.2.11(vue@3.5.13(typescript@5.8.3))
|
||||||
'@tinymce/tinymce-vue':
|
'@tinymce/tinymce-vue':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 6.1.0(vue@3.5.13(typescript@5.8.3))
|
version: 6.1.0(vue@3.5.13(typescript@5.8.3))
|
||||||
|
'@types/lodash.clonedeep':
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 4.5.9
|
||||||
'@vben/access':
|
'@vben/access':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/effects/access
|
version: link:../../packages/effects/access
|
||||||
|
|
@ -728,12 +746,18 @@ importers:
|
||||||
highlight.js:
|
highlight.js:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 11.11.1
|
version: 11.11.1
|
||||||
|
lodash.clonedeep:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 4.5.0
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.3.1
|
specifier: ^2.3.1
|
||||||
version: 2.3.1(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))
|
version: 2.3.1(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.13
|
specifier: ^3.5.13
|
||||||
version: 3.5.13(typescript@5.8.3)
|
version: 3.5.13(typescript@5.8.3)
|
||||||
|
vue-dompurify-html:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 5.2.0(vue@3.5.13(typescript@5.8.3))
|
||||||
vue-router:
|
vue-router:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 4.5.0(vue@3.5.13(typescript@5.8.3))
|
version: 4.5.0(vue@3.5.13(typescript@5.8.3))
|
||||||
|
|
@ -2631,6 +2655,10 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/runtime-corejs3@7.27.0':
|
||||||
|
resolution: {integrity: sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@babel/runtime@7.27.0':
|
'@babel/runtime@7.27.0':
|
||||||
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
|
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -3548,6 +3576,39 @@ packages:
|
||||||
'@floating-ui/vue@1.1.6':
|
'@floating-ui/vue@1.1.6':
|
||||||
resolution: {integrity: sha512-XFlUzGHGv12zbgHNk5FN2mUB7ROul3oG2ENdTpWdE+qMFxyNxWSRmsoyhiEnpmabNm6WnUvR1OvJfUfN4ojC1A==}
|
resolution: {integrity: sha512-XFlUzGHGv12zbgHNk5FN2mUB7ROul3oG2ENdTpWdE+qMFxyNxWSRmsoyhiEnpmabNm6WnUvR1OvJfUfN4ojC1A==}
|
||||||
|
|
||||||
|
'@form-create/ant-design-vue@3.2.22':
|
||||||
|
resolution: {integrity: sha512-A848lhCnNnQCPq/aGLxxJTIhy+vL9h7+YCU2tx1rvqyxmGAYQlJgMy0ET3MpL88eK5DkuB2eyZE5Pt4i4vybeg==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.13
|
||||||
|
|
||||||
|
'@form-create/antd-designer@3.2.11':
|
||||||
|
resolution: {integrity: sha512-i2MijDIAJeMWfcikqX1LVonWs85K6q5ZoQdOSXAD4MhiFGovQXBTXIenSEzRUlBlGgkwr8u1PeblMO8jCt4sXg==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.13
|
||||||
|
|
||||||
|
'@form-create/component-antdv-frame@3.2.18':
|
||||||
|
resolution: {integrity: sha512-b6qGkqJnA9JlSnOvEMnkyfcPLg31oSl79i7yytJ3BLCUR8igyNO4O81dhFt4lWoaDe69LQWAtyDLEiF2Ls4OoA==}
|
||||||
|
|
||||||
|
'@form-create/component-antdv-group@3.2.22':
|
||||||
|
resolution: {integrity: sha512-HfQw5cf7+ikcAXW++T3bLs4yocM9BJH10OgcEy5R0mo2fZfTQ49MCeUhhAUlpoHtJz/4nRijfyhQ6j+D6oAK4g==}
|
||||||
|
|
||||||
|
'@form-create/component-antdv-upload@3.2.18':
|
||||||
|
resolution: {integrity: sha512-cobjChcblnfO0ph4MunJDUiBLyRwpzekXo6MFRsB5iq9ln73UjLnyLps4YuM2KRZ/Cn9FEoWN1kYvTFf1zKdjg==}
|
||||||
|
|
||||||
|
'@form-create/component-subform@3.1.34':
|
||||||
|
resolution: {integrity: sha512-OJcFH/7MTHx7JLEjDK/weS27qfuFWAI+OK+gXTJ2jIt9aZkGWF/EWkjetiJLt5a0KMw4Z15wOS2XCY9pVK9vlA==}
|
||||||
|
|
||||||
|
'@form-create/component-wangeditor@3.2.14':
|
||||||
|
resolution: {integrity: sha512-N/U/hFBdBu2OIguxoKe1Kslq5fW6XmtyhKDImLfKLn1xI6X5WUtt3r7QTaUPcVUl2vntpM9wJ/FBdG17RzF/Dg==}
|
||||||
|
|
||||||
|
'@form-create/core@3.2.22':
|
||||||
|
resolution: {integrity: sha512-GC3b4Yrpy9TiPLqJFL9fiUFPjEv6ZBcHnOMB+GeF6iLsMV4TpZc0o/oFBPlhZqIYeljaNuxJyO2ABCStceOrZQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.13
|
||||||
|
|
||||||
|
'@form-create/utils@3.2.18':
|
||||||
|
resolution: {integrity: sha512-C98bFPdFVMltiHQvEZqv4rVdhcqthJgvxMbWDlniL03HS5oyusnUvxUE8jf0I9zk5dZRDGmxKOUtzE3JDWP9nQ==}
|
||||||
|
|
||||||
'@gar/promisify@1.1.3':
|
'@gar/promisify@1.1.3':
|
||||||
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
||||||
|
|
||||||
|
|
@ -5444,6 +5505,10 @@ packages:
|
||||||
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
codemirror@6.65.7:
|
||||||
|
resolution: {integrity: sha512-HcfnUFJwI2FvH73YWVbbMh7ObWxZiHIycEhv9ZEXy6e8ZKDjtZKbbYFUtsLN46HFXPvU5V2Uvc2d55Z//oFW5A==}
|
||||||
|
deprecated: This is an accidentally mis-tagged instance of 5.65.7
|
||||||
|
|
||||||
color-convert@1.9.3:
|
color-convert@1.9.3:
|
||||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||||
|
|
||||||
|
|
@ -5616,6 +5681,9 @@ packages:
|
||||||
core-js-compat@3.41.0:
|
core-js-compat@3.41.0:
|
||||||
resolution: {integrity: sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==}
|
resolution: {integrity: sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==}
|
||||||
|
|
||||||
|
core-js-pure@3.41.0:
|
||||||
|
resolution: {integrity: sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==}
|
||||||
|
|
||||||
core-js@3.41.0:
|
core-js@3.41.0:
|
||||||
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
|
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
|
||||||
|
|
||||||
|
|
@ -6084,6 +6152,9 @@ packages:
|
||||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
dompurify@3.2.5:
|
||||||
|
resolution: {integrity: sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==}
|
||||||
|
|
||||||
domutils@2.8.0:
|
domutils@2.8.0:
|
||||||
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
|
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
|
||||||
|
|
||||||
|
|
@ -9671,6 +9742,9 @@ packages:
|
||||||
resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==}
|
resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==}
|
||||||
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||||
|
|
||||||
|
sortablejs@1.14.0:
|
||||||
|
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
|
||||||
|
|
||||||
sortablejs@1.15.6:
|
sortablejs@1.15.6:
|
||||||
resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
|
resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
|
||||||
|
|
||||||
|
|
@ -10704,6 +10778,11 @@ packages:
|
||||||
'@vue/composition-api':
|
'@vue/composition-api':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vue-dompurify-html@5.2.0:
|
||||||
|
resolution: {integrity: sha512-GX+BStkKEJ8wu/+hU1EK2nu/gzXWhb4XzBu6aowpsuU/3nkvXvZ2jx4nZ9M3jtS/Vu7J7MtFXjc7x3cWQ+zbVQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.13
|
||||||
|
|
||||||
vue-eslint-parser@9.4.3:
|
vue-eslint-parser@9.4.3:
|
||||||
resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
|
resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
|
||||||
engines: {node: ^14.17.0 || >=16.0.0}
|
engines: {node: ^14.17.0 || >=16.0.0}
|
||||||
|
|
@ -10751,6 +10830,11 @@ packages:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vuedraggable@4.1.0:
|
||||||
|
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.13
|
||||||
|
|
||||||
vueuc@0.4.64:
|
vueuc@0.4.64:
|
||||||
resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==}
|
resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -10762,6 +10846,9 @@ packages:
|
||||||
vxe-table@4.13.7:
|
vxe-table@4.13.7:
|
||||||
resolution: {integrity: sha512-nwybQ0uPgmAQOvw0gs4oiJ7ifUVCsW0grGyfMC+FdNxFxP2WuiziuKDztjAc16EgHgSDg+KpN4oZoHUAwC55tg==}
|
resolution: {integrity: sha512-nwybQ0uPgmAQOvw0gs4oiJ7ifUVCsW0grGyfMC+FdNxFxP2WuiziuKDztjAc16EgHgSDg+KpN4oZoHUAwC55tg==}
|
||||||
|
|
||||||
|
wangeditor@4.7.15:
|
||||||
|
resolution: {integrity: sha512-aPTdREd8BxXVyJ5MI+LU83FQ7u1EPd341iXIorRNYSOvoimNoZ4nPg+yn3FGbB93/owEa6buLw8wdhYnMCJQLg==}
|
||||||
|
|
||||||
warning@4.0.3:
|
warning@4.0.3:
|
||||||
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
|
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
|
||||||
|
|
||||||
|
|
@ -11889,6 +11976,11 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@babel/runtime-corejs3@7.27.0':
|
||||||
|
dependencies:
|
||||||
|
core-js-pure: 3.41.0
|
||||||
|
regenerator-runtime: 0.14.1
|
||||||
|
|
||||||
'@babel/runtime@7.27.0':
|
'@babel/runtime@7.27.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime: 0.14.1
|
regenerator-runtime: 0.14.1
|
||||||
|
|
@ -12892,6 +12984,55 @@ snapshots:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@form-create/ant-design-vue@3.2.22(vue@3.5.13(typescript@5.8.3))':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/component-antdv-frame': 3.2.18
|
||||||
|
'@form-create/component-antdv-group': 3.2.22
|
||||||
|
'@form-create/component-antdv-upload': 3.2.18
|
||||||
|
'@form-create/component-subform': 3.1.34
|
||||||
|
'@form-create/core': 3.2.22(vue@3.5.13(typescript@5.8.3))
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
|
'@form-create/antd-designer@3.2.11(vue@3.5.13(typescript@5.8.3))':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/ant-design-vue': 3.2.22(vue@3.5.13(typescript@5.8.3))
|
||||||
|
'@form-create/component-wangeditor': 3.2.14
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
ant-design-vue: 4.2.6(vue@3.5.13(typescript@5.8.3))
|
||||||
|
codemirror: 6.65.7
|
||||||
|
element-plus: 2.9.8(vue@3.5.13(typescript@5.8.3))
|
||||||
|
js-beautify: 1.15.4
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
vuedraggable: 4.1.0(vue@3.5.13(typescript@5.8.3))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
|
||||||
|
'@form-create/component-antdv-frame@3.2.18':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
|
||||||
|
'@form-create/component-antdv-group@3.2.22':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
|
||||||
|
'@form-create/component-antdv-upload@3.2.18':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
|
||||||
|
'@form-create/component-subform@3.1.34': {}
|
||||||
|
|
||||||
|
'@form-create/component-wangeditor@3.2.14':
|
||||||
|
dependencies:
|
||||||
|
wangeditor: 4.7.15
|
||||||
|
|
||||||
|
'@form-create/core@3.2.22(vue@3.5.13(typescript@5.8.3))':
|
||||||
|
dependencies:
|
||||||
|
'@form-create/utils': 3.2.18
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
|
'@form-create/utils@3.2.18': {}
|
||||||
|
|
||||||
'@gar/promisify@1.1.3': {}
|
'@gar/promisify@1.1.3': {}
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
@ -15137,6 +15278,8 @@ snapshots:
|
||||||
|
|
||||||
cluster-key-slot@1.1.2: {}
|
cluster-key-slot@1.1.2: {}
|
||||||
|
|
||||||
|
codemirror@6.65.7: {}
|
||||||
|
|
||||||
color-convert@1.9.3:
|
color-convert@1.9.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.3
|
color-name: 1.1.3
|
||||||
|
|
@ -15290,6 +15433,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.24.4
|
browserslist: 4.24.4
|
||||||
|
|
||||||
|
core-js-pure@3.41.0: {}
|
||||||
|
|
||||||
core-js@3.41.0: {}
|
core-js@3.41.0: {}
|
||||||
|
|
||||||
core-util-is@1.0.3: {}
|
core-util-is@1.0.3: {}
|
||||||
|
|
@ -15793,6 +15938,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
|
dompurify@3.2.5:
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
domutils@2.8.0:
|
domutils@2.8.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
dom-serializer: 1.4.1
|
dom-serializer: 1.4.1
|
||||||
|
|
@ -19703,6 +19852,8 @@ snapshots:
|
||||||
ip-address: 9.0.5
|
ip-address: 9.0.5
|
||||||
smart-buffer: 4.2.0
|
smart-buffer: 4.2.0
|
||||||
|
|
||||||
|
sortablejs@1.14.0: {}
|
||||||
|
|
||||||
sortablejs@1.15.6: {}
|
sortablejs@1.15.6: {}
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
|
|
@ -20919,6 +21070,11 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.13(typescript@5.8.3)
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
|
vue-dompurify-html@5.2.0(vue@3.5.13(typescript@5.8.3)):
|
||||||
|
dependencies:
|
||||||
|
dompurify: 3.2.5
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
vue-eslint-parser@9.4.3(eslint@9.25.1(jiti@2.4.2)):
|
vue-eslint-parser@9.4.3(eslint@9.25.1(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
|
|
@ -20976,6 +21132,11 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|
||||||
|
vuedraggable@4.1.0(vue@3.5.13(typescript@5.8.3)):
|
||||||
|
dependencies:
|
||||||
|
sortablejs: 1.14.0
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
vueuc@0.4.64(vue@3.5.13(typescript@5.8.3)):
|
vueuc@0.4.64(vue@3.5.13(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@css-render/vue3-ssr': 0.15.14(vue@3.5.13(typescript@5.8.3))
|
'@css-render/vue3-ssr': 0.15.14(vue@3.5.13(typescript@5.8.3))
|
||||||
|
|
@ -20999,6 +21160,12 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
wangeditor@4.7.15:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.27.0
|
||||||
|
'@babel/runtime-corejs3': 7.27.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
warning@4.0.3:
|
warning@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ catalog:
|
||||||
'@tanstack/vue-query': ^5.72.0
|
'@tanstack/vue-query': ^5.72.0
|
||||||
'@tanstack/vue-store': ^0.7.0
|
'@tanstack/vue-store': ^0.7.0
|
||||||
'@tinymce/tinymce-vue': ^6.1.0
|
'@tinymce/tinymce-vue': ^6.1.0
|
||||||
|
'@form-create/ant-design-vue': ^3.2.22
|
||||||
|
'@form-create/antd-designer': ^3.2.11
|
||||||
'@types/archiver': ^6.0.3
|
'@types/archiver': ^6.0.3
|
||||||
'@types/eslint': ^9.6.1
|
'@types/eslint': ^9.6.1
|
||||||
'@types/html-minifier-terser': ^7.0.2
|
'@types/html-minifier-terser': ^7.0.2
|
||||||
|
|
@ -182,6 +184,7 @@ catalog:
|
||||||
vitepress-plugin-group-icons: ^1.3.8
|
vitepress-plugin-group-icons: ^1.3.8
|
||||||
vitest: ^2.1.9
|
vitest: ^2.1.9
|
||||||
vue: ^3.5.13
|
vue: ^3.5.13
|
||||||
|
vue-dompurify-html: ^5.2.0
|
||||||
vue-eslint-parser: ^9.4.3
|
vue-eslint-parser: ^9.4.3
|
||||||
vue-i18n: ^11.1.3
|
vue-i18n: ^11.1.3
|
||||||
vue-json-viewer: ^3.0.4
|
vue-json-viewer: ^3.0.4
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue