style: lint格式化文件

This commit is contained in:
2025-10-21 18:04:02 +08:00
parent f9ca969fec
commit 7629120548
1092 changed files with 148218 additions and 157907 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,83 +1,83 @@
<template>
<div class="ah-container">
<a-layout-header class="btn-bar">
<slot></slot>
<a-button v-if="$attrs.uploadJson" type="link" size="small" @click="$emit('uploadJson')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="import" :size="14" />
</div>
</template>
{{ t('导入JSON') }}
</a-button>
<a-button v-if="$attrs.generateJson" type="link" size="small" @click="$emit('generateJson')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="printer" :size="14" />
</div>
</template>
{{ t('生成JSON') }}
</a-button>
<a-button v-if="$attrs.clearable" type="link" size="small" @click="$emit('clearable')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="clearable" :size="14" />
</div>
</template>
{{ t('清空') }}
</a-button>
<a-button v-if="$attrs.preview" type="link" size="small" @click="$emit('preview')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="action" :size="14" />
</div>
</template>
{{ t('预览') }}
</a-button>
<div class="ah-container">
<a-layout-header class="btn-bar">
<slot></slot>
<a-button v-if="$attrs.uploadJson" type="link" size="small" @click="$emit('uploadJson')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="import" :size="14" />
</div>
</template>
{{ t('导入JSON') }}
</a-button>
<a-button v-if="$attrs.generateJson" type="link" size="small" @click="$emit('generateJson')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="printer" :size="14" />
</div>
</template>
{{ t('生成JSON') }}
</a-button>
<a-button v-if="$attrs.clearable" type="link" size="small" @click="$emit('clearable')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="clearable" :size="14" />
</div>
</template>
{{ t('清空') }}
</a-button>
<a-button v-if="$attrs.preview" type="link" size="small" @click="$emit('preview')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="action" :size="14" />
</div>
</template>
{{ t('预览') }}
</a-button>
<a-button v-if="$attrs.generateCode" type="link" size="small" @click="$emit('generateCode')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="generate-code" />
</div>
</template>
{{ t('生成代码') }}
</a-button>
</a-layout-header>
</div>
<a-button v-if="$attrs.generateCode" type="link" size="small" @click="$emit('generateCode')">
<template #icon>
<div class="btn-icon">
<SvgIcon name="generate-code" />
</div>
</template>
{{ t('生成代码') }}
</a-button>
</a-layout-header>
</div>
</template>
<script lang="ts" setup>
import { SvgIcon } from '/@/components/Icon';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
import { SvgIcon } from '/@/components/Icon';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
defineEmits(['uploadJson', 'clearable', 'preview', 'generateJson', 'generateCode']);
defineEmits(['uploadJson', 'clearable', 'preview', 'generateJson', 'generateCode']);
</script>
<style scoped lang="less">
.ah-container {
padding: 0 10px;
border-bottom: 1px solid #f0f2f5;
}
.btn-bar {
line-height: 46px;
height: 46px;
padding: 0 20px;
background-color: #fff;
text-align: right;
.btn-icon {
display: inline-block;
border: 1px solid;
border-radius: 50%;
width: 25px;
height: 25px;
.ah-container {
padding: 0 10px;
border-bottom: 1px solid #f0f2f5;
}
}
:deep(.ant-btn) > span {
margin-left: 5px;
}
// @import '/@/assets/style/designer/index.css';
.btn-bar {
line-height: 46px;
height: 46px;
padding: 0 20px;
background-color: #fff;
text-align: right;
.btn-icon {
display: inline-block;
border: 1px solid;
border-radius: 50%;
width: 25px;
height: 25px;
}
}
:deep(.ant-btn) > span {
margin-left: 5px;
}
// @import '/@/assets/style/designer/index.css';
</style>

View File

@ -1,493 +1,448 @@
<template>
<div class="awf-container-center">
<div class="widget-form-container">
<div v-if="!widgetForm.list" class="form-empty">从左侧拖拽来添加字段</div>
<a-form
:layout="widgetForm.config.layout"
:labelAlign="widgetForm.config.labelAlign"
:labelCol="widgetForm.config.labelCol"
:disabled="true"
>
<Draggable
class="widget-form-list"
item-key="key"
ghostClass="ghost"
handle=".drag-widget"
:animation="200"
:group="{ name: 'people' }"
:list="widgetForm.list"
@add="handleMoveAdd"
>
<template #item="{ element, index }">
<transition-group name="fade" tag="div">
<AntdWidgetFormItem
v-if="element.key"
:key="element.key"
class="drag-widget"
:element="element"
:config="widgetForm.config"
:selectWidget="widgetFormSelect"
@click="handleItemClick(element)"
@copy="handleCopyClick(index, widgetForm.list)"
@delete="handleDeleteClick(index, widgetForm.list)"
@addRow="handleAddClick(element, 'row')"
@addCol="handleAddClick(element, 'col')"
/>
</transition-group>
</template>
</Draggable>
</a-form>
<div class="awf-container-center">
<div class="widget-form-container">
<div v-if="!widgetForm.list" class="form-empty">从左侧拖拽来添加字段</div>
<a-form :layout="widgetForm.config.layout" :labelAlign="widgetForm.config.labelAlign" :labelCol="widgetForm.config.labelCol" :disabled="true">
<Draggable class="widget-form-list" item-key="key" ghostClass="ghost" handle=".drag-widget" :animation="200" :group="{ name: 'people' }" :list="widgetForm.list" @add="handleMoveAdd">
<template #item="{ element, index }">
<transition-group name="fade" tag="div">
<AntdWidgetFormItem
v-if="element.key"
:key="element.key"
class="drag-widget"
:element="element"
:config="widgetForm.config"
:selectWidget="widgetFormSelect"
@click="handleItemClick(element)"
@copy="handleCopyClick(index, widgetForm.list)"
@delete="handleDeleteClick(index, widgetForm.list)"
@addRow="handleAddClick(element, 'row')"
@addCol="handleAddClick(element, 'col')"
/>
</transition-group>
</template>
</Draggable>
</a-form>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, nextTick, PropType, toRaw, inject, Ref, ref } from 'vue';
import Draggable from 'vuedraggable';
import AntdWidgetFormItem from './AntdWidgetFormItem.vue';
import { SvgIcon } from '/@/components/Icon';
import {
WidgetForm,
gridComponents,
TableInfo,
noHaveTableAndField,
noHaveField,
TableCell,
TableTh,
} from '../types';
import { buildUUID } from '/@/utils/uuid';
import { error } from '/@/utils/log';
import { changeToPinyin } from '/@/utils/event/design';
import { random } from 'lodash-es';
import { defineComponent, nextTick, PropType, toRaw, inject, Ref, ref } from 'vue';
import Draggable from 'vuedraggable';
import AntdWidgetFormItem from './AntdWidgetFormItem.vue';
import { SvgIcon } from '/@/components/Icon';
import { WidgetForm, gridComponents, TableInfo, noHaveTableAndField, noHaveField, TableCell, TableTh } from '../types';
import { buildUUID } from '/@/utils/uuid';
import { error } from '/@/utils/log';
import { changeToPinyin } from '/@/utils/event/design';
import { random } from 'lodash-es';
const handleListInsert = (key: string, list: any[], obj: any) => {
const newList: any[] = [];
list.forEach((item) => {
if (item.key === key) {
newList.push(item);
newList.push(obj);
} else {
if (item.columns) {
item.columns = item.columns.map((col: any) => ({
...col,
list: handleListInsert(key, col.list, obj),
}));
}
newList.push(item);
}
});
return newList;
};
const handleListDelete = (key: string, list: any[]) => {
const newList: any[] = [];
list.forEach((item) => {
if (item.key !== key) {
if (item.columns) {
item.columns = item.columns.map((col: any) => ({
...col,
list: handleListDelete(key, col.list),
}));
}
newList.push(item);
}
});
return newList;
};
export default defineComponent({
name: 'AntdWidgetForm',
components: {
SvgIcon,
Draggable,
AntdWidgetFormItem,
},
props: {
widgetForm: {
type: Object as PropType<WidgetForm>,
required: true,
},
widgetFormSelect: {
type: Object,
},
},
emits: ['update:widgetForm', 'update:widgetFormSelect'],
setup(props, context) {
const state = inject('state') as any;
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo') as Ref<TableInfo[]>;
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
const designType = inject('designType') as string;
const tablecell = inject<Ref<TableCell>>('tableCell');
const tableth = inject<Ref<TableTh>>('tableTh');
let mainTableName;
if (designType !== 'data') {
mainTableName = inject<string>('mainTableName');
}
const handleItemClick = (row: any) => {
state.widgetFormSelect = row;
if (row.type === 'table-layout') {
if (tablecell) tablecell.value = {};
if (tableth) tableth.value = {};
}
};
const handleAddClick = (row: any, type: string) => {
let tdMax = 0;
row.layout[0].list.forEach((o) => {
if (o.colspan) {
tdMax = tdMax + o.colspan;
} else {
tdMax += 1;
}
const handleListInsert = (key: string, list: any[], obj: any) => {
const newList: any[] = [];
list.forEach((item) => {
if (item.key === key) {
newList.push(item);
newList.push(obj);
} else {
if (item.columns) {
item.columns = item.columns.map((col: any) => ({
...col,
list: handleListInsert(key, col.list, obj)
}));
}
newList.push(item);
}
});
if (type == 'row') {
let lastRow: any = [];
for (let j = 0; j < tdMax; j++) {
lastRow.push({
width: '',
height: '',
class: 'tableLayoutTd',
children: [],
position: [row.layout.length, j],
});
}
row.layout.push({ list: lastRow });
} else {
row.layout.forEach((o, i) => {
o.list.push({
width: '',
height: '',
class: 'tableLayoutTd',
children: [],
position: [i, tdMax],
});
});
}
};
const handleCopyClick = (index: number, list: any[]) => {
const key = buildUUID().replaceAll('-', '');
const oldList = JSON.parse(JSON.stringify(props.widgetForm.list));
let copyData = {
...list[index],
key,
model: `${list[index].type}_${key}`,
rules: list[index].rules ?? [],
bindField: '',
};
return newList;
};
if (
list[index].type === 'radio' ||
list[index].type === 'checkbox' ||
list[index].type === 'select'
) {
copyData = {
...copyData,
options: {
...copyData.options,
staticOptions: copyData.options.staticOptions.map((item: any) => ({ ...item })),
const handleListDelete = (key: string, list: any[]) => {
const newList: any[] = [];
list.forEach((item) => {
if (item.key !== key) {
if (item.columns) {
item.columns = item.columns.map((col: any) => ({
...col,
list: handleListDelete(key, col.list)
}));
}
newList.push(item);
}
});
return newList;
};
export default defineComponent({
name: 'AntdWidgetForm',
components: {
SvgIcon,
Draggable,
AntdWidgetFormItem
},
props: {
widgetForm: {
type: Object as PropType<WidgetForm>,
required: true
},
};
}
copyLayoutData([copyData], mainTableName);
context.emit('update:widgetForm', {
...props.widgetForm,
list: handleListInsert(list[index].key, oldList, copyData),
});
inject('widgetFormSelect', copyData);
context.emit('update:widgetFormSelect', copyData);
};
const copyLayoutData = (data, tableName) => {
data.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
item.bindTable = '';
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
for (const child of item.layout!) {
copyLayoutData(child.list, tableName);
widgetFormSelect: {
type: Object
}
} else if (item.type === 'table-layout') {
item.bindTable = '';
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
for (const child of item.layout!) {
for (const list of child.list!) {
copyLayoutData(list.children, tableName);
}
},
emits: ['update:widgetForm', 'update:widgetFormSelect'],
setup(props, context) {
const state = inject('state') as any;
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo') as Ref<TableInfo[]>;
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
const designType = inject('designType') as string;
const tablecell = inject<Ref<TableCell>>('tableCell');
const tableth = inject<Ref<TableTh>>('tableTh');
let mainTableName;
if (designType !== 'data') {
mainTableName = inject<string>('mainTableName');
}
} else if (['form', 'one-for-one'].includes(item.type)) {
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item);
copyLayoutData(item.children, item.bindTable);
} else {
item.bindField = '';
item.bindTable = tableName;
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
}
});
};
const handleDeleteClick = async (index: number, list: any[]) => {
await deleteAdoptedCalc(index, list);
const oldList = JSON.parse(JSON.stringify(props.widgetForm.list));
if (list.length - 1 === index) {
if (index === 0) {
nextTick(() => context.emit('update:widgetFormSelect', null));
} else {
context.emit('update:widgetFormSelect', list[index - 1]);
}
} else {
context.emit('update:widgetFormSelect', list[index + 1]);
}
context.emit('update:widgetForm', {
...props.widgetForm,
list: handleListDelete(list[index].key, oldList),
});
};
const handleTabGridDeleteClick = (index: number, list: any[]) => {
list.splice(index, 1);
};
const handleMoveAdd = (event: any) => {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
const { newIndex } = event;
//判断如果是单表 不支持子表单组件 (仅数据优先时)
if (tableInfo.value.length < 2 && list[newIndex].type === 'form' && designType === 'data') {
list.splice(newIndex, 2); //必须2
context.emit('update:widgetForm', { ...props.widgetForm, list });
error('单表不能使用子表单组件!');
return;
}
const key = buildUUID().replaceAll('-', '');
list[newIndex] = {
...toRaw(list[newIndex]),
key,
model: `${list[newIndex].type}_${key}`,
rules: [],
};
if (
list[newIndex].type === 'radio' ||
list[newIndex].type === 'checkbox' ||
list[newIndex].type === 'select'
) {
list[newIndex] = {
...list[newIndex],
options: {
...list[newIndex].options,
staticOptions: list[newIndex].options.staticOptions.map((item: any) => ({
...item,
})),
},
};
}
if (list[newIndex].type === 'grid') {
list[newIndex].layout.map((item: any) => (item.list = []));
}
if (list[newIndex].type === 'form') {
list[newIndex].children.map((item: any) => (item.list = []));
}
if (list[newIndex].type === 'tab') {
list[newIndex].layout.map((item: any) => (item.list = []));
}
//从子表拖入主表时
list[newIndex].isSubFormChild = false;
list[newIndex].isSingleFormChild = false;
addBindTableAndField(list[newIndex]);
context.emit('update:widgetForm', { ...props.widgetForm, list });
context.emit('update:widgetFormSelect', list[newIndex]);
};
const handleColMoveAdd = (event: any, row: any, index: string | number | symbol) => {
const { newIndex, oldIndex, item } = event;
if (row.type === 'form') {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
if (item.className.includes('data-grid')) {
item.tagName === 'DIV' && list.splice(oldIndex, 0, row.children[newIndex]);
row.children[index].splice(newIndex, 1);
return false;
}
const key = buildUUID().replaceAll('-', '');
row.children[newIndex] = {
...toRaw(row.children[index]),
key,
model: `${row.children[newIndex].type}_${key}`,
rules: [],
};
if (
row.children[newIndex].type === 'radio' ||
row.children[newIndex].type === 'checkbox' ||
row.children[newIndex].type === 'select'
) {
row.children[newIndex] = {
...row.children[newIndex],
options: {
...row.children[newIndex].options,
staticOptions: row.children[newIndex].options.staticOptions.map((item: any) => ({
...item,
})),
},
const handleItemClick = (row: any) => {
state.widgetFormSelect = row;
if (row.type === 'table-layout') {
if (tablecell) tablecell.value = {};
if (tableth) tableth.value = {};
}
};
}
context.emit('update:widgetFormSelect', row.children[newIndex]);
} else {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
if (item.className.includes('data-grid')) {
item.tagName === 'DIV' && list.splice(oldIndex, 0, row.layout[index].list[newIndex]);
row.layout[index].list.splice(newIndex, 1);
return false;
}
const key = buildUUID().replaceAll('-', '');
row.layout[index].list[newIndex] = {
...toRaw(row.layout[index].list[newIndex]),
key,
model: `${row.layout[index].list[newIndex].type}_${key}`,
rules: [],
};
if (
row.layout[index].list[newIndex].type === 'radio' ||
row.layout[index].list[newIndex].type === 'checkbox' ||
row.layout[index].list[newIndex].type === 'select'
) {
row.layout[index].list[newIndex] = {
...row.layout[index].list[newIndex],
options: {
...row.layout[index].list[newIndex].options,
staticOptions: row.layout[index].list[newIndex].options.staticOptions.map(
(item: any) => ({
...item,
}),
),
},
const handleAddClick = (row: any, type: string) => {
let tdMax = 0;
row.layout[0].list.forEach((o) => {
if (o.colspan) {
tdMax = tdMax + o.colspan;
} else {
tdMax += 1;
}
});
if (type == 'row') {
let lastRow: any = [];
for (let j = 0; j < tdMax; j++) {
lastRow.push({
width: '',
height: '',
class: 'tableLayoutTd',
children: [],
position: [row.layout.length, j]
});
}
row.layout.push({ list: lastRow });
} else {
row.layout.forEach((o, i) => {
o.list.push({
width: '',
height: '',
class: 'tableLayoutTd',
children: [],
position: [i, tdMax]
});
});
}
};
}
const handleCopyClick = (index: number, list: any[]) => {
const key = buildUUID().replaceAll('-', '');
const oldList = JSON.parse(JSON.stringify(props.widgetForm.list));
let copyData = {
...list[index],
key,
model: `${list[index].type}_${key}`,
rules: list[index].rules ?? [],
bindField: ''
};
context.emit('update:widgetFormSelect', row.layout[index].list[newIndex]);
if (list[index].type === 'radio' || list[index].type === 'checkbox' || list[index].type === 'select') {
copyData = {
...copyData,
options: {
...copyData.options,
staticOptions: copyData.options.staticOptions.map((item: any) => ({ ...item }))
}
};
}
copyLayoutData([copyData], mainTableName);
context.emit('update:widgetForm', {
...props.widgetForm,
list: handleListInsert(list[index].key, oldList, copyData)
});
inject('widgetFormSelect', copyData);
context.emit('update:widgetFormSelect', copyData);
};
const copyLayoutData = (data, tableName) => {
data.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
item.bindTable = '';
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
for (const child of item.layout!) {
copyLayoutData(child.list, tableName);
}
} else if (item.type === 'table-layout') {
item.bindTable = '';
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
for (const child of item.layout!) {
for (const list of child.list!) {
copyLayoutData(list.children, tableName);
}
}
} else if (['form', 'one-for-one'].includes(item.type)) {
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item);
copyLayoutData(item.children, item.bindTable);
} else {
item.bindField = '';
item.bindTable = tableName;
item.key = buildUUID().replaceAll('-', '');
addBindTableAndField(item, tableName);
}
});
};
const handleDeleteClick = async (index: number, list: any[]) => {
await deleteAdoptedCalc(index, list);
const oldList = JSON.parse(JSON.stringify(props.widgetForm.list));
if (list.length - 1 === index) {
if (index === 0) {
nextTick(() => context.emit('update:widgetFormSelect', null));
} else {
context.emit('update:widgetFormSelect', list[index - 1]);
}
} else {
context.emit('update:widgetFormSelect', list[index + 1]);
}
context.emit('update:widgetForm', {
...props.widgetForm,
list: handleListDelete(list[index].key, oldList)
});
};
const handleTabGridDeleteClick = (index: number, list: any[]) => {
list.splice(index, 1);
};
const handleMoveAdd = (event: any) => {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
const { newIndex } = event;
//判断如果是单表 不支持子表单组件 (仅数据优先时)
if (tableInfo.value.length < 2 && list[newIndex].type === 'form' && designType === 'data') {
list.splice(newIndex, 2); //必须2
context.emit('update:widgetForm', { ...props.widgetForm, list });
error('单表不能使用子表单组件!');
return;
}
const key = buildUUID().replaceAll('-', '');
list[newIndex] = {
...toRaw(list[newIndex]),
key,
model: `${list[newIndex].type}_${key}`,
rules: []
};
if (list[newIndex].type === 'radio' || list[newIndex].type === 'checkbox' || list[newIndex].type === 'select') {
list[newIndex] = {
...list[newIndex],
options: {
...list[newIndex].options,
staticOptions: list[newIndex].options.staticOptions.map((item: any) => ({
...item
}))
}
};
}
if (list[newIndex].type === 'grid') {
list[newIndex].layout.map((item: any) => (item.list = []));
}
if (list[newIndex].type === 'form') {
list[newIndex].children.map((item: any) => (item.list = []));
}
if (list[newIndex].type === 'tab') {
list[newIndex].layout.map((item: any) => (item.list = []));
}
//从子表拖入主表时
list[newIndex].isSubFormChild = false;
list[newIndex].isSingleFormChild = false;
addBindTableAndField(list[newIndex]);
context.emit('update:widgetForm', { ...props.widgetForm, list });
context.emit('update:widgetFormSelect', list[newIndex]);
};
const handleColMoveAdd = (event: any, row: any, index: string | number | symbol) => {
const { newIndex, oldIndex, item } = event;
if (row.type === 'form') {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
if (item.className.includes('data-grid')) {
item.tagName === 'DIV' && list.splice(oldIndex, 0, row.children[newIndex]);
row.children[index].splice(newIndex, 1);
return false;
}
const key = buildUUID().replaceAll('-', '');
row.children[newIndex] = {
...toRaw(row.children[index]),
key,
model: `${row.children[newIndex].type}_${key}`,
rules: []
};
if (row.children[newIndex].type === 'radio' || row.children[newIndex].type === 'checkbox' || row.children[newIndex].type === 'select') {
row.children[newIndex] = {
...row.children[newIndex],
options: {
...row.children[newIndex].options,
staticOptions: row.children[newIndex].options.staticOptions.map((item: any) => ({
...item
}))
}
};
}
context.emit('update:widgetFormSelect', row.children[newIndex]);
} else {
const list = JSON.parse(JSON.stringify(props.widgetForm.list));
if (item.className.includes('data-grid')) {
item.tagName === 'DIV' && list.splice(oldIndex, 0, row.layout[index].list[newIndex]);
row.layout[index].list.splice(newIndex, 1);
return false;
}
const key = buildUUID().replaceAll('-', '');
row.layout[index].list[newIndex] = {
...toRaw(row.layout[index].list[newIndex]),
key,
model: `${row.layout[index].list[newIndex].type}_${key}`,
rules: []
};
if (row.layout[index].list[newIndex].type === 'radio' || row.layout[index].list[newIndex].type === 'checkbox' || row.layout[index].list[newIndex].type === 'select') {
row.layout[index].list[newIndex] = {
...row.layout[index].list[newIndex],
options: {
...row.layout[index].list[newIndex].options,
staticOptions: row.layout[index].list[newIndex].options.staticOptions.map((item: any) => ({
...item
}))
}
};
}
context.emit('update:widgetFormSelect', row.layout[index].list[newIndex]);
}
};
const addBindTableAndField = (component: any, table?) => {
//界面优先和简易模板
if (designType !== 'data') {
//需要绑定字段、组件没有绑定字段的情况下新增绑定字段
const rangeComponents = ['time-range', 'date-range'];
if (rangeComponents.includes(component.type)) {
component.bindStartTime = changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
component.bindEndTime = changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
} else if (!noHaveField.includes(component.type) && !component.bindField) {
component.bindField = changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
}
if (component.type === 'form' || component.type === 'one-for-one') {
component.bindTable = isFieldUpper.value ? `${mainTableName.value}_CHILD_${random(1000, 9999)}` : `${mainTableName.value}_child_${random(1000, 9999)}`;
} else if (!noHaveTableAndField.includes(component.type)) {
component.bindTable = table || mainTableName.value;
}
}
if (component.isGridChild) component.options.span = 7;
};
//删除引用已删除的财务组件的计算式配置
const deleteAdoptedCalc = (index: number, list: any[]) => {
const component = list[index];
if (['computational', 'money-chinese'].includes(component.type)) {
component.options.beAdoptedComponent?.map((key) => {
getLayoutComponent(list, key);
});
}
};
const getLayoutComponent = (list, key) => {
list?.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getLayoutComponent(child.list, key);
}
} else if (item.type === 'table-layout') {
for (const child of item.layout!) {
for (const list of child.list) {
getLayoutComponent(list.children, key);
}
}
} else if (item.type === 'form') {
item.children.map((child: any) => {
if (['computational', 'money-chinese'].includes(child.type) && child.key === key) {
child.options.computationalConfig = [];
child.options.computationalConfigValue = '== 请填写计算式配置 ==';
}
});
} else {
if (['computational', 'money-chinese'].includes(item.type) && item.key === key) {
item.options.computationalConfig = [];
item.options.computationalConfigValue = '== 请填写计算式配置 ==';
}
}
});
};
return {
handleItemClick,
handleAddClick,
handleCopyClick,
handleDeleteClick,
handleTabGridDeleteClick,
handleMoveAdd,
handleColMoveAdd,
gridComponents,
addBindTableAndField
};
}
};
const addBindTableAndField = (component: any, table?) => {
//界面优先和简易模板
if (designType !== 'data') {
//需要绑定字段、组件没有绑定字段的情况下新增绑定字段
const rangeComponents = ['time-range', 'date-range'];
if (rangeComponents.includes(component.type)) {
component.bindStartTime =
changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
component.bindEndTime =
changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
} else if (!noHaveField.includes(component.type) && !component.bindField) {
component.bindField =
changeToPinyin(component.label, isFieldUpper.value) + random(1000, 9999);
}
if (component.type === 'form' || component.type === 'one-for-one') {
component.bindTable = isFieldUpper.value
? `${mainTableName.value}_CHILD_${random(1000, 9999)}`
: `${mainTableName.value}_child_${random(1000, 9999)}`;
} else if (!noHaveTableAndField.includes(component.type)) {
component.bindTable = table || mainTableName.value;
}
}
if (component.isGridChild) component.options.span = 7;
};
//删除引用已删除的财务组件的计算式配置
const deleteAdoptedCalc = (index: number, list: any[]) => {
const component = list[index];
if (['computational', 'money-chinese'].includes(component.type)) {
component.options.beAdoptedComponent?.map((key) => {
getLayoutComponent(list, key);
});
}
};
const getLayoutComponent = (list, key) => {
list?.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getLayoutComponent(child.list, key);
}
} else if (item.type === 'table-layout') {
for (const child of item.layout!) {
for (const list of child.list) {
getLayoutComponent(list.children, key);
}
}
} else if (item.type === 'form') {
item.children.map((child: any) => {
if (['computational', 'money-chinese'].includes(child.type) && child.key === key) {
child.options.computationalConfig = [];
child.options.computationalConfigValue = '== 请填写计算式配置 ==';
}
});
} else {
if (['computational', 'money-chinese'].includes(item.type) && item.key === key) {
item.options.computationalConfig = [];
item.options.computationalConfigValue = '== 请填写计算式配置 ==';
}
}
});
};
return {
handleItemClick,
handleAddClick,
handleCopyClick,
handleDeleteClick,
handleTabGridDeleteClick,
handleMoveAdd,
handleColMoveAdd,
gridComponents,
addBindTableAndField,
};
},
});
});
</script>
<style scoped lang="less">
/* @import '/@/assets/style/designer/index.css'; */
.awf-container-center {
width: 100%;
height: calc(100%);
padding: 1px 10px 10px;
box-sizing: border-box;
overflow-y: auto;
}
/* @import '/@/assets/style/designer/index.css'; */
.awf-container-center {
width: 100%;
height: calc(100%);
padding: 1px 10px 10px;
box-sizing: border-box;
overflow-y: auto;
}
.widget-form-container {
width: 100%;
height: 100%;
background-color: #fff;
padding: 10px 5px;
box-sizing: border-box;
}
.widget-form-container {
width: 100%;
height: 100%;
background-color: #fff;
padding: 10px 5px;
box-sizing: border-box;
}
.widget-form-list {
height: 100%;
}
.widget-form-list {
height: 100%;
}
.fc-style .widget-form-container .widget-form-list {
background: #fff;
// border: 1px dashed #999;
min-height: calc(100vh - 305px);
}
.fc-style .widget-form-container .widget-form-list {
background: #fff;
// border: 1px dashed #999;
min-height: calc(100vh - 305px);
}
</style>

View File

@ -821,8 +821,7 @@
},
labelCol: {
type: Object,
default: () => {
}
default: () => {}
},
childIndex: {
type: Number

View File

@ -1,106 +1,92 @@
<template>
<Draggable
tag="ul"
item-key="type"
ghostClass="ghost"
:group="{ name: 'people', pull: 'clone', put: false }"
:sort="false"
:list="list"
class="cg-list-container"
:disabled="isDisabled"
>
<template #item="{ element }">
<li
v-if="fields.includes(element.type)"
class="form-edit-widget-label cg-list"
:class="{ 'no-put': element.type === 'divider' }"
@click="handleClick(element)"
>
<a>
<SvgIcon :name="element.type ?? element.icon" />
<span>{{ element.label }}</span>
</a>
</li>
</template>
</Draggable>
<div class="clear"></div>
<Draggable tag="ul" item-key="type" ghostClass="ghost" :group="{ name: 'people', pull: 'clone', put: false }" :sort="false" :list="list" class="cg-list-container" :disabled="isDisabled">
<template #item="{ element }">
<li v-if="fields.includes(element.type)" class="form-edit-widget-label cg-list" :class="{ 'no-put': element.type === 'divider' }" @click="handleClick(element)">
<a>
<SvgIcon :name="element.type ?? element.icon" />
<span>{{ element.label }}</span>
</a>
</li>
</template>
</Draggable>
<div class="clear"></div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue';
import Draggable from 'vuedraggable';
import { SvgIcon } from '/@/components/Icon';
import { PropType } from 'vue';
import Draggable from 'vuedraggable';
import { SvgIcon } from '/@/components/Icon';
const props = defineProps({
title: {
type: String,
},
fields: {
type: Array as PropType<Array<string>>,
required: true,
},
list: {
type: Array as PropType<Array<any>>,
required: true,
},
isDisabled: Boolean,
});
const emit = defineEmits(['copy']);
const handleClick = (element: any) => {
emit('copy', element);
};
const props = defineProps({
title: {
type: String
},
fields: {
type: Array as PropType<Array<string>>,
required: true
},
list: {
type: Array as PropType<Array<any>>,
required: true
},
isDisabled: Boolean
});
const emit = defineEmits(['copy']);
const handleClick = (element: any) => {
emit('copy', element);
};
</script>
<style scoped lang="less">
.left-title {
height: 14px;
line-height: 14px;
padding-left: 10px;
box-sizing: border-box;
margin: 15px 0;
border-left: 6px solid #5e95ff;
font-weight: bold;
font-size: 13px;
}
.left-title {
height: 14px;
line-height: 14px;
padding-left: 10px;
box-sizing: border-box;
margin: 15px 0;
border-left: 6px solid #5e95ff;
font-weight: bold;
font-size: 13px;
}
.cg-list-container {
width: 100%;
}
.cg-list-container {
width: 100%;
}
.cg-list {
float: left;
width: 44%;
margin-right: 6%;
margin-bottom: 8px;
padding: 8px 0;
text-align: center;
font-size: 12px;
border: 1px solid #e4e4e4;
}
.cg-list {
float: left;
width: 44%;
margin-right: 6%;
margin-bottom: 8px;
padding: 8px 0;
text-align: center;
font-size: 12px;
border: 1px solid #e4e4e4;
}
.cg-list:hover {
border: 1px dashed #eef4ff;
background-color: #eef4ff;
cursor: v-bind("props.isDisabled ? 'not-allowed' : 'pointer'");
}
.cg-list:hover {
border: 1px dashed #eef4ff;
background-color: #eef4ff;
cursor: v-bind("props.isDisabled ? 'not-allowed' : 'pointer'");
}
.cg-list:hover a {
color: #0960bd;
cursor: v-bind("props.isDisabled ? 'not-allowed' : 'pointer'");
}
.cg-list:hover a {
color: #0960bd;
cursor: v-bind("props.isDisabled ? 'not-allowed' : 'pointer'");
}
.cg-list a {
color: #333;
}
.cg-list a {
color: #333;
}
.cg-list a svg {
width: 14px !important;
height: 14px !important;
margin-right: 5px;
}
.cg-list a svg {
width: 14px !important;
height: 14px !important;
margin-right: 5px;
}
.clear {
clear: both;
}
// @import '/@/assets/style/designer/index.css';
.clear {
clear: both;
}
// @import '/@/assets/style/designer/index.css';
</style>

View File

@ -1,24 +1,24 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<div>
<CodeEditor v-model:value="defaultValue" language="js" />
</div>
<div>
<CodeEditor :value="tipContent" language="js" readonly />
</div>
</BasicModal>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<div>
<CodeEditor v-model:value="defaultValue" language="js" />
</div>
<div>
<CodeEditor :value="tipContent" language="js" readonly />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { CodeEditor } from '/@/components/CodeEditor';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { computed, ref, unref } from 'vue';
import { CodeEditor } from '/@/components/CodeEditor';
import { BasicModal, useModalInner } from '/@/components/Modal';
const emit = defineEmits(['success', 'register']);
const emit = defineEmits(['success', 'register']);
const eventType = ref('');
const eventType = ref('');
const tipContent = computed(() => {
return `
const tipContent = computed(() => {
return `
//事件方法参数 一共有3个
{
schema: FormSchema; //当前表单组件的schema
@ -36,28 +36,28 @@
}
`;
});
});
const defaultValue = ref(``);
const defaultValue = ref(``);
const title = computed(() => {
return `编辑${eventType.value}事件`;
});
const title = computed(() => {
return `编辑${eventType.value}事件`;
});
const [registerModal, { closeModal }] = useModalInner(async (data) => {
eventType.value = data.type;
//如果已经设置过当前事件代码 则显示已经设置的值 反之 显示默认值
if (data.content) {
defaultValue.value = data.content;
} else {
defaultValue.value = `
const [registerModal, { closeModal }] = useModalInner(async (data) => {
eventType.value = data.type;
//如果已经设置过当前事件代码 则显示已经设置的值 反之 显示默认值
if (data.content) {
defaultValue.value = data.content;
} else {
defaultValue.value = `
console.log(schema,formModel,formActionType);
`;
}
});
}
});
async function handleSubmit() {
emit('success', unref(eventType), unref(defaultValue));
closeModal();
}
async function handleSubmit() {
emit('success', unref(eventType), unref(defaultValue));
closeModal();
}
</script>

View File

@ -1,155 +1,138 @@
<template>
<div class="box-top">
<div class="title">{{ t('隐藏组件列表') }}</div>
<a-button type="primary" size="small" @click="addComponent">{{ t('添加组件') }}</a-button>
</div>
<a-form
v-for="(item, index) in hiddenComponent"
:key="item.key"
:labelCol="{ span: 8 }"
:wrapperCol="{ span: 16 }"
style="margin-top: 20px; position: relative"
@mouseenter="showDeleteIndex = index"
@mouseleave="showDeleteIndex = -1"
>
<a-form-item :label="t('组件编码')" required>
<a-input v-model:value="item.code" :placeholder="t('请输入组件编码')" />
</a-form-item>
<a-form-item :label="t('组件名称')" required>
<a-input
v-model:value="item.label"
:placeholder="t('请输入组件名称')"
@change="handleChangeLabel(item)"
/>
</a-form-item>
<a-form-item :label="t('组件值')" required>
<a-input v-model:value="item.value" :placeholder="t('请输入组件值')" />
</a-form-item>
<template v-if="designType === 'data'">
<a-form-item :label="t('绑定表')" required>
<a-select
v-model:value="item.bindTable"
:placeholder="t('请选择数据表')"
size="mini"
@change="changeTable"
>
<a-select-option v-for="(table, idx) in mainTable" :value="table.name" :key="idx" />
</a-select>
</a-form-item>
<a-form-item :label="t('绑定字段')" required>
<a-select v-model:value="item.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
<a-select-option v-for="(field, idx) in tableField" :value="field.name" :key="idx" />
</a-select>
</a-form-item>
</template>
<div class="icon-delete" v-show="index === showDeleteIndex">
<SvgIcon name="delete" @click.stop="deleteHiddenComponent(index)" class="svg-delete" />
<div class="box-top">
<div class="title">{{ t('隐藏组件列表') }}</div>
<a-button type="primary" size="small" @click="addComponent">{{ t('添加组件') }}</a-button>
</div>
</a-form>
<a-form v-for="(item, index) in hiddenComponent" :key="item.key" :labelCol="{ span: 8 }" :wrapperCol="{ span: 16 }" style="margin-top: 20px; position: relative" @mouseenter="showDeleteIndex = index" @mouseleave="showDeleteIndex = -1">
<a-form-item :label="t('组件编码')" required>
<a-input v-model:value="item.code" :placeholder="t('请输入组件编码')" />
</a-form-item>
<a-form-item :label="t('组件名称')" required>
<a-input v-model:value="item.label" :placeholder="t('请输入组件名称')" @change="handleChangeLabel(item)" />
</a-form-item>
<a-form-item :label="t('组件值')" required>
<a-input v-model:value="item.value" :placeholder="t('请输入组件值')" />
</a-form-item>
<template v-if="designType === 'data'">
<a-form-item :label="t('绑定表')" required>
<a-select v-model:value="item.bindTable" :placeholder="t('请选择数据表')" size="mini" @change="changeTable">
<a-select-option v-for="(table, idx) in mainTable" :value="table.name" :key="idx" />
</a-select>
</a-form-item>
<a-form-item :label="t('绑定字段')" required>
<a-select v-model:value="item.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
<a-select-option v-for="(field, idx) in tableField" :value="field.name" :key="idx" />
</a-select>
</a-form-item>
</template>
<div class="icon-delete" v-show="index === showDeleteIndex">
<SvgIcon name="delete" @click.stop="deleteHiddenComponent(index)" class="svg-delete" />
</div>
</a-form>
</template>
<script lang="ts" setup>
import { ref, watch, inject, Ref } from 'vue';
import { SvgIcon } from '/@/components/Icon';
import { TableInfo, FieldInfo, HiddenComponentInfo } from '../types';
import { changeToPinyin } from '/@/utils/event/design';
import { random } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
component: {
type: Array as PropType<HiddenComponentInfo[]>,
},
});
import { ref, watch, inject, Ref } from 'vue';
import { SvgIcon } from '/@/components/Icon';
import { TableInfo, FieldInfo, HiddenComponentInfo } from '../types';
import { changeToPinyin } from '/@/utils/event/design';
import { random } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
component: {
type: Array as PropType<HiddenComponentInfo[]>
}
});
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo') as Ref<TableInfo[]>;
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo') as Ref<TableInfo[]>;
const designType = inject<string>('designType');
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
const designType = inject<string>('designType');
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
const hiddenComponent = ref<HiddenComponentInfo[]>(props.component!);
const mainTable = ref<TableInfo[]>([]);
const tableField = ref<FieldInfo[]>([]);
const showDeleteIndex = ref<number>(-1);
let mainTableName;
if (designType !== 'data') {
mainTableName = inject<string>('mainTableName');
}
const hiddenComponent = ref<HiddenComponentInfo[]>(props.component!);
const mainTable = ref<TableInfo[]>([]);
const tableField = ref<FieldInfo[]>([]);
const showDeleteIndex = ref<number>(-1);
let mainTableName;
if (designType !== 'data') {
mainTableName = inject<string>('mainTableName');
}
watch(
() => tableInfo,
() => {
mainTable.value = tableInfo.value?.filter((item: any) => item.isMain); //隐藏组件只能选择主表的字段进行绑定
},
{
deep: true,
immediate: true,
},
);
const addComponent = () => {
const key = `hiddenComponent${Date.now()}`;
const com: HiddenComponentInfo = {
key,
type: 'hiddenComponent',
code: '',
label: '',
value: '',
bindTable: '',
bindField: '',
watch(
() => tableInfo,
() => {
mainTable.value = tableInfo.value?.filter((item: any) => item.isMain); //隐藏组件只能选择主表的字段进行绑定
},
{
deep: true,
immediate: true
}
);
const addComponent = () => {
const key = `hiddenComponent${Date.now()}`;
const com: HiddenComponentInfo = {
key,
type: 'hiddenComponent',
code: '',
label: '',
value: '',
bindTable: '',
bindField: ''
};
if (designType !== 'data') {
com.bindTable = mainTableName.value!;
}
hiddenComponent.value.push(com);
};
if (designType !== 'data') {
com.bindTable = mainTableName.value!;
}
hiddenComponent.value.push(com);
};
const handleChangeLabel = (info) => {
if (designType !== 'data') {
info.bindField = changeToPinyin(info.label, isFieldUpper.value) + random(1000, 9999);
}
};
const deleteHiddenComponent = (index: number) => {
hiddenComponent.value.splice(index, 1);
};
const changeTable = (val: string) => {
const chooseTable = mainTable.value.find((table: any) => table.name === val)!;
tableField.value = chooseTable?.fields;
};
const handleChangeLabel = (info) => {
if (designType !== 'data') {
info.bindField = changeToPinyin(info.label, isFieldUpper.value) + random(1000, 9999);
}
};
const deleteHiddenComponent = (index: number) => {
hiddenComponent.value.splice(index, 1);
};
const changeTable = (val: string) => {
const chooseTable = mainTable.value.find((table: any) => table.name === val)!;
tableField.value = chooseTable?.fields;
};
</script>
<style lang="less" scoped>
.box-top {
display: flex;
justify-content: space-between;
margin: 5px 0 10px 10px;
align-items: center;
.box-top {
display: flex;
justify-content: space-between;
margin: 5px 0 10px 10px;
align-items: center;
.title {
font-size: 14px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
.title {
font-size: 14px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
}
}
.icon-delete {
position: absolute;
top: -12px;
right: -5px;
width: 24px;
text-align: center;
border: 1px solid #f64c4c;
color: #f64c4c;
border-radius: 50%;
cursor: pointer;
.icon-delete {
position: absolute;
top: -12px;
right: -5px;
width: 24px;
text-align: center;
border: 1px solid #f64c4c;
color: #f64c4c;
border-radius: 50%;
cursor: pointer;
.svg-delete {
width: 13px !important;
height: 13px !important;
.svg-delete {
width: 13px !important;
height: 13px !important;
}
}
}
:deep(.ant-form-item) {
margin-bottom: 10px !important;
}
:deep(.ant-form-item) {
margin-bottom: 10px !important;
}
</style>

View File

@ -1,59 +1,52 @@
<template>
<div>
<BasicDrawer
v-bind="$attrs"
size="large"
@register="registerDrawer"
@close="handleCloser"
:title="t('表单预览')"
>
<div>
<SimpleForm ref="formRef" :formProps="state.formProps" :formModel="state.formModel">
</SimpleForm>
</div>
</BasicDrawer>
</div>
<div>
<BasicDrawer v-bind="$attrs" size="large" @register="registerDrawer" @close="handleCloser" :title="t('表单预览')">
<div>
<SimpleForm ref="formRef" :formProps="state.formProps" :formModel="state.formModel"> </SimpleForm>
</div>
</BasicDrawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, inject } from 'vue';
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { buildOption } from '/@/utils/helper/designHelper';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const state = reactive({
formProps: {} as any,
formModel: {},
});
const formRef = ref();
const isCustomForm = inject<boolean>('isCustomForm', false);
//使用表单钩子 注册表单 获取到 操作表单的方法
//使用抽屉内部钩子 获取到 操作抽屉的方法
const [registerDrawer, { setDrawerProps }] = useDrawerInner(async (option) => {
//每次进来置空
setDrawerProps({
destroyOnClose: true,
width: option?.config?.formWidth || 800,
canFullscreen: true,
import { reactive, ref, inject } from 'vue';
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { buildOption } from '/@/utils/helper/designHelper';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const state = reactive({
formProps: {} as any,
formModel: {}
});
setTimeout(() => {
formRef.value.setProps({
...buildOption(option, !isCustomForm),
showResetButton: true,
showSubmitButton: true,
});
formRef.value.setDefaultValue();
}, 50);
});
const formRef = ref();
//关闭方法
const handleCloser = () => {
formRef.value!.resetFields(); //重置表单
state.formProps = {};
};
const isCustomForm = inject<boolean>('isCustomForm', false);
//使用表单钩子 注册表单 获取到 操作表单的方法
//使用抽屉内部钩子 获取到 操作抽屉的方法
const [registerDrawer, { setDrawerProps }] = useDrawerInner(async (option) => {
//每次进来置空
setDrawerProps({
destroyOnClose: true,
width: option?.config?.formWidth || 800,
canFullscreen: true
});
setTimeout(() => {
formRef.value.setProps({
...buildOption(option, !isCustomForm),
showResetButton: true,
showSubmitButton: true
});
formRef.value.setDefaultValue();
}, 50);
});
//关闭方法
const handleCloser = () => {
formRef.value!.resetFields(); //重置表单
state.formProps = {};
};
</script>

View File

@ -1,138 +1,125 @@
<template>
<div v-for="(item, index) in render" :key="item.type">
<div :class="index == render.length - 1 ? 'mb-0' : 'event-box'" v-if="isShowEvent(item.name)">
<div class="event-title">{{ item.name }}</div>
<div>
<a-row type="flex" align="middle">
<a-col :offset="1" :span="6">
<span>{{ t('配置脚本:') }}</span>
</a-col>
<a-col :span="17">
<a-input
:placeholder="t('点击配置脚本')"
:value="configTxt(item.type)"
@click="showConfigModal(item.type)"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-col>
</a-row>
</div>
<div v-for="(item, index) in render" :key="item.type">
<div :class="index == render.length - 1 ? 'mb-0' : 'event-box'" v-if="isShowEvent(item.name)">
<div class="event-title">{{ item.name }}</div>
<div>
<a-row type="flex" align="middle">
<a-col :offset="1" :span="6">
<span>{{ t('配置脚本:') }}</span>
</a-col>
<a-col :span="17">
<a-input :placeholder="t('点击配置脚本')" :value="configTxt(item.type)" @click="showConfigModal(item.type)">
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-col>
</a-row>
</div>
</div>
</div>
</div>
<ScriptConfig @register="registerModal" @success="submitConfig" :disabled="disabled" />
<ScriptConfig @register="registerModal" @success="submitConfig" :disabled="disabled" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Icon } from '/@/components/Icon';
import { useModal } from '/@/components/Modal';
import ScriptConfig from './settings/ScriptConfig.vue';
import {
noBlurComponentEvent,
noChangeComponentEvent,
noClickComponentEvent,
noHaveTableAndField,
} from '../../types';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
select: { type: Object, default: {} },
widgetForm: { type: Object, default: {} },
disabled: { type: Boolean, default: false },
});
const render = [
{
name: t('失焦事件'),
type: 'blur',
},
{
name: t('点击事件'),
type: 'click',
},
{
name: t('值改变事件'),
type: 'change',
},
];
const [registerModal, { openModal }] = useModal();
const getMainTableComponents = (list, mainTableComponent) => {
const rangeComponents = ['time-range', 'date-range'];
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getMainTableComponents(child.list, mainTableComponent);
import { ref } from 'vue';
import { Icon } from '/@/components/Icon';
import { useModal } from '/@/components/Modal';
import ScriptConfig from './settings/ScriptConfig.vue';
import { noBlurComponentEvent, noChangeComponentEvent, noClickComponentEvent, noHaveTableAndField } from '../../types';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
select: { type: Object, default: {} },
widgetForm: { type: Object, default: {} },
disabled: { type: Boolean, default: false }
});
const render = [
{
name: t('失焦事件'),
type: 'blur'
},
{
name: t('点击事件'),
type: 'click'
},
{
name: t('值改变事件'),
type: 'change'
}
} else if (rangeComponents.includes(item.type)) {
//时间范围、日期范围需改为两个字段
mainTableComponent.push({
label: t(`{name}开始时间`, { name: item.label }),
bindField: item.bindStartTime,
];
const [registerModal, { openModal }] = useModal();
const getMainTableComponents = (list, mainTableComponent) => {
const rangeComponents = ['time-range', 'date-range'];
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getMainTableComponents(child.list, mainTableComponent);
}
} else if (rangeComponents.includes(item.type)) {
//时间范围、日期范围需改为两个字段
mainTableComponent.push({
label: t(`{name}开始时间`, { name: item.label }),
bindField: item.bindStartTime
});
mainTableComponent.push({
label: t(`{name}结束时间`, { name: item.label }),
bindField: item.bindEndTime
});
} else if (item.type !== 'form' && item.type !== 'one-for-one' && !noHaveTableAndField.includes(item.type)) {
//除去不绑定表和字段的组件 以及表格组件
mainTableComponent.push(item);
}
});
mainTableComponent.push({
label: t(`{name}结束时间`, { name: item.label }),
bindField: item.bindEndTime,
};
const isShowEvent = (name) => {
const type = props.select?.type;
switch (name) {
case t('失焦事件'):
return !noBlurComponentEvent.includes(type);
case t('点击事件'):
return !noClickComponentEvent.includes(type);
case t('值改变事件'):
return !noChangeComponentEvent.includes(type);
}
};
const showConfigModal = (val) => {
const mainTableComponent = ref<any[]>([]);
getMainTableComponents(props.widgetForm?.list, mainTableComponent.value);
openModal(true, {
type: val,
content: props.select?.options.events[val] || '',
list: mainTableComponent.value //这样传才能每次点击都更新
});
} else if (
item.type !== 'form' &&
item.type !== 'one-for-one' &&
!noHaveTableAndField.includes(item.type)
) {
//除去不绑定表和字段的组件 以及表格组件
mainTableComponent.push(item);
}
});
};
};
const isShowEvent = (name) => {
const type = props.select?.type;
switch (name) {
case t('失焦事件'):
return !noBlurComponentEvent.includes(type);
case t('点击事件'):
return !noClickComponentEvent.includes(type);
case t('值改变事件'):
return !noChangeComponentEvent.includes(type);
}
};
const showConfigModal = (val) => {
const mainTableComponent = ref<any[]>([]);
getMainTableComponents(props.widgetForm?.list, mainTableComponent.value);
openModal(true, {
type: val,
content: props.select?.options.events[val] || '',
list: mainTableComponent.value, //这样传才能每次点击都更新
});
};
const configTxt = (type) => {
return props.select.options.events[type] ? t('已配置') : '';
};
const submitConfig = (type, value) => {
if (props.select?.options.events) {
props.select.options.events[type] = value;
} else {
props.select.options.events = {
[type]: value,
};
}
};
const configTxt = (type) => {
return props.select.options.events[type] ? t('已配置') : '';
};
const submitConfig = (type, value) => {
if (props.select?.options.events) {
props.select.options.events[type] = value;
} else {
props.select.options.events = {
[type]: value
};
}
};
</script>
<style lang="less" scoped>
.event-box {
margin-bottom: 25px;
}
.event-box {
margin-bottom: 25px;
}
.event-title {
margin: 5px 0 10px 10px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.event-title {
margin: 5px 0 10px 10px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
</style>

View File

@ -468,10 +468,7 @@
<a-input-number v-model:value="data.options.span" :max="24" :min="0" addonAfter="/ 24" @change="handleSpanChange" />
</a-form-item>
<a-form-item v-if="hasKey('mode')" label="模式选择">
<a-select
ref="select"
v-model:value="data.options.mode"
>
<a-select ref="select" v-model:value="data.options.mode">
<a-select-option value="">单选</a-select-option>
<a-select-option value="multiple">多选</a-select-option>
</a-select>
@ -498,8 +495,8 @@
<SvgIcon name="upload" style="margin-right: 10px" />
{{ t('点击上传') }}
</a-button>
<div v-if="VITE_GLOB_UPLOAD_ALERT_TIP?.trim()" style="color: red; margin-top: 8px;">
{{VITE_GLOB_UPLOAD_ALERT_TIP}}
<div v-if="VITE_GLOB_UPLOAD_ALERT_TIP?.trim()" style="color: red; margin-top: 8px">
{{ VITE_GLOB_UPLOAD_ALERT_TIP }}
</div>
</a-upload>
</a-form-item>
@ -790,7 +787,7 @@
import StaticData from './settings/StaticData.vue';
import { ApiConfig } from '/@/components/ApiConfig';
import { DicItemSelect } from '/@/components/DicItemSelect';
import { getAppEnvConfig } from '/@/utils/env';
import { getAppEnvConfig } from '/@/utils/env';
import { ColorPicker } from '/@/components/ColorPicker';
import { TimePicker } from '/@/components/TimePicker';
import SelectDepartmentV2 from '/@/components/Form/src/components/SelectDepartmentV2.vue';
@ -1594,14 +1591,13 @@
}
function findTableFields(obj) {
if (!props.widgetForm?.list) {
return [];
}
const bTable = obj.bindTable;
const pTable = props.widgetForm.list.find((item) => item.type === 'form' && item.bindTable === bTable);
if (!pTable) {
const tableFormList = props.widgetForm.list.filter(item => item.type !== 'form' && item.type !== 'associate-popup')
const tableFormList = props.widgetForm.list.filter((item) => item.type !== 'form' && item.type !== 'associate-popup');
return tableFormList;
}
return pTable.children;

View File

@ -1,146 +1,134 @@
<template>
<div class="top-box">
<div class="regular-title">{{ t('正则校验列表') }}</div>
<a-button type="primary" size="small" @click="addRegular" class="add-btn">
{{ t('添加正则') }}
</a-button>
</div>
<a-form
layout="vertical"
v-for="(item, index) in data.options?.rules"
:key="index"
class="reg-item"
@mouseenter="showDeleteIndex = index"
@mouseleave="showDeleteIndex = -1"
>
<a-form-item :label="t('正则表达式')">
<a-textarea
:placeholder="t('请输入正则表达式')"
v-model:value="item.pattern"
size="small"
@blur="checkPattern(item.pattern)"
/>
</a-form-item>
<a-form-item :label="t('错误提示')">
<a-textarea :placeholder="t('请输入错误提示')" v-model:value="item.message" size="small" />
</a-form-item>
<div class="delete-btn" v-show="index === showDeleteIndex">
<SvgIcon name="delete" @click.stop="deleteRegList(index)" class="svg-delete" />
<div class="top-box">
<div class="regular-title">{{ t('正则校验列表') }}</div>
<a-button type="primary" size="small" @click="addRegular" class="add-btn">
{{ t('添加正则') }}
</a-button>
</div>
</a-form>
<a-form layout="vertical" v-for="(item, index) in data.options?.rules" :key="index" class="reg-item" @mouseenter="showDeleteIndex = index" @mouseleave="showDeleteIndex = -1">
<a-form-item :label="t('正则表达式')">
<a-textarea :placeholder="t('请输入正则表达式')" v-model:value="item.pattern" size="small" @blur="checkPattern(item.pattern)" />
</a-form-item>
<a-form-item :label="t('错误提示')">
<a-textarea :placeholder="t('请输入错误提示')" v-model:value="item.message" size="small" />
</a-form-item>
<div class="delete-btn" v-show="index === showDeleteIndex">
<SvgIcon name="delete" @click.stop="deleteRegList(index)" class="svg-delete" />
</div>
</a-form>
</template>
<script lang="ts" setup>
import { SvgIcon } from '/@/components/Icon';
import { message } from 'ant-design-vue';
import { watch, ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
select: { type: Object },
});
const emit = defineEmits(['update:select']);
const data = ref<any>();
const showDeleteIndex = ref<number>(-1);
watch(
() => props.select,
(val) => {
data.value = val;
},
{
immediate: true,
deep: true,
},
);
watch(
data,
(val) => {
emit('update:select', val);
},
{
deep: true,
},
);
const addRegular = () => {
data.value.options.rules.push({
pattern: '',
message: '',
import { SvgIcon } from '/@/components/Icon';
import { message } from 'ant-design-vue';
import { watch, ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
select: { type: Object }
});
data.value.options.required = true;
};
const deleteRegList = (index) => {
data.value.options.rules.splice(index, 1);
};
const checkPattern = (pattern) => {
if (!pattern) return;
const start = pattern.substr(0, 1);
const end = pattern.substr(-1);
if (start !== '/' || end !== '/') {
message.error(t('正则表达式不合法,提醒:是否表达式前后带上了/'));
return;
}
};
const emit = defineEmits(['update:select']);
const data = ref<any>();
const showDeleteIndex = ref<number>(-1);
watch(
() => props.select,
(val) => {
data.value = val;
},
{
immediate: true,
deep: true
}
);
watch(
data,
(val) => {
emit('update:select', val);
},
{
deep: true
}
);
const addRegular = () => {
data.value.options.rules.push({
pattern: '',
message: ''
});
data.value.options.required = true;
};
const deleteRegList = (index) => {
data.value.options.rules.splice(index, 1);
};
const checkPattern = (pattern) => {
if (!pattern) return;
const start = pattern.substr(0, 1);
const end = pattern.substr(-1);
if (start !== '/' || end !== '/') {
message.error(t('正则表达式不合法,提醒:是否表达式前后带上了/'));
return;
}
};
</script>
<style lang="less" scoped>
.top-box {
display: flex;
justify-content: space-between;
align-items: center;
margin: 5px 0 10px 10px;
.top-box {
display: flex;
justify-content: space-between;
align-items: center;
margin: 5px 0 10px 10px;
.regular-title {
font-size: 14px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
}
.reg-item {
position: relative;
padding: 5px 0 5px 15px;
margin-bottom: 18px;
:deep(.ant-form-item-label) > label {
font-size: 12px;
.regular-title {
font-size: 14px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
}
.delete-btn {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: -3px;
right: -3px;
width: 22px;
height: 22px;
border: 1px solid #f64c4c;
color: #f64c4c;
border-radius: 50%;
cursor: pointer;
.reg-item {
position: relative;
padding: 5px 0 5px 15px;
margin-bottom: 18px;
.svg-delete {
width: 12px !important;
height: 12px !important;
}
:deep(.ant-form-item-label) > label {
font-size: 12px;
}
.delete-btn {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: -3px;
right: -3px;
width: 22px;
height: 22px;
border: 1px solid #f64c4c;
color: #f64c4c;
border-radius: 50%;
cursor: pointer;
.svg-delete {
width: 12px !important;
height: 12px !important;
}
}
:deep(.ant-form-item) {
margin-bottom: 10px;
}
:deep(.ant-form-item:last-child) {
margin-bottom: 0;
}
}
:deep(.ant-form-item) {
margin-bottom: 10px;
}
// .add-btn {
// display: flex;
// align-items: center;
:deep(.ant-form-item:last-child) {
margin-bottom: 0;
}
}
// .add-btn {
// display: flex;
// align-items: center;
// /deep/.app-iconify {
// display: block !important;
// }
// }
// /deep/.app-iconify {
// display: block !important;
// }
// }
</style>

View File

@ -1,165 +1,157 @@
<template>
<div class="awc-containter">
<a-form
v-bind="layout"
v-if="tablecell && Object.keys(tablecell).length > 0"
:key="1"
size="small"
>
<a-form-item label="类型">
<a-input v-model:value="tablecell.class" size="mini" />
</a-form-item>
<a-form-item label="高度">
<a-input-number v-model:value="tablecell.height" size="mini" />
</a-form-item>
</a-form>
<a-form v-bind="layout" v-if="tableth && Object.keys(tableth).length > 0" :key="2" size="small">
<a-form-item label="类型">
<a-input v-model:value="tableth.class" size="mini" />
</a-form-item>
<a-form-item label="宽度">
<a-input-number v-model:value="tableth.width" size="mini" />
</a-form-item>
</a-form>
<a-form v-bind="layout" v-if="showData" :key="3" size="small">
<a-form-item :label="t('标题')">
<a-input v-model:value="data.label" size="mini" />
</a-form-item>
<a-form-item label="边框宽度">
<a-input-number v-model:value="data.options.borderWidth" size="mini" />
</a-form-item>
<a-form-item label="边框颜色">
<ColorPicker v-model:value="data.options.borderColor" />
</a-form-item>
<a-form-item label="自定义类">
<a-input v-model:value="data.options.class" size="mini" />
</a-form-item>
<a-form-item :label="t('是否显示')">
<a-switch v-model:checked="data.options.isShow" />
</a-form-item>
</a-form>
</div>
<div class="awc-containter">
<a-form v-bind="layout" v-if="tablecell && Object.keys(tablecell).length > 0" :key="1" size="small">
<a-form-item label="类型">
<a-input v-model:value="tablecell.class" size="mini" />
</a-form-item>
<a-form-item label="高度">
<a-input-number v-model:value="tablecell.height" size="mini" />
</a-form-item>
</a-form>
<a-form v-bind="layout" v-if="tableth && Object.keys(tableth).length > 0" :key="2" size="small">
<a-form-item label="类型">
<a-input v-model:value="tableth.class" size="mini" />
</a-form-item>
<a-form-item label="宽度">
<a-input-number v-model:value="tableth.width" size="mini" />
</a-form-item>
</a-form>
<a-form v-bind="layout" v-if="showData" :key="3" size="small">
<a-form-item :label="t('标题')">
<a-input v-model:value="data.label" size="mini" />
</a-form-item>
<a-form-item label="边框宽度">
<a-input-number v-model:value="data.options.borderWidth" size="mini" />
</a-form-item>
<a-form-item label="边框颜色">
<ColorPicker v-model:value="data.options.borderColor" />
</a-form-item>
<a-form-item label="自定义类">
<a-input v-model:value="data.options.class" size="mini" />
</a-form-item>
<a-form-item :label="t('是否显示')">
<a-switch v-model:checked="data.options.isShow" />
</a-form-item>
</a-form>
</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref, Ref, watch } from 'vue';
import { defineComponent, inject, ref, Ref, watch } from 'vue';
import Draggable from 'vuedraggable';
import { SvgIcon, IconPicker, Icon } from '/@/components/Icon';
import Draggable from 'vuedraggable';
import { SvgIcon, IconPicker, Icon } from '/@/components/Icon';
import { ColorPicker } from '/@/components/ColorPicker';
import { ColorPicker } from '/@/components/ColorPicker';
import { useI18n } from '/@/hooks/web/useI18n';
import { TableCell, TableTh } from '../../types';
import { useI18n } from '/@/hooks/web/useI18n';
import { TableCell, TableTh } from '../../types';
const { t } = useI18n();
export default defineComponent({
name: 'PropertyOption',
components: {
Draggable,
SvgIcon,
IconPicker,
Icon,
ColorPicker,
},
emits: ['update:select'],
props: {
//所选组件配置
select: {
type: Object,
},
widgetForm: {
type: Object,
},
},
setup(props, context) {
const showData = ref(false);
const data = ref<any>();
const tablecell = inject<Ref<TableCell>>('tableCell');
const tableth = inject<Ref<TableTh>>('tableTh');
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
watch(
() => props.select,
(val) => {
data.value = val;
if (
(tableth?.value && Object.keys(tableth.value).length > 0) ||
(tablecell?.value && Object.keys(tablecell.value).length > 0)
) {
showData.value = false;
} else {
showData.value = true;
}
const { t } = useI18n();
export default defineComponent({
name: 'PropertyOption',
components: {
Draggable,
SvgIcon,
IconPicker,
Icon,
ColorPicker
},
{
deep: true,
immediate: true,
emits: ['update:select'],
props: {
//所选组件配置
select: {
type: Object
},
widgetForm: {
type: Object
}
},
);
watch(
() => tablecell?.value,
(val) => {
if (val && Object.keys(val).length > 0) {
showData.value = false;
} else if (tableth?.value && Object.keys(tableth.value).length <= 0) {
showData.value = true;
}
},
{
deep: true,
immediate: true,
},
);
watch(
() => tableth?.value,
(val) => {
if (val && Object.keys(val).length > 0) {
showData.value = false;
} else if (tablecell?.value && Object.keys(tablecell.value).length <= 0) {
showData.value = true;
}
},
{
deep: true,
immediate: true,
},
);
watch(
data,
(val) => {
context.emit('update:select', val);
},
{
immediate: true,
deep: true,
},
);
setup(props, context) {
const showData = ref(false);
const data = ref<any>();
const tablecell = inject<Ref<TableCell>>('tableCell');
const tableth = inject<Ref<TableTh>>('tableTh');
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 }
};
watch(
() => props.select,
(val) => {
data.value = val;
if ((tableth?.value && Object.keys(tableth.value).length > 0) || (tablecell?.value && Object.keys(tablecell.value).length > 0)) {
showData.value = false;
} else {
showData.value = true;
}
},
{
deep: true,
immediate: true
}
);
watch(
() => tablecell?.value,
(val) => {
if (val && Object.keys(val).length > 0) {
showData.value = false;
} else if (tableth?.value && Object.keys(tableth.value).length <= 0) {
showData.value = true;
}
},
{
deep: true,
immediate: true
}
);
watch(
() => tableth?.value,
(val) => {
if (val && Object.keys(val).length > 0) {
showData.value = false;
} else if (tablecell?.value && Object.keys(tablecell.value).length <= 0) {
showData.value = true;
}
},
{
deep: true,
immediate: true
}
);
watch(
data,
(val) => {
context.emit('update:select', val);
},
{
immediate: true,
deep: true
}
);
return {
layout,
tablecell,
tableth,
showData,
data,
t,
};
},
});
return {
layout,
tablecell,
tableth,
showData,
data,
t
};
}
});
</script>
<style scoped lang="less">
*,
:deep(.ant-input) {
font-size: 12px !important;
}
*,
:deep(.ant-input) {
font-size: 12px !important;
}
:deep(.ant-row) {
align-items: center;
}
:deep(.ant-row) {
align-items: center;
}
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-input-number) {
width: 100%;
}
</style>

View File

@ -1,412 +1,382 @@
<template>
<a-modal
:width="1000"
v-model:visible="visible"
:title="title"
:maskClosable="false"
@ok="handleSubmit"
@cancel="handleClose"
:bodyStyle="{ padding: '0 10px 10px' }"
>
<div class="list-title">{{ t('字段列表') }}</div>
<a-row type="flex" align="middle" :gutter="12" style="margin: 15px 0">
<a-col flex="auto" class="text-right">{{ t('调用接口:') }}</a-col>
<a-col flex="75%">
<a-input v-model:value="apiConfigInfo.path" disabled />
</a-col>
<a-col flex="auto">
<a-button key="submit" type="primary" @click="viewInterfaceInfo">{{
t('查看接口信息')
}}</a-button>
</a-col>
</a-row>
<a-table
:dataSource="apiConfigInfo.outputParams"
:columns="apiColumns"
:pagination="false"
:scroll="{ y: '400px' }"
>
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
{{ t('是否在弹层列表显示') }}
<a-checkbox v-model:checked="checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'name'">
<a-input
v-model:value="record.name"
:placeholder="t('请填写字段名')"
:disabled="disabled"
/>
</template>
<template v-else-if="column.key === 'component'">
<a-select
v-model:value="record.component"
style="width: 100%"
:placeholder="t('请选择填充组件')"
@change="(value) => selectBindField(value, record)"
allowClear
:disabled="disabled"
>
<a-select-option :value="val.key" v-for="(val, idx) in selectedList" :key="idx">
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" :placeholder="t('请填写表头名称')" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input
v-model:value="record.width"
:disabled="!record.show"
:placeholder="t('请填写列表宽度')"
/>
</template>
<template v-else-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteParamas(index)" />
</template>
</template>
</a-table>
<a-button type="dashed" block @click="addOutPutParamas" :disabled="disabled">
<PlusOutlined />
{{ t('新增') }}
</a-button>
</a-modal>
<InterfaceInfoModal @register="registerModal" />
<a-modal :width="1000" v-model:visible="visible" :title="title" :maskClosable="false" @ok="handleSubmit" @cancel="handleClose" :bodyStyle="{ padding: '0 10px 10px' }">
<div class="list-title">{{ t('字段列表') }}</div>
<a-row type="flex" align="middle" :gutter="12" style="margin: 15px 0">
<a-col flex="auto" class="text-right">{{ t('调用接口:') }}</a-col>
<a-col flex="75%">
<a-input v-model:value="apiConfigInfo.path" disabled />
</a-col>
<a-col flex="auto">
<a-button key="submit" type="primary" @click="viewInterfaceInfo">{{ t('查看接口信息') }}</a-button>
</a-col>
</a-row>
<a-table :dataSource="apiConfigInfo.outputParams" :columns="apiColumns" :pagination="false" :scroll="{ y: '400px' }">
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
{{ t('是否在弹层列表显示') }}
<a-checkbox v-model:checked="checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'name'">
<a-input v-model:value="record.name" :placeholder="t('请填写字段名')" :disabled="disabled" />
</template>
<template v-else-if="column.key === 'component'">
<a-select v-model:value="record.component" style="width: 100%" :placeholder="t('请选择填充组件')" @change="(value) => selectBindField(value, record)" allowClear :disabled="disabled">
<a-select-option :value="val.key" v-for="(val, idx) in selectedList" :key="idx">
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" :placeholder="t('请填写表头名称')" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input v-model:value="record.width" :disabled="!record.show" :placeholder="t('请填写列表宽度')" />
</template>
<template v-else-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteParamas(index)" />
</template>
</template>
</a-table>
<a-button type="dashed" block @click="addOutPutParamas" :disabled="disabled">
<PlusOutlined />
{{ t('新增') }}
</a-button>
</a-modal>
<InterfaceInfoModal @register="registerModal" />
</template>
<script lang="ts" setup>
import { watch, ref, reactive } from 'vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { PlusOutlined, DeleteTwoTone } from '@ant-design/icons-vue';
import InterfaceInfoModal from './InterfaceInfoModal.vue';
import { getInterfaceInfo } from '/@/api/system/interface/index';
import { useModal } from '/@/components/Modal';
import { message } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { watch, ref, reactive } from 'vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { PlusOutlined, DeleteTwoTone } from '@ant-design/icons-vue';
import InterfaceInfoModal from './InterfaceInfoModal.vue';
import { getInterfaceInfo } from '/@/api/system/interface/index';
import { useModal } from '/@/components/Modal';
import { message } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
apiAssoDia: { type: Boolean },
apiConfig: {
type: Object as PropType<any>,
},
selectedList: { type: Array as any },
type: { type: String },
disabled: { type: Boolean, default: () => false },
});
const emit = defineEmits(['update:apiAssoDia', 'update:apiConfig']);
const visible = ref<boolean>(props.apiAssoDia);
const { t } = useI18n();
const props = defineProps({
apiAssoDia: { type: Boolean },
apiConfig: {
type: Object as PropType<any>
},
selectedList: { type: Array as any },
type: { type: String },
disabled: { type: Boolean, default: () => false }
});
const emit = defineEmits(['update:apiAssoDia', 'update:apiConfig']);
const visible = ref<boolean>(props.apiAssoDia);
const [registerModal, { openModal }] = useModal();
const [registerModal, { openModal }] = useModal();
const apiConfigInfo = ref();
const apiColumns = ref<ColumnProps[]>([]);
const title = ref<string>('');
const checkedAll = ref<boolean>(false);
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50,
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200,
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 150,
},
{
title: t('是否在弹层中显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 100,
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50,
},
]);
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50,
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200,
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center',
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200,
},
{
title: t('是否在弹层列表显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 100,
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50,
},
]);
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50,
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200,
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center',
},
{
title: t('填充组件绑定字段'),
dataIndex: 'bindField',
key: 'bindField',
align: 'center',
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50,
},
]);
const preloadColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50,
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50,
},
]);
watch(
() => props.type,
(val) => {
switch (val) {
case 'button':
case 'associate-popup':
apiColumns.value = associatePopupColumns;
title.value = t('联想数据配置-API');
break;
case 'multiple-popup':
apiColumns.value = multiplePopupColumns;
title.value = t('多选数据配置-API');
break;
case 'associate-select':
apiColumns.value = associateSelectColumns;
title.value = t('联想数据配置-API');
break;
case 'preload-title':
apiColumns.value = preloadColumns;
title.value = t('表头配置-API');
break;
}
},
{ immediate: true },
);
watch(
() => props.apiConfig,
(val) => {
apiConfigInfo.value = cloneDeep(val);
},
{
deep: true,
immediate: true,
},
);
watch(
() => apiConfigInfo.value,
(val) => {
checkedAll.value = val?.outputParams?.every((item: any) => {
return item.show;
});
},
{
deep: true,
immediate: true,
},
);
function selectBindField(value, record) {
if (!value || !isNaN(value)) {
message.error(t('请先选择该组件的绑定表及绑定字段'));
record.bindField = null;
record.bindTable = undefined;
} else {
let obj = props.selectedList.find((o) => o.key == value);
if (obj) {
record.bindField = obj.bindField;
if (obj.isSingleFormChild || obj.isSubFormChild) {
record.bindTable = obj.bindTable;
} else {
record.bindTable = undefined;
const apiConfigInfo = ref();
const apiColumns = ref<ColumnProps[]>([]);
const title = ref<string>('');
const checkedAll = ref<boolean>(false);
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 150
},
{
title: t('是否在弹层中显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 100
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50
}
}
}
}
function handleClose() {
emit('update:apiAssoDia', false);
}
function handleChecked(e) {
apiConfigInfo.value.outputParams.map((item: any) => (item.show = e.target.checked));
}
]);
function handleSubmit() {
const bindFieldArr = <any>[];
apiConfigInfo.value.outputParams?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
});
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error(t('组件存在重复绑定,请更改'));
return;
}
emit('update:apiConfig', apiConfigInfo.value);
emit('update:apiAssoDia', false);
}
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center'
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200
},
{
title: t('是否在弹层列表显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 100
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50
}
]);
const viewInterfaceInfo = async () => {
//在弹窗内调接口会格式不对 所以在此处调接口传进去
const info = await getInterfaceInfo({ id: props.apiConfig.apiId });
openModal(true, {
script: info?.script || '',
});
};
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
width: 200
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center'
},
{
title: t('填充组件绑定字段'),
dataIndex: 'bindField',
key: 'bindField',
align: 'center'
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50
}
]);
const preloadColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 50
},
{
title: t('接口返回参数字段名'),
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center'
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50
}
]);
watch(
() => props.type,
(val) => {
switch (val) {
case 'button':
case 'associate-popup':
apiColumns.value = associatePopupColumns;
title.value = t('联想数据配置-API');
break;
case 'multiple-popup':
apiColumns.value = multiplePopupColumns;
title.value = t('多选数据配置-API');
break;
case 'associate-select':
apiColumns.value = associateSelectColumns;
title.value = t('联想数据配置-API');
break;
case 'preload-title':
apiColumns.value = preloadColumns;
title.value = t('表头配置-API');
break;
}
},
{ immediate: true }
);
const addOutPutParamas = () => {
if (!apiConfigInfo.value.outputParams) {
apiConfigInfo.value.outputParams = [];
watch(
() => props.apiConfig,
(val) => {
apiConfigInfo.value = cloneDeep(val);
},
{
deep: true,
immediate: true
}
);
watch(
() => apiConfigInfo.value,
(val) => {
checkedAll.value = val?.outputParams?.every((item: any) => {
return item.show;
});
},
{
deep: true,
immediate: true
}
);
function selectBindField(value, record) {
if (!value || !isNaN(value)) {
message.error(t('请先选择该组件的绑定表及绑定字段'));
record.bindField = null;
record.bindTable = undefined;
} else {
let obj = props.selectedList.find((o) => o.key == value);
if (obj) {
record.bindField = obj.bindField;
if (obj.isSingleFormChild || obj.isSubFormChild) {
record.bindTable = obj.bindTable;
} else {
record.bindTable = undefined;
}
}
}
}
let configObj = {};
switch (props.type) {
case 'button':
case 'associate-popup':
configObj = {
name: '',
tableTitle: '',
bindField: '',
show: true,
width: 150,
};
break;
case 'multiple-popup':
configObj = {
name: '',
tableTitle: '',
show: true,
width: 150,
};
break;
case 'associate-select':
configObj = {
name: '',
bindField: '',
};
break;
function handleClose() {
emit('update:apiAssoDia', false);
}
apiConfigInfo.value.outputParams.push(configObj);
};
const deleteParamas = (index) => {
if (props.disabled) return;
apiConfigInfo.value.outputParams.splice(index, 1);
};
function handleChecked(e) {
apiConfigInfo.value.outputParams.map((item: any) => (item.show = e.target.checked));
}
function handleSubmit() {
const bindFieldArr = <any>[];
apiConfigInfo.value.outputParams?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
});
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error(t('组件存在重复绑定,请更改'));
return;
}
emit('update:apiConfig', apiConfigInfo.value);
emit('update:apiAssoDia', false);
}
const viewInterfaceInfo = async () => {
//在弹窗内调接口会格式不对 所以在此处调接口传进去
const info = await getInterfaceInfo({ id: props.apiConfig.apiId });
openModal(true, {
script: info?.script || ''
});
};
const addOutPutParamas = () => {
if (!apiConfigInfo.value.outputParams) {
apiConfigInfo.value.outputParams = [];
}
let configObj = {};
switch (props.type) {
case 'button':
case 'associate-popup':
configObj = {
name: '',
tableTitle: '',
bindField: '',
show: true,
width: 150
};
break;
case 'multiple-popup':
configObj = {
name: '',
tableTitle: '',
show: true,
width: 150
};
break;
case 'associate-select':
configObj = {
name: '',
bindField: ''
};
break;
}
apiConfigInfo.value.outputParams.push(configObj);
};
const deleteParamas = (index) => {
if (props.disabled) return;
apiConfigInfo.value.outputParams.splice(index, 1);
};
</script>
<style lang="less" scoped>
.list-title {
margin: 10px 0 5px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.list-title {
margin: 10px 0 5px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
</style>

View File

@ -1,461 +1,417 @@
<template>
<a-modal
:width="1000"
:height="550"
v-model:visible="dialog"
:title="t('计算式配置')"
:maskClosable="false"
@ok="submitDialog"
@cancel="closeDialog"
style="overflow: auto"
>
<a-layout class="config-container" hasSider>
<a-layout-sider width="300">
<div class="component-list">
<a-collapse v-model:activeKey="activeKey" expand-icon-position="right">
<a-collapse-panel
v-for="(item, index) in selectedComponents"
:key="index"
:header="item.label"
>
<draggable
v-model="item.children"
item-key="item.key"
:group="{ name: 'computation', pull: 'clone', put: false }"
:sort="false"
>
<template #item="{ element }">
<a-tag
:color="tagColor(item, element)"
:class="{ 'disabled-tag': disabledCom(element.key) }"
>
{{ element.label }}
</a-tag>
</template>
</draggable>
</a-collapse-panel>
</a-collapse>
</div>
</a-layout-sider>
<a-layout-content class="bgcWhite">
<div class="title">{{ t('计算公式') }}</div>
<div class="btn-list">
<div>
<draggable
v-model="operatingBtnList"
:group="{ name: 'computation', pull: 'clone', put: false }"
item-key="item.label"
:sort="false"
>
<template #item="{ element }">
<a-tag>
{{ element.label }}
</a-tag>
</template>
</draggable>
</div>
<div class="input-number">
<span>{{ t('数字:') }}</span>
<a-input-number v-model:value="number" />
<a-button type="primary" @click="addNumber">{{ t('确定') }}</a-button>
</div>
</div>
<div class="computational-box">
<div v-if="!computationalList.length" class="computational-text">
<p style="font-size: 24px; color: #606266">{{ t('拖拽左侧到此处进行添加') }}</p>
<p>{{ t('变量之间可通过计算公式进行运算(例:销售单价 * 销售数量)') }}</p>
</div>
<draggable
v-model="computationalList"
group="computation"
item-key="key"
@add="addComputationalList"
class="draggable"
>
<template #item="{ element, index }">
<div @mouseenter="mouseenter(index)" @mouseleave="isMouseenter = false">
<a-select
v-model:value="element.computationalMethod"
style="width: 85px"
v-if="element.computationalMethod"
>
<a-select-option value="sum">{{ t('总和') }}</a-select-option>
<a-select-option value="mean">{{ t('平均数') }}</a-select-option>
<a-select-option value="max">{{ t('最大值') }}</a-select-option>
<a-select-option value="min">{{ t('最小值') }}</a-select-option>
</a-select>
<a-tag color="blue" closable style="padding: 5px" @close="delTag(index)">
<span>{{ element.label }}</span>
</a-tag>
</div>
</template>
</draggable>
</div>
</a-layout-content>
</a-layout>
</a-modal>
<a-modal :width="1000" :height="550" v-model:visible="dialog" :title="t('计算式配置')" :maskClosable="false" @ok="submitDialog" @cancel="closeDialog" style="overflow: auto">
<a-layout class="config-container" hasSider>
<a-layout-sider width="300">
<div class="component-list">
<a-collapse v-model:activeKey="activeKey" expand-icon-position="right">
<a-collapse-panel v-for="(item, index) in selectedComponents" :key="index" :header="item.label">
<draggable v-model="item.children" item-key="item.key" :group="{ name: 'computation', pull: 'clone', put: false }" :sort="false">
<template #item="{ element }">
<a-tag :color="tagColor(item, element)" :class="{ 'disabled-tag': disabledCom(element.key) }">
{{ element.label }}
</a-tag>
</template>
</draggable>
</a-collapse-panel>
</a-collapse>
</div>
</a-layout-sider>
<a-layout-content class="bgcWhite">
<div class="title">{{ t('计算公式') }}</div>
<div class="btn-list">
<div>
<draggable v-model="operatingBtnList" :group="{ name: 'computation', pull: 'clone', put: false }" item-key="item.label" :sort="false">
<template #item="{ element }">
<a-tag>
{{ element.label }}
</a-tag>
</template>
</draggable>
</div>
<div class="input-number">
<span>{{ t('数字:') }}</span>
<a-input-number v-model:value="number" />
<a-button type="primary" @click="addNumber">{{ t('确定') }}</a-button>
</div>
</div>
<div class="computational-box">
<div v-if="!computationalList.length" class="computational-text">
<p style="font-size: 24px; color: #606266">{{ t('拖拽左侧到此处进行添加') }}</p>
<p>{{ t('变量之间可通过计算公式进行运算(例:销售单价 * 销售数量)') }}</p>
</div>
<draggable v-model="computationalList" group="computation" item-key="key" @add="addComputationalList" class="draggable">
<template #item="{ element, index }">
<div @mouseenter="mouseenter(index)" @mouseleave="isMouseenter = false">
<a-select v-model:value="element.computationalMethod" style="width: 85px" v-if="element.computationalMethod">
<a-select-option value="sum">{{ t('总和') }}</a-select-option>
<a-select-option value="mean">{{ t('平均数') }}</a-select-option>
<a-select-option value="max">{{ t('最大值') }}</a-select-option>
<a-select-option value="min">{{ t('最小值') }}</a-select-option>
</a-select>
<a-tag color="blue" closable style="padding: 5px" @close="delTag(index)">
<span>{{ element.label }}</span>
</a-tag>
</div>
</template>
</draggable>
</div>
</a-layout-content>
</a-layout>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, inject, reactive, onMounted } from 'vue';
import { ComputationalConfig } from '/@/components/Designer/src/types';
import draggable from 'vuedraggable';
import { cloneDeep, random } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
computationalDialog: { type: Boolean },
computationalConfig: {
type: Array as PropType<ComputationalConfig[]>,
default: [],
},
});
const { widgetForm, widgetFormSelect } = inject('state') as any; //整个表单json配置
const operatingBtnList = reactive([
{
key: 1,
operation: '',
label: '+',
type: 'operation',
},
{
key: 2,
operation: '',
label: '-',
type: 'operation',
},
{
key: 3,
operation: '×',
label: '*',
type: 'operation',
},
{
key: 4,
operation: '÷',
label: '/',
type: 'operation',
},
{
key: 5,
operation: '(',
label: '(',
type: 'operation',
},
{
key: 6,
operation: ')',
label: ')',
type: 'operation',
},
{
key: 7,
operation: '.',
label: '.',
type: 'operation',
},
]);
const number = ref<string>('');
const isMouseenter = ref<Boolean>(false);
const computationalList = ref<Array<any>>([]);
const mouseenterIndex = ref<number>();
const activeKey = ref([0]);
const selectedComponents = ref<Array<any>>([]);
const dialog = ref(props.computationalDialog);
onMounted(() => {
if (widgetForm.list.length) {
getTableComponents(widgetForm.list, t('主表组件列表'));
}
if (props.computationalConfig.length) {
computationalList.value = cloneDeep(props.computationalConfig);
}
});
const emit = defineEmits([
'update:computationalDialog',
'setComputationalConfigValue',
'update:computationalConfig',
]);
const getTableComponents = (list, tableName, key?) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getTableComponents(child.list, tableName);
import { ref, inject, reactive, onMounted } from 'vue';
import { ComputationalConfig } from '/@/components/Designer/src/types';
import draggable from 'vuedraggable';
import { cloneDeep, random } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
computationalDialog: { type: Boolean },
computationalConfig: {
type: Array as PropType<ComputationalConfig[]>,
default: []
}
} else if (item.type === 'table-layout') {
for (const child of item.layout!) {
for (const list of child.list) {
getTableComponents(list.children, key);
}
});
const { widgetForm, widgetFormSelect } = inject('state') as any; //整个表单json配置
const operatingBtnList = reactive([
{
key: 1,
operation: '',
label: '+',
type: 'operation'
},
{
key: 2,
operation: '',
label: '-',
type: 'operation'
},
{
key: 3,
operation: '×',
label: '*',
type: 'operation'
},
{
key: 4,
operation: '÷',
label: '/',
type: 'operation'
},
{
key: 5,
operation: '(',
label: '(',
type: 'operation'
},
{
key: 6,
operation: ')',
label: ')',
type: 'operation'
},
{
key: 7,
operation: '.',
label: '.',
type: 'operation'
}
} else if (item.type === 'one-for-one') {
getTableComponents(item.children, `${item.label}-${item.key}`, item.key);
} else if (item.type === 'form') {
const hasMethods = item.children.some((child) => {
return child.key === widgetFormSelect.key;
});
const itemChildren = item.children.filter((child) => {
child.computationalMethod = hasMethods ? undefined : 'sum';
return child.type === 'computational' || child.type === 'money-chinese';
});
]);
const number = ref<string>('');
const isMouseenter = ref<Boolean>(false);
const computationalList = ref<Array<any>>([]);
const mouseenterIndex = ref<number>();
const activeKey = ref([0]);
const selectedComponents = ref<Array<any>>([]);
const dialog = ref(props.computationalDialog);
onMounted(() => {
if (widgetForm.list.length) {
getTableComponents(widgetForm.list, t('主表组件列表'));
}
if (props.computationalConfig.length) {
computationalList.value = cloneDeep(props.computationalConfig);
}
});
selectedComponents.value.push({
label: `${item.label}-${item.key}`,
key: item.key,
children: itemChildren,
const emit = defineEmits(['update:computationalDialog', 'setComputationalConfigValue', 'update:computationalConfig']);
const getTableComponents = (list, tableName, key?) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getTableComponents(child.list, tableName);
}
} else if (item.type === 'table-layout') {
for (const child of item.layout!) {
for (const list of child.list) {
getTableComponents(list.children, key);
}
}
} else if (item.type === 'one-for-one') {
getTableComponents(item.children, `${item.label}-${item.key}`, item.key);
} else if (item.type === 'form') {
const hasMethods = item.children.some((child) => {
return child.key === widgetFormSelect.key;
});
const itemChildren = item.children.filter((child) => {
child.computationalMethod = hasMethods ? undefined : 'sum';
return child.type === 'computational' || child.type === 'money-chinese';
});
selectedComponents.value.push({
label: `${item.label}-${item.key}`,
key: item.key,
children: itemChildren
});
} else if (item.type === 'computational' || item.type === 'money-chinese') {
const index = selectedComponents.value.findIndex((x) => x.label === tableName);
if (index !== -1) {
selectedComponents.value[index].children.push(item);
} else {
const tableChildren = [item];
if (tableName === t('主表组件列表')) {
selectedComponents.value.unshift({
label: tableName,
key: '1',
children: tableChildren
});
} else {
selectedComponents.value.push({
label: tableName,
key: key,
children: tableChildren
});
}
}
}
});
} else if (item.type === 'computational' || item.type === 'money-chinese') {
const index = selectedComponents.value.findIndex((x) => x.label === tableName);
if (index !== -1) {
selectedComponents.value[index].children.push(item);
} else {
const tableChildren = [item];
if (tableName === t('主表组件列表')) {
selectedComponents.value.unshift({
label: tableName,
key: '1',
children: tableChildren,
};
const tagColor = (tableItem, componentItem) => {
if (widgetFormSelect.key === componentItem.key) return '#f4f4f5';
const currentItem = tableItem.children.some((item: any) => {
return item.key === widgetFormSelect.key;
});
return currentItem ? 'green' : 'blue';
};
const disabledCom = (currentKey) => {
if (widgetFormSelect.key === currentKey) return true;
const isDisabled = widgetFormSelect.options.beAdoptedComponent.some((key: string) => {
return key === currentKey;
});
return isDisabled;
};
const setBeAdoptedComponent = (list, key) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
setBeAdoptedComponent(child.list, key);
}
} else if (item.type === 'form') {
setBeAdoptedComponent(item.children, key);
} else if (item.key === key) {
if (!item.options.beAdoptedComponent.includes(widgetFormSelect.key)) {
if (!item.options.beAdoptedComponent) item.options.beAdoptedComponent = [];
//被引用的组件不能引用当前组件
item.options.beAdoptedComponent.push(widgetFormSelect.key);
if (widgetFormSelect.options?.beAdoptedComponent.length) {
item.options.beAdoptedComponent.push(...widgetFormSelect.options?.beAdoptedComponent);
}
}
}
});
};
//编辑时 去掉已经删除的组件引用
const deleteBeAdoptedComponent = (list, keys) => {
keys.forEach((key) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
deleteBeAdoptedComponent(child.list, keys);
}
} else if (item.type === 'form') {
deleteBeAdoptedComponent(item.children, keys);
} else if (item.key === key) {
//被引用的组件不能引用当前组件
item.options.beAdoptedComponent = item.options.beAdoptedComponent.filter((x) => x !== widgetFormSelect.key);
}
});
} else {
selectedComponents.value.push({
label: tableName,
key: key,
children: tableChildren,
});
};
const submitDialog = () => {
let configValue = '';
const computationalConfig: ComputationalConfig[] = [];
computationalList.value.map((item: any) => {
const text = item.computationalMethod ? `${item.computationalMethod}(${item.label})` : item.label;
let isMainForm;
if (item.hasOwnProperty('isMainForm')) {
isMainForm = item.isMainForm;
} else {
if (item.type === 'computational' || item.type === 'money-chinese') {
isMainForm = !(item?.isSubFormChild || item?.isSingleFormChild);
} else {
isMainForm = undefined;
}
}
configValue = configValue.concat(text);
computationalConfig.push({
$index: item.$index,
label: item.label,
type: item.type,
key: item.key,
bindTable: item.bindTable,
bindField: item.bindField,
computationalMethod: item?.computationalMethod,
isMainForm: isMainForm
});
}
setBeAdoptedComponent(widgetForm.list, item.key);
});
const currentConfigKeys = computationalConfig.map((x) => x.bindTable && x.key);
const oldConfigKeys = props.computationalConfig.map((x) => x.bindTable && x.key);
// 被删除的引用组件
const needDeleteKeys = oldConfigKeys.filter((old) => {
return old && currentConfigKeys.indexOf(old) === -1;
});
deleteBeAdoptedComponent(widgetForm.list, needDeleteKeys);
emit('setComputationalConfigValue', configValue);
emit('update:computationalConfig', computationalConfig);
emit('update:computationalDialog', false);
};
const closeDialog = () => {
emit('update:computationalDialog', false);
};
const addComputationalList = (e) => {
computationalList.value[e.newIndex] = cloneDeep(computationalList.value[e.newIndex]);
computationalList.value[e.newIndex].$index = random(100, 999);
};
const addNumber = () => {
if (number.value) {
computationalList.value.push({ label: number.value, type: 'operation' });
}
}
});
};
const tagColor = (tableItem, componentItem) => {
if (widgetFormSelect.key === componentItem.key) return '#f4f4f5';
const currentItem = tableItem.children.some((item: any) => {
return item.key === widgetFormSelect.key;
});
return currentItem ? 'green' : 'blue';
};
const disabledCom = (currentKey) => {
if (widgetFormSelect.key === currentKey) return true;
const isDisabled = widgetFormSelect.options.beAdoptedComponent.some((key: string) => {
return key === currentKey;
});
return isDisabled;
};
const setBeAdoptedComponent = (list, key) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
setBeAdoptedComponent(child.list, key);
}
} else if (item.type === 'form') {
setBeAdoptedComponent(item.children, key);
} else if (item.key === key) {
if (!item.options.beAdoptedComponent.includes(widgetFormSelect.key)) {
if (!item.options.beAdoptedComponent) item.options.beAdoptedComponent = [];
//被引用的组件不能引用当前组件
item.options.beAdoptedComponent.push(widgetFormSelect.key);
if (widgetFormSelect.options?.beAdoptedComponent.length) {
item.options.beAdoptedComponent.push(...widgetFormSelect.options?.beAdoptedComponent);
}
}
}
});
};
//编辑时 去掉已经删除的组件引用
const deleteBeAdoptedComponent = (list, keys) => {
keys.forEach((key) => {
list.forEach((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
deleteBeAdoptedComponent(child.list, keys);
}
} else if (item.type === 'form') {
deleteBeAdoptedComponent(item.children, keys);
} else if (item.key === key) {
//被引用的组件不能引用当前组件
item.options.beAdoptedComponent = item.options.beAdoptedComponent.filter(
(x) => x !== widgetFormSelect.key,
);
}
});
});
};
const submitDialog = () => {
let configValue = '';
const computationalConfig: ComputationalConfig[] = [];
computationalList.value.map((item: any) => {
const text = item.computationalMethod
? `${item.computationalMethod}(${item.label})`
: item.label;
let isMainForm;
if (item.hasOwnProperty('isMainForm')) {
isMainForm = item.isMainForm;
} else {
if (item.type === 'computational' || item.type === 'money-chinese') {
isMainForm = !(item?.isSubFormChild || item?.isSingleFormChild);
} else {
isMainForm = undefined;
}
}
configValue = configValue.concat(text);
computationalConfig.push({
$index: item.$index,
label: item.label,
type: item.type,
key: item.key,
bindTable: item.bindTable,
bindField: item.bindField,
computationalMethod: item?.computationalMethod,
isMainForm: isMainForm,
});
setBeAdoptedComponent(widgetForm.list, item.key);
});
const currentConfigKeys = computationalConfig.map((x) => x.bindTable && x.key);
const oldConfigKeys = props.computationalConfig.map((x) => x.bindTable && x.key);
// 被删除的引用组件
const needDeleteKeys = oldConfigKeys.filter((old) => {
return old && currentConfigKeys.indexOf(old) === -1;
});
deleteBeAdoptedComponent(widgetForm.list, needDeleteKeys);
emit('setComputationalConfigValue', configValue);
emit('update:computationalConfig', computationalConfig);
emit('update:computationalDialog', false);
};
const closeDialog = () => {
emit('update:computationalDialog', false);
};
const addComputationalList = (e) => {
computationalList.value[e.newIndex] = cloneDeep(computationalList.value[e.newIndex]);
computationalList.value[e.newIndex].$index = random(100, 999);
};
const addNumber = () => {
if (number.value) {
computationalList.value.push({ label: number.value, type: 'operation' });
}
};
const mouseenter = (index: number) => {
mouseenterIndex.value = index;
isMouseenter.value = true;
};
const delTag = (index) => {
computationalList.value.splice(index, 1);
};
};
const mouseenter = (index: number) => {
mouseenterIndex.value = index;
isMouseenter.value = true;
};
const delTag = (index) => {
computationalList.value.splice(index, 1);
};
</script>
<style lang="less" scoped>
.bgcWhite {
background: #fff;
.bgcWhite {
background: #fff;
.btn-list {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #dcdfe6;
.btn-list {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #dcdfe6;
:deep(.ant-tag) {
font-size: 18px !important;
padding: 5px 15px !important;
margin-bottom: 0;
cursor: move;
}
:deep(.ant-tag) {
font-size: 18px !important;
padding: 5px 15px !important;
margin-bottom: 0;
cursor: move;
}
.input-number {
display: flex;
align-items: center;
.input-number {
display: flex;
align-items: center;
:deep(.ant-input-number) {
width: 100px;
margin-right: 10px;
:deep(.ant-input-number) {
width: 100px;
margin-right: 10px;
}
}
}
.computational-box {
height: 100%;
padding: 5px 10px;
min-height: 300px;
.draggable {
font-size: 13px;
height: 100%;
> div {
display: inline-block;
margin-top: 5px;
}
> span:nth-of-type(1) {
height: 30px;
display: inline-block;
align-items: center;
}
p:nth-child(1) {
color: #606266;
}
p:nth-child(2) {
color: #9e9d9d;
}
}
.computational-text {
position: absolute;
left: 45%;
top: 300px;
text-align: center;
}
}
}
}
.computational-box {
height: 100%;
padding: 5px 10px;
min-height: 300px;
.config-container {
margin: 10px;
border: 1px solid #dcdfe6;
.draggable {
.component-list {
display: flex;
flex-wrap: wrap;
:deep(.ant-collapse) {
width: 100%;
border: none;
}
}
}
:deep(.ant-tag) {
padding: 8px;
font-size: 13px;
height: 100%;
> div {
display: inline-block;
margin-top: 5px;
}
> span:nth-of-type(1) {
height: 30px;
display: inline-block;
align-items: center;
}
p:nth-child(1) {
color: #606266;
}
p:nth-child(2) {
color: #9e9d9d;
}
}
.computational-text {
position: absolute;
left: 45%;
top: 300px;
text-align: center;
}
cursor: move;
margin-bottom: 10px;
}
}
.config-container {
margin: 10px;
border: 1px solid #dcdfe6;
.title {
padding: 15px 10px;
border-bottom: 1px solid #dcdfe6;
}
.component-list {
display: flex;
flex-wrap: wrap;
:deep(.ant-collapse) {
width: 100%;
.disabled-tag {
cursor: not-allowed;
pointer-events: none;
border: none;
}
color: #c3c5ca;
}
}
:deep(.ant-tag) {
padding: 8px;
font-size: 13px;
cursor: move;
margin-bottom: 10px;
}
:deep(.ant-layout-sider) {
background: #fff;
border-right: 1px solid #dcdfe6;
}
.title {
padding: 15px 10px;
border-bottom: 1px solid #dcdfe6;
}
.disabled-tag {
cursor: not-allowed;
pointer-events: none;
border: none;
color: #c3c5ca;
}
:deep(.ant-layout-sider) {
background: #fff;
border-right: 1px solid #dcdfe6;
}
:deep(.ant-collapse-header) {
word-break: break-all;
}
:deep(.ant-collapse-header) {
word-break: break-all;
}
</style>

View File

@ -1,260 +1,239 @@
<template>
<a-modal
:width="1000"
v-model:visible="visible"
:title="state.title"
:maskClosable="false"
@ok="handleSubmit"
@cancel="handleClose"
>
<div class="list-title">字段列表</div>
<a-table
:dataSource="configDataSource"
:columns="state.configColumns"
:pagination="false"
:scroll="{ y: '300px' }"
>
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
是否在弹层列表显示
<a-checkbox v-model:checked="state.checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'component'">
<a-select
v-model:value="record.bindField"
style="width: 100%"
placeholder="请选择填充组件"
allowClear
>
<a-select-option
:value="val.bindField"
v-for="(val, index) in selectedList"
:key="index"
>
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input v-model:value="record.width" :disabled="!record.show" />
</template>
</template>
</a-table>
</a-modal>
<a-modal :width="1000" v-model:visible="visible" :title="state.title" :maskClosable="false" @ok="handleSubmit" @cancel="handleClose">
<div class="list-title">字段列表</div>
<a-table :dataSource="configDataSource" :columns="state.configColumns" :pagination="false" :scroll="{ y: '300px' }">
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
是否在弹层列表显示
<a-checkbox v-model:checked="state.checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'component'">
<a-select v-model:value="record.bindField" style="width: 100%" placeholder="请选择填充组件" allowClear>
<a-select-option :value="val.bindField" v-for="(val, index) in selectedList" :key="index">
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input v-model:value="record.width" :disabled="!record.show" />
</template>
</template>
</a-table>
</a-modal>
</template>
<script lang="ts" setup>
import { watch, reactive, ref } from 'vue';
import { DataSourceConfig } from '../../../types';
import { message } from 'ant-design-vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { cloneDeep } from 'lodash-es';
import { watch, reactive, ref } from 'vue';
import { DataSourceConfig } from '../../../types';
import { message } from 'ant-design-vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { cloneDeep } from 'lodash-es';
const props = defineProps({
dataSourceAssoDia: { type: Boolean },
type: { type: String },
selectedList: { type: Array as any },
dataSourceOptions: { type: Array as PropType<DataSourceConfig[]> },
});
const emit = defineEmits(['update:dataSourceAssoDia', 'update:dataSourceOptions']);
const state = reactive({
title: '',
checkedAll: false as boolean,
configColumns: [] as ColumnProps[],
});
const visible = ref<boolean>(props.dataSourceAssoDia);
const configDataSource = ref<DataSourceConfig[]>([]);
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '表头名称',
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200,
},
{
title: '是否在弹层列表显示',
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: '列表宽度',
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150,
},
]);
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '填充组件',
dataIndex: 'component',
key: 'component',
align: 'center',
width: 250,
},
{
title: '表头名称',
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200,
},
{
title: '是否在弹层列表显示',
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: '列表宽度',
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150,
},
]);
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '填充组件',
dataIndex: 'component',
key: 'component',
align: 'center',
},
{
title: '填充组件绑定字段',
dataIndex: 'bindField',
key: 'bindField',
align: 'center',
},
]);
watch(
() => props.type,
(val) => {
switch (val) {
case 'associate-popup':
state.configColumns = associatePopupColumns;
state.title = '联想数据配置-数据源';
break;
case 'multiple-popup':
state.configColumns = multiplePopupColumns;
state.title = '多选数据配置-数据源';
break;
case 'associate-select':
state.configColumns = associateSelectColumns;
state.title = '联想数据配置-数据源';
break;
}
},
{ immediate: true },
);
watch(
() => props.dataSourceOptions,
(val) => {
if (!val?.length) return;
configDataSource.value = cloneDeep(val)!;
},
{
deep: true,
immediate: true,
},
);
watch(
configDataSource.value,
(val) => {
state.checkedAll = val!.every((item: any) => {
return item.show;
});
},
{
deep: true,
immediate: true,
},
);
function handleClose() {
emit('update:dataSourceAssoDia', false);
}
function handleChecked(e: any) {
configDataSource.value.map((item: any) => (item.show = e.target.checked));
}
function handleSubmit() {
const bindFieldArr = [] as any;
configDataSource.value?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
const props = defineProps({
dataSourceAssoDia: { type: Boolean },
type: { type: String },
selectedList: { type: Array as any },
dataSourceOptions: { type: Array as PropType<DataSourceConfig[]> }
});
const emit = defineEmits(['update:dataSourceAssoDia', 'update:dataSourceOptions']);
const state = reactive({
title: '',
checkedAll: false as boolean,
configColumns: [] as ColumnProps[]
});
const visible = ref<boolean>(props.dataSourceAssoDia);
const configDataSource = ref<DataSourceConfig[]>([]);
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: '表头名称',
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200
},
{
title: '是否在弹层列表显示',
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: '列表宽度',
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150
}
]);
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error('组件存在重复绑定,请更改');
return;
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: '填充组件',
dataIndex: 'component',
key: 'component',
align: 'center',
width: 250
},
{
title: '表头名称',
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200
},
{
title: '是否在弹层列表显示',
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: '列表宽度',
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150
}
]);
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: '字段名',
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: '填充组件',
dataIndex: 'component',
key: 'component',
align: 'center'
},
{
title: '填充组件绑定字段',
dataIndex: 'bindField',
key: 'bindField',
align: 'center'
}
]);
watch(
() => props.type,
(val) => {
switch (val) {
case 'associate-popup':
state.configColumns = associatePopupColumns;
state.title = '联想数据配置-数据源';
break;
case 'multiple-popup':
state.configColumns = multiplePopupColumns;
state.title = '多选数据配置-数据源';
break;
case 'associate-select':
state.configColumns = associateSelectColumns;
state.title = '联想数据配置-数据源';
break;
}
},
{ immediate: true }
);
watch(
() => props.dataSourceOptions,
(val) => {
if (!val?.length) return;
configDataSource.value = cloneDeep(val)!;
},
{
deep: true,
immediate: true
}
);
watch(
configDataSource.value,
(val) => {
state.checkedAll = val!.every((item: any) => {
return item.show;
});
},
{
deep: true,
immediate: true
}
);
function handleClose() {
emit('update:dataSourceAssoDia', false);
}
function handleChecked(e: any) {
configDataSource.value.map((item: any) => (item.show = e.target.checked));
}
emit('update:dataSourceAssoDia', false);
emit('update:dataSourceOptions', configDataSource.value);
}
function handleSubmit() {
const bindFieldArr = [] as any;
configDataSource.value?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
});
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error('组件存在重复绑定,请更改');
return;
}
emit('update:dataSourceAssoDia', false);
emit('update:dataSourceOptions', configDataSource.value);
}
</script>
<style lang="less" scoped>
.list-title {
margin: 10px 0 5px 15px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.list-title {
margin: 10px 0 5px 15px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
</style>

View File

@ -1,328 +1,255 @@
<template>
<a-form-item :label="t('数据来源')">
<a-radio-group
button-style="solid"
v-model:value="info.options.datasourceType"
size="small"
:disabled="disabled"
@change="handleDatasourceChange"
>
<a-radio-button
value="staticData"
v-if="
info.type === 'radio' ||
info.type === 'checkbox' ||
info.type === 'select' ||
info.type === 'auto-complete'
"
>
{{ t('静态数据') }}
</a-radio-button>
<a-radio-button value="api">API</a-radio-button>
<!-- <a-radio-button value="datasource">数据源</a-radio-button> -->
<a-radio-button value="dic">{{ t('数据字典') }}</a-radio-button>
</a-radio-group>
</a-form-item>
<div v-if="info.options.datasourceType === 'staticData'">
<StaticData
:disabled="disabled"
:options="info.options"
@handleOptionsRemove="
(index) => {
emit('handleOptionsRemove', index);
}
"
@handleInsertOption="handleInsertOption"
@handleDefaultSelect="
(val) => {
info.options.defaultSelect = val;
}
"
:type="info.type === 'checkbox' ? 'checkbox' : 'radio'"
/>
</div>
<div v-if="info.options.datasourceType === 'datasource'">
<a-form-item :label="t('数据选项')">
<DatasourceSelect v-model:value="info.options.datasourceId" @change="handleFieldChange" />
<a-form-item :label="t('数据来源')">
<a-radio-group button-style="solid" v-model:value="info.options.datasourceType" size="small" :disabled="disabled" @change="handleDatasourceChange">
<a-radio-button value="staticData" v-if="info.type === 'radio' || info.type === 'checkbox' || info.type === 'select' || info.type === 'auto-complete'">
{{ t('静态数据') }}
</a-radio-button>
<a-radio-button value="api">API</a-radio-button>
<!-- <a-radio-button value="datasource">数据源</a-radio-button> -->
<a-radio-button value="dic">{{ t('数据字典') }}</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item :label="t('显示字段')">
<a-select v-model:value="info.options.labelField" :placeholder="t('请选择显示字段')">
<a-select-option v-for="(item, idx) in fieldOptions" :value="item" :key="idx" />
</a-select>
<div v-if="info.options.datasourceType === 'staticData'">
<StaticData
:disabled="disabled"
:options="info.options"
@handleOptionsRemove="
(index) => {
emit('handleOptionsRemove', index);
}
"
@handleInsertOption="handleInsertOption"
@handleDefaultSelect="
(val) => {
info.options.defaultSelect = val;
}
"
:type="info.type === 'checkbox' ? 'checkbox' : 'radio'"
/>
</div>
<div v-if="info.options.datasourceType === 'datasource'">
<a-form-item :label="t('数据选项')">
<DatasourceSelect v-model:value="info.options.datasourceId" @change="handleFieldChange" />
</a-form-item>
<a-form-item :label="t('显示字段')">
<a-select v-model:value="info.options.labelField" :placeholder="t('请选择显示字段')">
<a-select-option v-for="(item, idx) in fieldOptions" :value="item" :key="idx" />
</a-select>
</a-form-item>
<a-form-item :label="t('保存字段')">
<a-select v-model:value="info.options.valueField" :placeholder="t('请选择保存字段')">
<a-select-option v-for="(item, idx) in fieldOptions" :value="item" :key="idx" />
</a-select>
</a-form-item>
</div>
<div v-if="info.options.datasourceType === 'api'">
<a-form-item :label="t('接口配置')">
<a-input v-model:value="info.options.apiConfig.path" :placeholder="t('点击进行接口配置')" :disabled="disabled" @click="apiConfigDialog = true">
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-form-item>
</div>
<div v-if="info.options.datasourceType === 'dic'">
<a-form-item :label="t('数据选项')">
<DicTreeSelect v-model:value="info.options.itemId" @change="handleDicChange" :disabled="disabled" />
</a-form-item>
</div>
<a-form-item :label="formLabel" v-if="info.type === 'associate-popup' || info.type === 'multiple-popup' || info.type === 'associate-select' || (info.type === 'button' && info.options.isSpecial && info.options.buttonType == 1)">
<a-input :value="configText" :placeholder="t('点击进行配置')" @click="showConfigDialog">
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-form-item>
<a-form-item :label="t('保存字段')">
<a-select v-model:value="info.options.valueField" :placeholder="t('请选择保存字段')">
<a-select-option v-for="(item, idx) in fieldOptions" :value="item" :key="idx" />
</a-select>
</a-form-item>
</div>
<div v-if="info.options.datasourceType === 'api'">
<a-form-item :label="t('接口配置')">
<a-input
v-model:value="info.options.apiConfig.path"
:placeholder="t('点击进行接口配置')"
:disabled="disabled"
@click="apiConfigDialog = true"
>
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-form-item>
</div>
<div v-if="info.options.datasourceType === 'dic'">
<a-form-item :label="t('数据选项')">
<DicTreeSelect
v-model:value="info.options.itemId"
@change="handleDicChange"
:disabled="disabled"
/>
</a-form-item>
</div>
<a-form-item
:label="formLabel"
v-if="
info.type === 'associate-popup' ||
info.type === 'multiple-popup' ||
info.type === 'associate-select' ||
(info.type === 'button' && info.options.isSpecial && info.options.buttonType == 1)
"
>
<a-input :value="configText" :placeholder="t('点击进行配置')" @click="showConfigDialog">
<template #suffix>
<Icon icon="ant-design:ellipsis-outlined" />
</template>
</a-input>
</a-form-item>
<DataSourceAssoConfig
v-if="dataSourceAssoDia"
v-model:dataSourceAssoDia="dataSourceAssoDia"
v-model:dataSourceOptions="info.options.dataSourceOptions"
:selectedList="list"
:type="info.type"
/>
<ApiAssoConfig
v-if="apiAssoDia"
:disabled="disabled"
v-model:apiAssoDia="apiAssoDia"
v-model:apiConfig="info.options.apiConfig"
:selectedList="list"
:type="info.type"
/>
<DicAssoConfig
v-if="dicAssoDia"
v-model:dicAssoDia="dicAssoDia"
v-model:dicOptions="info.options.dicOptions"
:selectedList="list"
:type="info.type"
:disabled="disabled"
/>
<ApiConfig
v-if="apiConfigDialog"
:isSubForm="info.isSubFormChild"
v-model:apiConfigDialog="apiConfigDialog"
v-model:apiConfig="info.options.apiConfig"
:formItem="info"
:title="t('API配置')"
/>
<DataSourceAssoConfig v-if="dataSourceAssoDia" v-model:dataSourceAssoDia="dataSourceAssoDia" v-model:dataSourceOptions="info.options.dataSourceOptions" :selectedList="list" :type="info.type" />
<ApiAssoConfig v-if="apiAssoDia" :disabled="disabled" v-model:apiAssoDia="apiAssoDia" v-model:apiConfig="info.options.apiConfig" :selectedList="list" :type="info.type" />
<DicAssoConfig v-if="dicAssoDia" v-model:dicAssoDia="dicAssoDia" v-model:dicOptions="info.options.dicOptions" :selectedList="list" :type="info.type" :disabled="disabled" />
<ApiConfig v-if="apiConfigDialog" :isSubForm="info.isSubFormChild" v-model:apiConfigDialog="apiConfigDialog" v-model:apiConfig="info.options.apiConfig" :formItem="info" :title="t('API配置')" />
</template>
<script lang="ts" setup>
import { watch, ref, inject, computed } from 'vue';
import DataSourceAssoConfig from './DataSourceAssoConfig.vue';
import ApiAssoConfig from './ApiAssoConfig.vue';
import DicAssoConfig from './DicAssoConfig.vue';
import StaticData from './StaticData.vue';
import { getDatasourceColumn } from '/@/api/system/datasource';
import { Icon } from '/@/components/Icon';
import { DatasourceSelect } from '/@/components/DataSourceSelect';
import { ApiConfig } from '/@/components/ApiConfig';
import { DicTreeSelect } from '/@/components/DicTreeSelect';
import { watch, ref, inject, computed } from 'vue';
import DataSourceAssoConfig from './DataSourceAssoConfig.vue';
import ApiAssoConfig from './ApiAssoConfig.vue';
import DicAssoConfig from './DicAssoConfig.vue';
import StaticData from './StaticData.vue';
import { getDatasourceColumn } from '/@/api/system/datasource';
import { Icon } from '/@/components/Icon';
import { DatasourceSelect } from '/@/components/DataSourceSelect';
import { ApiConfig } from '/@/components/ApiConfig';
import { DicTreeSelect } from '/@/components/DicTreeSelect';
import { message } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
data: { type: Object },
disabled: { type: Boolean, default: () => false },
});
const { widgetForm, widgetFormSelect } = inject('state') as any; //整个表单json配置
const emit = defineEmits(['update:data', 'handleOptionsRemove']);
const info = ref<any>(props.data);
const dataSourceAssoDia = ref<boolean>(false);
const apiAssoDia = ref<boolean>(false);
const dicAssoDia = ref<boolean>(false);
const apiConfigDialog = ref<boolean>(false);
const list = ref<any[]>([]);
const selectedList = ref<any[]>([]);
const selectedSingleList = ref<any[]>([]); //单表组件内部引用列表
const fieldOptions = ref<string[]>([]);
const notBindFieldConfig = ['select', 'radio', 'checkbox', 'associate-select'];
const noAssociateComponents = [
'multiple-popup',
'associate-popup',
'associate-select',
'form',
'tab',
'card',
'grid',
'one-for-one',
'button',
];
watch(
() => props.data,
(val) => {
info.value = val;
},
{
immediate: true,
deep: true,
},
);
watch(
info.value,
(val) => {
emit('update:data', val);
},
{
immediate: true,
deep: true,
},
);
const getSelectedList = (list, isOneForOne = false) => {
list?.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getSelectedList(child.list, isOneForOne);
}
} else if (item.type == 'table-layout') {
for (const child of item.layout!) {
for (const item of child.list) {
getSelectedList(item.children, isOneForOne);
}
}
} else if (item.type === 'one-for-one') {
getSelectedList(item.children, true);
} else if (item.type === 'form') {
if (item.bindTable === widgetFormSelect.bindTable) {
selectedList.value = item.children.filter((child: any) => {
return !noAssociateComponents.includes(child.type);
});
}
} else {
if (!noAssociateComponents.includes(item.type)) {
if (isOneForOne) item.isSingleFormChild = true;
selectedList.value.push(item);
}
}
import { message } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
data: { type: Object },
disabled: { type: Boolean, default: () => false }
});
};
const { widgetForm, widgetFormSelect } = inject('state') as any; //整个表单json配置
watch(
() => widgetForm.list,
() => {
if (widgetForm.list.length) {
selectedSingleList.value = [];
selectedList.value = [];
getSelectedList(widgetForm.list);
list.value = selectedList.value;
}
},
{
immediate: true,
deep: true,
},
);
const emit = defineEmits(['update:data', 'handleOptionsRemove']);
const info = ref<any>(props.data);
const dataSourceAssoDia = ref<boolean>(false);
const apiAssoDia = ref<boolean>(false);
const dicAssoDia = ref<boolean>(false);
const apiConfigDialog = ref<boolean>(false);
const list = ref<any[]>([]);
const selectedList = ref<any[]>([]);
const selectedSingleList = ref<any[]>([]); //单表组件内部引用列表
const configText = computed(() => {
const datasourceType = info.value.options.datasourceType;
const dicOptions = info.value.options.dicOptions;
const outputParams = info.value.options.apiConfig?.outputParams;
if (
(datasourceType === 'dic' && dicOptions?.length) ||
(datasourceType === 'api' && outputParams?.length)
) {
return t('已配置');
} else {
return '';
}
});
const formLabel = computed(() => {
return info.value?.type === 'multiple-popup' ? t('显示配置') : t('联想配置');
});
const fieldOptions = ref<string[]>([]);
const notBindFieldConfig = ['select', 'radio', 'checkbox', 'associate-select'];
const noAssociateComponents = ['multiple-popup', 'associate-popup', 'associate-select', 'form', 'tab', 'card', 'grid', 'one-for-one', 'button'];
const handleDatasourceChange = () => {
info.value.options.params = null;
info.value.options.labelField = 'label';
info.value.options.valueField = 'value';
watch(
() => props.data,
(val) => {
info.value = val;
},
{
immediate: true,
deep: true
}
);
watch(
info.value,
(val) => {
emit('update:data', val);
},
{
immediate: true,
deep: true
}
);
if (info.value.options.datasourceType === 'dic') {
info.value.options.params = info.value.options.itemId
? { itemId: info.value.options.itemId }
: null;
info.value.options.labelField = 'name';
}
if (info.value.options.datasourceType !== 'staticData') {
if (info.value.type == 'multiple-popup' || info.value.type == 'checkbox') {
info.value.options.defaultTempValue = [];
}
info.value.options.defaultSelect = null;
}
};
const handleInsertOption = () => {
const index = info.value.options.staticOptions.length + 1;
info.value.options.staticOptions.push({
key: index,
label: `Option ${index}`,
value: `Option ${index}`,
});
};
const handleFieldChange = async (id: string) => {
if (!id) return;
info.value.options.labelField = undefined;
info.value.options.valueField = undefined;
info.value.options.dataSourceOptions = [];
info.value.options.params = id;
fieldOptions.value = await getDatasourceColumn(id);
const getSelectedList = (list, isOneForOne = false) => {
list?.map((item) => {
if (['tab', 'grid', 'card'].includes(item.type)) {
for (const child of item.layout!) {
getSelectedList(child.list, isOneForOne);
}
} else if (item.type == 'table-layout') {
for (const child of item.layout!) {
for (const item of child.list) {
getSelectedList(item.children, isOneForOne);
}
}
} else if (item.type === 'one-for-one') {
getSelectedList(item.children, true);
} else if (item.type === 'form') {
if (item.bindTable === widgetFormSelect.bindTable) {
selectedList.value = item.children.filter((child: any) => {
return !noAssociateComponents.includes(child.type);
});
}
} else {
if (!noAssociateComponents.includes(item.type)) {
if (isOneForOne) item.isSingleFormChild = true;
selectedList.value.push(item);
}
}
});
};
if (fieldOptions.value.length) {
fieldOptions.value.map((item, index) => {
let options = {};
if (notBindFieldConfig.includes(info.value.type)) {
options = { key: index + 1, name: item };
watch(
() => widgetForm.list,
() => {
if (widgetForm.list.length) {
selectedSingleList.value = [];
selectedList.value = [];
getSelectedList(widgetForm.list);
list.value = selectedList.value;
}
},
{
immediate: true,
deep: true
}
);
const configText = computed(() => {
const datasourceType = info.value.options.datasourceType;
const dicOptions = info.value.options.dicOptions;
const outputParams = info.value.options.apiConfig?.outputParams;
if ((datasourceType === 'dic' && dicOptions?.length) || (datasourceType === 'api' && outputParams?.length)) {
return t('已配置');
} else {
options = {
key: index + 1,
name: item,
tableTitle: item,
bindField: '',
show: true,
width: '150',
};
return '';
}
info.value.options.dataSourceOptions.push(options);
});
}
};
});
const formLabel = computed(() => {
return info.value?.type === 'multiple-popup' ? t('显示配置') : t('联想配置');
});
const handleDicChange = (val) => {
info.value.options.params = { itemId: val };
};
const showConfigDialog = () => {
if (info.value.options.datasourceType === 'datasource') {
if (info.value.type === 'associate-popup' && !selectedList.value.length) {
message.error(t('请保证存在一个或一个以上可联想绑定数据的组件'));
} else {
dataSourceAssoDia.value = true;
}
} else if (info.value.options.datasourceType === 'api') {
apiAssoDia.value = true;
} else if (info.value.options.datasourceType === 'dic') {
dicAssoDia.value = true;
}
};
const handleDatasourceChange = () => {
info.value.options.params = null;
info.value.options.labelField = 'label';
info.value.options.valueField = 'value';
if (info.value.options.datasourceType === 'dic') {
info.value.options.params = info.value.options.itemId ? { itemId: info.value.options.itemId } : null;
info.value.options.labelField = 'name';
}
if (info.value.options.datasourceType !== 'staticData') {
if (info.value.type == 'multiple-popup' || info.value.type == 'checkbox') {
info.value.options.defaultTempValue = [];
}
info.value.options.defaultSelect = null;
}
};
const handleInsertOption = () => {
const index = info.value.options.staticOptions.length + 1;
info.value.options.staticOptions.push({
key: index,
label: `Option ${index}`,
value: `Option ${index}`
});
};
const handleFieldChange = async (id: string) => {
if (!id) return;
info.value.options.labelField = undefined;
info.value.options.valueField = undefined;
info.value.options.dataSourceOptions = [];
info.value.options.params = id;
fieldOptions.value = await getDatasourceColumn(id);
if (fieldOptions.value.length) {
fieldOptions.value.map((item, index) => {
let options = {};
if (notBindFieldConfig.includes(info.value.type)) {
options = { key: index + 1, name: item };
} else {
options = {
key: index + 1,
name: item,
tableTitle: item,
bindField: '',
show: true,
width: '150'
};
}
info.value.options.dataSourceOptions.push(options);
});
}
};
const handleDicChange = (val) => {
info.value.options.params = { itemId: val };
};
const showConfigDialog = () => {
if (info.value.options.datasourceType === 'datasource') {
if (info.value.type === 'associate-popup' && !selectedList.value.length) {
message.error(t('请保证存在一个或一个以上可联想绑定数据的组件'));
} else {
dataSourceAssoDia.value = true;
}
} else if (info.value.options.datasourceType === 'api') {
apiAssoDia.value = true;
} else if (info.value.options.datasourceType === 'dic') {
dicAssoDia.value = true;
}
};
</script>

View File

@ -1,388 +1,364 @@
<template>
<a-modal
:width="1000"
v-model:visible="visible"
:title="title"
:maskClosable="false"
@ok="handleSubmit"
@cancel="handleClose"
:bodyStyle="{ padding: '0 10px 10px' }"
>
<div class="list-title">{{ t('字段列表') }}</div>
<a-table
:dataSource="dicInfo"
:columns="dicColumns"
:pagination="false"
:scroll="{ y: '400px' }"
>
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
{{ t('是否在弹层列表显示') }}
<a-checkbox v-model:checked="checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'component'">
<a-select
v-model:value="record.component"
style="width: 100%"
:placeholder="t('请选择填充组件')"
@change="(value) => selectBindField(value, record)"
allowClear
:disabled="disabled"
>
<a-select-option :value="val.key" v-for="(val, idx) in selectedList" :key="idx">
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" :placeholder="t('请填写表头名称')" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input
v-model:value="record.width"
:disabled="!record.show"
:placeholder="t('请填写列表宽度')"
/>
</template>
</template>
</a-table>
</a-modal>
<a-modal :width="1000" v-model:visible="visible" :title="title" :maskClosable="false" @ok="handleSubmit" @cancel="handleClose" :bodyStyle="{ padding: '0 10px 10px' }">
<div class="list-title">{{ t('字段列表') }}</div>
<a-table :dataSource="dicInfo" :columns="dicColumns" :pagination="false" :scroll="{ y: '400px' }">
<template #headerCell="{ column }">
<template v-if="column.key === 'show'">
{{ t('是否在弹层列表显示') }}
<a-checkbox v-model:checked="checkedAll" @change="handleChecked" />
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'component'">
<a-select v-model:value="record.component" style="width: 100%" :placeholder="t('请选择填充组件')" @change="(value) => selectBindField(value, record)" allowClear :disabled="disabled">
<a-select-option :value="val.key" v-for="(val, idx) in selectedList" :key="idx">
{{ val.label }}
</a-select-option>
</a-select>
</template>
<template v-else-if="column.key === 'tableTitle'">
<a-input v-model:value="record.tableTitle" :placeholder="t('请填写表头名称')" />
</template>
<template v-else-if="column.key === 'show'">
<a-checkbox v-model:checked="record.show" />
</template>
<template v-else-if="column.key === 'width'">
<a-input v-model:value="record.width" :disabled="!record.show" :placeholder="t('请填写列表宽度')" />
</template>
</template>
</a-table>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { message } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
dicAssoDia: { type: Boolean },
dicOptions: { type: Object as PropType<any> },
selectedList: { type: Array as any },
type: { type: String },
disabled: { type: Boolean, default: () => false },
});
const emit = defineEmits(['update:dicAssoDia', 'update:dicOptions']);
const dicInfo = ref<any[]>([]);
const title = ref<string>('');
const checkedAll = ref<boolean>(false);
let dicColumns;
const visible = ref<boolean>(props.dicAssoDia);
watch(
() => props.dicOptions,
(val) => {
if (val && val.length) {
dicInfo.value = cloneDeep(val);
}
},
{
immediate: true,
deep: true,
},
);
watch(
() => dicInfo.value,
(val) => {
checkedAll.value = val.every((item: any) => {
return item.show;
});
},
{
immediate: true,
deep: true,
},
);
onMounted(() => {
if (!dicInfo.value.length) {
switch (props.type) {
case 'button':
case 'associate-popup':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
bindField: '',
tableTitle: '',
show: true,
width: 150,
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
bindField: '',
tableTitle: '',
show: true,
width: 150,
},
];
break;
case 'multiple-popup':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
tableTitle: '',
show: true,
width: 150,
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
tableTitle: '',
show: true,
width: 150,
},
];
break;
case 'associate-select':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
bindField: '',
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
bindField: '',
},
];
break;
case 'preload-title':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
tableTitle: 'name',
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
tableTitle: 'value',
},
];
break;
}
}
});
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200,
},
{
title: t('显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150,
},
]);
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center',
width: 250,
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200,
},
{
title: t('是否在弹层列表显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200,
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150,
},
]);
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center',
},
{
title: t('填充组件绑定字段'),
dataIndex: 'bindField',
key: 'bindField',
align: 'center',
},
]);
const preloadColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
},
]);
watch(
() => props.type,
(val) => {
console.log('props.type', val);
switch (val) {
case 'button':
case 'associate-popup':
dicColumns = associatePopupColumns;
title.value = t('联想数据配置-数据字典');
break;
case 'multiple-popup':
dicColumns = multiplePopupColumns;
title.value = t('多选数据配置-数据字典');
break;
case 'associate-select':
dicColumns = associateSelectColumns;
title.value = t('联想数据配置-数据字典');
break;
case 'preload-title':
dicColumns = preloadColumns;
title.value = t('表头配置-数据字典');
break;
}
},
{ immediate: true },
);
const selectBindField = (value, record) => {
if (!value || !isNaN(value)) {
message.error(t('请先选择该组件的绑定表及绑定字段'));
record.bindField = null;
record.bindTable = undefined;
} else {
let obj = props.selectedList.find((o) => o.key == value);
if (obj) {
record.bindField = obj.bindField;
if (obj.isSingleFormChild || obj.isSubFormChild) {
record.bindTable = obj.bindTable;
} else {
record.bindTable = undefined;
}
}
}
};
const handleChecked = (e: any) => {
dicInfo.value.map((item: any) => (item.show = e.target.checked));
};
const handleClose = () => {
emit('update:dicAssoDia', false);
};
const handleSubmit = () => {
const bindFieldArr = [] as any;
dicInfo.value?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
import { ref, reactive, watch, onMounted } from 'vue';
import { ColumnProps } from 'ant-design-vue/lib/table/Column';
import { message } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
dicAssoDia: { type: Boolean },
dicOptions: { type: Object as PropType<any> },
selectedList: { type: Array as any },
type: { type: String },
disabled: { type: Boolean, default: () => false }
});
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error(t('组件存在重复绑定,请更改'));
return;
}
emit('update:dicAssoDia', false);
emit('update:dicOptions', dicInfo.value);
};
const emit = defineEmits(['update:dicAssoDia', 'update:dicOptions']);
const dicInfo = ref<any[]>([]);
const title = ref<string>('');
const checkedAll = ref<boolean>(false);
let dicColumns;
const visible = ref<boolean>(props.dicAssoDia);
watch(
() => props.dicOptions,
(val) => {
if (val && val.length) {
dicInfo.value = cloneDeep(val);
}
},
{
immediate: true,
deep: true
}
);
watch(
() => dicInfo.value,
(val) => {
checkedAll.value = val.every((item: any) => {
return item.show;
});
},
{
immediate: true,
deep: true
}
);
onMounted(() => {
if (!dicInfo.value.length) {
switch (props.type) {
case 'button':
case 'associate-popup':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
bindField: '',
tableTitle: '',
show: true,
width: 150
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
bindField: '',
tableTitle: '',
show: true,
width: 150
}
];
break;
case 'multiple-popup':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
tableTitle: '',
show: true,
width: 150
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
tableTitle: '',
show: true,
width: 150
}
];
break;
case 'associate-select':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
bindField: ''
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
bindField: ''
}
];
break;
case 'preload-title':
dicInfo.value = [
{
dataIndex: 'name',
title: 'name',
name: 'name',
tableTitle: 'name'
},
{
dataIndex: 'value',
title: 'value',
name: 'value',
tableTitle: 'value'
}
];
break;
}
}
});
const multiplePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200
},
{
title: t('显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150
}
]);
const associatePopupColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center',
width: 250
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center',
width: 200
},
{
title: t('是否在弹层列表显示'),
dataIndex: 'show',
key: 'show',
align: 'center',
width: 200
},
{
title: t('列表宽度'),
dataIndex: 'width',
key: 'width',
align: 'center',
width: 150
}
]);
const associateSelectColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: t('填充组件'),
dataIndex: 'component',
key: 'component',
align: 'center'
},
{
title: t('填充组件绑定字段'),
dataIndex: 'bindField',
key: 'bindField',
align: 'center'
}
]);
const preloadColumns = reactive<ColumnProps[]>([
{
title: '#',
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80
},
{
title: t('字段名'),
dataIndex: 'name',
key: 'name',
align: 'center'
},
{
title: t('表头名称'),
dataIndex: 'tableTitle',
key: 'tableTitle',
align: 'center'
}
]);
watch(
() => props.type,
(val) => {
console.log('props.type', val);
switch (val) {
case 'button':
case 'associate-popup':
dicColumns = associatePopupColumns;
title.value = t('联想数据配置-数据字典');
break;
case 'multiple-popup':
dicColumns = multiplePopupColumns;
title.value = t('多选数据配置-数据字典');
break;
case 'associate-select':
dicColumns = associateSelectColumns;
title.value = t('联想数据配置-数据字典');
break;
case 'preload-title':
dicColumns = preloadColumns;
title.value = t('表头配置-数据字典');
break;
}
},
{ immediate: true }
);
const selectBindField = (value, record) => {
if (!value || !isNaN(value)) {
message.error(t('请先选择该组件的绑定表及绑定字段'));
record.bindField = null;
record.bindTable = undefined;
} else {
let obj = props.selectedList.find((o) => o.key == value);
if (obj) {
record.bindField = obj.bindField;
if (obj.isSingleFormChild || obj.isSubFormChild) {
record.bindTable = obj.bindTable;
} else {
record.bindTable = undefined;
}
}
}
};
const handleChecked = (e: any) => {
dicInfo.value.map((item: any) => (item.show = e.target.checked));
};
const handleClose = () => {
emit('update:dicAssoDia', false);
};
const handleSubmit = () => {
const bindFieldArr = [] as any;
dicInfo.value?.map((item: any) => {
if (item.bindField) {
bindFieldArr.push(item.bindField);
}
});
if (new Set(bindFieldArr).size !== bindFieldArr.length) {
message.error(t('组件存在重复绑定,请更改'));
return;
}
emit('update:dicAssoDia', false);
emit('update:dicOptions', dicInfo.value);
};
</script>
<style lang="less" scoped>
.list-title {
margin: 10px 0 5px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.list-title {
margin: 10px 0 5px;
font-size: 14px;
line-height: 16px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
</style>

View File

@ -1,51 +1,51 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="t('查看接口信息')" @ok="closeModal">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" :tab="t('接口信息')">
<CodeEditor :value="script" language="json" readonly />
</a-tab-pane>
<a-tab-pane key="2" :tab="t('接口出参示例')">
<CodeEditor :value="infoExample" language="json" readonly />
</a-tab-pane>
</a-tabs>
</BasicModal>
<BasicModal v-bind="$attrs" @register="registerModal" :title="t('查看接口信息')" @ok="closeModal">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" :tab="t('接口信息')">
<CodeEditor :value="script" language="json" readonly />
</a-tab-pane>
<a-tab-pane key="2" :tab="t('接口出参示例')">
<CodeEditor :value="infoExample" language="json" readonly />
</a-tab-pane>
</a-tabs>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { CodeEditor } from '/@/components/CodeEditor';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const activeKey = ref<string>('1');
const script = ref<string>('');
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
script.value = data.script;
setModalProps({
confirmLoading: false,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
destroyOnClose: true,
width: 800,
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { CodeEditor } from '/@/components/CodeEditor';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const activeKey = ref<string>('1');
const script = ref<string>('');
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
script.value = data.script;
setModalProps({
confirmLoading: false,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
destroyOnClose: true,
width: 800
});
});
});
const infoExample = JSON.stringify({
code: 0,
msg: t('提示信息'),
data: [
{
label: t('选项一'),
value: 1,
},
{
label: t('选项二'),
value: 1,
},
{
label: t('选项三'),
value: 1,
},
],
});
const infoExample = JSON.stringify({
code: 0,
msg: t('提示信息'),
data: [
{
label: t('选项一'),
value: 1
},
{
label: t('选项二'),
value: 1
},
{
label: t('选项三'),
value: 1
}
]
});
</script>

View File

@ -1,132 +1,95 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:title="t('脚本配置')"
@ok="handleSubmit"
@visible-change="visibleChange"
width="70%"
>
<div class="config-box">
<div>
<a-tabs
v-model:activeKey="configTab"
centered
style="margin-bottom: 8px; background: #fff; height: 100%"
>
<a-tab-pane key="widget" :tab="t('脚本示例')">
<BasicModal v-bind="$attrs" @register="registerModal" :title="t('脚本配置')" @ok="handleSubmit" @visible-change="visibleChange" width="70%">
<div class="config-box">
<div>
<div
v-for="(item, index) in example"
:key="index"
:class="['script-name', { 'active-name': activeIndex === index }]"
@click="activeIndex = index"
>
{{ item.title }}
</div>
</div>
</a-tab-pane>
<a-tab-pane key="component" :tab="t('选择组件')">
<div class="overflow-y-scroll">
<Draggable
v-model="widgetFormList"
:group="{ name: 'script', pull: false, put: false }"
item-key="key"
@end="dragEnd"
:sort="false"
>
<template #item="{ element }">
<a-tag color="blue">{{ `${element.label}(${element.bindField})` }}</a-tag>
</template>
</Draggable>
</div>
</a-tab-pane>
</a-tabs>
<!-- <div class="box-top">
<a-tabs v-model:activeKey="configTab" centered style="margin-bottom: 8px; background: #fff; height: 100%">
<a-tab-pane key="widget" :tab="t('脚本示例')">
<div>
<div v-for="(item, index) in example" :key="index" :class="['script-name', { 'active-name': activeIndex === index }]" @click="activeIndex = index">
{{ item.title }}
</div>
</div>
</a-tab-pane>
<a-tab-pane key="component" :tab="t('选择组件')">
<div class="overflow-y-scroll">
<Draggable v-model="widgetFormList" :group="{ name: 'script', pull: false, put: false }" item-key="key" @end="dragEnd" :sort="false">
<template #item="{ element }">
<a-tag color="blue">{{ `${element.label}(${element.bindField})` }}</a-tag>
</template>
</Draggable>
</div>
</a-tab-pane>
</a-tabs>
<!-- <div class="box-top">
<div class="title">{{ t('脚本示例') }}</div>
<a-button type="primary" size="small" @click="componentVisible = true">{{
t('选择组件')
}}</a-button>
</div> -->
</div>
<div style="max-width: 500px">
<div class="box-top">
<div class="title">{{ t('脚本示例代码') }}</div>
</div>
<div style="max-width: 500px">
<div class="box-top">
<div class="title">{{ t('脚本示例代码') }}</div>
</div>
<div style="height: calc(100% - 40px)">
<CodeEditor :value="example[activeIndex].code" language="js" readonly />
</div>
</div>
<div>
<div class="box-top">
<div class="title">{{ t('自定义脚本') }}</div>
</div>
<div style="height: calc(100% - 40px)">
<CodeEditor v-model:value="definedScript" language="js" :readonly="disabled" />
</div>
</div>
</div>
<div style="height: calc(100% - 40px)">
<CodeEditor :value="example[activeIndex].code" language="js" readonly />
</div>
</div>
<div>
<div class="box-top">
<div class="title">{{ t('自定义脚本') }}</div>
</div>
<div style="height: calc(100% - 40px)">
<CodeEditor v-model:value="definedScript" language="js" :readonly="disabled" />
</div>
</div>
</div>
<a-drawer
:title="t('组件列表(可将组件拖入自定义脚本当中)')"
placement="left"
:visible="componentVisible"
:get-container="false"
:style="{ position: 'absolute' }"
:mask="false"
width="50%"
:closable="false"
>
<Draggable
v-model="widgetFormList"
:group="{ name: 'script', pull: false, put: false }"
item-key="key"
@end="dragEnd"
:sort="false"
>
<template #item="{ element }">
<a-tag color="blue">{{ `${element.label}(${element.bindField})` }}</a-tag>
</template>
</Draggable>
<template #extra>
<CloseOutlined @click="componentVisible = false" />
</template>
</a-drawer>
</BasicModal>
<a-drawer :title="t('组件列表(可将组件拖入自定义脚本当中)')" placement="left" :visible="componentVisible" :get-container="false" :style="{ position: 'absolute' }" :mask="false" width="50%" :closable="false">
<Draggable v-model="widgetFormList" :group="{ name: 'script', pull: false, put: false }" item-key="key" @end="dragEnd" :sort="false">
<template #item="{ element }">
<a-tag color="blue">{{ `${element.label}(${element.bindField})` }}</a-tag>
</template>
</Draggable>
<template #extra>
<CloseOutlined @click="componentVisible = false" />
</template>
</a-drawer>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, reactive, inject } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { CodeEditor } from '/@/components/CodeEditor';
import Draggable from 'vuedraggable';
import { CloseOutlined } from '@ant-design/icons-vue';
import { camelCaseString } from '/@/utils/event/design';
import { useI18n } from '/@/hooks/web/useI18n';
defineProps({
disabled: { type: Boolean, default: false },
});
const { t } = useI18n();
const eventType = ref('');
const definedScript = ref(``);
const emit = defineEmits(['success', 'register']);
const activeIndex = ref(0);
const componentVisible = ref<boolean>(false);
const configIndex = ref();
const configTab = ref('widget');
import { ref, reactive, inject } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { CodeEditor } from '/@/components/CodeEditor';
import Draggable from 'vuedraggable';
import { CloseOutlined } from '@ant-design/icons-vue';
import { camelCaseString } from '/@/utils/event/design';
import { useI18n } from '/@/hooks/web/useI18n';
defineProps({
disabled: { type: Boolean, default: false }
});
const { t } = useI18n();
const eventType = ref('');
const definedScript = ref(``);
const emit = defineEmits(['success', 'register']);
const activeIndex = ref(0);
const componentVisible = ref<boolean>(false);
const configIndex = ref();
const configTab = ref('widget');
const widgetFormList = ref([]); //整个表单json配置
const isCustomForm = inject<boolean>('isCustomForm', false);
const example = reactive([
{
title: t('读写组件值'),
code: `const value = formModel.bindField //获取组件值
const widgetFormList = ref([]); //整个表单json配置
const isCustomForm = inject<boolean>('isCustomForm', false);
const example = reactive([
{
title: t('读写组件值'),
code: `const value = formModel.bindField //获取组件值
formModel.bindField = 1 //修改组件值
// bindField: 组件的绑定字段`,
},
{
title: t('ajax加载服务器数据'),
code: `formActionType.httpRequest({
// bindField: 组件的绑定字段`
},
{
title: t('ajax加载服务器数据'),
code: `formActionType.httpRequest({
requestType: 'get', //请求方式有: get、post、put、delete
requestUrl: '/system/dictionary-detail', //请求地址
params: {
@ -137,19 +100,19 @@
// errorMessageMode='message' 错误提示为消息提示
// errorMessageMode='modal' 显示modal错误弹窗用于一些比较重要的错误
// errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
`,
},
{
title: t('修改组件样式'),
code: `formActionType.changeStyle(schema, { border:'2px solid #5e95ff' },'bindField')
`
},
{
title: t('修改组件样式'),
code: `formActionType.changeStyle(schema, { border:'2px solid #5e95ff' },'bindField')
// schema:必须要写
// 中间对象为需要修改的样式
// bindField:需要修改组件的绑定字段(字符串),如修改本组件 则不填`,
},
{
title: t('弹出对话框'),
code: `formActionType.showModal({
// bindField:需要修改组件的绑定字段(字符串),如修改本组件 则不填`
},
{
title: t('弹出对话框'),
code: `formActionType.showModal({
title: '我是标题', // 对话框标题
content: '我是内容', // 对话框内容
onOk() {
@ -158,124 +121,122 @@
onCancel() {
console.log('onCancel') // 点击取消按钮的处理逻辑
},
});`,
},
{
title: t('if判断'),
code: `const value = 3
});`
},
{
title: t('if判断'),
code: `const value = 3
if(value === 3){ /* 括号内为判断条件 */
console.log('value为3')
/* 处理逻辑 */
}`,
},
{
title: t('循环'),
code: `for(let i=0; i<3; i++){ /* 括号内为循环条件 */
}`
},
{
title: t('循环'),
code: `for(let i=0; i<3; i++){ /* 括号内为循环条件 */
console.log('i的值为', i)
/* 处理逻辑 */
}`,
},
{
title: t('正则校验'),
code: `formActionType.regTest({
}`
},
{
title: t('正则校验'),
code: `formActionType.regTest({
regExpression: /^[0-9]/,// 正则表达式
testValue: 123,// 需要进行校验的值
successMessage: '校验成功',// 校验成功的提示信息
errorMessage: '校验失败'// 校验失败的提示信息
})`,
},
{
title: t('刷新API'),
code: `formActionType.refreshAPI('bindField')
})`
},
{
title: t('刷新API'),
code: `formActionType.refreshAPI('bindField')
// bindField:需要刷新API组件的绑定字段字符串`,
},
]);
const [registerModal, { closeModal, setModalProps }] = useModalInner(async (data) => {
widgetFormList.value = data.list;
eventType.value = data.type;
//如果已经设置过当前事件代码 则显示已经设置的值 反之 显示默认值
definedScript.value = data.content;
configIndex.value = data.index;
setModalProps({
fixedHeight: true,
// bindField:需要刷新API组件的绑定字段字符串`
}
]);
const [registerModal, { closeModal, setModalProps }] = useModalInner(async (data) => {
widgetFormList.value = data.list;
eventType.value = data.type;
//如果已经设置过当前事件代码 则显示已经设置的值 反之 显示默认值
definedScript.value = data.content;
configIndex.value = data.index;
setModalProps({
fixedHeight: true
});
});
});
const dragEnd = ({ item }) => {
const component = item._underlying_vm_;
const str = `${component.label}(${component.bindField})`;
const replaceStr = isCustomForm
? component.bindField
: `${camelCaseString(component.bindField)}`;
definedScript.value = definedScript.value.replace(str, replaceStr);
};
const handleSubmit = () => {
emit('success', eventType.value, definedScript.value, configIndex.value);
closeModal();
};
const dragEnd = ({ item }) => {
const component = item._underlying_vm_;
const str = `${component.label}(${component.bindField})`;
const replaceStr = isCustomForm ? component.bindField : `${camelCaseString(component.bindField)}`;
definedScript.value = definedScript.value.replace(str, replaceStr);
};
const handleSubmit = () => {
emit('success', eventType.value, definedScript.value, configIndex.value);
closeModal();
};
const visibleChange = (visible: boolean) => {
if (!visible) componentVisible.value = false;
};
const visibleChange = (visible: boolean) => {
if (!visible) componentVisible.value = false;
};
</script>
<style lang="less" scoped>
.config-box {
display: flex;
height: 100%;
.config-box {
display: flex;
height: 100%;
div:nth-child(1) {
flex: 1;
div:nth-child(1) {
flex: 1;
}
div {
flex: 2;
}
.box-top {
display: flex;
justify-content: space-between;
height: 30px;
border-bottom: 1px solid #f0f0f0;
margin: 0 0 10px 10px;
}
.script-name {
cursor: pointer;
padding: 10px 0 10px 15px;
&:hover {
background: #eef4ff;
}
}
.active-name {
background: #eef4ff;
}
}
div {
flex: 2;
:deep(.ant-drawer-title),
.title {
font-size: 16px;
height: 20px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.box-top {
display: flex;
justify-content: space-between;
height: 30px;
border-bottom: 1px solid #f0f0f0;
margin: 0 0 10px 10px;
:deep(.ant-tag) {
padding: 8px;
font-size: 13px;
margin-bottom: 7px;
cursor: move;
}
.script-name {
cursor: pointer;
padding: 10px 0 10px 15px;
&:hover {
background: #eef4ff;
}
:deep(.ant-tabs-tab) {
padding: 0 0 8px;
}
.active-name {
background: #eef4ff;
:deep(.ant-tabs-content) {
height: 100%;
}
}
:deep(.ant-drawer-title),
.title {
font-size: 16px;
height: 20px;
line-height: 18px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
:deep(.ant-tag) {
padding: 8px;
font-size: 13px;
margin-bottom: 7px;
cursor: move;
}
:deep(.ant-tabs-tab) {
padding: 0 0 8px;
}
:deep(.ant-tabs-content) {
height: 100%;
}
</style>

View File

@ -1,197 +1,175 @@
<template>
<div class="static-box">
<div class="static-top">
<div class="static-title">{{ t('静态数据列表') }}</div>
<div>
<a-button
:disabled="disabled"
type="primary"
size="small"
@click="handleReset"
class="mr-0.5"
<div class="static-box">
<div class="static-top">
<div class="static-title">{{ t('静态数据列表') }}</div>
<div>
<a-button :disabled="disabled" type="primary" size="small" @click="handleReset" class="mr-0.5"> 取消选中 </a-button>
<a-button :disabled="disabled" type="primary" size="small" @click="emits('handleInsertOption')">
{{ t('添加数据') }}
</a-button>
</div>
</div>
<a-table
:dataSource="options?.staticOptions"
:row-selection="{
type: type,
selectedRowKeys: selectedRowKey,
onChange: onChange
}"
:pagination="false"
:columns="columns"
>
取消选中
</a-button>
<a-button
:disabled="disabled"
type="primary"
size="small"
@click="emits('handleInsertOption')"
>
{{ t('添加数据') }}
</a-button>
</div>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'label'">
<a-input v-model:value="record[column.key]" :disabled="disabled" :placeholder="t('请输入')" />
</template>
<template v-if="column.key === 'value'">
<a-input v-model:value="record[column.key]" :disabled="disabled" :placeholder="t('请输入')" @change="(val) => changeValue(val, record.key)" />
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="emits('handleOptionsRemove', index)" />
</template>
</template>
</a-table>
</div>
<a-table
:dataSource="options?.staticOptions"
:row-selection="{
type: type,
selectedRowKeys: selectedRowKey,
onChange: onChange,
}"
:pagination="false"
:columns="columns"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'label'">
<a-input
v-model:value="record[column.key]"
:disabled="disabled"
:placeholder="t('请输入')"
/>
</template>
<template v-if="column.key === 'value'">
<a-input
v-model:value="record[column.key]"
:disabled="disabled"
:placeholder="t('请输入')"
@change="(val) => changeValue(val, record.key)"
/>
</template>
<template v-if="column.key === 'action'">
<DeleteTwoTone two-tone-color="#ff8080" @click="emits('handleOptionsRemove', index)" />
</template>
</template>
</a-table>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { DeleteTwoTone } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
options: Object,
type: String,
disabled: { type: Boolean, default: () => false },
});
const emits = defineEmits(['handleOptionsRemove', 'handleInsertOption', 'handleDefaultSelect']);
const selectedRowKey = ref<string[] | number[]>([1]);
const selectedRow = ref<any[]>([]);
const columns = reactive([
{
title: t('显示值'),
dataIndex: 'label',
key: 'label',
align: 'center',
},
{
title: t('保存值'),
dataIndex: 'value',
key: 'value',
align: 'center',
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50,
},
]);
watch(
() => props.options,
(val) => {
if (props.type === 'radio') {
//单选组件、下拉选择
if (!!val?.defaultSelect) {
val.staticOptions.map((item: any, index: number) => {
if (item.value === val?.defaultSelect) {
selectedRowKey.value = [index + 1];
}
});
}
} else {
//多选组件
if (val?.defaultSelect) {
selectedRowKey.value = [];
val.staticOptions.map((item: any, index: number) => {
val?.defaultSelect.split(',').map((select: [string, number]) => {
if (item.value === select) {
(selectedRowKey.value as number[]).push(index + 1);
}
});
});
} else {
selectedRowKey.value = [];
}
}
},
{ deep: true, immediate: true },
);
onMounted(() => {
selectedRowKey.value = [props.options?.staticOptions[0].key];
selectedRow.value = [props.options?.staticOptions[0]];
emits('handleDefaultSelect', props.options?.staticOptions[0].value);
});
watch(
() => selectedRow.value,
(val) => {
const selected = val?.map((x) => x.value).join(',');
emits('handleDefaultSelect', selected);
},
{ deep: true },
);
const onChange = (selectedRowKeys: string[] | number[], selectedRows: any[]) => {
if (props.disabled) return;
selectedRowKey.value = selectedRowKeys;
selectedRow.value = selectedRows;
let selectedValue;
if (props.type === 'radio') {
selectedValue = selectedRows[0].value;
} else {
selectedValue = selectedRows.map((item) => item.value).join(',');
}
emits('handleDefaultSelect', selectedValue);
};
const handleReset = () => {
selectedRowKey.value = [];
selectedRow.value = [];
emits('handleDefaultSelect', '');
};
const changeValue = (e, key) => {
selectedRow.value.map((row) => {
if (row.key === key) {
row.value = e.target.value;
}
import { ref, reactive, watch, onMounted } from 'vue';
import { DeleteTwoTone } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = defineProps({
options: Object,
type: String,
disabled: { type: Boolean, default: () => false }
});
};
const emits = defineEmits(['handleOptionsRemove', 'handleInsertOption', 'handleDefaultSelect']);
const selectedRowKey = ref<string[] | number[]>([1]);
const selectedRow = ref<any[]>([]);
const columns = reactive([
{
title: t('显示值'),
dataIndex: 'label',
key: 'label',
align: 'center'
},
{
title: t('保存值'),
dataIndex: 'value',
key: 'value',
align: 'center'
},
{
title: t('操作'),
dataIndex: 'action',
key: 'action',
align: 'center',
width: 50
}
]);
watch(
() => props.options,
(val) => {
if (props.type === 'radio') {
//单选组件、下拉选择
if (!!val?.defaultSelect) {
val.staticOptions.map((item: any, index: number) => {
if (item.value === val?.defaultSelect) {
selectedRowKey.value = [index + 1];
}
});
}
} else {
//多选组件
if (val?.defaultSelect) {
selectedRowKey.value = [];
val.staticOptions.map((item: any, index: number) => {
val?.defaultSelect.split(',').map((select: [string, number]) => {
if (item.value === select) {
(selectedRowKey.value as number[]).push(index + 1);
}
});
});
} else {
selectedRowKey.value = [];
}
}
},
{ deep: true, immediate: true }
);
onMounted(() => {
selectedRowKey.value = [props.options?.staticOptions[0].key];
selectedRow.value = [props.options?.staticOptions[0]];
emits('handleDefaultSelect', props.options?.staticOptions[0].value);
});
watch(
() => selectedRow.value,
(val) => {
const selected = val?.map((x) => x.value).join(',');
emits('handleDefaultSelect', selected);
},
{ deep: true }
);
const onChange = (selectedRowKeys: string[] | number[], selectedRows: any[]) => {
if (props.disabled) return;
selectedRowKey.value = selectedRowKeys;
selectedRow.value = selectedRows;
let selectedValue;
if (props.type === 'radio') {
selectedValue = selectedRows[0].value;
} else {
selectedValue = selectedRows.map((item) => item.value).join(',');
}
emits('handleDefaultSelect', selectedValue);
};
const handleReset = () => {
selectedRowKey.value = [];
selectedRow.value = [];
emits('handleDefaultSelect', '');
};
const changeValue = (e, key) => {
selectedRow.value.map((row) => {
if (row.key === key) {
row.value = e.target.value;
}
});
};
</script>
<style lang="less" scoped>
.static-box {
padding: 5px 0 10px 20px;
.static-box {
padding: 5px 0 10px 20px;
:deep(.ant-table) {
font-size: 14px;
:deep(.ant-table) {
font-size: 14px;
}
:deep(.ant-table-tbody tr td) {
padding: 8px;
}
:deep(.ant-table .ant-table-thead tr th) {
padding: 8px;
}
.static-top {
display: flex;
justify-content: space-between;
font-size: 14px;
margin-top: 5px;
.static-title {
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
}
}
:deep(.ant-table-tbody tr td) {
padding: 8px;
}
:deep(.ant-table .ant-table-thead tr th) {
padding: 8px;
}
.static-top {
display: flex;
justify-content: space-between;
font-size: 14px;
margin-top: 5px;
.static-title {
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
}
}
</style>

View File

@ -1,158 +1,135 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="批量设置权限所属人"
@ok="handleSubmit"
@cancel="handleClose"
>
<BasicTable @register="registerTable">
<template #tableTitle>
<div class="table-title">数据列表</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.componentType === 'switch'">
<a-switch
v-model:checked="record[column.dataIndex]"
:unCheckedValue="0"
:checkedValue="1"
:disabled="true"
/>
</template>
<template v-if="column.componentType === 'picker-color'">
<ColorPicker v-model:value="record[column.dataIndex]" :disabled="true" />
</template>
<template v-if="column.componentType === 'money-chinese'">
{{
!isNaN(record[column.dataIndex]) && record[column.dataIndex] !== null
? nzhcn.encodeB(record[column.dataIndex]) + ''
: ''
}}
</template>
</template>
<template #toolbar>
<SelectUser
class="select-box"
:selectedIds="state.userIds"
:multiple="false"
@change="getRuleUserIds"
@changeNames="getRuleUserNames"
>
<a-button type="primary"> 设置权限所属人 </a-button>
</SelectUser>
</template>
</BasicTable>
</BasicModal>
<BasicModal v-bind="$attrs" @register="registerModal" title="批量设置权限所属人" @ok="handleSubmit" @cancel="handleClose">
<BasicTable @register="registerTable">
<template #tableTitle>
<div class="table-title">数据列表</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.componentType === 'switch'">
<a-switch v-model:checked="record[column.dataIndex]" :unCheckedValue="0" :checkedValue="1" :disabled="true" />
</template>
<template v-if="column.componentType === 'picker-color'">
<ColorPicker v-model:value="record[column.dataIndex]" :disabled="true" />
</template>
<template v-if="column.componentType === 'money-chinese'">
{{ !isNaN(record[column.dataIndex]) && record[column.dataIndex] !== null ? nzhcn.encodeB(record[column.dataIndex]) + '' : '' }}
</template>
</template>
<template #toolbar>
<SelectUser class="select-box" :selectedIds="state.userIds" :multiple="false" @change="getRuleUserIds" @changeNames="getRuleUserNames">
<a-button type="primary"> 设置权限所属人 </a-button>
</SelectUser>
</template>
</BasicTable>
</BasicModal>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
import { cloneDeep } from 'lodash-es';
import { isFunction } from '/@/utils/is';
import { SelectUser } from '/@/components/SelectOrganizational/index';
import { getAllUserList } from '/@/api/system/user';
import { ColorPicker } from '/@/components/ColorPicker';
import nzhcn from 'nzh/cn';
import { reactive } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
import { cloneDeep } from 'lodash-es';
import { isFunction } from '/@/utils/is';
import { SelectUser } from '/@/components/SelectOrganizational/index';
import { getAllUserList } from '/@/api/system/user';
import { ColorPicker } from '/@/components/ColorPicker';
import nzhcn from 'nzh/cn';
const emit = defineEmits(['success', 'register']);
const emit = defineEmits(['success', 'register']);
const state = reactive({
rowKey: '',
dataSource: [] as any[],
columns: [] as BasicColumn[],
userIds: [],
setDataAuthApi: null as any,
apiParams: null,
ruleUserIdName: 'ruleUserId',
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
state.rowKey = data.rowKey;
state.columns = cloneDeep(data.columns);
state.dataSource = cloneDeep(data.dataSource);
state.setDataAuthApi = data.setDataAuthApi;
state.apiParams = data.params;
state.ruleUserIdName = !!data.isCustomForm ? 'rule_user_id' : 'ruleUserId';
setModalProps({
destroyOnClose: true,
maskClosable: false,
fixedHeight: true,
width: 800,
const state = reactive({
rowKey: '',
dataSource: [] as any[],
columns: [] as BasicColumn[],
userIds: [],
setDataAuthApi: null as any,
apiParams: null,
ruleUserIdName: 'ruleUserId'
});
if (state.columns?.length && state.columns[0].dataIndex !== state.ruleUserIdName) {
state.columns.unshift({
dataIndex: state.ruleUserIdName,
title: '当前权限人',
});
}
const userInfo = await getAllUserList();
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
state.rowKey = data.rowKey;
state.columns = cloneDeep(data.columns);
state.dataSource = cloneDeep(data.dataSource);
state.setDataAuthApi = data.setDataAuthApi;
state.apiParams = data.params;
state.ruleUserIdName = !!data.isCustomForm ? 'rule_user_id' : 'ruleUserId';
state.dataSource.map((item) => {
if (item[state.ruleUserIdName]) {
const userIdsArr = item[state.ruleUserIdName].split(',');
const userNamesArr = [] as string[];
userInfo.map((user) => {
if (userIdsArr.includes(user.id)) {
userNamesArr.push(user.name);
}
setModalProps({
destroyOnClose: true,
maskClosable: false,
fixedHeight: true,
width: 800
});
item[state.ruleUserIdName] = userNamesArr.join(',');
}
if (state.columns?.length && state.columns[0].dataIndex !== state.ruleUserIdName) {
state.columns.unshift({
dataIndex: state.ruleUserIdName,
title: '当前权限人'
});
}
const userInfo = await getAllUserList();
state.dataSource.map((item) => {
if (item[state.ruleUserIdName]) {
const userIdsArr = item[state.ruleUserIdName].split(',');
const userNamesArr = [] as string[];
userInfo.map((user) => {
if (userIdsArr.includes(user.id)) {
userNamesArr.push(user.name);
}
});
item[state.ruleUserIdName] = userNamesArr.join(',');
}
});
setTableData(state.dataSource);
setColumns(state.columns);
});
setTableData(state.dataSource);
setColumns(state.columns);
});
const [registerTable, { setTableData, setColumns }] = useTable({
rowKey: state.rowKey,
columns: state.columns,
dataSource: state.dataSource,
pagination: false,
});
const [registerTable, { setTableData, setColumns }] = useTable({
rowKey: state.rowKey,
columns: state.columns,
dataSource: state.dataSource,
pagination: false
});
async function handleSubmit() {
const params = {
userIdList: state.userIds,
dataIdList: state.dataSource.map((x) => x[state.rowKey]),
};
if (isFunction(state.setDataAuthApi)) {
!!state.apiParams
? await state.setDataAuthApi(state.apiParams, params)
: await state.setDataAuthApi(params);
async function handleSubmit() {
const params = {
userIdList: state.userIds,
dataIdList: state.dataSource.map((x) => x[state.rowKey])
};
if (isFunction(state.setDataAuthApi)) {
!!state.apiParams ? await state.setDataAuthApi(state.apiParams, params) : await state.setDataAuthApi(params);
}
closeModal();
emit('success');
state.userIds = [];
}
closeModal();
emit('success');
state.userIds = [];
}
function handleClose() {
state.userIds = [];
}
function handleClose() {
state.userIds = [];
}
function getRuleUserIds(ids) {
state.userIds = ids;
}
function getRuleUserIds(ids) {
state.userIds = ids;
}
function getRuleUserNames(names) {
state.dataSource.map((item) => {
item[state.ruleUserIdName] = names;
});
setTableData(state.dataSource);
}
function getRuleUserNames(names) {
state.dataSource.map((item) => {
item[state.ruleUserIdName] = names;
});
setTableData(state.dataSource);
}
</script>
<style lange="less" scoped>
.select-box {
text-align: right;
width: auto !important;
}
.select-box {
text-align: right;
width: auto !important;
}
.table-title {
font-size: 16px;
line-height: 20px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
.table-title {
font-size: 16px;
line-height: 20px;
padding-left: 6px;
border-left: 6px solid #5e95ff;
}
</style>