项目初始化

This commit is contained in:
2026-03-19 10:57:24 +08:00
commit ee94d420ad
3822 changed files with 582614 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
- 官方网站:<https://jeesite.com>
- 使用文档:<https://jeesite.com/docs>
- 后端代码:<https://gitee.com/thinkgem/jeesite5>
- 前端代码:<https://gitee.com/thinkgem/jeesite-vue>
------
<div align="center">
如果你喜欢 JeeSite请给她一个 ⭐️ Star您的支持将是我们前行的动力。
</div>
------
- 问题反馈:<https://gitee.com/thinkgem/jeesite-vue/issues> [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
- 需求收集:<https://gitee.com/thinkgem/jeesite-vue/issues/new>
- QQ 群:`127515876``209330483``223507718``709534275``730390092``1373527``183903863(外包)`
- 微信群:添加客服微信 <http://s.jeesite.com> 邀请您进群
- 关注微信公众号,了解最新动态:
<p style="padding-left:40px">
<img alt="JeeSite微信公众号" src="https://jeesite.com/assets/images/mp.png" width="220" height="220">
</p>

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
*/
import { defHttp } from '@jeesite/core/utils/http/axios';
import { useGlobSetting } from '@jeesite/core/hooks/setting';
import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
import { TestDataChild } from '@jeesite/test/api/test/testDataChild';
const { adminPath } = useGlobSetting();
export interface TestData extends BasicModel<TestData> {
testInput?: string; // 单行文本
testTextarea?: string; // 多行文本
testSelect?: string; // 下拉框
testSelectMultiple?: string; // 下拉多选
testRadio?: string; // 单选框
testCheckbox?: string; // 复选框
testDate?: string; // 日期选择
testDatetime?: string; // 日期时间
testUser?: any; // 用户选择
testOffice?: any; // 机构选择
testAreaCode?: string; // 区域选择
testAreaName?: string; // 区域名称
testDataChildList?: TestDataChild[]; // 子表列表
}
export const testDataList = (params?: TestData | any) =>
defHttp.get<TestData>({ url: adminPath + '/test/testData/list', params });
export const testDataListData = (params?: TestData | any) =>
defHttp.post<Page<TestData>>({ url: adminPath + '/test/testData/listData', params });
export const testDataForm = (params?: TestData | any) =>
defHttp.get<TestData>({ url: adminPath + '/test/testData/form', params });
export const testDataSave = (params?: any, data?: TestData | any) =>
defHttp.postJson<TestData>({ url: adminPath + '/test/testData/save', params, data });
export const testDataDisable = (params?: TestData | any) =>
defHttp.get<TestData>({ url: adminPath + '/test/testData/disable', params });
export const testDataEnable = (params?: TestData | any) =>
defHttp.get<TestData>({ url: adminPath + '/test/testData/enable', params });
export const testDataDelete = (params?: TestData | any) =>
defHttp.get<TestData>({ url: adminPath + '/test/testData/delete', params });

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
*/
import { defHttp } from '@jeesite/core/utils/http/axios';
import { useGlobSetting } from '@jeesite/core/hooks/setting';
import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
import { TestData } from '@jeesite/test/api/test/testData';
const { adminPath } = useGlobSetting();
export interface TestDataChild extends BasicModel<TestData> {
testData?: TestData; // 父表对象
testSort?: number; // 数据排序
testInput?: string; // 单行文本
testTextarea?: string; // 多行文本
testSelect?: string; // 下拉框
testSelectMultiple?: string; // 下拉多选
testRadio?: string; // 单选框
testCheckbox?: string; // 复选框
testDate?: string; // 日期选择
testDatetime?: string; // 日期时间
testUser?: any; // 用户选择
testOffice?: any; // 机构选择
testAreaCode?: string; // 区域选择
testAreaName?: string; // 区域名称
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
*/
import { defHttp } from '@jeesite/core/utils/http/axios';
import { useGlobSetting } from '@jeesite/core/hooks/setting';
import { TreeDataModel, TreeModel } from '@jeesite/core/api/model/baseModel';
const { adminPath } = useGlobSetting();
export interface TestTree extends TreeModel<TestTree> {
treeCode?: string; // 节点编码
treeName?: string; // 节点名称
}
export const testTreeList = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/list', params });
export const testTreeListData = (params?: TestTree | any) =>
defHttp.post<TestTree[]>({ url: adminPath + '/test/testTree/listData', params });
export const testTreeForm = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/form', params });
export const testTreeCreateNextNode = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/createNextNode', params });
export const testTreeSave = (params?: any, data?: TestTree | any) =>
defHttp.postJson<TestTree>({ url: adminPath + '/test/testTree/save', params, data });
export const testTreeDisable = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/disable', params });
export const testTreeEnable = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/enable', params });
export const testTreeDelete = (params?: TestTree | any) =>
defHttp.get<TestTree>({ url: adminPath + '/test/testTree/delete', params });
export const testTreeTreeData = (params?: any) =>
defHttp.get<TreeDataModel[]>({ url: adminPath + '/test/testTree/treeData', params });

View File

@@ -0,0 +1,24 @@
{
"name": "@jeesite/test",
"version": "5.15.1",
"private": true,
"type": "module",
"scripts": {
"type:check": "vue-tsc --noEmit --skipLibCheck",
"uninstall": "rimraf node_modules",
"update": "ncu -u"
},
"homepage": "https://jeesite.com",
"repository": {
"type": "git",
"url": "https://gitee.com/thinkgem/jeesite-vue.git"
},
"bugs": {
"url": "https://gitee.com/thinkgem/jeesite-vue/issues"
},
"author": {
"name": "ThinkGem",
"email": "thinkgem@163.com",
"url": "https://gitee.com/thinkgem"
}
}

View File

@@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@jeesite/test/*": ["./*"]
}
},
"include": [
"./**/*.ts",
"./**/*.tsx",
"./**/*.vue"
],
"exclude": [
"node_modules",
"vite.config.ts",
"dist"
]
}

View File

@@ -0,0 +1,118 @@
<template>
<PageWrapper title="Markdown 组件实例" :contentFullHeight="false">
<CollapseContainer title="Markdown 演示" :expand="true">
<Markdown :bizKey="'123456'" :bizType="'testData'" v-model:value="valueRef" @change="handleChange" />
<a-button @click="clearValue" class="mt-2 mr-2" type="default"> 清空内容 </a-button>
<a href="https://ld246.com/article/1583308420519" target="_blank">语法速查手册</a>
<a href="https://ld246.com/article/1583129520165" target="_blank">基础语法</a>
<a href="https://ld246.com/article/1583305480675" target="_blank">扩展语法</a>
<a href="https://ld246.com/article/1582778815353" target="_blank">键盘快捷键</a>
</CollapseContainer>
<CollapseContainer title="Markdown 预览" :expand="false">
<MarkdownViewer :value="valueRef" />
</CollapseContainer>
<CollapseContainer title="Markdown 表单" :expand="false">
<BasicForm
:labelWidth="100"
:schemas="schemas"
:actionColOptions="{ span: 24, style: 'text-align: center' }"
:baseColProps="{ span: 24 }"
:showActionButtonGroup="true"
@submit="handleSubmit"
/>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, h } from 'vue';
import { Markdown, MarkdownViewer } from '@jeesite/core/components/Markdown';
import { PageWrapper } from '@jeesite/core/components/Page';
import { BasicForm, FormSchema } from '@jeesite/core/components/Form';
import { CollapseContainer } from '@jeesite/core/components/Container';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
const valueRef = ref(`
# 标题h1
##### 标题h5
**加粗**
*斜体*
~~删除线~~
[链接](https://jeesite.com)
↓分割线↓
---
* 无序列表1
* 无序列表1.1
1. 有序列表1
2. 有序列表2
* [ ] 任务列表1
* [x] 任务列表2
> 引用示例
\`\`\`js
// 代码块:
(() => {
var htmlRoot = document.getElementById('htmlRoot');
var theme = window.localStorage.getItem('__APP__DARK__MODE__');
if (htmlRoot && theme) {
htmlRoot.setAttribute('data-theme', theme);
theme = htmlRoot = null;
}
})();
\`\`\`
| 表格 | 示例 | 🎉️ |
| --- | --- | --- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
`);
function handleChange(v: string) {
valueRef.value = v;
}
function clearValue() {
valueRef.value = '';
}
// 在表单中展示
const schemas: FormSchema[] = [
{
field: 'title',
component: 'Input',
label: '标题',
defaultValue: '标题',
rules: [{ required: true }],
},
{
field: 'markdown',
component: 'Input',
label: '内容',
defaultValue: '内容',
rules: [{ required: true, trigger: 'blur' }],
render: ({ model, field }) => {
return h(Markdown, {
bizKey: '123456',
bizType: 'testData',
value: model[field],
onChange: (value: string) => {
model[field] = value;
},
});
},
},
];
const { createMessage } = useMessage();
function handleSubmit(values: any) {
createMessage.success('values:' + JSON.stringify(values));
}
</script>

View File

@@ -0,0 +1,66 @@
<template>
<PageWrapper title="组件传参实例">
<p class="mb-3">
<strong>1菜单组件参数</strong>
</p>
<p class="mb-3"> 如设置组件参数{ aa: 'aa1', bb: 'bb2' } </p>
<p class="mb-3"> 当前接受参数是{{ props }} </p>
<p class="mb-3">
<strong>2路由请求参数</strong>
</p>
<p class="mb-3">
路由请求参数
<Input
v-model:value="value"
placeholder="建议为url标准字符输入后点击切换"
style="width: 150px; margin-right: 10px"
/>
<a-button type="primary" @click="handleClickGo">切换路由</a-button>
(输入参数后点击切换路由按钮)
</p>
<p class="mb-3"> 接受请求参数是{{ query }} </p>
<p class="mb-3">
接受路径参数是{{ params }} <br />
注意 Vue Router 4.1.4 push params 被移除请使用 query 代替 <br />
原文https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#414-2022-08-22
<br />
那么如何使用 params 组件参数呢一般这样的地址会作为隐形路由使用隐藏的菜单或权限菜单<br />
菜单管理链接地址/demo/params/{test1}组件位置/demo/params是否可见隐藏 <br />
尝试地址栏填写http://127.0.0.1:3100/demo/params/test123 <br />
这样 unref(currentRoute).params.test1 将得到 test123 的值
</p>
</PageWrapper>
</template>
<script lang="ts" setup>
import { Input } from 'ant-design-vue';
import { computed, ref, unref } from 'vue';
import { useRouter } from 'vue-router';
import { PageWrapper } from '@jeesite/core/components/Page';
import { propTypes } from '@jeesite/core/utils/propTypes';
const props = defineProps({
id: propTypes.string,
aa: propTypes.string,
bb: propTypes.string,
});
const { currentRoute, push } = useRouter();
const value = ref<string>('');
const params = computed(() => {
return unref(currentRoute).params;
});
const query = computed(() => {
return unref(currentRoute).query;
});
const handleClickGo = () => {
const { name } = unref(currentRoute);
push({
name: name!,
params: { param: unref(value) },
query: { query: unref(value) },
});
};
</script>

View File

@@ -0,0 +1,26 @@
<template>
<PageWrapper title="页面水印示例">
<a-button type="primary" class="mr-2" @click="setWatermark('Hello JeeSite !')"> 创建水印 1 </a-button>
<a-button color="error" class="mr-2" @click="clear"> 清理水印 1 </a-button>
<a-button type="primary" class="mr-2" @click="setWatermark2('https://jeesite.com')"> 创建水印 2 </a-button>
<a-button color="error" class="mr-2" @click="clearAll"> 清理全部 </a-button>
</PageWrapper>
</template>
<script lang="ts" setup>
import { onUnmounted, ref } from 'vue';
import { useWatermark } from '@jeesite/core/hooks/web/useWatermark';
import { PageWrapper } from '@jeesite/core/components/Page';
const app = ref(document.body);
const { setWatermark, clear, clearAll } = useWatermark();
const { setWatermark: setWatermark2 } = useWatermark(app, {
fontColor: 'red',
fontSize: 12,
rotate: 30,
});
onUnmounted(() => {
clearAll();
});
</script>

View File

@@ -0,0 +1,315 @@
<!--
* Copyright (c) 2013-Now https://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: 'basicInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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: 'otherInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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: 'testDataChildInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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();
await 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 https://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,510 @@
<!--
* Copyright (c) 2013-Now https://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: 'basicInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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: 'otherInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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: 'testDataChildInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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,
});
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();
await 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,584 @@
<!--
* Copyright (c) 2013-Now https://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'"
@ok="handleSubmit"
@close="close"
>
<template #title>
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
<span> {{ getTitle.value }} </span>
</template>
<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 { meta } = unref(router.currentRoute);
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 getTitle = computed(() => ({
icon: meta.icon || 'i-ant-design:book-outlined',
value: 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,
});
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();
await setFieldsValue(record.value);
setTestDataChildTableData(res);
await setTitle(unref(getTitle).value);
loadingRef.value = false;
});
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');
setTimeout(close);
} 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 https://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 },
});
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();
await 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 https://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,165 @@
<!--
* Copyright (c) 2013-Now https://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: 'basicInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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: 'otherInfo',
component: 'FormGroup',
colProps: { md: 24, lg: 24 },
},
{
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;
}
await setFieldsValue(record.value);
await 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 https://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author ThinkGem
-->
<template>
<PageWrapper :sidebarWidth="200">
<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,247 @@
<!--
* Copyright (c) 2013-Now https://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, ref, unref, onMounted } 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, testTreeList, 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 record = ref<TestTree>({} as TestTree);
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, getForm }] = 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,
});
onMounted(async () => {
const res = await testTreeList();
record.value = (res.testTree || {}) as TestTree;
await getForm().setFieldsValue(record.value);
});
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>