新增前端vue

This commit is contained in:
2025-11-26 13:55:01 +08:00
parent ae391f1b94
commit ffd5a6ad66
781 changed files with 83348 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:okAuth="'test:testData:edit'"
@register="registerDrawer"
@ok="handleSubmit"
width="70%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<BasicForm @register="registerForm">
<template #remarks="{ model, field }">
<WangEditor
v-model:value="model[field]"
:bizKey="record.id"
:bizType="'testDataChild_' + field"
:height="300"
/>
</template>
<template #testDataChildList>
<FormDataChildList ref="formDataChildListRef" />
</template>
</BasicForm>
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsTestTestDataForm">
import { ref, unref, computed } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { TestData, testDataSave, testDataForm } from '@jeesite/test/api/test/testData';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { WangEditor } from '@jeesite/core/components/WangEditor';
// import { dateUtil, formatToDateTime } from '@jeesite/core/utils/dateUtil';
import FormDataChildList from './formDataChildList.vue';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('test.testData');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<TestData>({} as TestData);
const formDataChildListRef = ref<InstanceType<typeof FormDataChildList>>();
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增数据') : t('编辑数据'),
}));
const inputFormSchemas: FormSchema<TestData>[] = [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
componentProps: {
maxlength: 200,
// addonBefore: t('前'),
// addonAfter: t('后'),
},
required: true,
},
{
label: t('列表选择'),
field: 'testInput2',
fieldLabel: 'testTextarea',
component: 'ListSelect',
componentProps: {
configFile: import('./select'),
checkbox: true,
},
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'InputTextArea',
componentProps: {
maxlength: 200,
},
rules: [{ required: true }],
colProps: { md: 24, lg: 24 },
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
mode: 'multiple',
},
},
{
label: t('单选框'),
field: 'testRadio',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('复选框'),
field: 'testCheckbox',
component: 'CheckboxGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('日期选择'),
field: 'testDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
// defaultValue: dateUtil(new Date()),
// defaultValue: formatToDateTime(new Date()),
// defaultValue: '2024-05-31',
},
{
label: t('日期时间'),
field: 'testDatetime',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('日期范围'),
field: 'dateRange',
component: 'RangePicker',
colProps: { md: 24, lg: 24 },
},
{
label: t('用户选择'),
field: 'testUser.userCode',
fieldLabel: 'testUser.userName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
treeCheckable: true,
},
},
{
label: t('用户列表选择'),
field: 'testUser.userCode',
component: 'ListSelect',
componentProps: {
selectType: 'empUserSelect',
checkbox: true,
},
},
{
label: t('机构选择'),
field: 'testOffice.officeCode',
fieldLabel: 'testOffice.officeName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
allowClear: true,
},
},
{
label: t('区域选择'),
field: 'testAreaCode',
fieldLabel: 'testAreaName',
component: 'TreeSelect',
componentProps: {
api: areaTreeData,
allowClear: true,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
slot: 'remarks',
colProps: { md: 24, lg: 24 },
},
{
label: t('图片上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_image',
uploadType: 'image',
// imageMaxWidth: 1024,
// imageMaxHeight: 768,
// imageThumbName: '150x150.jpg',
},
colProps: { md: 24, lg: 24 },
// 文件上传的必填验证实例
// rules: [
// { required: true },
// {
// validator(_rule, value) {
// return new Promise((resolve, reject) => {
// const len = !value || value['testData_image__len'] || 0;
// if (len == 0) reject(t('请上传图片'));
// else resolve();
// });
// },
// },
// ],
},
{
label: t('文件上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_file',
uploadType: 'all',
},
colProps: { md: 24, lg: 24 },
},
{
label: t('子表数据'),
field: 'testDataChildList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'testDataChildList',
},
];
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
fieldMapToTime: [['dateRange', ['testDate', 'testDatetime']]],
});
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
await resetFields();
const res = await testDataForm(data);
record.value = (res.testData || {}) as TestData;
record.value.__t = new Date().getTime();
setFieldsValue(record.value);
formDataChildListRef.value?.setTableData(record.value);
setDrawerProps({ loading: false });
});
async function handleSubmit() {
try {
const data = await validate();
setDrawerProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
id: record.value.id,
};
await formDataChildListRef.value?.getTableData(data);
// console.log('submit', params, data, record);
const res = await testDataSave(params, data);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,309 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div>
<BasicTable @register="registerTable" @row-click="handleRowClick" @edit-change="onEditChange" />
<a-button class="mt-2" @click="handleRowAdd" v-auth="'test:testData:edit'">
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
</a-button>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { TestData } from '@jeesite/test/api/test/testData';
import { TestDataChild } from '@jeesite/test/api/test/testDataChild';
const { t } = useI18n('test.testDataChild');
const record = ref<TestData>({} as TestData);
const tableColumns: BasicColumn<TestDataChild>[] = [
{
title: t('单行文本'),
dataIndex: 'testInput',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editComponentProps: {
// addonBefore: t('前'),
// addonAfter: t('后'),
},
editRule: true,
},
{
title: t('多行文本'),
dataIndex: 'testTextarea',
width: 130,
align: 'left',
editRow: true,
editComponent: 'InputTextArea',
// 子表自定义验证实例
editRule: (value, _record) => {
return new Promise((resolve, reject) => {
if (!value || value === '') return resolve();
if (value.length < 3) return reject('至少3个字符');
return resolve(); // 验证成功
});
},
},
{
title: t('下拉框'),
dataIndex: 'testSelect',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
editRule: false,
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
mode: 'multiple',
allowClear: true,
},
editRule: false,
},
{
title: t('日期选择'),
dataIndex: 'testDate',
width: 130,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
editRule: false,
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
width: 215,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
editRule: false,
},
{
title: t('用户选择'),
dataIndex: 'testUser.userCode',
dataLabel: 'testUser.userName',
width: 130,
align: 'left',
editRow: true,
// editComponent: 'TreeSelect',
editComponent: 'ListSelect',
editComponentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
// configFile: import('./select'),
// checkbox: false,
},
// 编辑表格联动例子(选择框查询条件,实时读取第一列的数据)
// editComponentProps: ({ record: childRecord }) => {
// return {
// configFile: import('./select'),
// queryParams: {
// testInput: childRecord.editValueRefs.testInput,
// },
// };
// },
editRule: false,
},
{
title: t('机构选择'),
dataIndex: 'testOffice.officeCode',
dataLabel: 'testOffice.officeName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('区域选择'),
dataIndex: 'testAreaCode',
dataLabel: 'testAreaName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: areaTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('图片上传'),
dataIndex: 'dataMap',
width: 160,
align: 'left',
editRow: true,
editComponent: 'Upload',
editComponentProps: ({ record: childRecord }) => {
return {
loadTime: record.value.__t,
bizKey: childRecord.id,
bizType: 'testDataChild_image',
uploadType: 'image',
// imageMaxWidth: 1024,
// imageMaxHeight: 768,
// imageThumbName: '150x150.jpg',
size: 'small',
};
},
// 图片上传的必填验证实例
// editRule: (value: any, record: Recordable) => {
// return new Promise((resolve, reject) => {
// const len = !value || value['testDataChild_image__len'] || 0;
// if (len == 0) reject(t('请上传图片'));
// else resolve();
// });
// },
},
{
title: t('文件上传'),
dataIndex: 'dataMap',
width: 160,
align: 'left',
editRow: true,
editComponent: 'Upload',
editComponentProps: ({ record: childRecord }) => {
return {
loadTime: record.value.__t,
bizKey: childRecord.id,
bizType: 'testDataChild_file',
uploadType: 'all',
size: 'small',
};
},
// 文件上传的必填验证实例
// editRule: (value: any, record: Recordable) => {
// return new Promise((resolve, reject) => {
// const len = !value || value['testDataChild_file__len'] || 0;
// if (len == 0) reject(t('请上传文件'));
// else resolve();
// });
// },
},
];
const [registerTable, tableAction] = useTable<TestDataChild>({
columns: tableColumns,
actionColumn: {
width: 60,
actions: (record: TestDataChild) => [
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleRowDelete.bind(this, record),
},
auth: 'test:testData:edit',
},
],
},
rowKey: 'id',
pagination: false,
bordered: true,
size: 'small',
inset: true,
});
function onEditChange({ column, record }) {
// 当修改 testInput 的时候,更改 testTextarea 的值,例子(联动)
if (column.dataIndex == 'testInput' && record.editValueRefs) {
// record.testTextarea = record.editValueRefs.testInput;
}
}
function handleRowClick(data: Recordable) {
data.onEdit?.(true, false);
}
function handleRowAdd() {
tableAction.insertTableDataRecord({
id: 'rid_' + new Date().getTime(),
isNewRecord: true,
editable: true,
});
}
function handleRowDelete(data: Recordable) {
tableAction.deleteTableDataRecord(data);
}
async function getTableData(data: Recordable): Promise<Recordable> {
let valid = true;
let tableList: Recordable[] = [];
for (const record of tableAction.getDataSource()) {
if (!(await record.onEdit?.(false, true))) {
valid = false;
}
tableList.push({
...record,
id: !!record.isNewRecord ? '' : record.id,
});
}
for (const record of tableAction.getDelDataSource()) {
if (!!record.isNewRecord) continue;
tableList.push({
...record,
status: '1',
});
}
if (!valid) {
throw {
errorFields: [{ name: ['testDataChildList'] }],
message: t('测试数据子表填写有误,请根据提示修正'),
};
}
data.testDataChildList = tableList;
return tableList;
}
async function setTableData(data: Recordable) {
record.value = data as TestData;
tableAction.setTableData(data.testDataChildList || []);
}
defineExpose({
getTableData,
setTableData,
});
</script>

View File

@@ -0,0 +1,493 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicModal
v-bind="$attrs"
:showFooter="true"
:okAuth="'test:testData:edit'"
@register="registerModal"
@ok="handleSubmit"
width="70%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<BasicForm @register="registerForm">
<template #remarks="{ model, field }">
<WangEditor
v-model:value="model[field]"
:bizKey="record.id"
:bizType="'testDataChild_' + field"
:height="300"
/>
</template>
<template #testDataChildList>
<BasicTable @register="registerTestDataChildTable" @row-click="handleTestDataChildRowClick">
<template #testDataChildUpload="{ record: childRecord }">
<BasicUpload
v-model:value="childRecord.dataMap"
:bizKey="childRecord.id"
:bizType="'testDataChild_file'"
:uploadType="'all'"
:loadTime="record.__t"
:size="'small'"
/>
</template>
</BasicTable>
<a-button class="mt-2" @click="handleTestDataChildAdd" v-auth="'test:testData:edit'">
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
</a-button>
</template>
</BasicForm>
</BasicModal>
</template>
<script lang="ts" setup name="ViewsTestTestDataForm">
import { ref, unref, computed } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicTable, useTable } from '@jeesite/core/components/Table';
import { BasicModal, useModalInner } from '@jeesite/core/components/Modal';
import { TestData, testDataSave, testDataForm } from '@jeesite/test/api/test/testData';
import { TestDataChild } from '@jeesite/test/api/test/testDataChild';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { BasicUpload } from '@jeesite/core/components/Upload';
import { WangEditor } from '@jeesite/core/components/WangEditor';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('test.testData');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<TestData>({} as TestData);
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增数据') : t('编辑数据'),
}));
const inputFormSchemas: FormSchema<TestData>[] = [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
componentProps: {
maxlength: 200,
},
required: true,
},
{
label: t('列表选择'),
field: 'testInput2',
fieldLabel: 'testTextarea',
component: 'ListSelect',
componentProps: {
configFile: import('./select'),
checkbox: true,
},
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'InputTextArea',
componentProps: {
maxlength: 200,
},
rules: [{ required: true }],
colProps: { md: 24, lg: 24 },
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
mode: 'multiple',
},
},
{
label: t('单选框'),
field: 'testRadio',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('复选框'),
field: 'testCheckbox',
component: 'CheckboxGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('日期选择'),
field: 'testDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
},
{
label: t('日期时间'),
field: 'testDatetime',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('用户选择'),
field: 'testUser.userCode',
fieldLabel: 'testUser.userName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
},
{
label: t('用户列表选择'),
field: 'testUser.userCode',
component: 'ListSelect',
componentProps: {
selectType: 'empUserSelect',
},
},
{
label: t('机构选择'),
field: 'testOffice.officeCode',
fieldLabel: 'testOffice.officeName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
allowClear: true,
},
},
{
label: t('区域选择'),
field: 'testAreaCode',
fieldLabel: 'testAreaName',
component: 'TreeSelect',
componentProps: {
api: areaTreeData,
allowClear: true,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
slot: 'remarks',
colProps: { md: 24, lg: 24 },
},
{
label: t('图片上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_image',
uploadType: 'image',
// imageMaxWidth: 1024,
// imageMaxHeight: 768,
// imageThumbName: '150x150.jpg',
},
colProps: { md: 24, lg: 24 },
// 文件上传的必填验证实例
// rules: [
// { required: true },
// {
// validator(_rule, value) {
// return new Promise((resolve, reject) => {
// const len = !value || value['testData_image__len'] || 0;
// if (len == 0) reject(t('请上传图片'));
// else resolve();
// });
// },
// },
// ],
},
{
label: t('文件上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_file',
uploadType: 'all',
},
colProps: { md: 24, lg: 24 },
},
{
label: t('子表数据'),
field: 'testDataChildList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'testDataChildList',
},
];
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
const [registerTestDataChildTable, testDataChildTable] = useTable<TestDataChild>({
actionColumn: {
width: 60,
actions: (record: TestDataChild) => [
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleTestDataChildDelete.bind(this, record),
},
auth: 'test:testData:edit',
},
],
},
rowKey: 'id',
pagination: false,
bordered: true,
size: 'small',
inset: true,
});
async function setTestDataChildTableData(_res: Recordable) {
testDataChildTable.setColumns([
{
title: t('单行文本'),
dataIndex: 'testInput',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: true,
},
{
title: t('多行文本'),
dataIndex: 'testTextarea',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: false,
},
{
title: t('下拉框'),
dataIndex: 'testSelect',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
editRule: false,
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
mode: 'multiple',
allowClear: true,
},
editRule: false,
},
{
title: t('日期选择'),
dataIndex: 'testDate',
width: 130,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
editRule: false,
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
width: 215,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
editRule: false,
},
{
title: t('用户选择'),
dataIndex: 'testUser.userCode',
dataLabel: 'testUser.userName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('机构选择'),
dataIndex: 'testOffice.officeCode',
dataLabel: 'testOffice.officeName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('区域选择'),
dataIndex: 'testAreaCode',
dataLabel: 'testAreaName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: areaTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('文件上传'),
dataIndex: 'upload',
width: 160,
align: 'left',
slot: 'testDataChildUpload',
},
]);
testDataChildTable.setTableData(record.value.testDataChildList || []);
}
function handleTestDataChildRowClick(record: Recordable) {
record.onEdit?.(true, false);
}
function handleTestDataChildAdd() {
testDataChildTable.insertTableDataRecord({
id: new Date().getTime(),
isNewRecord: true,
editable: true,
});
}
function handleTestDataChildDelete(record: Recordable) {
testDataChildTable.deleteTableDataRecord(record);
}
async function getTestDataChildList() {
let testDataChildListValid = true;
let testDataChildList: Recordable[] = [];
for (const record of testDataChildTable.getDataSource()) {
if (!(await record.onEdit?.(false, true))) {
testDataChildListValid = false;
}
testDataChildList.push({
...record,
id: !!record.isNewRecord ? '' : record.id,
});
}
for (const record of testDataChildTable.getDelDataSource()) {
if (!!record.isNewRecord) continue;
testDataChildList.push({
...record,
status: '1',
});
}
if (!testDataChildListValid) {
throw { errorFields: [{ name: ['testDataChildList'] }] };
}
return testDataChildList;
}
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ loading: true });
await resetFields();
const res = await testDataForm(data);
record.value = (res.testData || {}) as TestData;
record.value.__t = new Date().getTime();
setFieldsValue(record.value);
setTestDataChildTableData(res);
setModalProps({ loading: false });
});
async function handleSubmit() {
try {
const data = await validate();
setModalProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
id: record.value.id,
};
data.testDataChildList = await getTestDataChildList();
// console.log('submit', params, data, record);
const res = await testDataSave(params, data);
showMessage(res.message);
setTimeout(closeModal);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,582 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<CollapseForm
:config="formConfig"
:loading="loadingRef"
:okLoading="okLoadingRef"
:okAuth="'test:testData:edit'"
@close="handleClose"
@ok="handleSubmit"
>
<template #form1>
<BasicForm @register="registerForm1">
<template #remarks="{ model, field }">
<WangEditor
v-model:value="model[field]"
:bizKey="record.id"
:bizType="'testDataChild_' + field"
:height="300"
/>
</template>
<template #testCheckbox="{ model, field }">
<Form.Item class="inline-block" :name="field">
<CheckboxGroup
:value="model[field]"
@change="
model[field] = $event || ''; // 给表单赋值(写到这里是为了方便演示,可写到一个函数里)
$event && (model[field + 'Other'] = ''); // 不选“无”的时候,清空后面的复选框和输入框
"
:options="[{ label: '无', value: '0' }]"
/>
<div class="ml-3 inline-block"></div>
</Form.Item>
<Form.Item class="inline-block" :name="field">
<CheckboxGroup
:value="model[field]"
@change="model[field] = $event || ''"
:dictType="'sys_menu_type'"
:disabled="model[field] == '0' /* 选择“无”的时候禁用 */"
/>
</Form.Item>
<div class="ml-2 inline-block"></div>
<Form.Item
class="inline-block"
:name="field + 'Other'"
:rules="[
// 如果选择了最后一个复选框,则出现输入框,并启用表单验证
//{ required: (',' + model[field] + ',').indexOf(',2,') != -1, message: '请填写' },
]"
v-show="(',' + model[field] + ',').indexOf(',2,') != -1 /* 是否显示输入框 */"
>
<Input
:value="model[field + 'Other']"
@change="model[field + 'Other'] = $event.target.value"
style="width: 200px"
/>
</Form.Item>
</template>
</BasicForm>
</template>
<template #form2>
<BasicForm @register="registerForm2">
<template #testDataChildList>
<BasicTable @register="registerTestDataChildTable" @row-click="handleTestDataChildRowClick">
<template #testDataChildUpload="{ record: childRecord }">
<BasicUpload
v-model:value="childRecord.dataMap"
:bizKey="childRecord.id"
:bizType="'testDataChild_file'"
:uploadType="'all'"
:loadTime="record.__t"
:size="'small'"
/>
</template>
</BasicTable>
<a-button class="mt-2" @click="handleTestDataChildAdd" v-auth="'test:testData:edit'">
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
</a-button>
</template>
</BasicForm>
</template>
</CollapseForm>
</template>
<script lang="ts" setup name="ViewsTestTestDataFormRoute">
import { ref, unref, computed, onMounted } from 'vue';
import { useEmitter } from '@jeesite/core/store/modules/user';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { CollapseForm } from '@jeesite/core/components/CollapseForm';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicTable, useTable } from '@jeesite/core/components/Table';
import { TestData, testDataSave, testDataForm } from '@jeesite/test/api/test/testData';
import { TestDataChild } from '@jeesite/test/api/test/testDataChild';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { BasicUpload } from '@jeesite/core/components/Upload';
import { WangEditor } from '@jeesite/core/components/WangEditor';
import { useQuery } from '@jeesite/core/hooks/web/usePage';
import { useTabs } from '@jeesite/core/hooks/web/useTabs';
import { CheckboxGroup } from '@jeesite/core/components/Form';
import { Input, Form } from 'ant-design-vue';
const formConfig = ref<any[]>([
{
label: '基础表单',
value: 'form1',
open: true,
},
{
label: '子表列表',
value: 'form2',
open: true,
},
]);
const emitter = useEmitter();
const { t } = useI18n('test.testData');
const { showMessage } = useMessage();
const { setTitle, close } = useTabs(router);
const record = ref<TestData>({} as TestData);
const loadingRef = ref<boolean>(false);
const okLoadingRef = ref<boolean>(false);
const query = useQuery();
const updateTabTitle = () => {
setTitle(record.value.isNewRecord ? t('新增数据') : t('编辑数据'));
};
const inputFormSchemas1: FormSchema<TestData>[] = [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
componentProps: {
maxlength: 200,
},
required: true,
},
{
label: t('列表选择'),
field: 'testInput2',
fieldLabel: 'testTextarea',
component: 'ListSelect',
componentProps: {
configFile: import('./select'),
checkbox: true,
},
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'InputTextArea',
componentProps: {
maxlength: 200,
},
rules: [{ required: true }],
colProps: { md: 24, lg: 24 },
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
mode: 'multiple',
},
},
{
label: t('单选框'),
field: 'testRadio',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('复选框'),
field: 'testCheckbox',
// component: 'CheckboxGroup',
// componentProps: {
// dictType: 'sys_menu_type',
// },
component: 'Input',
slot: 'testCheckbox',
},
{
label: t('日期选择'),
field: 'testDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
},
{
label: t('日期时间'),
field: 'testDatetime',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('用户选择'),
field: 'testUser.userCode',
fieldLabel: 'testUser.userName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
},
{
label: t('用户列表选择'),
field: 'testUser.userCode',
component: 'ListSelect',
componentProps: {
selectType: 'empUserSelect',
checkbox: true,
},
},
{
label: t('机构选择'),
field: 'testOffice.officeCode',
fieldLabel: 'testOffice.officeName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
allowClear: true,
},
},
{
label: t('区域选择'),
field: 'testAreaCode',
fieldLabel: 'testAreaName',
component: 'TreeSelect',
componentProps: {
api: areaTreeData,
allowClear: true,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
slot: 'remarks',
colProps: { md: 24, lg: 24 },
},
{
label: t('图片上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_image',
uploadType: 'image',
// imageMaxWidth: 1024,
// imageMaxHeight: 768,
// imageThumbName: '150x150.jpg',
},
colProps: { md: 24, lg: 24 },
// 文件上传的必填验证实例
// rules: [
// { required: true },
// {
// validator(_rule, value) {
// return new Promise((resolve, reject) => {
// const len = !value || value['testData_image__len'] || 0;
// if (len == 0) reject(t('请上传图片'));
// else resolve();
// });
// },
// },
// ],
},
{
label: t('文件上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_file',
uploadType: 'all',
},
colProps: { md: 24, lg: 24 },
},
];
const [registerForm1, formAction1] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas1,
baseColProps: { md: 24, lg: 12 },
});
const inputFormSchemas2: FormSchema<TestData>[] = [
{
field: 'testDataChildList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'testDataChildList',
},
];
const [registerForm2, formAction2] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas2,
baseColProps: { md: 24, lg: 12 },
});
const [registerTestDataChildTable, testDataChildTable] = useTable<TestDataChild>({
actionColumn: {
width: 60,
actions: (record: TestDataChild) => [
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleTestDataChildDelete.bind(this, record),
},
auth: 'test:testData:edit',
},
],
},
rowKey: 'id',
pagination: false,
bordered: true,
size: 'small',
inset: true,
});
async function setTestDataChildTableData(_res: Recordable) {
testDataChildTable.setColumns([
{
title: t('单行文本'),
dataIndex: 'testInput',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: true,
},
{
title: t('多行文本'),
dataIndex: 'testTextarea',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: false,
},
{
title: t('下拉框'),
dataIndex: 'testSelect',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
editRule: false,
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
mode: 'multiple',
allowClear: true,
},
editRule: false,
},
{
title: t('日期选择'),
dataIndex: 'testDate',
width: 130,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
editRule: false,
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
width: 215,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
editRule: false,
},
{
title: t('用户选择'),
dataIndex: 'testUser.userCode',
dataLabel: 'testUser.userName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('机构选择'),
dataIndex: 'testOffice.officeCode',
dataLabel: 'testOffice.officeName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('区域选择'),
dataIndex: 'testAreaCode',
dataLabel: 'testAreaName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: areaTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('文件上传'),
dataIndex: 'upload',
width: 160,
align: 'left',
slot: 'testDataChildUpload',
},
]);
testDataChildTable.setTableData(record.value.testDataChildList || []);
}
function handleTestDataChildRowClick(record: Recordable) {
record.onEdit?.(true, false);
}
function handleTestDataChildAdd() {
testDataChildTable.insertTableDataRecord({
id: new Date().getTime(),
isNewRecord: true,
editable: true,
});
}
function handleTestDataChildDelete(record: Recordable) {
testDataChildTable.deleteTableDataRecord(record);
}
async function getTestDataChildList() {
let testDataChildListValid = true;
let testDataChildList: Recordable[] = [];
for (const record of testDataChildTable.getDataSource()) {
if (!(await record.onEdit?.(false, true))) {
testDataChildListValid = false;
}
testDataChildList.push({
...record,
id: !!record.isNewRecord ? '' : record.id,
});
}
for (const record of testDataChildTable.getDelDataSource()) {
if (!!record.isNewRecord) continue;
testDataChildList.push({
...record,
status: '1',
});
}
if (!testDataChildListValid) {
throw { errorFields: [{ name: ['testDataChildList'] }] };
}
return testDataChildList;
}
async function resetFields() {
await formAction1.resetFields();
await formAction2.resetFields();
}
async function setFieldsValue(values: Recordable) {
await formAction1.setFieldsValue(values);
await formAction2.setFieldsValue(values);
}
async function validate(): Promise<Recordable> {
return Object.assign(await formAction1.validate(), await formAction2.validate());
}
onMounted(async () => {
loadingRef.value = true;
await resetFields();
const res = await testDataForm(unref(query));
record.value = (res.testData || {}) as TestData;
record.value.__t = new Date().getTime();
setFieldsValue(record.value);
setTestDataChildTableData(res);
updateTabTitle();
loadingRef.value = false;
});
function handleClose() {
setTimeout(close);
}
async function handleSubmit() {
try {
okLoadingRef.value = true;
const data = await validate();
const params: any = {
isNewRecord: record.value.isNewRecord,
id: record.value.id,
};
data.testDataChildList = await getTestDataChildList();
// console.log('submit', params, data, record);
const res = await testDataSave(params, data);
showMessage(res.message);
emitter.emit('test-testData-reload');
handleClose();
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
okLoadingRef.value = false;
}
}
</script>

View File

@@ -0,0 +1,516 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:okAuth="'test:testData:edit'"
@register="registerDrawer"
@ok="handleSubmit"
width="70%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<Tabs v-model:activeKey="activeKey" tabPosition="left">
<Tabs.TabPane key="1" :forceRender="true" tab="基本信息">
<BasicForm @register="registerForm1">
<template #remarks="{ model, field }">
<WangEditor
v-model:value="model[field]"
:bizKey="record.id"
:bizType="'testDataChild_' + field"
:height="300"
/>
</template>
</BasicForm>
</Tabs.TabPane>
<Tabs.TabPane key="2" :forceRender="true" tab="详细信息">
<BasicForm @register="registerForm2">
<template #testDataChildList>
<BasicTable @register="registerTestDataChildTable" @row-click="handleTestDataChildRowClick">
<template #testDataChildUpload="{ record: childRecord }">
<BasicUpload
v-model:value="childRecord.dataMap"
:bizKey="childRecord.id"
:bizType="'testDataChild_file'"
:uploadType="'all'"
:loadTime="record.__t"
:size="'small'"
/>
</template>
</BasicTable>
<a-button class="mt-2" @click="handleTestDataChildAdd" v-auth="'test:testData:edit'">
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
</a-button>
</template>
</BasicForm>
</Tabs.TabPane>
</Tabs>
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsTestTestDataForm">
import { ref, unref, computed, h } from 'vue';
import { Tabs } from 'ant-design-vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicTable, useTable } from '@jeesite/core/components/Table';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { TestData, testDataSave, testDataForm } from '@jeesite/test/api/test/testData';
import { TestDataChild } from '@jeesite/test/api/test/testDataChild';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { BasicUpload } from '@jeesite/core/components/Upload';
import { WangEditor } from '@jeesite/core/components/WangEditor';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('test.testData');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<TestData>({} as TestData);
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增数据') : t('编辑数据'),
}));
const activeKey = ref<string>('1');
const inputFormSchemas1: FormSchema<TestData>[] = [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
componentProps: {
maxlength: 200,
},
required: true,
},
{
field: 'testInputHelpMessage',
component: 'Text',
render: () => h('div', { class: 'ml-4 text-gray-500' }, '这是栅格帮助信息,优点是可以对齐。'),
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'InputTextArea',
componentProps: {
maxlength: 200,
},
suffix: h('div', { class: 'ml-4 text-gray-500' }, 'Suffix帮助信息优点是自由嵌入组件后。'),
rules: [{ required: true }],
colProps: { md: 24, lg: 24 },
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
mode: 'multiple',
},
},
{
label: t('单选框'),
field: 'testRadio',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('复选框'),
field: 'testCheckbox',
component: 'CheckboxGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('日期选择'),
field: 'testDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
},
{
label: t('日期时间'),
field: 'testDatetime',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('用户选择'),
field: 'testUser.userCode',
fieldLabel: 'testUser.userName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
},
{
label: t('用户列表选择'),
field: 'testUser.userCode',
component: 'ListSelect',
componentProps: {
selectType: 'empUserSelect',
},
},
{
label: t('机构选择'),
field: 'testOffice.officeCode',
fieldLabel: 'testOffice.officeName',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
allowClear: true,
},
},
{
label: t('区域选择'),
field: 'testAreaCode',
fieldLabel: 'testAreaName',
component: 'TreeSelect',
componentProps: {
api: areaTreeData,
allowClear: true,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
slot: 'remarks',
colProps: { md: 24, lg: 24 },
},
];
const inputFormSchemas2: FormSchema<TestData>[] = [
{
label: t('图片上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_image',
uploadType: 'image',
},
suffix: [
h('div', { class: 'ml-4 text-gray-500' }, '请上传图片格式,文件小于 5 M。'),
h('a', { href: 'https://jeesite.com', target: '_blank', class: 'mr-8' }, '查看模板'),
],
colProps: { md: 24, lg: 24 },
},
{
label: t('文件上传'),
field: 'dataMap',
component: 'Upload',
componentProps: {
loadTime: computed(() => record.value.__t),
bizKey: computed(() => record.value.id),
bizType: 'testData_file',
uploadType: 'all',
},
suffix: [
h('div', { class: 'ml-4 text-gray-500' }, '请上传文档格式,文件小于 5 M。'),
h('a', { href: 'https://jeesite.com', target: '_blank', class: 'mr-8' }, '查看模板'),
],
colProps: { md: 24, lg: 24 },
},
{
label: t('子表数据'),
field: 'testDataChildList',
component: 'Input',
colProps: { md: 24, lg: 24 },
slot: 'testDataChildList',
},
];
const [registerForm1, formAction1] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas1,
baseColProps: { md: 24, lg: 12 },
});
const [registerForm2, formAction2] = useForm<TestData>({
labelWidth: 120,
schemas: inputFormSchemas2,
baseColProps: { md: 24, lg: 12 },
});
async function resetFields() {
activeKey.value = '1';
await formAction1.resetFields();
await formAction2.resetFields();
}
async function setFieldsValue(values: Recordable) {
await formAction1.setFieldsValue(values);
await formAction2.setFieldsValue(values);
}
async function validate(): Promise<Recordable> {
return Object.assign(await formAction1.validate(), await formAction2.validate());
}
const [registerTestDataChildTable, testDataChildTable] = useTable<TestDataChild>({
actionColumn: {
width: 60,
actions: (record: Recordable) => [
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleTestDataChildDelete.bind(this, record),
},
auth: 'test:testData:edit',
},
],
},
rowKey: 'id',
pagination: false,
bordered: true,
size: 'small',
inset: true,
scroll: { x: 1000 },
});
async function setTestDataChildTableData(_res: Recordable) {
testDataChildTable.setColumns([
{
title: t('单行文本'),
dataIndex: 'testInput',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: true,
},
{
title: t('多行文本'),
dataIndex: 'testTextarea',
width: 130,
align: 'left',
editRow: true,
editComponent: 'Input',
editRule: false,
},
{
title: t('下拉框'),
dataIndex: 'testSelect',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
editRule: false,
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
width: 130,
align: 'left',
dictType: 'sys_menu_type',
editRow: true,
editComponent: 'Select',
editComponentProps: {
dictType: 'sys_menu_type',
mode: 'multiple',
allowClear: true,
},
editRule: false,
},
{
title: t('日期选择'),
dataIndex: 'testDate',
width: 130,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
editRule: false,
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
width: 215,
align: 'center',
editRow: true,
editComponent: 'DatePicker',
editComponentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
editRule: false,
},
{
title: t('用户选择'),
dataIndex: 'testUser.userCode',
dataLabel: 'testUser.userName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('机构选择'),
dataIndex: 'testOffice.officeCode',
dataLabel: 'testOffice.officeName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: officeTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('区域选择'),
dataIndex: 'testAreaCode',
dataLabel: 'testAreaName',
width: 130,
align: 'left',
editRow: true,
editComponent: 'TreeSelect',
editComponentProps: {
api: areaTreeData,
canSelectParent: false,
allowClear: true,
},
editRule: false,
},
{
title: t('文件上传'),
dataIndex: 'upload',
width: 160,
align: 'left',
slot: 'testDataChildUpload',
},
]);
testDataChildTable.setTableData(record.value.testDataChildList || []);
}
function handleTestDataChildRowClick(record: Recordable) {
record.onEdit?.(true, false);
}
function handleTestDataChildAdd() {
testDataChildTable.insertTableDataRecord({
id: new Date().getTime(),
isNewRecord: true,
editable: true,
});
}
function handleTestDataChildDelete(record: Recordable) {
testDataChildTable.deleteTableDataRecord(record);
}
async function getTestDataChildList() {
let testDataChildListValid = true;
let testDataChildList: Recordable[] = [];
for (const record of testDataChildTable.getDataSource()) {
if (!(await record.onEdit?.(false, true))) {
testDataChildListValid = false;
}
testDataChildList.push({
...record,
id: !!record.isNewRecord ? '' : record.id,
});
}
for (const record of testDataChildTable.getDelDataSource()) {
if (!!record.isNewRecord) continue;
testDataChildList.push({
...record,
status: '1',
});
}
if (!testDataChildListValid) {
throw { errorFields: [{ name: ['testDataChildList'] }] };
}
return testDataChildList;
}
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
await resetFields();
const res = await testDataForm(data);
record.value = (res.testData || {}) as TestData;
record.value.__t = new Date().getTime();
setFieldsValue(record.value);
setTestDataChildTableData(res);
setDrawerProps({ loading: false });
});
async function handleSubmit() {
try {
const data = await validate();
setDrawerProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
id: record.value.id,
};
data.testDataChildList = await getTestDataChildList();
// console.log('submit', params, data, record);
const res = await testDataSave(params, data);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,505 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #toolbar>
<a-button type="primary" @click="handleForm({})" v-auth="'test:testData:edit'">
<Icon icon="i-fluent:add-12-filled" /> {{ t('新增') }}
</a-button>
</template>
<template #firstColumn="{ record }">
<a @click="handleForm({ id: record.id })">
{{ record.testInput }}
</a>
</template>
<template #expandedRowRender="{ record }">
<div>编号: {{ record.id }}这里生成内容自定义也可以加载子表</div>
</template>
<template #customFilterIcon="filter">
<Icon icon="i-ant-design:search-outlined" :style="{ color: filter.filtered ? '#108ee9' : undefined }" />
</template>
<template #customFilterDropdown="filter">
<div class="p-2" v-if="filter.column.dataIndex == 'testInput'">
<a-input
ref="searchInput"
:placeholder="t('搜索单行文本')"
:value="filter.selectedKeys[0]"
class="!w-168px !mb-8px !block"
@change="(e: any) => filter.setSelectedKeys(e.target.value ? [e.target.value] : [])"
/>
<a-button type="primary" size="small" class="mr-2 w-20" @click="filter.confirm()">
{{ t('确定') }}
</a-button>
<a-button
size="small"
class="w-20"
@click="
filter.clearFilters();
filter.confirm();
"
>
{{ t('重置') }}
</a-button>
</div>
</template>
</BasicTable>
<InputForm @register="registerDrawer" @success="handleSuccess" />
<InputFormTabs @register="registerDrawerTabs" @success="handleSuccess" />
<InputFormModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup name="ViewsTestTestDataList">
import { unref, h } from 'vue';
import { useEmitter } from '@jeesite/core/store/modules/user';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { useGo } from '@jeesite/core/hooks/web/usePage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { TestData, testDataDelete, testDataListData } from '@jeesite/test/api/test/testData';
import { testDataDisable, testDataEnable } from '@jeesite/test/api/test/testData';
import { officeTreeData } from '@jeesite/core/api/sys/office';
import { areaTreeData } from '@jeesite/core/api/sys/area';
import { useDrawer } from '@jeesite/core/components/Drawer';
import { useModal } from '@jeesite/core/components/Modal';
import { FormProps } from '@jeesite/core/components/Form';
import InputForm from './form.vue';
import InputFormTabs from './formTabs.vue';
import InputFormModal from './formModal.vue';
// import { dateUtil, formatToDateTime } from '@jeesite/core/utils/dateUtil';
const emitter = useEmitter();
const { t } = useI18n('test.testData');
const { meta } = unref(router.currentRoute);
const { showMessage } = useMessage();
const getTitle = {
icon: meta.icon || 'ant-design:book-outlined',
value: meta.title || t('数据管理'),
};
const go = useGo();
const searchForm: FormProps<TestData> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'Input',
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
},
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Select',
componentProps: {
dictType: 'sys_menu_type',
allowClear: true,
mode: 'multiple',
},
},
{
label: t('单选框'),
field: 'testRadio',
component: 'RadioGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('复选框'),
field: 'testCheckbox',
component: 'CheckboxGroup',
componentProps: {
dictType: 'sys_menu_type',
},
},
{
label: t('日期选择起'),
field: 'testDate_gte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
// defaultValue: dateUtil(new Date()),
// defaultValue: formatToDateTime(new Date()),
// defaultValue: '2024-05-31',
},
{
label: t('日期选择止'),
field: 'testDate_lte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
showTime: false,
},
},
{
label: t('用户选择'),
field: 'testUser.userCode',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
params: { isLoadUser: true, userIdPrefix: '' },
canSelectParent: false,
allowClear: true,
},
},
{
label: t('机构选择'),
field: 'testOffice.officeCode',
component: 'TreeSelect',
componentProps: {
api: officeTreeData,
allowClear: true,
},
},
{
label: t('区域选择'),
field: 'testAreaCode',
component: 'TreeSelect',
componentProps: {
api: areaTreeData,
allowClear: true,
},
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'sys_search_status',
allowClear: true,
onChange: handleSuccess,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'Input',
},
{
label: t('日期时间'),
field: 'dateRange',
component: 'RangePicker',
},
],
fieldMapToTime: [['dateRange', ['testDatetime_gte', 'testDatetime_lte']]],
};
const tableColumns: BasicColumn<TestData>[] = [
{
// title: t('单行文本'),
// title: h('font', { color: '#f00' }, '单行文本'),
title: [h('font', { color: '#cf0202' }, '单行'), h('font', { color: '#25b110' }, '文本')],
dataIndex: 'testInput',
key: 'a.test_input',
sorter: true,
width: 130,
align: 'center',
slot: 'firstColumn',
// 方式一:简单配置过滤窗口
// filters: [
// { text: 'Male', value: '1' },
// { text: 'Female', value: '2' },
// ],
// filterMultiple: true,
// onFilter: (value: any, record: TestData) => {
// console.log('onFilter', value, record);
// return record.userName === value;
// },
// 方式一:简单配置过滤窗口
customFilterDropdown: true,
},
{
title: t('多行文本'),
dataIndex: 'testTextarea',
key: 'a.test_textarea',
sorter: true,
width: 130,
align: 'left',
// 根据数据状态改变单元格的颜色(例子)
customCell: (record: Recordable) => {
const color = record.status === '2' ? '#f8d8d8' : '';
return {
// innerHTML: record.testTextarea, // 原样输出不进行html编码例子
style: `background-color: ${color} !important`,
};
},
},
{
title: t('下拉框'),
dataIndex: 'testSelect',
key: 'a.test_select',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_menu_type',
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
key: 'a.test_select_multiple',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_menu_type',
},
{
title: t('单选框'),
dataIndex: 'testRadio',
key: 'a.test_radio',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_menu_type',
},
{
title: t('复选框'),
dataIndex: 'testCheckbox',
key: 'a.test_checkbox',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_menu_type',
},
{
title: t('日期选择'),
dataIndex: 'testDate',
key: 'a.test_date',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
key: 'a.test_datetime',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('用户选择'),
dataIndex: 'testUser.userName',
key: 'a.test_user_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('机构选择'),
dataIndex: 'testOffice.officeName',
key: 'a.test_office_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('区域选择'),
dataIndex: 'testAreaName',
key: 'a.test_area_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_status',
},
{
title: t('更新时间'),
dataIndex: 'updateDate',
key: 'a.update_date',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('备注信息'),
dataIndex: 'remarks',
key: 'a.remarks',
sorter: true,
width: 130,
align: 'left',
},
];
const actionColumn: BasicColumn<TestData> = {
width: 250,
actions: (record: TestData) => [
{
icon: 'i-clarity:note-edit-line',
title: t('抽屉模式编辑'),
onClick: handleForm.bind(this, { id: record.id }),
auth: 'test:testData:edit',
},
{
icon: 'i-clarity:timeline-line',
title: t('表单页签编辑'),
onClick: handleFormTabs.bind(this, { id: record.id }),
auth: 'test:testData:edit',
},
{
icon: 'i-ant-design:file-markdown-outlined',
title: t('弹窗模式编辑'),
onClick: handleFormModal.bind(this, { id: record.id }),
auth: 'test:testData:edit',
},
{
icon: 'i-ant-design:layout-outlined',
title: t('路由模式编辑'),
onClick: handleFormRoute.bind(this, { id: record.id }),
auth: 'test:testData:edit',
},
{
icon: 'i-ant-design:stop-outlined',
color: 'error',
title: t('停用数据'),
popConfirm: {
title: t('是否确认停用数据'),
confirm: handleDisable.bind(this, { id: record.id }),
},
auth: 'test:testData:edit',
ifShow: () => record.status === '0',
},
{
icon: 'i-ant-design:check-circle-outlined',
color: 'success',
title: t('启用数据'),
popConfirm: {
title: t('是否确认启用数据'),
confirm: handleEnable.bind(this, { id: record.id }),
},
auth: 'test:testData:edit',
ifShow: () => record.status === '2',
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
title: t('删除数据'),
popConfirm: {
title: t('是否确认删除数据'),
confirm: handleDelete.bind(this, { id: record.id }),
},
auth: 'test:testData:edit',
},
],
};
const [registerDrawer, { openDrawer }] = useDrawer();
const [registerDrawerTabs, { openDrawer: openDrawerTabs }] = useDrawer();
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload /*, getForm*/ }] = useTable<TestData>({
api: testDataListData,
beforeFetch: (params) => {
// 查询前增加默认条件(例子)
// params.testDate_gte = '2022-05-31';
// getForm().setFieldsValue(params);
return params;
},
columns: tableColumns,
actionColumn: actionColumn,
formConfig: searchForm,
showTableSetting: true,
useSearchForm: true,
canResize: true,
// 设置为true可以通过点击行来展开 expandedRowRender 插槽(例子)
expandRowByClick: false,
// 给单行文本列标题上添加一个过滤按钮,提交到后台过滤(例子)
filterFn: (data: Partial<Recordable<string[]>>) => {
const testInput = 'a.test_input';
if (data[testInput]) {
data['testInput'] = data[testInput]?.join(',') as any;
delete data[testInput];
}
console.log(data);
return data;
},
// 根据数据状态改变行的颜色(例子)
rowClassName: (record: Recordable) => {
return record.status === '2' ? 'table-tr-red' : '';
},
});
function handleForm(record: Recordable) {
openDrawer(true, record);
}
function handleFormTabs(record: Recordable) {
openDrawerTabs(true, record);
}
function handleFormModal(record: Recordable) {
openModal(true, record);
}
function handleFormRoute(record: Recordable) {
go({
path: '/test/testData/formRoute',
query: record,
});
}
async function handleDisable(record: Recordable) {
const res = await testDataDisable(record);
showMessage(res.message);
handleSuccess();
}
async function handleEnable(record: Recordable) {
const res = await testDataEnable(record);
showMessage(res.message);
handleSuccess();
}
async function handleDelete(record: Recordable) {
const res = await testDataDelete(record);
showMessage(res.message);
handleSuccess();
}
function handleSuccess() {
reload();
}
emitter.on('test-testData-reload', reload, true);
</script>
<style lang="less">
.table-tr-red {
td {
background-color: #fde7e7 !important;
}
}
</style>

View File

@@ -0,0 +1,242 @@
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table';
import { TestData, testDataListData } from '@jeesite/test/api/test/testData';
const { t } = useI18n('sys.testData');
const modalProps = {
title: t('测试数据选择'),
};
const searchForm: FormProps<TestData> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('单行文本'),
field: 'testInput',
component: 'Input',
},
{
label: t('多行文本'),
field: 'testTextarea',
component: 'Input',
},
{
label: t('下拉框'),
field: 'testSelect',
component: 'Input',
},
{
label: t('下拉多选'),
field: 'testSelectMultiple',
component: 'Input',
},
{
label: t('单选框'),
field: 'testRadio',
component: 'Input',
},
{
label: t('复选框'),
field: 'testCheckbox',
component: 'Input',
},
{
label: t('日期选择'),
field: 'testDate',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('日期时间'),
field: 'testDatetime',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('用户选择'),
field: 'testUserCode',
component: 'Input',
},
{
label: t('机构选择'),
field: 'testOfficeCode',
component: 'Input',
},
{
label: t('区域选择'),
field: 'testAreaCode',
component: 'Input',
},
{
label: t('区域名称'),
field: 'testAreaName',
component: 'Input',
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'sys_search_status',
allowClear: true,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'Input',
},
],
};
const tableColumns: BasicColumn<TestData>[] = [
{
title: t('单行文本'),
dataIndex: 'testInput',
key: 'a.test_input',
sorter: true,
width: 230,
align: 'left',
slot: 'firstColumn',
},
// {
// title: t('多行文本'),
// dataIndex: 'testTextarea',
// key: 'a.test_textarea',
// sorter: true,
// width: 130,
// align: 'left',
// },
{
title: t('下拉框'),
dataIndex: 'testSelect',
key: 'a.test_select',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('下拉多选'),
dataIndex: 'testSelectMultiple',
key: 'a.test_select_multiple',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('单选框'),
dataIndex: 'testRadio',
key: 'a.test_radio',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('复选框'),
dataIndex: 'testCheckbox',
key: 'a.test_checkbox',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('日期选择'),
dataIndex: 'testDate',
key: 'a.test_date',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('日期时间'),
dataIndex: 'testDatetime',
key: 'a.test_datetime',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('用户选择'),
dataIndex: 'testUserCode',
key: 'a.test_user_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('机构选择'),
dataIndex: 'testOfficeCode',
key: 'a.test_office_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('区域选择'),
dataIndex: 'testAreaCode',
key: 'a.test_area_code',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('区域名称'),
dataIndex: 'testAreaName',
key: 'a.test_area_name',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 130,
align: 'center',
dictType: 'sys_search_status',
},
{
title: t('更新时间'),
dataIndex: 'updateDate',
key: 'a.update_date',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('备注信息'),
dataIndex: 'remarks',
key: 'a.remarks',
sorter: true,
width: 130,
align: 'left',
},
];
const tableProps: BasicTableProps<TestData> = {
api: testDataListData,
beforeFetch: (params) => {
params['isAll'] = true;
return params;
},
columns: tableColumns,
formConfig: searchForm,
rowKey: 'id',
};
export default {
modalProps,
tableProps,
itemCode: 'id',
itemName: 'testInput',
isShowCode: false,
};

View File

@@ -0,0 +1,153 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<BasicDrawer
v-bind="$attrs"
:showFooter="true"
:okAuth="'test:testTree:edit'"
@register="registerDrawer"
@ok="handleSubmit"
width="70%"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<BasicForm @register="registerForm" />
</BasicDrawer>
</template>
<script lang="ts" setup name="ViewsTestTestTreeForm">
import { ref, unref, computed } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicForm, FormSchema, useForm } from '@jeesite/core/components/Form';
import { BasicDrawer, useDrawerInner } from '@jeesite/core/components/Drawer';
import { TestTree, testTreeSave, testTreeForm, testTreeTreeData } from '@jeesite/test/api/test/testTree';
const emit = defineEmits(['success', 'register']);
const { t } = useI18n('test.testTree');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const record = ref<TestTree>({} as TestTree);
const getTitle = computed(() => ({
icon: meta.icon || 'ant-design:book-outlined',
value: record.value.isNewRecord ? t('新增数据') : t('编辑数据'),
}));
const inputFormSchemas: FormSchema<TestTree>[] = [
{
label: t('上级数据'),
field: 'parentCode',
fieldLabel: 'parentName',
component: 'TreeSelect',
componentProps: {
allowClear: true,
},
},
{
label: t('节点编码'),
field: 'treeCode',
component: 'Input',
componentProps: {
maxlength: 64,
},
rules: [{ required: true }, { pattern: /^[a-zA-Z0-9_]*$/, message: t('请输入字母数字下划线') }],
},
{
label: t('节点名称'),
field: 'treeName',
component: 'Input',
componentProps: {
maxlength: 200,
},
required: true,
},
{
label: t('排序号'),
field: 'treeSort',
helpMessage: '升序',
component: 'InputNumber',
componentProps: {
maxlength: 10,
},
defaultValue: '30',
required: true,
},
{
label: t('备注信息'),
field: 'remarks',
component: 'InputTextArea',
componentProps: {
maxlength: 500,
},
colProps: { md: 24, lg: 24 },
},
];
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm<TestTree>({
labelWidth: 120,
schemas: inputFormSchemas,
baseColProps: { md: 24, lg: 12 },
});
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
setDrawerProps({ loading: true });
await resetFields();
const res = await testTreeForm(data);
record.value = (res.testTree || {}) as TestTree;
if (data.parentCode && data.parentName) {
record.value.parentCode = data.parentCode;
record.value.parentName = data.parentName;
}
setFieldsValue(record.value);
updateSchema([
{
field: 'parentCode',
componentProps: {
api: testTreeTreeData,
params: {
excludeCode: record.value.id,
isShowRawName: true,
},
},
},
{
field: 'treeCode',
componentProps: {
disabled: !record.value.isNewRecord,
},
},
]);
setDrawerProps({ loading: false });
});
async function handleSubmit() {
try {
const data = await validate();
setDrawerProps({ confirmLoading: true });
const params: any = {
isNewRecord: record.value.isNewRecord,
treeCode: record.value.treeCode,
};
data.oldParentCode = record.value.parentCode;
// console.log('submit', params, data, record);
const res = await testTreeSave(params, data);
showMessage(res.message);
setTimeout(closeDrawer);
emit('success', data);
} catch (error: any) {
if (error && error.errorFields) {
showMessage(error.message || t('common.validateError'));
}
console.log('error', error);
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,32 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<PageWrapper :sidebarWidth="230">
<template #sidebar>
<BasicTree
:title="t('数据')"
:search="true"
:toolbar="true"
:showIcon="true"
:api="testTreeTreeData"
:defaultExpandLevel="2"
v-model:selectedKeys="treeCodes"
/>
</template>
<ListView v-model:treeCodes="treeCodes" />
</PageWrapper>
</template>
<script lang="ts" setup name="ViewsTestTestTreeIndex">
import { ref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { PageWrapper } from '@jeesite/core/components/Page';
import { BasicTree } from '@jeesite/core/components/Tree';
import { testTreeTreeData } from '@jeesite/test/api/test/testTree';
import ListView from './list.vue';
const { t } = useI18n('sys.testTree');
const treeCodes = ref<string[]>([]);
</script>

View File

@@ -0,0 +1,240 @@
<!--
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<div>
<BasicTable @register="registerTable" @fetch-success="fetchSuccess">
<template #tableTitle>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<template #toolbar>
<a-button @click="expandAll" :title="t('展开一级')">
<Icon icon="i-bi:chevron-double-down" /> {{ t('展开') }}
</a-button>
<a-button @click="collapseAll" :title="t('折叠全部')">
<Icon icon="i-bi:chevron-double-up" /> {{ t('折叠') }}
</a-button>
<a-button type="primary" @click="handleForm({})" v-auth="'test:testTree:edit'">
<Icon icon="i-fluent:add-12-filled" /> {{ t('新增') }}
</a-button>
</template>
<template #firstColumn="{ record }">
<span class="cursor-pointer" @click="expandCollapse(record)"> ( {{ record.treeCode }} ) </span>
<a @click="handleForm({ treeCode: record.treeCode })">
{{ record.treeName }}
</a>
</template>
</BasicTable>
<InputForm @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup name="ViewsTestTestTreeList">
import { watch, nextTick, unref } from 'vue';
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { router } from '@jeesite/core/router';
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { TestTree, testTreeDelete, testTreeListData } from '@jeesite/test/api/test/testTree';
import { testTreeDisable, testTreeEnable } from '@jeesite/test/api/test/testTree';
import { useDrawer } from '@jeesite/core/components/Drawer';
import { FormProps } from '@jeesite/core/components/Form';
import { isEmpty } from '@jeesite/core/utils/is';
import InputForm from './form.vue';
const props = defineProps({
treeCodes: Array as PropType<String[]>,
});
const emit = defineEmits(['update:treeCodes']);
const { t } = useI18n('test.testTree');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const getTitle = {
icon: meta.icon || 'ant-design:book-outlined',
value: meta.title || t('数据管理'),
};
const searchForm: FormProps<TestTree> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('节点名称'),
field: 'treeName',
component: 'Input',
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'sys_search_status',
allowClear: true,
onChange: handleSuccess,
},
},
{
label: t('备注信息'),
field: 'remarks',
component: 'Input',
},
],
resetFunc: async () => {
emit('update:treeCodes', []);
},
};
const tableColumns: BasicColumn<TestTree>[] = [
{
title: t('节点名称'),
dataIndex: 'treeName',
width: 230,
align: 'left',
slot: 'firstColumn',
},
{
title: t('排序号'),
dataIndex: 'treeSort',
width: 130,
align: 'center',
},
{
title: t('状态'),
dataIndex: 'status',
width: 130,
align: 'center',
dictType: 'sys_status',
},
{
title: t('更新时间'),
dataIndex: 'updateDate',
width: 130,
align: 'center',
},
{
title: t('备注信息'),
dataIndex: 'remarks',
width: 130,
align: 'left',
},
];
const actionColumn: BasicColumn<TestTree> = {
width: 160,
actions: (record: TestTree) => [
{
icon: 'i-clarity:note-edit-line',
title: t('编辑数据'),
onClick: handleForm.bind(this, { treeCode: record.treeCode }),
auth: 'test:testTree:edit',
},
{
icon: 'i-ant-design:stop-outlined',
color: 'error',
title: t('停用数据'),
popConfirm: {
title: t('是否确认停用数据'),
confirm: handleDisable.bind(this, record),
},
auth: 'test:testTree:edit',
ifShow: () => record.status === '0',
},
{
icon: 'i-ant-design:check-circle-outlined',
color: 'success',
title: t('启用数据'),
popConfirm: {
title: t('是否确认启用数据'),
confirm: handleEnable.bind(this, record),
},
auth: 'test:testTree:edit',
ifShow: () => record.status === '2',
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
title: t('删除数据'),
popConfirm: {
title: t('是否确认删除数据'),
confirm: handleDelete.bind(this, record),
},
auth: 'test:testTree:edit',
},
{
icon: 'i-fluent:add-circle-24-regular',
title: t('新增下级数据'),
onClick: handleForm.bind(this, {
parentCode: record.treeCode,
parentName: record.treeName,
}),
auth: 'test:testTree:edit',
},
],
};
const [registerDrawer, { openDrawer }] = useDrawer();
const [registerTable, { reload, expandAll, collapseAll, expandCollapse }] = useTable<TestTree>({
api: testTreeListData,
beforeFetch: (params) => {
params.id = !isEmpty(props.treeCodes) ? props.treeCodes[0] : '';
return params;
},
columns: tableColumns,
actionColumn: actionColumn,
formConfig: searchForm,
showTableSetting: true,
useSearchForm: true,
isTreeTable: true,
pagination: false,
canResize: true,
});
watch(
() => props.treeCodes,
() => {
if (!isEmpty(props.treeCodes)) {
reload();
}
},
);
function fetchSuccess() {
if (!isEmpty(props.treeCodes)) {
nextTick(expandAll);
}
}
function handleForm(record: Recordable) {
openDrawer(true, record);
}
async function handleDisable(record: Recordable) {
const params = { treeCode: record.treeCode };
const res = await testTreeDisable(params);
showMessage(res.message);
handleSuccess(record);
}
async function handleEnable(record: Recordable) {
const params = { treeCode: record.treeCode };
const res = await testTreeEnable(params);
showMessage(res.message);
handleSuccess(record);
}
async function handleDelete(record: Recordable) {
const params = { treeCode: record.treeCode };
const res = await testTreeDelete(params);
showMessage(res.message);
handleSuccess(record);
}
function handleSuccess(record: Recordable) {
reload({ record });
}
</script>