我是小白(主要记录的是我实操的过程),可能有许多地方不对,请见谅,欢迎大佬补充

前言

​ 书接上回,导师要换项目架构,选择了Jeecg,目前已经通过Jeecg的代码生成器搭建了个简单的前后端,数据库导师也搭完了,然后让我接手剩下的,完善需求。

开发记录

LambdaQueryWrapper

错误版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ApiOperation(value = "病人基础信息表-分页列表查询", notes = "病人基础信息表-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<SiPatientsbaseinfo>> queryPageList(
SiPatientsbaseinfo siPatientsbaseinfo,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
LambdaQueryWrapper<SiPatientsbaseinfo> queryWrapper
= new LambdaQueryWrapper<SiPatientsbaseinfo>()
.like(SiPatientsbaseinfo::getUsername, siPatientsbaseinfo.getUsername())
.like(SiPatientsbaseinfo::getIdcard, siPatientsbaseinfo.getIdcard())
.like(SiPatientsbaseinfo::getName, siPatientsbaseinfo.getName());
Page<SiPatientsbaseinfo> page = new Page<SiPatientsbaseinfo>(pageNo, pageSize);
IPage<SiPatientsbaseinfo> pageList = siPatientsbaseinfoService.page(page, queryWrapper);
return Result.OK(pageList);
}

正确版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ApiOperation(value = "病人基础信息表-分页列表查询", notes = "病人基础信息表-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<SiPatientsbaseinfo>> queryPageList(
SiPatientsbaseinfo siPatientsbaseinfo,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
LambdaQueryWrapper<SiPatientsbaseinfo> queryWrapper = new LambdaQueryWrapper<>();
if (siPatientsbaseinfo != null) {
if (siPatientsbaseinfo.getUsername() != null) {
queryWrapper.like(SiPatientsbaseinfo::getUsername, siPatientsbaseinfo.getUsername());
}
if (siPatientsbaseinfo.getIdcard() != null) {
queryWrapper.like(SiPatientsbaseinfo::getIdcard, siPatientsbaseinfo.getIdcard());
}
if (siPatientsbaseinfo.getName() != null) {
queryWrapper.like(SiPatientsbaseinfo::getName, siPatientsbaseinfo.getName());
}
}
Page<SiPatientsbaseinfo> page = new Page<SiPatientsbaseinfo>(pageNo, pageSize);
IPage<SiPatientsbaseinfo> pageList = siPatientsbaseinfoService.page(page, queryWrapper);
return Result.OK(pageList);
}

如果 siPatientsbaseinfo 的值为空,会使用空值进行模糊查询,这可能会导致不同的结果。

  1. 如果 siPatientsbaseinfo 对象的属性都是 null,那么模糊查询条件会成为 LIKE '%',这意味着会匹配所有记录,因为 % 是通配符,代表任意字符。这种情况下会查询到所有记录。
  2. 如果 siPatientsbaseinfo 对象中只有部分属性是 null,那么只有那些属性为 null 的条件会被添加到查询中,而其他属性的条件会被忽略。这可能会导致结果不完全匹配你的预期。

@Dict

字典注解@Dict - JeecgBoot 文档中心

1
2
3
4
@Excel(name = "评估症状", width = 15, dictTable = "si_patientsymptom", dicText = "name", dicCode = "id")
@Dict(dictTable = "si_patientsymptom", dicText = "name", dicCode = "id")
@ApiModelProperty(value = "评估症状")
private java.lang.String psymptom;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//列表数据
export const columns: BasicColumn[] = [
{
title: '评估症状',
align: 'center',
dataIndex: 'psymptom_dictText',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '评估症状',
field: 'psymptom',
component: 'JSearchSelect',
componentProps: {
dict: 'si_patientsymptom,name,id',
},
},
];
// 高级查询数据
export const superQuerySchema = {
psymptom: {
title: '评估症状',
order: 0,
view: 'sel_search',
type: 'string',
dictTable: 'si_patientsymptom',
dictCode: 'id',
dictText: 'name',
},
};

参考链接

需求分析

为了书写方便,会简化说明项目架构

​ 目前有三张单表:病态评估治疗方案疗效数据。其中的物理外键关系是,病态评估治疗方案一对多,病态评估疗效数据一对一。

在代码生成器生成的前端代码中,这三者是独立的,现在需求是,只能从病态评估页面中对其余两张表新增操作(需绑定病态评估信息)

可分为以下两点处理

  • 疗效数据,一对一,从病态评估页面中打开原本疗效数据页面的新增弹窗
  • 治疗方案,一对多,从病态评估页面中打开治疗方案页面,需携带病态评估单行的数据

原始数据

这里只给出需要改动的部分

病态评估SiPatientsymptom

SiPatientsymptomList.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增 </a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出 </a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls"> 导入 </j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<!-- <super-query :config="superQueryConfig" @search="handleSuperQuery" />-->
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }"></template>
</BasicTable>
<!-- 表单区域 -->
<SiPatientsymptomModal @register="registerModal" @success="handleSuccess"></SiPatientsymptomModal>
</div>
</template>

<script lang="ts" name="siPatientsymptom-siPatientsymptom" setup>
import { ref, reactive, computed, unref } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import SiPatientsymptomModal from './components/SiPatientsymptomModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './SiPatientsymptom.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SiPatientsymptom.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';

const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '病人症状(病态评估)',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 300,
fixed: 'right',
},
beforeFetch: (params) => {
return Object.assign(params, queryParam);
},
},
exportConfig: {
name: '病人症状(病态评估)',
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});

const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;

// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);

/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}

/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}

/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}

/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}

/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}

/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}

/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}

/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
];
}

/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
},
];
}
</script>

<style scoped></style>

治疗方案SiTherapeuticschedule

SiTherapeuticscheduleList.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<!-- <super-query :config="superQueryConfig" @search="handleSuperQuery" />-->
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }">
<template v-if="column.dataIndex === 'content'">
<!--富文本件字段回显插槽-->
<div v-html="text"></div>
</template>
</template>
</BasicTable>
<!-- 表单区域 -->
<SiTherapeuticscheduleModal @register="registerModal" @success="handleSuccess"></SiTherapeuticscheduleModal>
</div>
</template>

<script lang="ts" name="siTherapeuticschedule-siTherapeuticschedule" setup>
import { ref, reactive, computed, unref } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import SiTherapeuticscheduleModal from './components/SiTherapeuticscheduleModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './SiTherapeuticschedule.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SiTherapeuticschedule.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';
const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '治疗方案',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 120,
fixed: 'right',
},
beforeFetch: (params) => {
return Object.assign(params, queryParam);
},
},
exportConfig: {
name: '治疗方案',
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});

const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;

// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);

/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
},
];
}
</script>

<style scoped></style>

SiTherapeuticschedule.data.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { rules } from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
{
title: '病人',
align: 'center',
dataIndex: 'patientName',
},
{
title: '评估症状',
align: 'center',
dataIndex: 'psymptomName',
},
{
title: '方案名称',
align: 'center',
dataIndex: 'scheme_dictText',
},
{
title: '方案内容',
align: 'center',
dataIndex: 'content',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'JPopup',
componentProps: ({ formActionType }) => {
const { setFieldsValue } = formActionType;
return {
setFieldsValue: setFieldsValue,
code: 'si_patientsbaseinfo',
fieldConfig: [
{ source: 'name', target: 'patientName' },
{ source: 'id', target: 'patient' },
],
multi: true,
};
},
},
{
label: '评估症状',
field: 'psymptomName',
component: 'Input',
},
{
label: '方案名称',
field: 'scheme',
component: 'JTreeSelect',
componentProps: {
dict: 'si_schemetemplate,name,id',
pidValue: '0',
},
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'Input',
},
{
label: '评估症状',
field: 'psymptom',
component: 'JSearchSelect',
componentProps: {
dict: 'si_patientsymptom,name,id',
},
dynamicRules: ({ model, schema }) => {
return [{ required: true, message: '请输入评估症状!' }];
},
},
{
label: '方案名称',
field: 'scheme',
component: 'JTreeSelect',
componentProps: {
dict: 'si_schemetemplate,name,id',
pidValue: '0',
},
dynamicRules: ({ model, schema }) => {
return [{ required: true, message: '请输入方案名称!' }];
},
},
{
label: '方案内容',
field: 'content',
component: 'JEditor',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];

// 高级查询数据
export const superQuerySchema = {
psymptom: {
title: '评估症状',
order: 0,
view: 'sel_search',
type: 'string',
dictTable: 'si_patientsymptom',
dictCode: 'id',
dictText: 'name',
},
scheme: {
title: '方案名称',
order: 1,
view: 'sel_tree',
type: 'string',
dict: 'si_schemetemplate,name,id',
pidValue: '0',
},
content: { title: '方案内容', order: 2, view: 'umeditor', type: 'string' },
};

/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}

SiTherapeuticscheduleModal.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>

<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../SiTherapeuticschedule.data';
import {saveOrUpdate} from '../SiTherapeuticschedule.api';
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: {span: 24}
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>

<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number){
width: 100%
}

:deep(.ant-calendar-picker){
width: 100%
}
</style>

疗效数据SiCurativeeffect

SiCurativeeffectList.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<!-- <super-query :config="superQueryConfig" @search="handleSuperQuery" />-->
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }"> </template>
</BasicTable>
<!-- 表单区域 -->
<SiCurativeeffectModal @register="registerModal" @success="handleSuccess"></SiCurativeeffectModal>
</div>
</template>

<script lang="ts" name="siCurativeeffect-siCurativeeffect" setup>
import { ref, reactive, computed, unref } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import SiCurativeeffectModal from './components/SiCurativeeffectModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './SiCurativeeffect.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SiCurativeeffect.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';
const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '疗效数据表',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 120,
fixed: 'right',
},
beforeFetch: (params) => {
return Object.assign(params, queryParam);
},
},
exportConfig: {
name: '疗效数据表',
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});

const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;

// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);

/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
},
];
}
</script>

<style scoped></style>

SiCurativeeffect.data.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { rules } from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
{
title: '病人',
align: 'center',
dataIndex: 'patientName',
},
{
title: '症状评估依据',
align: 'center',
dataIndex: 'psymptomName',
},
{
title: '颈椎',
align: 'center',
dataIndex: 'vertebrae',
},
{
title: '双肩',
align: 'center',
dataIndex: 'bothShoulders',
},
{
title: '圆肩含胸',
align: 'center',
dataIndex: 'roundShouldersChest',
},
{
title: '驼背',
align: 'center',
dataIndex: 'hunchbacked',
},
{
title: '平背',
align: 'center',
dataIndex: 'flatback',
},
{
title: '翼状肩',
align: 'center',
dataIndex: 'wingedShoulder',
},
{
title: '肋骨外翻',
align: 'center',
dataIndex: 'costalValgus',
},
{
title: '双肘距离',
align: 'center',
dataIndex: 'elbowDistance',
},
{
title: '脊柱侧弯',
align: 'center',
dataIndex: 'scoliosis',
},
{
title: '骨盆',
align: 'center',
dataIndex: 'pelvic',
},
{
title: '腿型',
align: 'center',
dataIndex: 'legtype',
},
{
title: '足型',
align: 'center',
dataIndex: 'foottype',
},
{
title: '长短腿',
align: 'center',
dataIndex: 'unevenleg',
},
{
title: '足',
align: 'center',
dataIndex: 'foot',
},
{
title: '膝',
align: 'center',
dataIndex: 'knee',
},
{
title: '肩',
align: 'center',
dataIndex: 'shoulder',
},
{
title: 'LPHC',
align: 'center',
dataIndex: 'lphc',
},
{
title: '平均得分',
align: 'center',
dataIndex: 'avgvalue',
},
{
title: '总得分',
align: 'center',
dataIndex: 'sumvalue',
},
{
title: '备注',
align: 'center',
dataIndex: 'remarks',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'JPopup',
componentProps: ({ formActionType }) => {
const { setFieldsValue } = formActionType;
return {
setFieldsValue: setFieldsValue,
code: 'si_patientsbaseinfo',
fieldConfig: [
{ source: 'name', target: 'patientName' },
{ source: 'id', target: 'patient' },
],
multi: true,
};
},
},
{
label: '症状评估依据',
field: 'psymptomName',
component: 'Input',
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'Input',
},
{
label: '症状评估依据',
field: 'psymptom',
component: 'JSearchSelect',
componentProps: {
dict: 'si_patientsymptom,name,id',
},
},
{
label: '颈椎',
field: 'vertebrae',
component: 'InputNumber',
},
{
label: '双肩',
field: 'bothShoulders',
component: 'InputNumber',
},
{
label: '圆肩含胸',
field: 'roundShouldersChest',
component: 'InputNumber',
},
{
label: '驼背',
field: 'hunchbacked',
component: 'InputNumber',
},
{
label: '平背',
field: 'flatback',
component: 'InputNumber',
},
{
label: '翼状肩',
field: 'wingedShoulder',
component: 'InputNumber',
},
{
label: '肋骨外翻',
field: 'costalValgus',
component: 'InputNumber',
},
{
label: '双肘距离',
field: 'elbowDistance',
component: 'InputNumber',
},
{
label: '脊柱侧弯',
field: 'scoliosis',
component: 'InputNumber',
},
{
label: '骨盆',
field: 'pelvic',
component: 'InputNumber',
},
{
label: '腿型',
field: 'legtype',
component: 'InputNumber',
},
{
label: '足型',
field: 'foottype',
component: 'InputNumber',
},
{
label: '长短腿',
field: 'unevenleg',
component: 'InputNumber',
},
{
label: '足',
field: 'foot',
component: 'InputNumber',
},
{
label: '膝',
field: 'knee',
component: 'InputNumber',
},
{
label: '肩',
field: 'shoulder',
component: 'InputNumber',
},
{
label: 'LPHC',
field: 'lphc',
component: 'InputNumber',
},
{
label: '平均得分',
field: 'avgvalue',
component: 'InputNumber',
dynamicDisabled: true,
},
{
label: '总得分',
field: 'sumvalue',
component: 'InputNumber',
dynamicDisabled: true,
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];

// 高级查询数据
export const superQuerySchema = {
psymptom: {
title: '症状评估依据',
order: 0,
view: 'sel_search',
type: 'string',
dictTable: 'si_patientsymptom',
dictCode: 'id',
dictText: 'name',
},
vertebrae: { title: '颈椎', order: 1, view: 'number', type: 'number' },
bothShoulders: { title: '双肩', order: 2, view: 'number', type: 'number' },
roundShouldersChest: { title: '圆肩含胸', order: 3, view: 'number', type: 'number' },
hunchbacked: { title: '驼背', order: 4, view: 'number', type: 'number' },
flatback: { title: '平背', order: 5, view: 'number', type: 'number' },
wingedShoulder: { title: '翼状肩', order: 6, view: 'number', type: 'number' },
costalValgus: { title: '肋骨外翻', order: 7, view: 'number', type: 'number' },
elbowDistance: { title: '双肘距离', order: 8, view: 'number', type: 'number' },
scoliosis: { title: '脊柱侧弯', order: 9, view: 'number', type: 'number' },
pelvic: { title: '骨盆', order: 10, view: 'number', type: 'number' },
legtype: { title: '腿型', order: 11, view: 'number', type: 'number' },
foottype: { title: '足型', order: 12, view: 'number', type: 'number' },
unevenleg: { title: '长短腿', order: 13, view: 'number', type: 'number' },
foot: { title: '足', order: 14, view: 'number', type: 'number' },
knee: { title: '膝', order: 15, view: 'number', type: 'number' },
shoulder: { title: '肩', order: 16, view: 'number', type: 'number' },
lphc: { title: 'LPHC', order: 17, view: 'number', type: 'number' },
avgvalue: { title: '平均得分', order: 18, view: 'number', type: 'number' },
sumvalue: { title: '总得分', order: 19, view: 'number', type: 'number' },
remarks: { title: '备注', order: 20, view: 'textarea', type: 'string' },
};

/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}

SiCurativeeffectModal.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1024" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>

<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../SiCurativeeffect.data';
import {saveOrUpdate} from '../SiCurativeeffect.api';
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: {span: 8}
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>

<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number){
width: 100%
}

:deep(.ant-calendar-picker){
width: 100%
}
</style>

定义打开弹窗的按钮

需要在列表数据的操作栏后添加对应按钮

SiPatientsymptomList.vue中,找到操作栏getTableAction函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '新增方案', // 一对多,页面弹窗
onClick: addTherapeuticschedule.bind(null, record),
},
{
label: '新增疗效', // 一对一,组件弹窗
onClick: addCurativeeffect.bind(null, record),
},
];
}

再定义相关函数addTherapeuticscheduleaddCurativeeffect,目前只在控制台打印一下,不做其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 新增治疗方案
*/
function addTherapeuticschedule(record: Recordable) {
console.log('新增治疗方案');
}

/**
* 新增疗效数据
*/
function addCurativeeffect(record: Recordable) {
console.log('新增疗效数据');
}

再适当调整一下操作栏的宽度,找到actionColumn,将width改为300

组件弹窗

先从简单的下手,疗效数据的新增功能只需要在病态评估页面中调用其原本的新增弹窗即可。

1. 导入注册组件弹窗

因为在SiCurativeeffectModal.vue中调用了新增修改接口,所以确定这是我们需要的组件弹窗

然后可以在SiCurativeeffectList.vue中观察如何导入使用的(爆红是我编译器问题,提示没用到变量之类的)

可分为以下三步

  1. 导入该组件
  2. 注册弹窗Modal
  3. 引用并绑定

所以我们也可以照搬在病态评估页面中

也分为上面三步走,因为注册的组件不能重名,所以要修改第二步的注册信息,第三步的成功回调函数直接用原页面的即可,以下是修改后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    ...
<!-- 表单区域 -->
<SiPatientsymptomModal @register="registerModal" @success="handleSuccess"></SiPatientsymptomModal>
<SiCurativeeffectModal @register="registerCurativeeffectModal" @success="handleSuccess" />
</div>
</template>

<script lang="ts" name="siPatientsymptom-siPatientsymptom" setup>
import { ref, reactive, computed, unref } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import SiPatientsymptomModal from './components/SiPatientsymptomModal.vue';
import SiCurativeeffectModal from '@/views/gaitsys/siCurativeeffect/vue3/components/SiCurativeeffectModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './SiPatientsymptom.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SiPatientsymptom.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';

const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
//注册model
const [registerModal, { openModal }] = useModal();
const [registerCurativeeffectModal, { openModal: openCurativeeffectModal }] = useModal();
...

2. 调用并携带数据

回到SiPatientsymptomList.vue页面中,观察原页面中的新增编辑操作。新增是直接打开弹窗,但是并不会携带数据,后台是插入;编辑是打开弹窗并携带数据,后台是修改。我们需要的是携带数据并插入,所以要改变一下

因为要携带数据,确保映射到弹窗的表单数据中

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 新增疗效数据
*/
function addCurativeeffect(record: Recordable) {
console.log('新增疗效数据');
record.psymptom = record.id; // 映射数据
openCurativeeffectModal(true, {
record,
isUpdate: false,
showFooter: true,
});
}

要确保数据能映射到弹窗,回到弹窗组件SiCurativeeffectModal.vue,找到useModalInner函数,将赋值setFieldsValue函数移动到if外,使其能接收到传入的数据

1
2
3
4
5
6
7
8
9
10
11
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
await setFieldsValue({
...data.record,
});
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter });
});

需求上说只能在病态评估页面新增,所以要将疗效数据页面的新增按钮去掉,再将弹窗中传递来的病人名称与评估数据给禁用

修改疗效数据页面SiCurativeeffectList.vue,注释或删除新增按钮

修改弹窗SiCurativeeffect.data.ts数据显示,添加disabled字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//表单数据
export const formSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '症状评估依据',
field: 'psymptom',
component: 'JSearchSelect',
componentProps: {
dict: 'si_patientsymptom,name,id',
disabled: true,
},
},
...
]

到这一步,组件弹窗应该就可以使用了

页面弹窗

先分析一下,我们需要在病态评估页面中打开治疗方案页面,还需要携带病态评估单行的数据(即弹窗只能展示出该病人的系列治疗方案),然后在打开新增弹窗时,还要显示出携带来的病人名称与评估数据

可以分为三步

  1. 先将治疗方案页面做成一个弹窗
  2. 再将数据携带传到该弹窗,即初始化时,携带查询参数
  3. 新增时携带数据(和前面的组件弹窗类似)

参考链接

1. 将页面改为弹窗

复制SiTherapeuticscheduleList.vue到同级的components目录下,重命名为SiTherapeuticscheduleListModal.vue

把页面的div标签改为弹窗BasicModal

1
2
3
<BasicModal v-bind="$attrs" @register="registerListModal" destroyOnClose :title="title" :width="1200" @ok="closeModal">
...
</BasicModal>

这时候会爆红,一是没导入弹窗组件,二是有些变量没创建

先导入

1
import { BasicModal, useModalInner } from '/@/components/Modal';

再注册,这个页面弹窗只需要展示,不需要其他操作,所以@ok绑定的事件就直接用closeModaldestroyOnClose看字面意思就是关闭时销毁

1
2
3
4
5
6
7
8
9
10
//注册model
const [registerListModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
});
//设置标题
const title = '管理治疗方案';

2. 传参

先在SiPatientsymptomList.vue导入该页面弹窗,和上面组件弹窗步骤一样(先导入,再注册,最后引用)

1
2
3
4
5
6
7
8
9
// 1.导入组件
import SiTherapeuticscheduleListModal from '@/views/gaitsys/siTherapeuticschedule/vue3/components/SiTherapeuticscheduleListModal.vue';
import SiCurativeeffectModal from '@/views/gaitsys/siCurativeeffect/vue3/components/SiCurativeeffectModal.vue';

// 2.注册model
const [registerTherapeuticscheduleListModal, { openModal: openTherapeuticscheduleListModal }] = useModal();

// 3.引用组件
<SiTherapeuticscheduleListModal @register="registerTherapeuticscheduleListModal" @success="handleSuccess" />

然后就是打开弹窗的函数,这里传了一个record数据过去

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 新增治疗方案
*/
function addTherapeuticschedule(record: Recordable) {
console.log('新增治疗方案');
record.psymptom = record.id; // 映射数据
openTherapeuticscheduleListModal(true, {
record,
isUpdate: false,
showFooter: false,
});
}

相应的,页面弹窗组件也要接收该数据

在注册页面弹窗的时候,调用了useModalInner函数,异步传来了data数据,就用这个获取到传入的参数

1
2
3
4
5
6
7
8
9
10
11
let record = reactive<any>({});

//注册model
const [registerListModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
record = data.record;
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
});

最后就是打开该弹窗时,只查询传入病人的信息

找到useListPage表单注册函数,里面的beforeFetch就是查询时的函数

可以在查询时添加对应的参数,即可达成需求(需调整对应后端接口)

1
2
3
4
5
beforeFetch: (params) => {
params.patientName = record.patientName;
params.psymptomName = record.psymptomName;
return Object.assign(params, queryParam);
},

3. 携带参数打开弹窗

因为上面创建了个全局的变量record,所以在调用新增函数handleAdd的时候传入即可

1
2
3
4
5
6
7
8
9
10
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
record,
isUpdate: false,
showFooter: true,
});
}

确保能在新增弹窗赋值成功,需要修改SiPatientsymptomModal.vueuseModalInner和上面组件弹窗一样

1
2
3
4
5
6
7
8
9
10
11
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
await setFieldsValue({
...data.record,
});
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter });
});

然后添加表单弹窗的字段禁用,修改SiTherapeuticschedule.data.ts的表单数据formSchema,添加disabled字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//表单数据
export const formSchema: FormSchema[] = [
{
label: '病人',
field: 'patientName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '评估症状',
field: 'psymptom',
component: 'JSearchSelect',
componentProps: {
dict: 'si_patientsymptom,name,id',
disabled: true,
},
dynamicRules: ({ model, schema }) => {
return [{ required: true, message: '请输入评估症状!' }];
},
},
...
];

到这一步,页面弹窗应该就可以使用了