init
This commit is contained in:
2025-06-27 17:42:38 +08:00
commit bd6402478b
5317 changed files with 785994 additions and 0 deletions
@@ -0,0 +1,76 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
centerList = '/health-emergency/emergency/center/list',
list = '/health-emergency/emergency/emergencyGrouped/list',
save = '/health-emergency/emergency/emergencyGrouped/add',
edit = '/health-emergency/emergency/emergencyGrouped/edit',
deleteOne = '/health-emergency/emergency/emergencyGrouped/delete',
callDelete = '/health-emergency/emergency/emergencyGrouped/callDelete',
exportXls = '/health-emergency/api/qimo/exportXls',
getCallRecordList = '/health-emergency/emergency/emergencyGrouped/callList',
}
/**
* @description 获取应急中心列表
* @param params
*/
export const getEmergencyList = (params: any) => defHttp.get({ url: Api.centerList, params });
/**
* 列表接口
* @param params
*/
export const list = (params: any) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () =>
defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
}),
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params: any, isUpdate: boolean) => {
return isUpdate ? defHttp.put({ url: Api.edit, params }) : defHttp.post({ url: Api.save, params });
};
/**
* @description 通话记录列表
* @param params
*/
export const getCallRecordList = (params: any) => defHttp.get({ url: Api.getCallRecordList, params });
export const exportExcelUrl = (params: any) => defHttp.get({ url: Api.exportXls, params });
/**
* @description 通话记录删除
* @param params
* @param handleSuccess
*/
export const callDelete = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () =>
defHttp.delete({ url: Api.callDelete, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
}),
});
};
@@ -0,0 +1,169 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { orgSearchInfoByCode } from '/@/utils/orgSearchInfo';
import { getEmergencyList } from '/@/views/consult/agentManagement/agentManagement.api';
import { getTimeStr } from '/@/utils/common/compUtils';
const SeatStatus = {
0: '未知',
1: '忙碌',
2: '小休',
3: '正在通话',
4: '离线',
5: '空闲',
};
const centerProps = {
api: getEmergencyList,
params: {
pageNo: 1,
pageSize: 200,
},
labelField: 'centerName',
valueField: 'id',
immediate: true,
resultField: 'records',
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input?.trim()?.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
export const columns: BasicColumn[] = [
{
title: '坐席工号',
dataIndex: 'seatId',
},
{
title: '所属应急中心',
dataIndex: 'centerName',
},
{
title: '坐席状态',
dataIndex: 'seatStatus',
customRender: ({ text }) => {
return SeatStatus[text] || '';
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'centerId',
label: '所属应急中心',
component: 'ApiSelect',
componentProps: () => {
return centerProps;
},
},
];
export const formSchema: FormSchema[] = [
{
field: 'seatId',
label: '坐席工号',
component: 'InputNumber',
componentProps: () => {
return {
style: {
width: '100%',
},
min: 0,
};
},
required: true,
},
{
field: 'centerId',
label: '所属应急中心',
component: 'ApiSelect',
required: true,
componentProps: () => {
return {
...centerProps,
getPopupContainer: () => document.body,
};
},
},
{
field: 'id',
label: '隐藏id',
component: 'Input',
show: false,
},
];
export const recordColumns: BasicColumn[] = [
{
title: '单位名称',
dataIndex: 'orgName',
fixed: 'left',
customRender: ({ text }) => {
if (!text) return '未知';
return text;
},
},
{
title: '部门名称',
dataIndex: 'deptName',
fixed: 'left',
customRender: ({ text }) => {
if (!text) return '未知';
return text;
},
},
{
title: '员工姓名',
dataIndex: 'realName',
fixed: 'left',
width: 100,
},
{
title: '性别',
dataIndex: 'sex_dictText',
width: 80,
},
{
title: '呼叫中心',
dataIndex: 'centerName',
},
{
title: '坐席工号',
dataIndex: 'seatId',
width: 120,
},
{
title: '电话',
dataIndex: 'phone',
},
{
title: '通话时长',
dataIndex: 'callDuration',
customRender: ({ text }) => {
return getTimeStr(text);
},
},
{
title: '通话时间',
dataIndex: 'callTime',
},
];
export const recordSearchFormSchema: FormSchema[] = [
{
field: 'realName',
label: '员工姓名',
component: 'Input',
},
{
field: 'centerId',
label: '呼叫中心',
component: 'ApiSelect',
componentProps: () => {
return {
...centerProps,
getPopupContainer: () => document.body,
};
},
},
{
field: 'seatId',
label: '坐席工号',
component: 'Input',
},
...orgSearchInfoByCode('unit', 'department', 'orgCode'),
];
@@ -0,0 +1,95 @@
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined" v-auth="'emergency:emergency_grouped:add'">
新增
</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<CallRecordsModal @register="registerModal"></CallRecordsModal>
<AddaAgentManagementModal @register="registerAddaAgentManagement" @success="handleSuccess" />
</div>
</template>
<script setup lang="ts">
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from '/@/views/consult/agentManagement/agentManagement.data';
import { deleteOne, list } from '/@/views/consult/agentManagement/agentManagement.api';
import { useModal } from '/@/components/Modal';
import CallRecordsModal from '/@/views/consult/agentManagement/components/callRecordsModal.vue';
import AddaAgentManagementModal from '/@/views/consult/agentManagement/components/addaAgentManagementModal.vue';
const [registerModal, { openModal }] = useModal();
const [registerAddaAgentManagement, { openModal: openAddModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '坐席管理-分组管理',
api: list,
columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
useSearchForm: true,
actionColumn: {
width: 200,
fixed: 'right',
},
},
});
const [registerTable, { reload }] = tableContext;
function handleAdd() {
openAddModal(true, {
type: '新增',
isUpdate: false,
showFooter: true,
});
}
function handleEdit(record: Recordable) {
openAddModal(true, {
type: '编辑',
record,
isUpdate: true,
showFooter: true,
});
}
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
function callRecords(record: Recordable) {
openModal(true, {
seatId: record.seatId,
});
}
function handleSuccess() {
reload();
}
function getTableAction(record: Recordable) {
return [
{
label: '通话记录',
onClick: callRecords.bind(null, record),
},
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'emergency:emergency_grouped:edit',
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'emergency:emergency_grouped:delete',
},
];
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,73 @@
<template>
<BasicTable @register="registerTable" size="small">
<template #tableTitle>
<a-button-group>
<a-button v-auth="'emergency:callLog:exportXls'" type="primary" preIcon="ant-design:export-outlined" @click="downloadExcelBtn"
>导出</a-button
>
<a-button v-auth="'emergency:callLog:exportXls'" type="primary" preIcon="ant-design:search-outlined" @click="handleExportList">
查看导出任务
</a-button>
</a-button-group>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<ExportUtil task-code="emergencyCallLogCode" @register="registerExport" drawer-title="通话记录导出" />
</template>
<script setup lang="ts">
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { callDelete, exportExcelUrl, getCallRecordList } from '/@/views/consult/agentManagement/agentManagement.api';
import { recordColumns, recordSearchFormSchema } from '/@/views/consult/agentManagement/agentManagement.data';
import ExportUtil from '/@/utils/export/exportUtil.vue';
import { useDrawer } from '/@/components/Drawer';
const emit = defineEmits(['register', 'success']);
const [registerExport, { openDrawer }] = useDrawer();
const { tableContext } = useListPage({
tableProps: {
title: '坐席管理-通话记录列表',
api: getCallRecordList,
columns: recordColumns,
canResize: false,
formConfig: {
labelWidth: 80,
schemas: recordSearchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToTime: [],
actionColOptions: {
style: {
marginLeft: '80px',
},
},
},
actionColumn: {
width: 100,
fixed: 'right',
},
},
});
const [registerTable, { reload, getForm }] = tableContext;
function downloadExcelBtn() {
exportExcelUrl(getForm().getFieldsValue());
}
function handleExportList() {
openDrawer(true);
}
function handleDelete(record: Recordable) {
callDelete({ id: record.id }, reload);
}
function getTableAction(record: Recordable) {
return [
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,60 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :showFooter="showFooter" :title="title" :width="600" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '/@/views/consult/agentManagement/agentManagement.data';
import { saveOrUpdate } from '/@/views/consult/agentManagement/agentManagement.api';
import { useModalInner, BasicModal } from '/@/components/Modal';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const showFooter = ref(true);
//设置标题
const title = ref('');
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
labelWidth: 120,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
showFooter.value = data.showFooter;
title.value = data.type;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//表单提交事件
async function handleSubmit() {
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></style>
@@ -0,0 +1,105 @@
<template>
<BasicModal z-index="999" v-bind="$attrs" @register="registerModal" destroyOnClose :title="'通话记录'" :width="1100">
<div style="height: 70vh">
<BasicTable @register="registerTable" size="small">
<template #tableTitle>
<a-button-group>
<a-button v-auth="'emergency:callLog:exportXls'" type="primary" preIcon="ant-design:export-outlined" @click="downloadExcelBtn"
>导出</a-button
>
<a-button
v-auth="'emergency:callLog:exportXls'"
type="primary"
preIcon="ant-design:search-outlined"
@click="handleExportList"
>
查看导出任务
</a-button>
</a-button-group>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</div>
</BasicModal>
<ExportUtil z-index="1099" task-code="emergencyCallLogCode" @register="registerExport" drawer-title="通话记录导出" />
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { exportExcelUrl, getCallRecordList } from '/@/views/consult/agentManagement/agentManagement.api';
import { recordColumns, recordSearchFormSchema } from '/@/views/consult/agentManagement/agentManagement.data';
import ExportUtil from '/@/utils/export/exportUtil.vue';
import { useDrawer } from '/@/components/Drawer';
const [registerExport, { openDrawer }] = useDrawer();
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const seatId = ref('');
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
seatId.value = data.seatId;
});
const { tableContext } = useListPage({
tableProps: {
title: '分组管理-通话记录',
api: getCallRecordList,
columns: recordColumns,
canResize: false,
formConfig: {
labelWidth: 80,
schemas: recordSearchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToTime: [],
baseColProps: {
xs: 12, // <576px
sm: 12, // ≥576px
md: 12, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
},
actionColOptions: {
offset: 0,
span: 24,
xs: 12, // <576px
sm: 12, // ≥576px
md: 12, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
},
},
beforeFetch: (params) => {
params['seatId'] = seatId.value;
return params;
},
actionColumn: {
width: 100,
fixed: 'right',
},
},
});
const [registerTable, { reload, getForm }] = tableContext;
function downloadExcelBtn() {
exportExcelUrl({ ...getForm().getFieldsValue(), seatId: seatId.value });
}
function handleExportList() {
openDrawer(true);
}
function handleDelete(record: Recordable) {}
function getTableAction(record: Recordable) {
return [
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,161 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm">
<template #itemOptions="{}">
<a-form :model="innerForm" ref="optionForm">
<a-form-item
v-for="(item, i) in innerForm.option"
:key="i"
class="option-item"
:name="['option', i, 'label']"
:rules="[
{
required: true,
message: '请输入选项',
trigger: 'change',
},
]"
>
<a-input v-model:value="item.label" @blur="inputBlur(item)" />
<MinusCircleOutlined class="delete-button" @click="removeDomain(item)" />
</a-form-item>
</a-form>
<a-button class="add-button" @click="addDomain">
<PlusOutlined />
</a-button>
</template>
</BasicForm>
</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 '../conHealthInfo.data';
import { saveOrUpdate } from '../conHealthInfo.api';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
// Emits声明
const emit = defineEmits(['register', 'success']);
const optionForm = ref();
const isUpdate = ref(true);
const innerForm = ref({
option: [],
});
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = 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,
});
let options = JSON.parse(data.record.itemOptions) || {};
const optionsArr = [];
if (Object.keys(options).length) {
for (const key in options) {
optionsArr.push({ label: key, value: options[key] });
}
}
innerForm.value.option = optionsArr;
}
// //表单赋值
// await setFieldsValue({
// ...data.record,
// });
await clearValidate();
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
function addDomain() {
let row = { label: '', value: '0' };
innerForm.value.option.push(row);
}
function inputBlur(item) {
if (!item.value) {
item.value = '0';
}
}
function removeDomain(item) {
let index = innerForm.value.option.indexOf(item);
if (index !== -1) {
innerForm.value.option.splice(index, 1);
}
}
function clearData() {
innerForm.value.option = [];
}
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
let newValues = await optionForm.value.validate();
let { itemType } = values;
let itemOptions = {};
if (['1', '2'].includes(itemType)) {
const obj = {};
innerForm.value.option.map((item: any) => {
if (!item.value) item.value = '0';
obj[item.label] = item.value;
});
itemOptions = obj;
}
const params = {
...values,
itemOptions: JSON.stringify(itemOptions),
};
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(params, isUpdate.value);
clearData();
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
:deep(.option-item) {
display: flex;
justify-content: space-between;
align-items: center;
margin: 5px 0;
.ant-form-item-control-input-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.delete-button {
margin-left: 16px;
color: red;
}
.add-button {
color: #1890ff;
}
}
</style>
@@ -0,0 +1,80 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conHealthInfo/list',
save = '/health-consultation/consultation/conHealthInfo/add',
edit = '/health-consultation/consultation/conHealthInfo/edit',
deleteOne = '/health-consultation/consultation/conHealthInfo/delete',
deleteBatch = '/health-consultation/consultation/conHealthInfo/deleteBatch',
importExcel = '/health-consultation/consultation/conHealthInfo/importExcel',
exportXls = '/health-consultation/consultation/conHealthInfo/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () =>
defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
}),
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,116 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
const itemType = {
'1': '单选',
'2': '多选',
'3': '文本',
};
//列表数据
export const columns: BasicColumn[] = [
{
title: '基本项',
align: 'center',
dataIndex: 'itemProblem',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => (text && text == '1' ? '启用' : '禁用'),
},
{
title: '问题类型',
align: 'center',
dataIndex: 'itemType',
customRender: ({ text }) => (text && itemType[text]) || '',
},
{
title: '创建时间',
align: 'center',
dataIndex: 'createTime',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '问题名称',
field: 'itemProblem',
component: 'Input',
required: true,
},
{
label: '问题类型',
field: 'itemType',
component: 'JDictSelectTag',
componentProps: {
options: [
{
label: '单选',
value: '1',
},
{
label: '多选',
value: '2',
},
{
label: '文本',
value: '3',
},
],
getPopupContainer: () => document.body,
},
required: true,
},
{
label: '选项',
field: 'itemOptions',
component: 'JDictSelectTag',
slot: 'itemOptions',
show: ({ values }) => {
return ['1', '2'].includes(values.itemType);
},
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
options: [
{
label: '启用',
value: '1',
},
{
label: '禁用',
value: '2',
},
],
getPopupContainer: () => document.body,
},
required: true,
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,145 @@
<template>
<div class="con-health-info-list">
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button v-auth="auth.add" type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button v-auth="auth.deleteBatch" @click="batchHandleDelete" type="primary" preIcon="ant-design:delete-outlined">批量删除</a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
</BasicTable>
<!-- 表单区域 -->
<conHealthInfoModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conHealthInfo" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import conHealthInfoModal from './components/conHealthInfoModal.vue';
import { columns, searchFormSchema } from './conHealthInfo.data';
import { list, deleteOne, batchDelete } from './conHealthInfo.api';
import { message } from 'ant-design-vue';
const auth = {
add: 'consultation:con_health_info:add',
edit: 'consultation:con_health_info:edit',
deleteOne: 'consultation:con_health_info:delete',
deleteBatch: 'consultation:con_health_info:deleteBatch',
};
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '健康信息',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
useSearchForm: true,
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
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);
}
/**
* @Description:批量删除
* @date 2023/6/20
* @param:
*/
async function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: auth.edit,
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: auth.deleteOne,
},
];
}
</script>
<style lang="less" scoped>
.con-health-info-list {
padding: 10px;
}
:deep(.ant-popover-buttons) {
display: flex !important;
}
</style>
@@ -0,0 +1,259 @@
<template>
<BasicDrawer
:showFooter="false"
v-bind="$attrs"
@register="registerDrawer"
destroyOnClose
:title="title"
:width="1200"
:maskClosable="false"
@ok="handleSubmit"
@close="resetData"
>
<a-spin :spinning="spinning">
<div class="detail-container">
<a-tabs v-model:activeKey="state.activeKey">
<a-tab-pane key="1" tab="个人档案">
<CommonDesc :key="updateRes" title="基本信息" :column="3" :descList="descList1" :desc-res="resList.resList1" />
<div>
<div class="ant-descriptions-header ant-descriptions-title">紧急联系人</div>
<div class="person-list">
<a-list :data-source="resList.sysUserEmergencyContactList" bordered>
<template #header>
<a-row>
<a-col flex="4" align="center">姓名</a-col>
<a-col flex="4" align="center">与员工关系</a-col>
<a-col flex="4" align="center">联系电话</a-col>
</a-row>
</template>
<template #renderItem="{ item }">
<a-row class="row-item">
<a-col flex="4" align="center">{{ item?.name }} </a-col>
<a-col flex="4" align="center">{{ item?.['familyRelation_dictText'] }}</a-col>
<a-col flex="4" align="center">{{ item?.['phone'] }}</a-col>
</a-row>
</template>
</a-list>
</div>
</div>
<CommonDesc :key="updateRes" title="健康信息" :column="3" :descList="resList.descList3" :desc-res="resList.resList3" />
</a-tab-pane>
<a-tab-pane key="2" tab="家庭档案">
<template v-if="resList.conFamilyMembersVOList.length">
<div v-for="(item, i) in resList.conFamilyMembersVOList" :key="i">
<div class="family-name">{{ i + 1 }}. {{ item?.['conFamilyMembers']?.name || '' }}</div>
<CommonDesc title="基本信息" :column="4" :descList="familyInfo" :desc-res="item['conFamilyMembers']" />
<CommonDesc title="健康信息" :column="4" :descList="dealFamilyHealthInfo(item.map)" :desc-res="item.map" />
</div>
</template>
<template v-else>
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</template>
</a-tab-pane>
</a-tabs>
</div>
</a-spin>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import CommonDesc from '/@/views/emergency/outburst/order/components/commonDesc.vue';
import { descList1, tableColumns, cardColumns2, familyInfo } from '../conHealthInfo.data';
import { DescList, State, TableState } from '../types';
import { personBasicInfo, queryById } from '../conHealthInfo.api';
import { Empty } from 'ant-design-vue';
// Emits声明
const emit = defineEmits(['register', 'success']);
const updateRes = ref(true);
const spinning = ref<boolean>(true);
const changeSpinning = () => {
spinning.value = !spinning.value;
};
//设置标题
const title = '详情';
const state = ref<State>({
activeKey: '1',
});
const tableState = ref<TableState>({
dataSource: [],
columns: tableColumns,
cardSource: [],
cardColumns2: cardColumns2,
});
interface ResList {
resList1: any;
resList3: any;
descList3: DescList[];
conFamilyMembersVOList: any[];
sysUserEmergencyContactList: any[];
}
let resList: ResList = reactive({
resList1: {},
descList3: [],
resList3: {},
conFamilyMembersVOList: [],
sysUserEmergencyContactList: [],
});
const record = ref();
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
const { userId, id } = data.record || {};
record.value = data.record;
getInitData(id, userId);
// 隐藏底部
setDrawerProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
});
/**
* @Description:处理个人基本信息数据
* @date 2023/6/21
*/
function dealBasicInfo({ res1, res2 }) {
let { extension, departTree } = res2 || {};
nextTick(() => {
resList.resList1 = {
empSysno: res2?.workNo || '',
realname: res2?.realname,
age: res1?.conFamilyMembers?.age || record.value?.age || '',
sex_dictText: res2?.sex_dictText,
idCard: res2?.idCard,
phone: res2?.phone,
height: res1?.conFamilyMembers?.height,
weight: res1?.conFamilyMembers?.weight,
depart: (departTree && departTree[0]) || '',
empJob_dictText: extension?.empJob_dictText,
empPosition: extension?.empPosition,
empLevel_dictText: extension?.empLevel_dictText || '',
empPolitical_dictText: extension?.empPolitical_dictText,
empDegree_dictText: extension?.empDegree_dictText,
empMarriage_dictText: extension?.empMarriage_dictText,
empNativeplace: extension?.empNativeplace,
empWorktime: extension?.empWorktime,
empType_dictText: extension?.empType_dictText,
workSpace: extension?.workSpace,
empStatus_dictText: extension?.empStatus_dictText,
liveSpace: extension?.liveSpace,
};
});
}
/**
* @Description:处理个人健康信息
* @date 2023/6/21
*/
function dealPersonHealInfo(mapObj) {
let descList3: DescList[] = [];
if (JSON.stringify(mapObj) !== '{}') {
for (const key in mapObj) {
descList3.push({ label: key, key });
}
resList.descList3 = descList3;
resList.resList3 = mapObj;
}
}
/**
* @Description:处理家庭健康信息
* @date 2023/6/21
* @param mapObj 数据
*/
function dealFamilyHealthInfo(mapObj) {
let descList: any[] = [];
if (JSON.stringify(mapObj) !== '{}') {
for (const key in mapObj) {
descList.push({ label: key, key });
}
}
return descList;
}
/**
* @Description:获取详情数据
* @date 2023/6/26
* @param id
* @param userId
* @return {res1,res2} res1:家庭档案、个人部分基本信息;res2:个人档案
*/
function getInitData(id: string, userId: string): void {
let r1 = queryById({ userId });
let r2 = personBasicInfo({ id: userId });
Promise.all([r1, r2])
.then(([res1, res2]) => {
const { map: mapObj } = res1 || {};
dealPersonHealInfo(mapObj);
dealBasicInfo({ res1, res2 });
resList.conFamilyMembersVOList = res1?.conFamilyMembersVOList || [];
resList.sysUserEmergencyContactList = res1?.sysUserEmergencyContactList || [];
updateRes.value = !updateRes.value;
changeSpinning();
})
.catch((e) => {
console.log('e', e);
changeSpinning();
});
}
function resetData() {
state.value.activeKey = '1';
spinning.value = true;
resList = {
resList1: {},
descList3: [],
resList3: {},
conFamilyMembersVOList: [],
sysUserEmergencyContactList: [],
};
tableState.value = {
dataSource: [],
columns: tableColumns,
cardSource: [],
cardColumns2: cardColumns2,
};
}
//表单提交事件
async function handleSubmit() {
try {
setDrawerProps({ confirmLoading: true });
//关闭弹窗
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
.detail-container {
overflow: auto;
}
:deep(.ant-descriptions-header) {
margin: 16px 0 !important;
}
.person-list {
width: 50%;
}
.family-name {
color: #000;
font-weight: 700;
line-height: 1.5;
font-size: 16px;
}
:deep(.ant-descriptions-title) {
font-size: 14px !important;
}
.row-item {
line-height: 2.4;
border-bottom: 1px solid #ddd;
}
</style>
@@ -0,0 +1,79 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conHealthInfoAnswer/list',
save = '/health-consultation/consultation/conHealthInfo/add',
edit = '/health-consultation/consultation/conHealthInfo/edit',
deleteOne = '/health-consultation/consultation/conHealthInfo/delete',
deleteBatch = '/health-consultation/consultation/conHealthInfo/deleteBatch',
importExcel = '/health-consultation/consultation/conHealthInfo/importExcel',
exportXls = '/health-consultation/consultation/conHealthInfo/exportXls',
queryById = '/health-consultation/consultation/conHealthInfoAnswer/queryByUserId',
// personBasicInfo = '/sys/healthUserEmployeeEx/queryById',
personBasicInfo = '/sys/healthUserEmployeeEx/queryArchivesById', //替换后
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* @Description:获取详情信息
* @date 2023/6/21
* @param:{id}
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/**
* @Description:个人基本信息
* @date 2023/6/21
* @param:{id}
*/
export const personBasicInfo = (params) => defHttp.get({ url: Api.personBasicInfo, params });
@@ -0,0 +1,453 @@
import { getSecondaryDepartmentList, getThirdDepartmentList } from '/@/views/system/user/user.api';
import { BasicColumn, FormSchema } from '/@/components/Table';
import { getFamaleDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { h } from 'vue';
import { EyeOutlined } from '@ant-design/icons-vue';
import { Image } from 'ant-design-vue';
import { render } from '/@/utils/common/renderUtils';
import { useMessage } from '/@/hooks/web/useMessage';
import { DescList, TableColumns } from '/@/views/consult/archives/consult/types';
import { useDepartment } from '/@/utils/auth/formAuth';
const { createMessage } = useMessage();
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '头像',
align: 'center',
dataIndex: 'avatar',
width: 100,
customRender: ({ text, record }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getFamaleDefaultImage(record.sex),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
{
title: '员工姓名',
align: 'center',
dataIndex: 'fromName',
width: 100,
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
width: 60,
customRender: ({ text }) => {
return render.renderDict(text, 'gender');
},
},
{
title: '年龄',
align: 'center',
dataIndex: 'age',
width: 60,
},
{
title: '单位',
align: 'center',
dataIndex: 'secondDepartName',
},
{
title: '部门',
align: 'center',
dataIndex: 'thirdDepartName',
},
{
title: '身份证',
align: 'center',
dataIndex: 'idCard',
width: 200,
},
{
title: '手机号',
align: 'center',
dataIndex: 'phone',
},
{
title: '员工编号', // 原来为工号
align: 'center',
dataIndex: 'workNo',
},
{
title: '家庭成员',
align: 'center',
dataIndex: 'familyNum',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '员工姓名',
field: 'fromName',
component: 'Input',
},
{
label: '手机号',
field: 'phone',
component: 'Input',
},
{
label: '单位',
field: 'secondDepartId',
component: 'ApiSelect',
componentProps: ({ formModel, schema }) => {
const { secondSelectValue, secondSelectDisabled } = useDepartment({ schema, key: 'id' });
secondSelectValue();
return {
api: getSecondaryDepartmentList,
resultField: 'list',
labelField: 'departName',
valueField: 'id',
immediate: true,
onChange: (val) => {
if (val) {
formModel.thirdDepartId = '';
if (formModel.hasOwnProperty('thirdDepartId')) {
formModel.thirdDepartId = '';
}
}
},
onDeselect: () => {
formModel.secondDepartId = '';
if (formModel.hasOwnProperty('thirdDepartId')) {
formModel.thirdDepartId = '';
}
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
disabled: secondSelectDisabled,
};
},
},
{
label: '部门',
field: 'thirdDepartId',
component: 'ApiSelect',
componentProps: ({ formModel, schema }) => {
const { thirdSelectValue, thirdSelectDisabled } = useDepartment({ schema, key: 'id' });
thirdSelectValue();
return {
api: getThirdDepartmentList,
resultField: 'list',
labelField: 'departName',
params: {
secondDepartId: getSecond(formModel),
},
valueField: 'id',
immediate: true,
onFocus: () => {
if (!formModel.secondDepartId) {
return createMessage.warn('请先选择单位!');
}
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
disabled: thirdSelectDisabled,
};
},
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '模板id',
field: 'templateId',
component: 'Input',
},
{
label: '区域[1生活习惯,2身体情况]',
field: 'itemArea',
component: 'Input',
},
{
label: '问题名称',
field: 'itemProblem',
component: 'Input',
},
{
label: '问题类型[1单选,2多选,3文本,4单选加文本]',
field: 'itemType',
component: 'Input',
},
{
label: '选项',
field: 'itemOptions',
component: 'Input',
},
{
label: '状态(1-激活,2-锁定)',
field: 'status',
component: 'Input',
dynamicRules: () => {
return [{ required: true, message: '请输入状态(1-激活,2-锁定)!' }];
},
},
{
label: '删除状态(0-正常,1-已删除)',
field: 'delFlag',
component: 'Input',
dynamicRules: () => {
return [{ required: true, message: '请输入删除状态(0-正常,1-已删除)!' }];
},
},
{
label: '备注',
field: 'memo',
component: 'Input',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
// 个人档案
// 1.基本信息
export const descList1: DescList[] = [
{
label: '员工编号',
key: 'empSysno',
spanNumber: 1,
},
{
label: '员工姓名',
key: 'realname',
spanNumber: 1,
},
{
label: '性别',
key: 'sex_dictText',
spanNumber: 1,
},
{
label: '年龄',
key: 'age',
},
{
label: '身份证',
key: 'idCard',
},
{
label: '手机号',
key: 'phone',
},
{
label: '身高(cm)',
key: 'height',
},
{
label: '体重(kg)',
key: 'weight',
},
{
label: '所属公司',
key: 'depart',
},
{
label: '员工职务',
key: 'empJob_dictText',
},
{
label: '员工职位',
key: 'empPosition',
},
{
label: '行政级别',
key: 'empLevel_dictText',
},
{
label: '政治面貌',
key: 'empPolitical_dictText',
},
{
label: '学历',
key: 'empDegree_dictText',
},
{
label: '婚姻状况',
key: 'empMarriage_dictText',
},
{
label: '籍贯',
key: 'empNativeplace',
},
{
label: '入职时间',
key: 'empWorktime',
},
{
label: '用工形式',
key: 'empType_dictText',
},
{
label: '员工状态',
key: 'empStatus_dictText',
},
{
label: '居住地点',
key: 'liveSpace',
},
{
label: '工作地点',
key: 'workSpace',
},
];
// 3.健康信息
export const descList3: DescList[] = [
{
label: '疾病史',
key: '',
},
{
label: '家族史',
key: '',
},
{
label: '既往史',
key: '',
},
{
label: '过敏史',
key: '',
},
{
label: '手术史',
key: '',
},
{
label: '肝功能',
key: '',
},
{
label: '肾功能',
key: '',
},
{
label: '生活习惯',
key: '',
},
{
label: '烟酒习惯',
key: '',
},
{
label: '用药史',
key: '',
},
];
// 健康记录
export const tableColumns: TableColumns[] = [
{
title: '日期',
dataIndex: 'name',
key: 'name',
},
{
title: '诊断',
dataIndex: 'name',
key: 'name',
},
{
title: '机构',
dataIndex: 'name',
key: 'name',
},
{
title: '医生',
dataIndex: 'name',
key: 'name',
},
];
// 就诊卡号
export const cardColumns2: TableColumns[] = [
{
title: '医院',
dataIndex: 'name',
key: 'name',
},
{
title: '就诊卡号',
dataIndex: 'name',
key: 'name',
},
];
// 家庭成员信息
export const familyInfo: DescList[] = [
{
label: '与员工关系',
key: 'familyRelation_dictText',
},
{
label: '性别',
key: 'gender',
render: (text) => {
if (!text) return '';
return text == '1' ? '女' : text == '2' ? '男' : '';
},
},
{
label: '年龄',
key: 'age',
},
// {
// label: '所处城市',
// key: 'address',
// },
// {
// label: '身份证',
// key: 'idCard',
// },
{
label: '身高(cm)',
key: 'height',
},
{
label: '体重(kg)',
key: 'weight',
},
];
const getSecond = (formModel) => {
return formModel.hasOwnProperty('secondDepartId') && formModel.secondDepartId !== '' && formModel.secondDepartId !== undefined
? formModel.secondDepartId
: 'dw&*^^';
};
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,74 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable">
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<ConHealthInfoDrawer @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useDrawer } from '/@/components/Drawer';
import { useListPage } from '/@/hooks/system/useListPage';
import ConHealthInfoDrawer from './components/conHealthInfoDrawer.vue';
import { columns, searchFormSchema } from './conHealthInfo.data';
import { list } from './conHealthInfo.api';
//注册Drawer
const [registerDrawer, { openDrawer }] = useDrawer();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '咨询档案',
api: list,
columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { reload }] = tableContext;
/**
* 详情
*/
function handleDetail(record: Recordable) {
openDrawer(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
];
}
</script>
<style scoped></style>
@@ -0,0 +1,27 @@
interface Fn<T = any, R = T> {
(...arg: T[]): R;
}
export interface TableColumns {
title: string;
dataIndex: string;
key: string;
}
export interface DescList {
label: string;
key: string;
slot?: string;
render?: Fn;
spanNumber?: string | number;
}
export interface State {
activeKey: string;
}
interface DataSource {
[propName: string]: any;
}
export interface TableState {
dataSource: DataSource[];
columns: TableColumns[];
cardSource: DataSource[];
cardColumns2: TableColumns[];
}
@@ -0,0 +1,95 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conKnowledge/list',
save = '/health-consultation/consultation/conKnowledge/add',
edit = '/health-consultation/consultation/conKnowledge/edit',
deleteOne = '/health-consultation/consultation/conKnowledge/delete',
deleteBatch = '/health-consultation/consultation/conKnowledge/deleteBatch',
importExcel = '/health-consultation/consultation/conKnowledge/importExcel',
exportXls = '/health-consultation/consultation/conKnowledge/exportXls',
knowledgeType = '/health-consultation/consultation/conKnowledgeCategory/getCategoryAll',
tfTopKnow = '/health-consultation/consultation/conKnowledge/tfTopKnow',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* 获取知识库分类
* @param params
*/
export const getKnowledge = (params) => {
return defHttp.get({ url: Api.knowledgeType, params });
};
export const tfTopKnowApi = (params) => {
return defHttp.get({ url: Api.tfTopKnow, params });
};
@@ -0,0 +1,200 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
import { h } from 'vue';
import { Image } from 'ant-design-vue';
import { getDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { EyeOutlined } from '@ant-design/icons-vue';
import { getKnowledge } from '/@/views/consult/conKnowledge/ConKnowledge.api';
//列表数据
// @ts-ignore
export const columns: BasicColumn[] = [
{
title: '文章标题',
align: 'center',
dataIndex: 'knowTitle',
},
{
title: '知识类型',
align: 'center',
dataIndex: 'knowType',
customRender: ({ text }) => {
return render.renderDict(text, 'know_type');
},
},
{
title: '知识分类',
align: 'center',
dataIndex: 'knowClassName',
},
{
title: '知识图片',
align: 'center',
dataIndex: 'knowPic',
width: 100,
customRender: ({ text }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getDefaultImage(),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
// {
// title: '已学习次数',
// align: 'center',
// dataIndex: 'knowLookNumber',
// },
{
title: '是否推荐',
align: 'center',
dataIndex: 'tfRecommend',
customRender: ({ text }) => (text === '0' ? '否' : '是'),
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
return render.renderDict(text, 'freeze_status');
},
},
{
title: '排序',
align: 'center',
dataIndex: 'sort',
width: '70px',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '标题',
field: 'knowTitle',
component: 'Input',
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '知识标题',
field: 'knowTitle',
component: 'Input',
required: true,
},
{
label: '知识类型',
field: 'knowType',
component: 'JDictSelectTag',
componentProps: {
// dictCode: 'know_type',
options: [
{
label: '图文',
value: '0',
},
],
},
defaultValue: '0',
required: true,
},
{
label: '知识图片',
field: 'knowPic',
component: 'JImageUpload',
componentProps: {
fileMax: 1,
},
rules: [{ required: true, message: '请选择知识图片' }],
},
{
label: '知识分类',
field: 'knowClass',
component: 'ApiSelect',
componentProps: {
api: getKnowledge,
resultField: 'list',
labelField: 'name',
valueField: 'id',
immediate: true,
},
required: true,
},
// {
// label: '已学习次数',
// field: 'knowLookNumber',
// component: 'InputNumber',
// required: true,
// },
{
label: '是否推荐',
field: 'tfRecommend',
component: 'JDictSelectTag',
componentProps: {
type: 'radio',
options: [
{
value: '1',
label: '是',
},
{
value: '0',
label: '否',
},
],
},
defaultValue: '1',
required: true,
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
type: 'radio',
dictCode: 'freeze_status',
},
defaultValue: '1',
required: true,
},
{
label: '知识内容',
field: 'knowContent',
component: 'JEditor',
componentProps: {
showImageUpload: true,
},
rules: [{ required: true, message: '请输入知识内容' }],
},
{
label: '排序',
field: 'sort',
component: 'Input',
rules: [{ required: true, message: '请输入排序' }],
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,179 @@
<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" @click="batchHandleDelete" preIcon="ant-design:delete-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" />-->
<!-- 删除-->
<!-- </a-menu-item>-->
<!-- </a-menu>-->
<!-- </template>-->
<!-- <a-button-->
<!-- >批量操作-->
<!-- <Icon icon="mdi:chevron-down" />-->
<!-- </a-button>-->
<!-- </a-dropdown>-->
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
<template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)"
>下载
</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConKnowledgeModal @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="conKnowledge-conKnowledge" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import ConKnowledgeModal from './components/ConKnowledgeModal.vue';
import { columns, searchFormSchema } from './ConKnowledge.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, tfTopKnowApi } from './ConKnowledge.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { message } from 'ant-design-vue';
import { useDrawer } from '/@/components/Drawer';
//注册Drawer
const [registerDrawer, { openDrawer }] = useDrawer();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '知识库',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 220,
fixed: 'right',
},
},
exportConfig: {
name: '知识库',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openDrawer(true, {
isUpdate: false,
showFooter: true,
type: '新增',
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openDrawer(true, {
record,
isUpdate: true,
showFooter: true,
type: '编辑',
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openDrawer(true, {
record,
isUpdate: true,
showFooter: false,
type: '详情',
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
function handleTop(record: object) {
tfTopKnowApi({ id: record.id }).then(() => {
handleSuccess();
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: record.tfTop === '1' ? '取消置顶' : '置顶',
onClick: handleTop.bind(null, record),
},
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
</script>
<style scoped></style>
@@ -0,0 +1,69 @@
<template>
<BasicDrawer v-bind="$attrs" @register="registerDrawer" destroyOnClose :showFooter="showFooter" :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../ConKnowledge.data';
import { saveOrUpdate } from '../ConKnowledge.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const showFooter = ref<boolean>(true);
//设置标题
const title = ref(String);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
//重置表单
await resetFields();
setDrawerProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
showFooter.value = data.showFooter;
title.value = data.type;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setDrawerProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,81 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conKnowledgeCategory/list',
save = '/health-consultation/consultation/conKnowledgeCategory/add',
edit = '/health-consultation/consultation/conKnowledgeCategory/edit',
deleteOne = '/health-consultation/consultation/conKnowledgeCategory/delete',
deleteBatch = '/health-consultation/consultation/conKnowledgeCategory/deleteBatch',
importExcel = '/health-consultation/consultation/conKnowledgeCategory/importExcel',
exportXls = '/health-consultation/consultation/conKnowledgeCategory/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,73 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '分类名称',
align: 'center',
dataIndex: 'name',
},
{
title: '分类介绍',
align: 'center',
dataIndex: 'memo',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '分类名称',
field: 'name',
component: 'Input',
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '分类名称',
field: 'name',
component: 'Input',
required: true,
},
// {
// label: 'sort',
// field: 'sort',
// component: 'Input',
// },
// {
// label: '状态(1-正常,2-冻结)',
// field: 'status',
// component: 'Input',
// dynamicRules: ({ model, schema }) => {
// return [{ required: true, message: '请输入状态(1-正常,2-冻结)!' }];
// },
// },
// {
// label: '删除状态(0-正常,1-已删除)',
// field: 'delFlag',
// component: 'Input',
// dynamicRules: ({ model, schema }) => {
// return [{ required: true, message: '请输入删除状态(0-正常,1-已删除)!' }];
// },
// },
{
label: '分类介绍',
field: 'memo',
component: 'InputTextArea',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,146 @@
<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" @click="batchHandleDelete" preIcon="ant-design:delete-outlined"> 批量删除 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
<template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)"
>下载
</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConKnowledgeCategoryModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="conKnowledgeCategory-conKnowledgeCategory" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConKnowledgeCategoryModal from './components/ConKnowledgeCategoryModal.vue';
import { columns, searchFormSchema } from './ConKnowledgeCategory.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './ConKnowledgeCategory.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { message } from 'ant-design-vue';
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '知识库分类',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
exportConfig: {
name: '知识库分类',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
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,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
</script>
@@ -0,0 +1,71 @@
<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 '../ConKnowledgeCategory.data';
import { saveOrUpdate } from '../ConKnowledgeCategory.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,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
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>
@@ -0,0 +1,139 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { message } from 'ant-design-vue';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/conDepartment/list',
save = '/health-consultation/conDepartment/add',
edit = '/health-consultation/conDepartment/edit',
deleteOne = '/health-consultation/conDepartment/delete',
deleteBatch = '/health-consultation/conDepartment/deleteBatch',
importExcel = '/health-consultation/conDepartment/importExcel',
exportXls = '/health-consultation/conDepartment/exportXls',
department = '/health-consultation/conDepartment/getConDepartmentAll',
queryById = '/health-consultation/conDepartment/queryById',
listLevelOne = '/health-consultation/conDepartment/listLevelOne',
selectRelevancyById = '/health-consultation/conDepartment/selectRelevancyById',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params: any) => defHttp.get({ url: Api.list, params });
/**
* 根据id获取信息
* @param params
*/
export const resEditData = (params: any) => defHttp.get({ url: Api.queryById, params });
/**
* 科室列表
* @param params
*/
export const getDepartment = (params: any) => defHttp.get({ url: Api.department, params });
/**
* @Description:
* @date 2023/7/22
* @param params
* @param handleSuccess
* @param res
*/
export const deleteOne = (params: any, handleSuccess: Function, res: { doctorNum: any; resourceNum: any; sickNum: any }) => {
const { doctorNum, resourceNum, sickNum } = res;
if (doctorNum === 0 && resourceNum === 0 && sickNum === 0) {
return createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
} else {
let str = '该科室下';
const list: { num: number; msg: string }[] = [
{
num: doctorNum,
msg: `${doctorNum}名医生;`,
},
{
num: sickNum,
msg: `关联${sickNum}类疾病;`,
},
{
num: resourceNum,
msg: `${resourceNum}个医院使用该科室;`,
},
];
list.forEach((item) => {
if (item.num > 0) {
str += item.msg;
}
});
message.error(str + '禁止删除此数据!');
}
};
/**
* 批量删除
* @param params
* @param handleSuccess
*/
export const batchDelete = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params: any, isUpdate: boolean) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* @Description:获取一级科室列表
* @date 2023/7/18
* @param params
*/
export const listLevelOne = (params: any) => defHttp.get({ url: Api.listLevelOne, params });
/**
* @Description:查询该科室关联专家-疾病-医院
* @date 2023/7/22
* @param params 科室id
*/
export const selectRelevancyById = (params: any) => defHttp.get({ url: Api.selectRelevancyById, params });
@@ -0,0 +1,162 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { h } from 'vue';
import { Image } from 'ant-design-vue';
import { getDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { EyeOutlined } from '@ant-design/icons-vue';
import { listLevelOne } from '/@/views/consult/department/ConDepartment.api';
//列表数据
export const columns: BasicColumn[] = [
{
title: '科室名称',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '图片',
align: 'center',
dataIndex: 'image',
width: 100,
customRender: ({ text }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getDefaultImage(),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
{
title: '医生数',
align: 'center',
dataIndex: 'doctorNum',
},
{
title: '简介',
align: 'left',
dataIndex: 'mark',
},
{
title: '是否置顶',
align: 'center',
dataIndex: 'tfTop',
customRender: ({ text }) => (text === 1 ? '是' : '否'),
},
{
title: '排序',
align: 'center',
dataIndex: 'sort',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '科室名称',
field: 'departmentName',
component: 'Input',
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '科室名称',
field: 'departmentName',
component: 'Input',
required: true,
},
{
label: '所属科室',
field: 'officeparid',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
return {
api: listLevelOne,
labelField: 'departmentName',
valueField: 'id',
getPopupContainer: () => document.body,
immediate: true,
afterFetch: (res: any[]) => {
if (formModel.id && Array.isArray(res) && res.length > 0) {
return res?.filter((item) => item?.id !== formModel.id);
}
return res;
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
},
{
label: '科室图片',
field: 'image',
component: 'JImageUpload',
componentProps: {
fileMax: 1,
},
},
{
label: '科室简介',
field: 'mark',
component: 'InputTextArea',
componentProps: () => {
return {
minRows: 4,
};
},
},
{
label: '是否置顶',
field: 'tfTop',
component: 'RadioGroup',
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '',
field: 'value',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,150 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="null">
<!--插槽:table标题-->
<template #tableTitle>
<a-button v-auth="'consultation:con_department:add'" type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined">
新增
</a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConDepartmentModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conDepartment" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConDepartmentModal from './components/ConDepartmentModal.vue';
import { columns, searchFormSchema } from './ConDepartment.data';
import { list, deleteOne, resEditData, selectRelevancyById } from './ConDepartment.api';
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '医院科室',
api: list,
columns,
canResize: false,
// size: 'small',
isTreeTable: true,
pagination: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 180,
fixed: 'right',
},
},
});
const [registerTable, { reload }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
title: '新增',
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
resEditData({ id: record.id }).then((res) => {
openModal(true, {
record: res,
isUpdate: true,
showFooter: true,
title: '编辑',
});
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
resEditData({ id: record.id }).then((res) => {
openModal(true, {
record: res,
isUpdate: true,
showFooter: false,
title: '详情',
});
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
let res = await selectRelevancyById({ id: record.id });
// res.doctorNum = 50;
// console.log('res', res);
await deleteOne({ id: record.id }, handleSuccess, res);
}
/**
* 批量删除事件
*/
// async function batchHandleDelete() {
// if (selectedRowKeys.value.length === 0) {
// return message.warning('未选中任何数据');
// }
// await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
// }
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'consultation:con_department:edit',
},
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'consultation:con_department:delete',
},
];
}
</script>
<style scoped></style>
@@ -0,0 +1,73 @@
<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, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../ConDepartment.data';
import { saveOrUpdate } from '../ConDepartment.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 },
});
//设置标题
let title = ref<string>('');
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
title.value = data.title;
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
value: data.record.id,
officeparid: data.record.officeparid || '',
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
const params = {
...values,
officeparid: values.officeparid ? values.officeparid : null,
};
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(params, 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>
@@ -0,0 +1,117 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
export enum Api {
list = '/health-consultation/conDoctor/accountList',
save = '/health-consultation/conDoctor/add',
edit = '/health-consultation/conDoctor/edit',
deleteOne = '/health-consultation/conDoctor/delete',
deleteBatch = '/health-consultation/conDoctor/deleteBatch',
importExcel = '/health-consultation/conDoctor/importExcel',
exportXls = '/health-consultation/conDoctor/exportXlsAccount',
getCostLogByType = '/health-consultation/conDoctor/getCostLogByType',
updateCostByTitle = '/health-consultation/conDoctor/updateCostByTitle',
updateCostByUserId = '/health-consultation/conDoctor/updateCostByUserId',
getDoctorCard = '/health-consultation/consultation/conDoctorCard/list',
deleteCard = '/health-consultation/consultation/conDoctorCard/delete',
getCostList = '/health-consultation/conCostStatistics/list',
withdraw = '/health-consultation/conCostStatistics/payment',
getRecordDetail1 = '/health-consultation/consultation/conSession/queryByIdDetails',
getRecordDetail2 = '/health-consultation/conCostStatistics/queryById',
addCard = '/health-consultation/consultation/conDoctorCard/addList',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
* @param handleSuccess
*/
export const batchDelete = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params: any, isUpdate: boolean) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
// 账户结算/ 提现
export const withdraw = (params) => defHttp.post({ url: Api.withdraw, params });
// 查看历史费用
export const getCostLogByType = (params) => {
return defHttp.get({ url: Api.getCostLogByType, params });
};
// 统一设置费用
export const updateCostByTitle = (params) => defHttp.get({ url: Api.updateCostByTitle, params });
// 单独设置非同
export const updateCostByUserId = (params) => defHttp.get({ url: Api.updateCostByUserId, params });
//获取所有银行卡
export const getDoctorCard = (params) => {
return new Promise(async (resolve) => {
try {
const res = await defHttp.get({ url: Api.getDoctorCard, params });
return resolve(res['records']);
} catch {
return resolve([]);
}
});
};
// 账户历史查询
export const getCostList = (params) => defHttp.get({ url: Api.getCostList, params });
//获取记录详情
export const getRecordDetail = (params) => defHttp.get({ url: Api.getRecordDetail1, params });
// 提现详情
export const getRecordDetail2 = (params) => defHttp.get({ url: Api.getRecordDetail2, params });
//导出专家账户
export const exportFile = (params) => defHttp.post({ url: Api.exportXls, params });
// 添加专家银行卡
export const addCard = (params) => defHttp.post({ url: Api.addCard, params });
//删除专家银行卡
export const deleteCard = (params) => defHttp.delete({ url: Api.deleteCard + '?id=' + params.id });
@@ -0,0 +1,669 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
import { BODY_CONTAINER } from '/@/utils/domUtils';
import { getDoctorCard } from '/@/views/consult/doctor/account/account.api';
import { selectResourceList } from '/@/views/consult/doctor/message/conDoctor.api';
import { h } from 'vue';
//列表数据
export const columns: BasicColumn[] = [
{
title: '姓名',
align: 'center',
dataIndex: 'doctorName',
fixed: 'left',
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
width: 70,
customRender: ({ text }) => {
return render.renderDict(text, 'sex2');
},
},
{
title: '手机号',
align: 'center',
dataIndex: 'phone',
},
{
title: '身份证号',
align: 'center',
dataIndex: 'idCard',
width: 200,
},
{
title: '医院',
align: 'center',
dataIndex: 'resourceName',
},
{
title: '科室',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '职称',
align: 'center',
dataIndex: 'doctorTitle_dictText',
},
{
title: '账户余额',
align: 'center',
dataIndex: 'accountMoneyToString',
},
{
title: '专家银行卡',
align: 'center',
dataIndex: 'cardNum',
width: 200,
},
{
title: '开户行',
align: 'center',
dataIndex: 'cardMsg',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '姓名',
field: 'doctorName',
component: 'Input',
},
{
label: '医院',
field: 'hospitalName',
component: 'ApiSelect',
componentProps: {
api: selectResourceList,
resultField: 'list',
labelField: 'name',
valueField: 'id',
immediate: true,
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
},
},
{
label: '科室',
field: 'departmentName',
component: 'JTreeDepartment',
componentProps: () => {
return {
allDep: true,
placeholder: '请选择科室',
showSearch: true,
treeNodeFilterProp: 'label',
};
},
},
{
label: '职称',
field: 'doctorTitle',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'z_doct_lev',
},
},
{
label: '手机号',
field: 'phone',
component: 'Input',
rules: [{ pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误', trigger: 'blur' }],
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '职称',
field: 'doctorJob',
component: 'Input',
},
{
label: '职务',
field: 'doctorTitle',
component: 'Input',
},
{
label: '擅长',
field: 'goodAt',
component: 'InputTextArea',
},
{
label: '执业经历',
field: 'experience',
component: 'InputTextArea',
},
{
label: '所属医院id',
field: 'resourceId',
component: 'Input',
},
{
label: '所属科室id',
field: 'departmentId',
component: 'Input',
},
{
label: '擅长症状',
field: 'goodAtSymptom',
component: 'InputTextArea',
},
{
label: '擅长疾病',
field: 'goodAtSickness',
component: 'InputTextArea',
},
{
label: '值班时间',
field: 'onDutyTime',
component: 'Input',
},
{
label: '是否接受咨询',
field: 'isAccept',
component: 'Input',
},
{
label: '专家类型',
field: 'type',
component: 'Input',
},
{
label: '医生状态',
field: 'doctorStatus',
component: 'Input',
},
{
label: '高德地图id',
field: 'gdId',
component: 'Input',
},
{
label: '毕业时间',
field: 'outTime',
component: 'DatePicker',
},
{
label: '住址',
field: 'address',
component: 'Input',
},
{
label: '联系地址',
field: 'contactAddress',
component: 'Input',
},
{
label: '民族',
field: 'nationality',
component: 'Input',
},
{
label: '学历',
field: 'eQ',
component: 'Input',
},
{
label: '籍贯',
field: 'nativePlace',
component: 'Input',
},
{
label: '参加工作时间',
field: 'joinWork',
component: 'DatePicker',
},
{
label: '健康状态',
field: 'healthStatus',
component: 'Input',
},
{
label: '毕业学校',
field: 'school',
component: 'Input',
},
{
label: '获奖或者论文',
field: 'award',
component: 'Input',
},
{
label: '标签',
field: 'doctorLabel',
component: 'Input',
},
{
label: '状态(1-激活,2-锁定)',
field: 'status',
component: 'InputNumber',
dynamicRules: () => {
return [{ required: true, message: '请输入状态(1-激活,2-锁定)!' }];
},
},
{
label: '删除状态(0-正常,1-已删除)',
field: 'delFlag',
component: 'InputNumber',
dynamicRules: () => {
return [{ required: true, message: '请输入删除状态(0-正常,1-已删除)!' }];
},
},
{
label: '备注',
field: 'memo',
component: 'Input',
},
{
label: '医生姓名',
field: 'doctorName',
component: 'Input',
},
{
label: '综合评价 上限是5',
field: 'overallMerit',
component: 'InputNumber',
},
{
label: '回复率',
field: 'responseRate',
component: 'Input',
},
{
label: '热度',
field: 'degreeHeat',
component: 'Input',
},
{
label: '头像',
field: 'photo',
component: 'Input',
},
{
label: '简介',
field: 'introduction',
component: 'Input',
},
{
label: '用户总评分',
field: 'userScore',
component: 'InputNumber',
},
{
label: '评分用户总数',
field: 'userScoreNum',
component: 'InputNumber',
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
},
{
label: '回复数',
field: 'replyNum',
component: 'InputNumber',
},
{
label: '消息总数',
field: 'messageNum',
component: 'InputNumber',
},
{
label: '是否推荐 0否1是',
field: 'tfRecommend',
component: 'InputNumber',
},
{
label: '医院名称',
field: 'resourceName',
component: 'Input',
},
{
label: '科室名称',
field: 'departmentName',
component: 'Input',
},
{
label: '是否展示小火苗 0否1是',
field: 'tfShowFire',
component: 'InputNumber',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
// 账户结算form
export const accountForm: FormSchema[] = [
{
label: '专家',
field: 'doctorName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '所属医院',
field: 'resourceName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '手机号',
field: 'phone',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '身份证号',
field: 'idCard',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '账户余额',
field: 'accountMoney',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '提现方式',
field: 'withdraw',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'withdrawal_type',
getPopupContainer: () => document.body,
},
rules: [{ required: true }],
},
{
label: '提现银行卡',
field: 'paymentAccount',
component: 'ApiSelect',
rules: [{ required: true }],
ifShow: ({ values }) => {
return values.withdraw == '0';
},
componentProps: ({ formModel }) => {
return {
api: getDoctorCard,
resultField: 'list',
params: { doctorId: formModel.id },
valueField: 'cardNum',
labelField: 'cardNum',
immediate: false,
};
},
},
{
label: '提现金额',
field: 'paymentMoney',
component: 'InputNumber',
componentProps: {
style: {
width: '100%',
},
},
rules: [{ required: true }],
},
{
label: '实际结算金额',
field: 'realityMoney',
component: 'InputNumber',
componentProps: {
style: {
width: '100%',
},
},
rules: [{ required: true }],
},
{
label: '提现说明',
field: 'remarks',
component: 'InputTextArea',
rules: [{ required: true }],
},
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
// 咨询费用统一设置
export const costSettingForm: FormSchema[] = [
{
label: '专家职称',
required: true,
field: 'titleId',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'z_doct_lev',
getPopupContainer: () => document.body,
},
},
{
label: '图文咨询',
field: 'graphicCost',
component: 'InputNumber',
componentProps: {
style: {
width: '100%',
},
placeholder: '请输入图文咨询费用',
},
suffix: '元/次',
rules: [{ required: true, trigger: 'blur', message: '请输入图文咨询费用' }],
},
{
label: '视频咨询',
field: 'videoCost',
component: 'InputNumber',
componentProps: {
placeholder: '请输入视频咨询费用',
style: {
width: '100%',
},
},
suffix: '元/次',
rules: [{ required: true, trigger: 'blur', message: '请输入视频咨询费用' }],
},
];
// 咨询费用单个设置
export const costSettingSelfForm: FormSchema[] = [
{
label: '专家',
field: 'doctorName',
component: 'Input',
required: true,
componentProps: {
disabled: true,
},
},
{
label: '所属医院',
field: 'resourceName',
component: 'Input',
required: true,
componentProps: {
disabled: true,
},
},
{
label: '图文咨询',
field: 'graphicCost',
component: 'InputNumber',
componentProps: {
placeholder: '请输入图文咨询费用',
style: {
width: '100%',
},
},
suffix: '元/次',
rules: [{ required: true, trigger: 'blur', message: '请输入图文咨询费用' }],
},
{
label: '视频咨询',
field: 'videoCost',
component: 'InputNumber',
componentProps: {
placeholder: '请输入视频咨询费用',
style: {
width: '100%',
},
},
suffix: '元/次',
rules: [{ required: true, trigger: 'blur', message: '请输入视频咨询费用' }],
},
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
// 账户历史
export const accountDetailForm: FormSchema[] = [
{
label: '专家姓名',
field: 'doctorName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '所属医院',
field: 'resourceName',
component: 'Input',
componentProps: {
disabled: true,
},
},
{
label: '账单类型',
field: 'operateType',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'historical_type',
// allowClear: false,
showChooseOption: false,
},
// defaultValue: '0',
},
{
label: '选择日期',
field: 'Date',
component: 'RangePicker',
componentProps: ({ formModel }) => {
return {
valueType: 'Date',
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => BODY_CONTAINER,
'onUpdate:value': (value) => {
if (value) {
formModel.startDate = value[0];
formModel.endDate = value[1];
} else {
formModel.startDate = null;
formModel.endDate = null;
}
},
};
},
},
{
label: '',
field: 'startDate',
component: 'Input',
show: false,
},
{
label: '',
field: 'endDate',
component: 'Input',
show: false,
},
];
export const accountDetailColumn: BasicColumn[] = [
{
title: '业务时间',
align: 'center',
dataIndex: 'incomeTime',
},
{
title: '业务',
align: 'center',
dataIndex: 'operateName',
},
{
title: '业务金额',
align: 'center',
dataIndex: 'income',
customRender: ({ text, record }) => {
if (!text) return '';
if (record?.operateType == '5') {
return h('div', { style: { color: 'red' } }, `- ¥${text}`);
}
return h('div', { style: { color: 'green' } }, `+ ¥${text}`);
},
},
{
title: '账户余额',
align: 'center',
dataIndex: 'accountMoneyToString',
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
export function dealResData(data) {
const arr = [];
data?.map((item) => {
arr.push({ time: item.memo, ...item });
});
return arr;
}
export function numberPrefix(number, prefix) {
if (number || number === 0) {
return prefix + number;
} else {
return '';
}
}
export interface CardType {
cardMsg: string;
cardNum: string;
status: boolean;
}
export interface FormState {
data: CardType[];
doctorId: string;
}
@@ -0,0 +1,180 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="accountWithdrawalBtn">账户结算</a-button>
<a-button type="primary" @click="freeSetting">统一费用设置</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="exportRecord"> 查看导出任务 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<account-withdrawal @register="registerModal" @success="handleSuccess" />
<!-- 咨询费用统一设置 -->
<SingleCostSetting @register="singleCostModal" />
<!-- 个人咨询费用设置 -->
<CostSettingSelf @register="costSettingSelfModal" @success="handleSuccess" />
<!-- 账户历史-->
<AccountDetails @register="accountDetailModal" />
<export-util task-code="comDoctorAccount" @register="registerExport" />
<!--添加银行卡-->
<BankCard @register="bankCardModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-account" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './account.data';
import { exportFile, getExportUrl, getImportUrl, list } from './account.api';
import AccountWithdrawal from './components/accountWithdrawal.vue';
import SingleCostSetting from '/@/views/consult/doctor/account/components/singleCostSetting.vue';
import CostSettingSelf from './components/costSettingSelf.vue';
import AccountDetails from './components/accountDetails.vue';
import BankCard from '/@/views/consult/doctor/account/components/bankCard.vue';
import { message } from 'ant-design-vue';
import ExportUtil from '/@/utils/export/exportUtil.vue';
import { useDrawer } from '/@/components/Drawer';
//注册model
const [registerModal, { openModal }] = useModal();
const [singleCostModal, { openModal: openSingleCostModal }] = useModal();
const [costSettingSelfModal, { openModal: openCostSettingSelfModal }] = useModal();
const [accountDetailModal, { openModal: openAccountDetailModal }] = useModal();
const [bankCardModal, { openModal: openBankCardModalModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '专家账户',
api: list,
columns,
canResize: false,
formConfig: {
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 164,
fixed: 'right',
},
},
exportConfig: {
name: 'con_doctor',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload, getDataSource, getForm }, { rowSelection, selectedRowKeys }] = tableContext;
const [registerExport, { openDrawer }] = useDrawer();
// 账户结算
function accountWithdrawalBtn() {
if (!selectedRowKeys.value.length || selectedRowKeys.value.length > 1) {
return message.warn('请选择一位专家');
}
const [id] = selectedRowKeys?._rawValue;
const data = getDataSource();
const records = data.filter((item) => {
if (item.id == id) {
return true;
}
});
openModal(true, {
records: records[0],
isUpdate: false,
showFooter: true,
});
}
// 费用设置统一费用设置
function freeSetting() {
openSingleCostModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openAccountDetailModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
//单独咨询费用设置
function openSettingModal(record: Recordable) {
openCostSettingSelfModal(true, {
record,
isUpdate: false,
showFooter: true,
});
}
/**
* @Description:导出到记录表
* @date 2023/7/3
*/
async function onExportXls() {
const form = getForm().getFieldsValue();
await exportFile(form);
}
function exportRecord() {
openDrawer(true, {});
}
/**
* @Description:添加银行卡
* @date 2023/7/19
*/
function addBankCard(record: Recordable) {
openBankCardModalModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '咨询费用设置',
onClick: openSettingModal.bind(null, record),
},
];
}
function getDropDownAction(record: Recordable) {
return [
{
label: '账户历史',
onClick: handleDetail.bind(null, record),
},
{
label: '添加银行卡',
onClick: addBankCard.bind(null, record),
},
];
}
</script>
@@ -0,0 +1,171 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1100" @ok="handleSubmit" @cancel="clearData">
<div style="height: 65vh">
<!--引用表格-->
<BasicTable @register="registerTable">
<!--插槽:table标题-->
<template #tableTitle>
<div>
<span class="ml10"><span class="text-label">账户金额</span>{{ numberPrefix(doctorAccount.accountMoney, '¥') }}</span>
<span class="ml10"><span class="text-label">累计收益</span>{{ numberPrefix(doctorAccount.accumulatedIncome, '¥') }}</span>
<span class="ml10"><span class="text-label">提现金额</span>{{ numberPrefix(doctorAccount.withdrawalAmount, '¥') }}</span>
</div>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!--咨询详情-->
<staffForDoctorModal @register="registerDetailModal" />
<!--提现详情-->
<accountWithdrawal @register="registerAccountModal" />
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import { accountDetailForm, accountDetailColumn, numberPrefix } from '/@/views/consult/doctor/account/account.data';
import { BasicTable, TableAction } from '/@/components/Table';
import { getCostList, getRecordDetail, getRecordDetail2 } from '/@/views/consult/doctor/account/account.api';
import { ColEx } from '/@/components/Form/src/types';
import StaffForDoctorModal from '/@/views/consult/staff/staffForDoctor/components/staffForDoctorModal.vue';
import AccountWithdrawal from '/@/views/consult/doctor/account/components/accountWithdrawal.vue';
const title = '账户历史';
const record = ref({});
const doctorAccount = ref({});
// 自适应列配置
const adaptiveColProps: Partial<ColEx> = {
offset: 0,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
};
//表单赋值
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
record.value = data.record;
// setForm();
await getForm().setFieldsValue({
...record.value,
});
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
// // 隐藏底部时禁用整个表单
// await getForm().setProps({ disabled: !data?.showFooter });
});
//咨询详情
const [registerDetailModal, { openModal: openDetailModal }] = useModal();
//提现详情
const [registerAccountModal, { openModal: openAccountModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '专家账户',
api: getCostList,
columns: accountDetailColumn,
canResize: false,
formConfig: {
baseColProps: adaptiveColProps,
schemas: accountDetailForm,
autoSubmitOnEnter: true,
resetFunc: () => {
let timer = setTimeout(async () => {
clearTimeout(timer);
// 保留姓名和医院
return await getForm().setFieldsValue({
...record.value,
});
});
},
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
actionColOptions: {
offset: 1,
span: 8,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
},
},
beforeFetch: (params) => {
// 赋值专家ID
params.userId = record.value.id;
return params;
},
afterFetch: () => {
const dataSource = getRawDataSource();
doctorAccount.value = {
accountMoney: dataSource.accountMoney,
accumulatedIncome: dataSource.accumulatedIncome,
withdrawalAmount: dataSource.withdrawalAmount,
};
return dataSource.records;
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { getForm, getRawDataSource }] = tableContext;
// 咨询详情
function openConsultModal(record: Recordable) {
if (!record.sessionId) return;
getRecordDetail({ id: record.sessionId }).then((res) => {
openDetailModal(true, {
record: res,
isUpdate: true,
showFooter: false,
});
});
}
//提现详情
function openWithDrawModal(row) {
getRecordDetail2({ id: row.id }).then((res) => {
openAccountModal(true, {
records: { ...res, ...res.conDoctor, accountMoney: res.accountMoney },
isUpdate: true,
showFooter: false,
});
});
// registerAccountModal()
}
function getTableAction(record: Recordable) {
return [
{
label: '咨询详情',
ifShow: () => record.operateType !== null && record.operateType !== '5',
onClick: openConsultModal.bind(null, record),
},
{
label: '提现详情',
ifShow: () => record.operateType == '5',
onClick: openWithDrawModal.bind(null, record),
},
];
}
function handleSubmit() {}
function clearData() {}
</script>
<style scoped lang="less">
.ml10 {
margin-left: 10px;
}
.text-label {
color: rgb(0, 0, 0.85);
}
</style>
@@ -0,0 +1,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 '../account.data';
import { saveOrUpdate } from '../account.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,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
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>
@@ -0,0 +1,56 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit" @cancel="clearData">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { accountForm } from '/@/views/consult/doctor/account/account.data';
import { withdraw } from '/@/views/consult/doctor/account/account.api';
const emit = defineEmits(['success']);
const title = '账户结算';
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = useForm({
//labelWidth: 150,
schemas: accountForm,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
console.log('data', data);
//重置表单
await resetFields();
await setFieldsValue({
...data.records,
});
await clearValidate();
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
function clearData() {}
// withdraw
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
const json = JSON.parse(JSON.stringify(values).replace(/id/g, 'userId'));
//提交表单
await withdraw(json);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,130 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit" @cancel="clearData">
<a-form class="form-card-container" ref="bankForm" :model="formState.data" :rules="rules">
<a-list :data-source="formState.data" :bordered="true">
<template #header>
<a-row>
<a-col flex="4" class="text-c">开户行</a-col>
<a-col flex="4" class="text-c">银行卡号</a-col>
<a-col flex="3" class="text-c">状态</a-col>
<a-col flex="2" class="text-c">
<a-button size="small" title="添加" type="primary" preIcon="ant-design:plus-outlined" @click="handleAdd" />
</a-col>
</a-row>
</template>
<template #renderItem="{ item, index }">
<a-row class="row-self">
<a-col flex="4" class="text-c">
<a-form-item :name="[index, 'cardMsg']" :prop="`data.${index}.cardMsg`" :rules="rules.cardMsg">
<a-input v-model:value="item.cardMsg" style="width: 80%" placeholder="请输入开户行" />
</a-form-item>
</a-col>
<a-col flex="4" class="text-c">
<a-form-item :name="[index, 'cardNum']" :prop="`data.${index}.cardNum`" :rules="rules.cardNum">
<a-input v-model:value="item.cardNum" style="width: 80%" placeholder="请输入银行卡号" />
</a-form-item>
</a-col>
<a-col flex="3" class="text-c">
<a-form-item :name="[index, 'status']" :prop="`data.${index}.status`" :rules="rules.status">
<a-switch v-model:checked="item.status" />
</a-form-item>
</a-col>
<a-col flex="2" class="text-c">
<a-button size="small" title="删除" preIcon="ant-design:delete-outlined" @click="handleDelete(index)" />
</a-col>
</a-row>
</template>
</a-list>
</a-form>
</BasicModal>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { FormInstance } from 'ant-design-vue';
import { addCard, getDoctorCard, deleteCard } from '/@/views/consult/doctor/account/account.api';
import { FormState, CardType } from '/@/views/consult/doctor/account/account.data';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
// Emits声明
const emit = defineEmits(['register', 'success']);
const title = '专家银行卡';
const bankForm = ref<FormInstance>();
const formState = reactive<FormState>({
data: [],
doctorId: '',
});
const rules = {
cardMsg: [{ required: true, trigger: 'blur', pattern: /[\u4e00-\u9fa5]/, message: '请输入正确的银行卡开户行' }],
cardNum: [{ required: true, trigger: 'blur', pattern: /^([1-9])(\d{14,18})$/, message: '银行卡号格式有误' }],
status: [{ required: true, trigger: 'blur', message: '请选择是否启用该卡号' }],
};
const [registerModal, { closeModal }] = useModalInner(async (data) => {
formState.doctorId = data.record.id;
try {
let res = await getDoctorCard({ doctorId: data.record.id });
if (res && res.length) {
formState.data = res.map((item) => ({
cardMsg: item.cardMsg,
cardNum: item.cardNum,
status: item.status == '1',
id: item.id,
}));
}
} catch (e) {}
});
function handleAdd() {
const item: CardType = { cardMsg: '', cardNum: '', status: true };
formState.data.unshift(item);
}
function handleDelete(index) {
let id = formState.data[index].id || '';
if (id) {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await deleteCard({ id: id });
formState.data.splice(index, 1);
},
});
} else {
formState.data.splice(index, 1);
}
}
function clearData() {
formState.data = [];
formState.doctorId = '';
}
async function handleSubmit() {
try {
await bankForm.value?.validateFields();
const params = formState.data.map((item) => ({ ...item, status: item?.status ? 1 : 2, doctorId: formState.doctorId }));
await addCard(params);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
clearData();
}
}
</script>
<style scoped lang="less">
.row-self {
margin-top: 8px;
padding: 0 24px;
}
.text-c {
text-align: center;
}
.form-card-container {
max-height: 420px;
overflow: auto;
}
</style>
@@ -0,0 +1,130 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit" @cancel="clearData">
<div style="height: 60vh">
<BasicForm @register="registerForm" />
<div class="time-line-container">
<a-button type="primary" @click="openTimeLine"> 历史费用 </a-button>
<div class="time-line-box" :class="[isShrink ? 'unfold' : 'shrink']">
<TimeLine class="mt20" :dataList="timeLineList">
<template #customSlot="{ data }">
<div class="time-content mt10">
<div class="content-item"> 图文咨询 {{ data.graphicCost || '' }} /</div>
<div class="content-item"> 视频咨询 {{ data.videoCost || '' }} / </div>
</div>
</template>
</TimeLine>
</div>
</div>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { costSettingSelfForm, dealResData } from '/@/views/consult/doctor/account/account.data';
import { message } from 'ant-design-vue';
import { getCostLogByType, updateCostByUserId } from '/@/views/consult/doctor/account/account.api';
import TimeLine from '/@/views/consult/doctor/account/components/timeLine.vue';
// Emits声明
const emit = defineEmits(['register', 'success']);
const title = '咨询费用设置';
const isShrink = ref(false);
const timeLineList = ref([]);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, getFieldsValue, clearValidate }] = useForm({
//labelWidth: 150,
schemas: costSettingSelfForm,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
await setFieldsValue({
...data.record,
});
await getTimeLineData();
await clearValidate();
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
async function getTimeLineData() {
let { id } = getFieldsValue();
if (!id) {
return message.warn('请先选择专家职称!');
}
const data = await getCostLogByType({ userId: id + '', type: 0, titleId: '' });
await setFieldsValue({
graphicCost: data[0]?.graphicCost,
videoCost: data[0]?.videoCost,
});
timeLineList.value = dealResData(data) || [];
}
function openTimeLine() {
isShrink.value = !isShrink.value;
if (!timeLineList.value.length) {
return message.info('暂无历史费用!');
}
}
function clearData() {
timeLineList.value = [];
isShrink.value = false;
}
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await updateCostByUserId({ ...values, userId: values.id });
clearData();
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style scoped lang="less">
.time-line-container {
padding: 0 20px;
}
.mt20 {
margin-top: 20px;
}
.mt10 {
margin-top: 10px;
}
.time-content {
width: 45%;
display: flex;
justify-content: space-between;
color: #303133;
.content-item {
width: 50%;
}
}
.time-line-box {
transition: all 0.3s linear;
&.shrink {
max-height: 0;
height: 0;
overflow: auto;
}
&.unfold {
max-height: 500px;
}
}
</style>
@@ -0,0 +1,131 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit" @cancel="clearData">
<div class="detail-container">
<BasicForm @register="registerForm" />
<div class="time--line-container">
<a-button type="primary" @click="openTimeLine"> 查看历史费用</a-button>
<div class="time-line-box" :class="[isShrink ? 'unfold' : 'shrink']">
<TimeLine class="mt20" :dataList="timeLineList">
<template #customSlot="{ data }">
<div class="time-content mt10">
<div class="content-item"> 图文咨询 {{ data.graphicCost || '' }} /</div>
<div class="content-item"> 视频咨询 {{ data.videoCost || '' }} / </div>
</div>
</template>
</TimeLine>
</div>
</div>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { costSettingForm, dealResData } from '/@/views/consult/doctor/account/account.data';
import { getCostLogByType, updateCostByTitle } from '/@/views/consult/doctor/account/account.api';
import { message } from 'ant-design-vue';
import TimeLine from '/@/views/consult/doctor/account/components/timeLine.vue';
const title = '咨询费用统一设置';
const isShrink = ref(false);
const timeLineList = ref([]);
//表单配置
const [registerForm, { setProps, resetFields, validate, getFieldsValue }] = useForm({
//labelWidth: 150,
schemas: costSettingForm,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
async function openTimeLine() {
isShrink.value = true;
let { titleId } = getFieldsValue();
if (!titleId) {
return message.warn('请先选择专家职称!');
}
const data = await getCostLogByType({ titleId: titleId + '', type: 1, userId: '' });
if (!data.length) {
timeLineList.value = [];
return message.info('暂无历史费用!');
}
timeLineList.value = dealResData(data) || [];
}
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
function clearData() {
timeLineList.value = [];
isShrink.value = false;
}
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await updateCostByTitle(values);
clearData();
//关闭弹窗
closeModal();
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style scoped lang="less">
.detail-container {
height: 65vh;
overflow: auto;
}
.time--line-container {
padding: 0 20px;
}
.mt20 {
margin-top: 20px;
}
.mt10 {
margin-top: 10px;
}
.time-line-item {
display: flex;
flex-direction: column;
margin-top: 8px;
.time-title {
color: #909399;
margin-bottom: 8px;
}
.time-content {
width: 45%;
display: flex;
justify-content: space-between;
color: #303133;
}
}
.time-line-box {
transition: all 0.3s linear;
&.shrink {
max-height: 0;
}
&.unfold {
max-height: 500px;
}
}
</style>
@@ -0,0 +1,192 @@
<template>
<ul class="timeline">
<li class="timeline-item" v-for="(item, i) in list" :key="i">
<div class="timeline-item-tail"></div>
<div class="timeline-item-head" :class="[itemColor(item.color)]"></div>
<div class="timeline-item-content">
<div>{{ item.time }}</div>
<slot name="customSlot" :data="item"></slot>
</div>
</li>
</ul>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
const props = defineProps({
dataList: {
type: Array,
},
});
const list = computed(() => props.dataList);
function itemColor(color) {
const colorType = {
green: 'timeline-item-head-green',
red: 'timeline-item-head-red',
gray: 'timeline-item-head-gray',
};
return (color && colorType[color]) || 'timeline-item-head-blue';
}
</script>
<style scoped lang="less">
.timeline {
box-sizing: border-box;
color: #000000d9;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5715;
margin: 0;
padding: 0;
list-style: none;
}
.timeline-item {
position: relative;
margin: 0;
padding: 0 0 20px;
font-size: 14px;
list-style: none;
}
.timeline-item-tail {
position: absolute;
top: 10px;
left: 4px;
height: calc(100% - 10px);
border-left: 2px solid #f0f0f0;
}
.timeline-item-pending .timeline-item-head {
font-size: 12px;
background-color: transparent;
}
.timeline-item-pending .timeline-item-tail {
display: none;
}
.timeline-item-head {
position: absolute;
width: 10px;
height: 10px;
background-color: #fff;
border: 2px solid transparent;
border-radius: 100px;
}
.timeline-item-head-blue {
color: #1890ff;
border-color: #1890ff;
}
.timeline-item-head-red {
color: #ff4d4f;
border-color: #ff4d4f;
}
.timeline-item-head-green {
color: #52c41a;
border-color: #52c41a;
}
.timeline-item-head-gray {
color: #00000040;
border-color: #00000040;
}
.timeline-item-head-custom {
position: absolute;
top: 5.5px;
left: 5px;
width: auto;
height: auto;
margin-top: 0;
padding: 3px 1px;
line-height: 1;
text-align: center;
border: 0;
border-radius: 0;
transform: translate(-50%, -50%);
}
.timeline-item-content {
position: relative;
top: -7.001px;
margin: 0 0 0 18px;
word-break: break-word;
}
.timeline-item-last > .timeline-item-tail {
display: none;
}
.timeline-item-last > .timeline-item-content {
min-height: 48px;
}
.timeline.timeline-alternate .timeline-item-tail,
.timeline.timeline-right .timeline-item-tail,
.timeline.timeline-alternate .timeline-item-head,
.timeline.timeline-right .timeline-item-head,
.timeline.timeline-alternate .timeline-item-head-custom,
.timeline.timeline-right .timeline-item-head-custom {
left: 50%;
}
.timeline.timeline-alternate .timeline-item-head,
.timeline.timeline-right .timeline-item-head {
margin-left: -4px;
}
.timeline.timeline-alternate .timeline-item-head-custom,
.timeline.timeline-right .timeline-item-head-custom {
margin-left: 1px;
}
.timeline.timeline-alternate .timeline-item-left .timeline-item-content,
.timeline.timeline-right .timeline-item-left .timeline-item-content {
left: calc(50% - 4px);
width: calc(50% - 14px);
text-align: left;
}
.timeline.timeline-alternate .timeline-item-right .timeline-item-content,
.timeline.timeline-right .timeline-item-right .timeline-item-content {
width: calc(50% - 12px);
margin: 0;
text-align: right;
}
.timeline.timeline-right .timeline-item-right .timeline-item-tail,
.timeline.timeline-right .timeline-item-right .timeline-item-head,
.timeline.timeline-right .timeline-item-right .timeline-item-head-custom {
left: calc(100% - 6px);
}
.timeline.timeline-right .timeline-item-right .timeline-item-content {
width: calc(100% - 18px);
}
.timeline.timeline-pending .timeline-item-last .timeline-item-tail {
display: block;
height: calc(100% - 14px);
border-left: 2px dotted #f0f0f0;
}
.timeline.timeline-reverse .timeline-item-last .timeline-item-tail {
display: none;
}
.timeline.timeline-reverse .timeline-item-pending .timeline-item-tail {
top: 15px;
display: block;
height: calc(100% - 15px);
border-left: 2px dotted #f0f0f0;
}
.timeline.timeline-reverse .timeline-item-pending .timeline-item-content {
min-height: 48px;
}
</style>
@@ -0,0 +1,105 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conDoctorChangeLog/list',
save = '/health-consultation/consultation/conDoctorChangeLog/add',
edit = '/health-consultation/consultation/conDoctorChangeLog/edit',
deleteOne = '/health-consultation/consultation/conDoctorChangeLog/delete',
deleteBatch = '/health-consultation/consultation/conDoctorChangeLog/deleteBatch',
updateSessionUrl = '/health-consultation/consultation/conDoctorChangeLog/updateSessionByLog',
undoUpdateSessionUrl = '/health-consultation/consultation/conDoctorChangeLog/undoUpdateSessionByLog',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
* @param handleSuccess
*/
export const batchDelete = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params: any, isUpdate: boolean) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
export const updateSession = (params, handleSuccess) => {
const data = { id: params.id };
createConfirm({
iconType: 'warning',
title: '提示',
content: `确认更新历史咨询数据吗?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.post({ url: Api.updateSessionUrl, data }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
export const undoUpdateSession = (params, handleSuccess) => {
const data = { id: params.id };
createConfirm({
iconType: 'warning',
title: '提示',
content: `确认撤销更新历史咨询数据吗?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.post({ url: Api.undoUpdateSessionUrl, data }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
@@ -0,0 +1,258 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: 'id',
align: 'center',
dataIndex: 'id',
ifShow: false,
},
{
title: '专家ID',
align: 'center',
dataIndex: 'doctorId',
ifShow: false,
},
{
title: '专家名称(表单使用)',
align: 'center',
dataIndex: 'doctorNameWhole',
ifShow: false,
customRender: ({ text, record }) => {
let doctorNameWhole = record.doctorName + '【' + record.resourceName + '-' + record.departmentName + '】';
record.doctorNameWhole = doctorNameWhole;
return doctorNameWhole;
},
},
{
title: '专家名称',
align: 'center',
dataIndex: 'doctorName',
},
{
title: '医院',
align: 'center',
dataIndex: 'resourceName',
},
{
title: '科室',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '专家职称',
align: 'center',
width: 200,
// dataIndex: 'doctorOldJob_dictText',
slots: { customRender: 'changeTagJob' },
},
{
title: '专家类型',
align: 'center',
width: 250,
// dataIndex: 'doctorOldType_dictText'
slots: { customRender: 'changeTagType' },
},
{
title: '专家状态',
align: 'center',
width: 200,
// dataIndex: 'doctorOldStatus_dictText'
slots: { customRender: 'changeTagStatus' },
},
{
title: '修改时间',
align: 'center',
dataIndex: 'changeDate',
customRender: ({ text }) => {
return !text ? '' : text.length > 10 ? text.substr(0, 10) : text;
},
},
{
title: '创建时间',
align: 'center',
dataIndex: 'createTime',
},
{
title: '修改时间',
align: 'center',
dataIndex: 'updateTime',
defaultHidden: true,
},
{
title: '是否已更新咨询信息',
align: 'center',
width: 200,
dataIndex: 'updateSessionFlag_dictText',
componentProps: {
dictCode: 'yes_no',
stringToNumber: true,
},
},
{
title: '更新咨询主表时间',
align: 'center',
dataIndex: 'updateSessionTime',
defaultHidden: true,
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '专家姓名',
field: 'doctorName',
component: 'Input',
//colProps: {span: 6},
},
{
label: '修改时间',
field: 'changeDate',
component: 'RangePicker',
componentProps: {
valueType: 'Date',
},
//colProps: {span: 6},
},
{
label: '是否已更新咨询信息',
field: 'updateSessionFlag',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'yes_no',
// stringToNumber: true,
},
//colProps: {span: 6},
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '专家名称',
field: 'doctorName',
component: 'Input',
required: true,
colProps: { span: 12 },
componentProps: {
placeholder: '点击选择专家',
readOnly: true,
},
},
{
label: '修改时间',
field: 'changeDate',
required: true,
colProps: { span: 12 },
component: 'DatePicker',
},
{
label: '医院',
field: 'resourceName',
component: 'Input',
colProps: { span: 12 },
dynamicDisabled: true,
},
{
label: '科室',
field: 'departmentName',
component: 'Input',
colProps: { span: 12 },
dynamicDisabled: true,
},
{
label: '',
field: 'doctorId',
component: 'Input',
colProps: { span: 12 },
show: false,
},
{
label: '原专家职称',
field: 'doctorOldJob',
component: 'JDictSelectTag',
colProps: { span: 12 },
componentProps: {
dictCode: 'z_doct_job',
},
dynamicDisabled: true,
},
{
label: '专家职称',
field: 'doctorJob',
component: 'JDictSelectTag',
colProps: { span: 12 },
componentProps: {
dictCode: 'z_doct_job',
},
},
{
label: '原专家类型',
field: 'doctorOldType',
component: 'JDictSelectTag',
colProps: { span: 12 },
componentProps: {
dictCode: 'z_doct_typ',
},
dynamicDisabled: true,
},
{
label: '专家类型',
field: 'doctorType',
component: 'JDictSelectTag',
colProps: { span: 12 },
componentProps: {
dictCode: 'z_doct_typ',
},
},
{
label: '原专家状态',
field: 'doctorOldStatus',
component: 'JDictSelectTag',
colProps: { span: 12 },
componentProps: {
dictCode: 'doc_status',
},
dynamicDisabled: true,
},
{
label: '专家状态',
field: 'doctorStatus',
colProps: { span: 12 },
component: 'JDictSelectTag',
componentProps: {
dictCode: 'doc_status',
},
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
// 高级查询数据
export const superQuerySchema = {
doctorId: { title: '专家ID', order: 0, view: 'text', type: 'string' },
doctorOldJob: { title: '原专家职称', order: 1, view: 'list', type: 'string', dictCode: 'z_doct_job' },
doctorOldType: { title: '原专家类型', order: 2, view: 'list', type: 'string', dictCode: 'z_doct_typ' },
doctorOldStatus: { title: '原专家状态', order: 3, view: 'list', type: 'string', dictCode: 'doc_status' },
doctorJob: { title: '专家职称', order: 4, view: 'list', type: 'string', dictCode: 'z_doct_job' },
doctorType: { title: '专家类型', order: 5, view: 'list', type: 'string', dictCode: 'z_doct_typ' },
doctorStatus: { title: '专家状态', order: 6, view: 'list', type: 'string', dictCode: 'doc_status' },
changeDate: { title: '修改时间', order: 7, view: 'date', type: 'string' },
createTime: { title: '创建时间', order: 8, view: 'datetime', type: 'string' },
updateTime: { title: '修改时间', order: 9, view: 'datetime', type: 'string' },
};
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData: any): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,243 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增 </a-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>
<template #changeTagJob="{ record }">
<Tag color="green">
{{ record.doctorOldJob_dictText }}
</Tag>
改为
<Tag :color="record.doctorOldJob == record.doctorJob ? 'green' : 'red'">
{{ record.doctorJob_dictText }}
</Tag>
</template>
<template #changeTagType="{ record }">
<Tag color="green">
{{ record.doctorOldType_dictText }}
</Tag>
改为
<Tag :color="record.doctorOldType == record.doctorType ? 'green' : 'red'">
{{ record.doctorType_dictText }}
</Tag>
</template>
<template #changeTagStatus="{ record }">
<Tag color="green">
{{ record.doctorOldStatus_dictText }}
</Tag>
改为
<Tag :color="record.doctorOldStatus == record.doctorStatus ? 'green' : 'red'">
{{ record.doctorStatus_dictText }}
</Tag>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConDoctorChangeLogModal @register="registerModal" @success="handleSuccess"></ConDoctorChangeLogModal>
</div>
</template>
<script lang="ts" name="consultation-conDoctorChangeLog" setup>
import { ref, reactive } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConDoctorChangeLogModal from './components/ConDoctorChangeLogModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './ConDoctorChangeLog.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, updateSession, undoUpdateSession } from './ConDoctorChangeLog.api';
import { useUserStore } from '/@/store/modules/user';
import { Tag } from 'ant-design-vue';
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: [['changeDate', ['changeDate_begin', 'changeDate_end'], 'YYYY-MM-DD']],
},
actionColumn: {
width: 280,
fixed: 'right',
},
beforeFetch: (params) => {
return Object.assign(params, queryParam);
},
//自定义默认排序
defSort: {
column: 'createTime',
order: 'desc',
},
},
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),
},
{
label: '更新数据',
// auth: 'consultation:con_doctor_change_log:update_session',
onClick: handleUpdateSession.bind(null, record),
},
{
label: '撤销更新',
// auth: 'consultation:con_doctor_change_log:update_session',
onClick: handleUndoUpdateSession.bind(null, record),
},
];
}
async function handleUpdateSession(record) {
await updateSession(record, handleSuccess);
}
async function handleUndoUpdateSession(record) {
await undoUpdateSession(record, handleSuccess);
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
},
];
}
</script>
<style scoped>
.jeecg-modal-wrapper,
.jeecg-modal-content {
height: 600px !important;
}
</style>
@@ -0,0 +1,85 @@
<template>
<div style="min-height: 800px">
<BasicForm @register="registerForm"></BasicForm>
<div style="width: 100%;text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary"> </a-button>
</div>
</div>
</template>
<script lang="ts">
import {BasicForm, useForm} from '/@/components/Form/index';
import {computed, defineComponent} from 'vue';
import {defHttp} from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import {getBpmFormSchema} from '../ConDoctorChangeLog.data';
import {saveOrUpdate} from '../ConDoctorChangeLog.api';
export default defineComponent({
name: "ConDoctorChangeLogForm",
components:{
BasicForm
},
props:{
formData: propTypes.object.def({}),
formBpm: propTypes.bool.def(true),
},
setup(props){
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: {span: 24}
});
const formDisabled = computed(()=>{
if(props.formData.disabled === false){
return false;
}
return true;
});
let formData = {};
const queryByIdUrl = '/consultation/conDoctorChangeLog/queryById';
async function initFormData(){
let params = {id: props.formData.dataId};
const data = await defHttp.get({url: queryByIdUrl, params});
formData = {...data}
//设置表单的值
await setFieldsValue(formData);
//默认是禁用
await setProps({disabled: formDisabled.value})
}
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
console.log('表单数据', params)
await saveOrUpdate(params, true)
}
initFormData();
return {
registerForm,
formDisabled,
submitForm,
}
}
});
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%
}
:deep(.ant-calendar-picker) {
width: 100%
}
.jeecg-modal-wrapper, .jeecg-modal-content {
height: 600px !important;
}
</style>
@@ -0,0 +1,98 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1000" height="800" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
<selectdoc ref="replaceOtherRef" title="选择用户" @choose-employ="onSelectUserOk" />
</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 '../ConDoctorChangeLog.data';
import { saveOrUpdate } from '../ConDoctorChangeLog.api';
import selectdoc from '/@/views/emergency/communication/components/selectdoc.vue';
// 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,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
const replaceOtherRef = ref();
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
formSchema[0].componentProps.onClick = () => {
replaceOtherRef.value.showModal();
};
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
// 选择用户成功
async function onSelectUserOk(e) {
let json = JSON.parse(e);
await setFieldsValue({
doctorName: json.doctorName,
resourceName: json.resourceName,
departmentName: json.departmentName,
doctorId: json.id,
doctorOldJob: json.doctorJob,
doctorOldType: json.type,
doctorOldStatus: json.doctorStatus,
doctorJob: json.doctorJob,
doctorType: json.type,
doctorStatus: json.doctorStatus,
});
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
.jeecg-modal-wrapper,
.jeecg-modal-content {
height: 600px !important;
}
</style>
@@ -0,0 +1,215 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
destroyOnClose
:title="title"
:width="adaptiveWidth * 1.5"
@ok="handleSubmit"
:showFooter="showFooter"
:maskClosable="false"
>
<div class="form-container">
<div class="title"><strong>基本信息</strong></div>
<BasicForm @register="registerForm" />
<div class="title"><strong>扩展信息</strong></div>
<BasicForm @register="registerFormData" />
<div class="title"><strong>其它信息</strong></div>
<BasicForm @register="registerFormOther" />
</div>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema, formSchemaData, formSchemaOther } from '../conDoctor.data';
import { saveOrUpdate } from '../conDoctor.api';
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
import { dealPassword } from '/@/views/system/user/user.data';
const { adaptiveWidth } = useDrawerAdaptiveWidth();
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref<boolean>(true);
const showFooter = ref<boolean>(true);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 12 },
});
//表单配置扩展
const [registerFormData, { setProps: setPropsData, setFieldsValue: setFieldsValueData, validate: validateData, clearValidate: clearValidate2 }] =
useForm({
labelWidth: 100,
schemas: formSchemaData,
showActionButtonGroup: false,
baseColProps: { span: 12 },
});
//表单配置其他
const [
registerFormOther,
{ setProps: setPropsOther, setFieldsValue: setFieldsValueOther, validate: validateOther, clearValidate: clearValidate3 },
] = useForm({
labelWidth: 100,
schemas: formSchemaOther,
showActionButtonGroup: false,
baseColProps: { span: 12 },
});
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
//重置表单
await resetFields();
setDrawerProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
showFooter.value = data.showFooter;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record.conDoctor,
...data.record.sysUserModel,
id: data?.record.conDoctor.id,
hospitalId: data?.record.conDoctor.resourceId,
value: data?.record.conDoctor.departmentId,
//调整一下,这里如果是空字符串,就直接转空数组
goodAtSickness: getSicknesses(data),
});
await setFieldsValueData({
...data.record.conDoctor,
...data.record.sysUserModel,
});
await setFieldsValueOther({
...data.record.conDoctor,
...data.record.sysUserModel,
});
await updateSchema([
{
field: 'password',
ifShow: false,
},
]);
await clearValidate();
await clearValidate2();
await clearValidate3();
} else {
await updateSchema([
{
field: 'password',
ifShow: true,
},
]);
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
await setPropsData({ disabled: !data?.showFooter });
await setPropsOther({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let data = {
...(await validate()),
...(await validateData()),
...(await validateOther()),
};
let passwordObj = {};
if (!isUpdate.value) {
passwordObj = {
password: dealPassword(data?.password),
};
}
//调整数据参数
let setData = {
conDoctor: {
id: data?.id,
award: data?.award,
doctorName: data?.doctorName,
contactAddress: data?.contactAddress,
nationality: data?.nationality,
nativePlace: data?.nativePlace,
email: data?.email,
departmentId: data?.departmentId,
doctorTitle: data?.doctorTitle,
idCard: data?.idCard,
eq: data?.eq,
experience: data?.experience,
goodAt: data?.goodAt,
workHistory: data?.workHistory,
introduction: data?.introduction,
joinWork: data?.joinWork,
phone: data?.phone,
outTime: data?.outTime,
resourceId: data?.resourceId,
school: data?.school,
sex: data?.sex,
type: data?.type,
doctorStatus: data?.doctorStatus, // 专家状态
workExperience: data?.workExperience, // 工作经历
doctorJob: data?.doctorJob,
goodAtSickness: data?.goodAtSickness,
doctorNo: data?.doctorNo,
regDate: data?.regDate,
offDate: data?.offDate,
tfRecommend: data?.tfRecommend,
sort: data?.sort,
},
sysUserModel: {
username: data?.username,
...passwordObj,
avatar: data?.avatar,
email: data?.email,
idCard: data?.idCard,
phone: data?.phone,
sex: data?.sex,
birthday: data?.birthday,
id: data?.id,
},
};
setDrawerProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(setData, isUpdate.value);
//关闭弹窗
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
const getSicknesses = (data) => {
const goodAtSickness = data?.record?.conDoctor?.goodAtSickness;
// 明确检查 goodAtSickness 是否为有效的非空字符串
return typeof goodAtSickness === 'string' && goodAtSickness.trim() !== '' ? goodAtSickness.split(',') : [];
};
</script>
<style lang="less" scoped>
.form-container {
//height: 65vh;
overflow: auto;
}
.title {
box-sizing: border-box;
color: #333;
font-weight: bold;
padding: 12px 16px;
}
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
:deep(.ant-picker) {
width: 100% !important;
}
</style>
@@ -0,0 +1,181 @@
<template>
<BasicDrawer
:maskClosable="false"
v-bind="$attrs"
@register="registerDrawer"
destroyOnClose
:title="title"
:showFooter="false"
:width="adaptiveWidth"
@close="cancel"
>
<div style="margin-bottom: 14px">
<span style="margin-right: 10px">视频状态</span>
<a-switch v-model:checked="state.audioStatus" @change="changeStatus" />
<span style="margin: 0 10px">是否跳过节假日</span>
<a-switch v-model:checked="state.tfJumpHoliday" @change="changeHolidayStatus" />
<a-button class="ml-3a0" v-if="!isEdit" @click="editTable" type="primary">编辑 </a-button>
<a-button class="ml-3a0" v-else type="primary" @click="handleSubmit" :loading="submitLoading">保存 </a-button>
</div>
<a-spin :spinning="spinning">
<a-table :columns="schedulingColumns" :data-source="state.columnData" size="small" :pagination="false">
<template #bodyCell="{ column, record, text }">
<template v-if="column.dataIndex === 'count'">
<div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
<a-input-number placeholder="请输入最多可约人数" style="width: 200px" v-model:value="record[record.type]" :min="0" />
</div>
<div v-else class="editable-cell-text-wrapper"> {{ text || '' }}</div>
</template>
</template>
</a-table>
</a-spin>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { schedulingColumns } from '/@/views/consult/doctor/message/conDoctor.data';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { getDoctorScheduling, updateAudioStatus, updateDoctorScheduling, updateJumpHoliday } from '/@/views/consult/doctor/message/conDoctor.api';
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
import { processingData, processStatus } from '/@/views/consult/doctor/message/scheduling.data';
// Emits声明
const emit = defineEmits(['register', 'success']);
const { adaptiveWidth } = useDrawerAdaptiveWidth();
//设置标题
const title = '排班设置';
const state = ref({
doctorId: '',
columnData: [],
audioStatus: false,
tfJumpHoliday: false,
});
const spinning = ref(false);
let editableData = reactive({});
const isEdit = ref(false);
const submitLoading = ref(false);
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
state.value.doctorId = data.record.id;
state.value.audioStatus = data.record.audioStatus === '1';
state.value.tfJumpHoliday = data.record.tfJumpHoliday === '1';
isEdit.value = false;
await initData();
});
function dealData(res) {
return processingData(res);
}
async function initData() {
spinning.value = true;
let res = await getDoctorScheduling({ doctorId: state.value.doctorId });
spinning.value = false;
state.value.columnData = dealData(res);
clearEditData();
}
function clearEditData() {
for (let key in editableData) {
if (editableData.hasOwnProperty(key)) {
editableData[key] = null;
}
}
editableData = {};
isEdit.value = false;
}
/**
* @Description:编辑修改
* @date 2023/7/14
*/
function editTable() {
const keys = state.value.columnData.map((item) => item.key);
keys.map((item, i) => {
editableData[item] = state.value.columnData[i];
});
isEdit.value = true;
}
async function changeStatus() {
let audioStatus = state.value.audioStatus ? '1' : '0';
const params = processStatus(state.value.doctorId, { audioStatus });
await updateAudioStatus(params);
// await initData();
}
async function changeHolidayStatus() {
let tfJumpHoliday = state.value.tfJumpHoliday ? '1' : '0';
const params = processStatus(state.value.doctorId, { tfJumpHoliday });
await updateJumpHoliday(params);
// await initData();
}
function cancel() {
Object.assign(editableData, {});
state.value.audioStatus = false;
state.value.tfJumpHoliday = false;
emit('success');
}
function submitParams() {
const params: any = [];
const result = state.value.columnData || [];
for (let i = 0; i < result.length; i += 2) {
const item_am: any = result[i];
const item_pm: any = result[i + 1];
const type = item_am?.amNum || item_pm?.pmNum ? 2 : 0;
const o: any = {
...item_am,
amNum: item_am?.amNum || 0,
pmNum: item_pm?.pmNum || 0,
type: type,
};
params.push(o);
}
return params;
}
//表单提交事件
async function handleSubmit() {
try {
const params = submitParams();
submitLoading.value = true;
await updateDoctorScheduling(params);
await initData();
} catch (e: any) {
submitLoading.value = false;
throw new Error('请求失败', e);
} finally {
for (const key in editableData) {
if (editableData[key]) {
delete editableData[key];
}
}
//关闭弹窗
closeDrawer();
submitLoading.value = false;
setDrawerProps({ confirmLoading: false });
emit('success');
cancel();
}
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
:deep(table tr, table th, table td) {
border-right: 1px solid #f0f0f0 !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.ml-3a0 {
margin-left: 30px;
}
</style>
@@ -0,0 +1,86 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1000">
<!--引用表格-->
<div style="height: 60vh">
<BasicTable @register="registerTable" />
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { BasicTable } from '/@/components/Table';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import { suspensionColumns, suspensionForm } from '/@/views/consult/doctor/message/conDoctor.data';
import { getServiceRecords } from '/@/views/consult/doctor/message/conDoctor.api';
import { ColEx } from '/@/components/Form/src/types';
import { ref } from 'vue';
const title = '暂停服务';
const record = ref({
id: '',
});
// 自适应列配置
const adaptiveColProps: Partial<ColEx> = {
offset: 0,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
};
//表单赋值
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
const { resetFields, setFieldsValue } = getForm();
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
record.value = data.record;
await setFieldsValue({
...data.record,
});
});
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '暂停服务',
api: getServiceRecords,
columns: suspensionColumns,
canResize: false,
formConfig: {
baseColProps: adaptiveColProps,
schemas: suspensionForm,
autoSubmitOnEnter: true,
showAdvancedButton: false,
showResetButton: false,
showSubmitButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
actionColOptions: {
offset: 0,
span: 8,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
},
},
actionColumn: {
ifShow: false,
},
beforeFetch: (info) => {
info['doctorId'] = record.value?.id;
return info;
},
},
});
const [registerTable, { getForm }] = tableContext;
</script>
<style scoped lang="less">
:deep(.ant-table-title) {
display: none;
}
</style>
@@ -0,0 +1,168 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
export enum Api {
list = '/health-consultation/conDoctor/list',
//排班
schedulingList = '/health-consultation/consultation/conDoctorSchedulingDate/list',
save = '/health-consultation/conDoctor/add',
edit = '/health-consultation/conDoctor/edit',
deleteOne = '/health-consultation/conDoctor/delete',
deleteBatch = '/health-consultation/conDoctor/deleteBatch',
importExcel = '/health-consultation/conDoctor/importDoctor',
exportXls = '/health-consultation/conDoctor/exportXls',
getServiceRecords = '/health-consultation/conDoctor/getServiceByDoctorId',
getDoctorScheduling = '/health-consultation/consultation/conDoctorScheduling/getDoctorScheduling',
updateDoctorScheduling = '/health-consultation/consultation/conDoctorScheduling/updateDoctorScheduling',
queryById = '/health-consultation/conDoctor/queryById',
selectResourceList = '/health-consultation/conResource/selectResourceList',
updateAudioStatus = '/health-consultation/conDoctor/updateAudioStatus',
updateJumpHoliday = '/health-consultation/conDoctor/updateJumpHoliday',
generateScheduling = '/consultation/conDoctorSchedulingDate/generateDoctorSchedulingDate',
exportXlsAccount = '/health-consultation/conDoctor/exportXlsAccount',
closeStatus = '/health-consultation/conService/add',
openStatus = '/health-consultation/conService/openDoctorStatus',
recommend = '/health-consultation/conDoctor/recommend',
}
export const recommendApi = (params) => defHttp.get({ url: Api.recommend, params });
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
* @param handleSuccess
*/
export const batchDelete = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params: any, isUpdate: boolean) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
*@description 暂停服务记录
* @param params 请求参数
* */
export const getServiceRecords = (params) => defHttp.get({ url: Api.getServiceRecords, params });
/**
* @description 获取专家排班
* @param params 请求参数
* */
export const getDoctorScheduling = (params) => defHttp.get({ url: Api.getDoctorScheduling, params });
/**
* @description 修改专家排班
* @param params 请求参数
* */
export const updateDoctorScheduling = (params) => defHttp.post({ url: Api.updateDoctorScheduling, params });
/**
* @description 获取专家信息详情
* @param params 请求参数
* */
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/**
* @Description:查询医院列表
* @date 2023/7/3
* @param params
*/
export const selectResourceList = (params) => defHttp.get({ url: Api.selectResourceList, params });
/**
* @Description:导出到记录表
* @date 2023/7/3
*/
export const exportFile = (params) => defHttp.post({ url: Api.exportXls, params });
/**
* @Description:修改音视频状态
* @date 2023/7/11
* @param params
*/
export const updateAudioStatus = (params: any) => defHttp.post({ url: Api.updateAudioStatus, params });
/**
* @Description:修改是否跳过节假日状态
* @date 2023/7/12
* @param params
*/
export const updateJumpHoliday = (params: any) => defHttp.post({ url: Api.updateJumpHoliday, params });
/**
* @Description:生成专家排班
* @date 2023/7/11
* @param params
*/
export const generateScheduling = (params: any) => defHttp.get({ url: Api.generateScheduling, params });
/*
* 开启关闭咨询
* */
export const getStatus = (params, handleSuccess, type) => {
createConfirm({
iconType: 'warning',
title: '提示',
content: `是否将此专家咨询置【${type ? '开启' : '关闭'}】状态?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
if (type) {
defHttp.post({ url: Api.openStatus, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
} else {
defHttp.post({ url: Api.closeStatus, params }).then(() => {
handleSuccess();
});
}
// const url = type ? Api.openStatus : Api.closeStatus;
// defHttp.post({ url, params }).then(() => {
// handleSuccess();
// });
},
});
};
@@ -0,0 +1,750 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { getExpertDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { h } from 'vue';
import { EyeOutlined } from '@ant-design/icons-vue';
import { Image, message } from 'ant-design-vue';
import { render } from '/@/utils/common/renderUtils';
import { selectResourceList } from '/@/views/consult/doctor/message/conDoctor.api';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDepartmentList } from '/@/views/consult/resource/conResource.api';
import { checkPassword } from '/@/hooks/checkPassword/checkPassword';
const { createMessage } = useMessage();
//列表数据
export const columns: BasicColumn[] = [
{
title: '用户账号',
align: 'center',
width: 150,
fixed: 'left',
dataIndex: 'doctorAccount',
},
{
title: '专家编号',
align: 'center',
width: 150,
fixed: 'left',
dataIndex: 'doctorNo',
},
{
title: '姓名',
align: 'center',
fixed: 'left',
dataIndex: 'doctorName',
},
{
title: '头像',
align: 'center',
dataIndex: 'photo',
width: 100,
customRender: ({ text }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getExpertDefaultImage(),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
{
title: '医院',
align: 'center',
dataIndex: 'resourceName',
},
{
title: '科室',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '职称',
align: 'center',
width: 90,
dataIndex: 'doctorJob_dictText',
},
{
title: '职务',
align: 'center',
dataIndex: 'doctorTitle_dictText',
},
{
title: '专家状态',
align: 'center',
slots: { customRender: 'doctorStatusTag' },
// dataIndex: 'doctorStatus',
// customRender: ({ text }) => {
// return render.renderDict(text, 'doc_status');
// },
},
{
title: '咨询状态',
align: 'center',
slots: { customRender: 'doctorAcceptTag' },
// dataIndex: 'isAccept',
// customRender: ({ text }) => {
// return render.renderDict(text, 'doc_accept_flag');
// },
},
{
title: '收费类型',
align: 'center',
dataIndex: 'type',
customRender: ({ text }) => {
return render.renderDict(text, 'z_doct_typ');
},
},
{
title: '排序',
align: 'center',
dataIndex: 'sort',
width: 80,
},
{
title: '是否推荐',
align: 'center',
dataIndex: 'tfRecommend',
width: 80,
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '姓名',
field: 'doctorName',
component: 'Input',
},
{
label: '医院',
field: 'resourceId',
component: 'ApiSelect',
componentProps: {
// @ts-ignore
api: selectResourceList,
resultField: 'list',
labelField: 'name',
valueField: 'id',
immediate: false,
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
},
},
{
label: '职称',
field: 'doctorJob',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_job',
},
},
{
label: '专家状态',
field: 'doctorStatus',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'doc_status',
},
},
{
label: '收费类型',
field: 'type',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_typ',
},
},
{
label: '咨询状态',
field: 'isAccept',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'doc_accept_flag',
},
},
];
async function checkSick(_, value) {
if (!value) {
return Promise.reject('请选择疾病');
} else {
return Promise.resolve();
}
}
//表单数据
export const formSchema: FormSchema[] = [
// 主键Id
{
label: '',
field: 'id',
show: false,
component: 'Input',
},
{
label: '用户账号',
field: 'username',
component: 'Input',
componentProps: ({ formModel }) => {
return {
autocomplete: 'off',
onInput: (event) => {
const target = event.target;
formModel.username = target.value.replace(/[^\w]/g, '');
},
};
},
required: true,
rules: [{ required: true, message: '请输入用户账号' }],
},
{
label: '密码',
field: 'password',
component: 'StrengthMeter',
componentProps: {
//@ts-ignore
autocomplete: 'new-password',
readOnly: true,
onfocus: (field) => {
field.target.readOnly = false;
},
},
dynamicRules: () => {
return [
{
required: true,
validator: (_, value) => {
let { message } = checkPassword(value);
if (message === 'ok') {
return Promise.resolve();
} else {
return Promise.reject(message);
}
},
trigger: 'blur',
},
];
},
show: ({ values }) => {
return !values.id;
},
},
{
label: '姓名',
field: 'doctorName',
component: 'Input',
required: true,
},
{
field: 'sex',
component: 'JDictSelectTag',
label: '性别',
componentProps: {
//@ts-ignore
dictCode: 'sex2',
type: 'radio',
},
rules: [{ required: true, message: '请选择性别' }],
},
{
field: 'avatar',
label: '头像',
component: 'JImageUpload',
// rules: [{ required: true, message: '请选择头像' }],
componentProps: {
//@ts-ignore
maxCount: 1,
},
},
{
label: '手机号',
field: 'phone',
component: 'Input',
rules: [{ pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' }],
},
{
label: '身份证号',
field: 'idCard',
component: 'Input',
rules: [
{
pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/,
message: '身份证号码格式有误',
},
],
},
{
field: 'email',
label: '邮箱',
component: 'Input',
rules: [
{
type: 'email',
pattern: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
message: '${label}格式有误',
trigger: 'blur',
},
],
componentProps: {
//@ts-ignore
placeholder: '请输入邮箱',
},
},
{
label: '医院',
field: 'resourceId',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
return {
api: selectResourceList,
resultField: 'list',
labelField: 'name',
valueField: 'id',
immediate: true,
onChange: () => {
formModel.departmentId = '';
formModel.goodAtSickness = [];
},
onDeselect: () => {
if (formModel.hasOwnProperty('departmentId')) {
formModel.departmentId = '';
}
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
rules: [{ required: true, message: '请选择医院', trigger: 'blur' }],
},
{
label: '科室',
field: 'departmentId',
component: 'JTreeDepartment',
componentProps: ({ formModel }) => {
return {
hospitalId: formModel.resourceId,
allDep: false,
immediate: true,
showSearch: true,
treeNodeFilterProp: 'label',
placeholder: '请选择科室',
onChange: () => {
formModel.goodAtSickness = [];
},
filterOption: (input: string, option: any): boolean => {
console.log(input, option);
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
onFocus: () => {
if (!formModel.resourceId) {
return createMessage.warn('请先选择医院!');
}
},
};
},
rules: [{ required: true, message: '请选择科室', trigger: 'blur' }],
},
{
label: '擅长疾病',
field: 'goodAtSickness',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
let departmentId = '1321354';
if (formModel?.departmentId) {
if (Array.isArray(formModel?.departmentId)) {
departmentId = formModel?.departmentId?.join(',');
} else {
departmentId = formModel?.departmentId;
}
}
return {
api: getDepartmentList,
params: {
departmentId: departmentId,
isDoctor: true,
},
labelField: 'sicksName',
valueField: 'id',
resultField: 'list',
mode: 'multiple',
immediate: true,
onFocus: () => {
if (!formModel?.departmentId || formModel?.departmentId.length < 1) {
return message.warn('请先选择科室');
}
},
onChange: (val) => {
if (val && val?.length) {
val = val.join(',');
formModel.goodAtSickness = val;
}
},
disabledInitValue: true,
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
rules: [{ trigger: 'blur', validator: checkSick }],
},
{
label: '职称',
field: 'doctorJob',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_job',
},
required: true,
},
{
label: '职务',
field: 'doctorTitle',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_lev',
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.trim().toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
},
rules: [{ required: true, message: '请选择职务' }],
},
{
label: '专家类型',
field: 'type',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_typ',
},
rules: [{ required: true, message: '请选择专家类型' }],
},
{
label: '专家状态',
field: 'doctorStatus',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'z_doct_sta_orgin',
type: 'radio',
},
rules: [{ required: true, message: '请选择专家状态' }],
},
{
label: '专家编号',
field: 'doctorNo',
component: 'Input',
dynamicRules: ({ values, model }) => {
return [
{
required: model?.type == '1',
validator: (_, value) => {
console.log(value, values, model);
if (model?.type !== '1' || value) {
return Promise.resolve();
} else {
return Promise.reject('请输入专家编号');
}
},
},
];
},
},
{
field: 'regDate',
label: '注册日期',
component: 'DatePicker',
componentProps: {
//@ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => document.body,
},
},
{
field: 'offDate',
label: '出库日期',
component: 'DatePicker',
componentProps: {
//@ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => document.body,
},
},
{
label: '是否推荐',
field: 'tfRecommend',
component: 'JDictSelectTag',
defaultValue: '0',
componentProps: {
//@ts-ignore
type: 'radio',
options: [
{ label: '是', value: '1' },
{ label: '否', value: '0' },
],
},
},
];
//扩展数据
export const formSchemaData: FormSchema[] = [
{
label: '学历',
field: 'eq',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'education',
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.trim().toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
},
},
{
label: '出生日期',
field: 'birthday',
component: 'DatePicker',
componentProps: {
//@ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
},
},
{
label: '毕业院校',
field: 'school',
component: 'Input',
},
{
field: 'outTime',
label: '毕业时间',
component: 'DatePicker',
componentProps: {
//@ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => document.body,
},
},
{
label: '民族',
field: 'nationality',
component: 'JDictSelectTag',
componentProps: () => {
return {
dictCode: 'nation',
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
},
{
label: '籍贯',
field: 'nativePlace',
component: 'Input',
},
{
label: '联系地址',
field: 'contactAddress',
component: 'Input',
},
{
label: '简介',
field: 'introduction',
component: 'InputTextArea',
},
];
//扩展数据
export const formSchemaOther: FormSchema[] = [
{
field: 'joinWork',
label: '参加工作时间',
component: 'DatePicker',
componentProps: {
//@ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
},
},
{
label: '擅长',
field: 'goodAt',
component: 'InputTextArea',
},
{
label: '执业经历',
field: 'experience',
component: 'InputTextArea',
/*componentProps: {
//@ts-ignore
rows: 5,
},*/
},
{
label: '获奖或论文',
field: 'award',
component: 'InputTextArea',
},
{
label: '工作经历',
field: 'workExperience',
component: 'InputTextArea',
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
componentProps: () => ({
min: 0,
style: {
width: '100%',
},
}),
},
];
// 暂停服务弹窗
export const suspensionForm: FormSchema[] = [
{
label: '专家',
field: 'doctorName',
component: 'Input',
componentProps: {
//@ts-ignore
readOnly: true,
allowClear: false,
},
},
{
label: '医院',
field: 'resourceName',
component: 'Input',
componentProps: {
//@ts-ignore
readOnly: true,
allowClear: false,
},
},
{
label: '',
field: 'doctorId',
component: 'Input',
show: false,
},
];
const Status = {
0: '停诊中',
1: '结束停诊',
};
export const suspensionColumns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '时间',
align: 'center',
dataIndex: 'resourceName',
customRender: ({ record }): string => {
const { startDay, endDay } = (record as any) || { startDay: '', endDay: '当前' };
if (!startDay) {
return '';
}
return `${startDay}${endDay}`;
},
},
{
title: '原因',
align: 'center',
dataIndex: 'stopReason',
},
{
title: '备注',
align: 'center',
dataIndex: 'memo',
},
{
title: '状态',
align: 'center',
dataIndex: 'serviceStatus',
customRender: ({ text }) => (text ? Status[text] : ''),
},
];
// 排班设置
export const schedulingColumns: BasicColumn[] = [
{
title: '星期',
dataIndex: 'week_dictText',
key: 'week_dictText',
width: 80,
align: 'center',
customCell: (_, index: number) => {
if (index % 2 === 0) {
return {
rowSpan: 2,
};
} else {
return {
rowSpan: 0,
};
}
},
},
{
title: '视频咨询时间',
align: 'center',
dataIndex: 'type_dictText',
},
{
title: '最多预约人数',
align: 'center',
dataIndex: 'count',
width: 220,
},
];
/**
* 流程表单调用这个方法获取formSchema
*/
export function getBpmFormSchema(): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,341 @@
<template>
<div>
<!--引用表格-->
<BasicTable :rowSelection="rowSelection" @register="registerTable">
<!--插槽:table标题-->
<template #tableTitle>
<a-button v-auth="'consultation:con_doctor:add'" preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">
新增专家</a-button
>
<a-button-group>
<a-button type="primary" preIcon="ant-design:download-outlined" @click="downloadTemplate">导入模板下载</a-button>
<a-upload name="file" :showUploadList="false" :customRequest="onImportXls" accept=".xlsx, .xls, application/vnd.ms-excel-=='">
<a-button v-auth="'consultation:con_doctor:importExcel'" type="primary" preIcon="ant-design:import-outlined">导入</a-button>
</a-upload>
<a-button
v-auth="'consultation:con_doctor:exportXls'"
type="primary"
preIcon="ant-design:import-outlined"
@click="openRecordDrawer('comDoctorImportMsg', '查看导入记录')"
>
查看导入任务
</a-button>
</a-button-group>
<a-button-group>
<a-button v-auth="'consultation:con_doctor:exportXls'" type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"
>导出</a-button
>
<a-button
v-auth="'consultation:con_doctor:exportXls'"
type="primary"
preIcon="ant-design:export-outlined"
@click="openRecordDrawer('comDoctorMsg', '查看导出记录')"
>
查看导出任务
</a-button>
</a-button-group>
<a-button
v-auth="'consultation:con_doctor:deleteBatch'"
@click="batchHandleDelete"
type="primary"
preIcon="ant-design:delete-outlined"
>批量删除</a-button
>
<a-button type="primary" @click="stopService">暂停服务记录</a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<template #doctorStatusTag="{ record }">
<Tag v-if="record.doctorStatus_dictText !== undefined" :color="record.doctorStatus == '1' ? 'green' : 'red'">
{{ record.doctorStatus_dictText }}
</Tag>
</template>
<template #doctorAcceptTag="{ record }">
<Tag v-if="record.isAccept_dictText !== undefined" :color="record.isAccept == '1' ? 'green' : 'red'">
{{ record.isAccept_dictText }}
</Tag>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex == 'tfRecommend'">
<a-switch
v-model:checked="record.tfRecommend"
@click="changeSwitch(record)"
checked-children=""
un-checked-children=""
checkedValue="1"
unCheckedValue="0"
/>
</template>
</template>
</BasicTable>
<!-- 表单区域 -->
<doctor-modal @register="registerDrawer" @success="handleSuccess" />
<!-- 专家排班 -->
<s-modal @register="sModalDrawer" @success="handleSuccess" />
<!--暂停服务-->
<suspension-service @register="registerService" />
<RestPass @register="restPassModal" />
<export-util :task-code="taskCode" :drawerTitle="drawerTitle" @register="registerExport" />
</div>
</template>
<script lang="ts" name="consultation-conDoctor" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import DoctorModal from './components/conDoctorModal.vue';
import SModal from './components/schedulingModal.vue';
import { columns, searchFormSchema } from './conDoctor.data';
import { Api, batchDelete, deleteOne, exportFile, getExportUrl, getImportUrl, getStatus, list, queryById, recommendApi } from './conDoctor.api';
import { message, Tag } from 'ant-design-vue';
import SuspensionService from '/@/views/consult/doctor/message/components/suspensionService.vue';
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
import ExportUtil from '/@/utils/export/exportUtil.vue';
import { lockUser } from '/@/views/system/user/hospitalDoctor/HospitalDoctor.api';
import { useDrawer } from '/@/components/Drawer';
import { useMethods } from '/@/hooks/system/useMethods';
import { useMessage } from '/@/hooks/web/useMessage';
import { ref } from 'vue';
import { downloadExcel } from '/@/views/healthMonitor/healMonitorManage/monitorToll/toolManagement/toolHooks';
const taskCode = ref('');
const drawerTitle = ref('');
const { handleImportXls } = useMethods();
const { createMessage } = useMessage();
const [registerDrawer, { openDrawer: openFormDrawer }] = useDrawer();
// 排班
const [sModalDrawer, { openDrawer: openSModalDrawer }] = useDrawer();
//注册model
const [registerService, { openModal: openServiceModal }] = useModal();
const [restPassModal, { openModal: restPaddModel }] = useModal();
const recommend = ref(1);
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_doctor',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 230,
fixed: 'right',
},
},
exportConfig: {
name: 'con_doctor',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload, getForm, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
const [registerExport, { openDrawer }] = useDrawer();
//新增专家
function handleAdd() {
openFormDrawer(true, {
isUpdate: false,
showFooter: true,
});
}
// 文件导入
function onImportXls(d) {
const size = d.file.size;
const m10 = 1024 * 1024 * 10;
if (size > m10) {
console.log('文件过大');
}
handleImportXls(d, Api.importExcel, () => {
reload();
});
}
// 模板下载
function downloadTemplate() {
downloadExcel('/static/importDoctor.xlsx', '专家信息导入模板');
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
let res = await queryById({ id: record.id });
openFormDrawer(true, {
record: res,
isUpdate: true,
showFooter: true,
});
}
/**
* 排班事件
*/
function handleScheduling(record: Recordable) {
openSModalDrawer(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/*
* 开启/关闭咨询
* */
function handleClose(record: Recordable) {
let params = {};
let isAccept = record.isAccept === '3';
if (isAccept) {
params['id'] = record.id;
} else {
params['doctorId'] = record.id;
params['doctorName'] = record.doctorName;
}
getStatus(params, handleSuccess, isAccept);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'consultation:con_doctor:edit',
},
{
label: '排班',
onClick: handleScheduling.bind(null, record),
},
{
type: record.isAccept === '1' ? 'danger' : 'primary',
label: record.isAccept === '1' ? '关闭咨询' : '开启咨询',
onClick: handleClose.bind(null, record),
},
];
}
async function changeSwitch(record) {
console.log(record);
try {
const params = {
id: record.id,
type: record.tfRecommend,
};
await recommendApi(params);
} catch (e) {
console.log(e);
}
}
// 重置密码
function restPass(record: Recordable) {
restPaddModel(true, {
record: record.id,
isUpdate: true,
showFooter: false,
});
}
// 1 锁定用户 2 激活用户
function handleUserOpt(record: Recordable, type: any) {
const params = {
userId: record.id,
status: type,
};
lockUser(params, type, handleSuccess);
}
/**
* @Description:导出到记录表
* @date 2023/7/3
*/
async function onExportXls() {
const form = getForm().getFieldsValue();
await exportFile(form);
}
function openRecordDrawer(code: string, title: string) {
taskCode.value = code;
drawerTitle.value = title;
setTimeout(() => {
openDrawer(true, {});
}, 500);
}
/**
* 下拉操作栏
*/
function getDropDownAction(record: Recordable) {
return [
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'consultation:con_doctor:delete',
},
{
label: '重置密码',
onClick: restPass.bind(null, record),
},
{
label: '锁定用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 2),
},
{
label: '激活用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 1),
},
];
}
function stopService() {
if (!selectedRowKeys.value.length || selectedRowKeys.value.length > 1) {
return message.warn('请选择一条数据!');
}
let id = selectedRowKeys.value[0];
let dataSource = getDataSource();
let record = dataSource.filter((item) => item.id === id)[0];
openServiceModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
</script>
<style lang="less" scoped>
:deep(.ant-popover-buttons) {
display: flex !important;
}
</style>
@@ -0,0 +1,64 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
//排班
list = '/health-consultation/consultation/conDoctorSchedulingDate/list',
save = '/health-consultation/conDoctor/add',
edit = '/health-consultation/conDoctor/edit',
deleteOne = '/health-consultation/conDoctor/delete',
deleteBatch = '/health-consultation/conDoctor/deleteBatch',
importExcel = '/health-consultation/conDoctor/importExcel',
exportXls = '/health-consultation/conDoctor/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,108 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '工作日',
align: 'center',
dataIndex: 'week',
width: 100,
},
{
title: '视频咨询时间',
align: 'center',
dataIndex: 'consultationTime',
slots: { customRender: 'consultationTime' },
},
{
title: '在线时间',
align: 'center',
dataIndex: 'departmentName',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '视频咨询时间',
field: 'scheduling_date',
component: 'Input',
required: true,
},
{
label: '在线时间',
field: 'password',
component: 'StrengthMeter',
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
// 生成随机字符
export function randomKey(keyLength = 18) {
let rlt = '';
for (let i = 0; i < keyLength; i++) {
if (Math.round(Math.random())) {
rlt += Math.ceil(Math.random() * 9);
} else {
const ranNum = Math.ceil(Math.random() * 23);
if (Math.round(Math.random())) {
rlt += String.fromCharCode(65 + ranNum);
} else {
rlt += String.fromCharCode(97 + ranNum);
}
}
}
return rlt;
}
export const week_text = (key) => {
const type = {
1: '星期一',
2: '星期二',
3: '星期三',
4: '星期四',
5: '星期五',
6: '星期六',
7: '星期日',
};
return type[key];
};
export function processingData(res) {
const arr: any[] = [];
function createItem(item: any, typeDictText: string, type: string): any {
return {
...item,
week_dictText: week_text([item['week']]),
type_dictText: typeDictText,
count: item[type],
key: randomKey(),
type: type,
};
}
res.forEach((item) => {
const am = createItem(item, '上午', 'amNum');
const pm = createItem(item, '下午', 'pmNum');
arr.push(am, pm);
});
return arr;
}
export function processStatus(id, status) {
return {
id,
...status,
};
}
@@ -0,0 +1,115 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<div class="form-container">
<BasicForm @register="registerForm" />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../statistics.data';
import { saveOrUpdate } from '../statistics.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
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,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let data = {
...(await validate()),
};
//调整数据参数
let setData = {
conDoctor: {
award: data.award,
contactAddress: data.contactAddress,
nationality: data.nationality,
nativePlace: data.nativePlace,
email: data.email,
birthDate: data.birthDate,
departmentName: data.departmentName,
doctorTitle: data.doctorTitle,
idCard: data.idCard,
eq: data.eq,
experience: data.experience,
goodAt: data.goodAt,
workHistory: data.workHistory,
isAccept: data.isAccept,
joinWork: data.joinWork,
phone: data.phone,
outTime: data.outTime,
resourceName: data.resourceName,
school: data.school,
sex: data.sex,
type: data.type,
registrationTime: data.registrationTime,
},
sysUserModel: {
username: data.username,
password: data.password,
avatar: data.avatar,
email: data.email,
idCard: data.idCard,
phone: data.phone,
sex: data.sex,
},
};
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(setData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
.form-container {
height: 65vh;
overflow: auto;
}
.title {
box-sizing: border-box;
color: #333;
font-weight: bold;
padding: 12px 16px;
}
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,54 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/conDoctor/statisticalList',
save = '/health-consultation/conDoctor/add',
edit = '/health-consultation/conDoctor/edit',
deleteOne = '/health-consultation/conDoctor/delete',
deleteBatch = '/health-consultation/conDoctor/deleteBatch',
importExcel = '/health-consultation/conDoctor/importExcel',
exportXls = '/health-consultation/conDoctor/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,148 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { BODY_CONTAINER } from '/@/utils/domUtils';
import { selectResourceList } from '/@/views/consult/doctor/message/conDoctor.api';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '专家姓名',
align: 'center',
dataIndex: 'doctorName',
},
{
title: '所属医院',
align: 'center',
dataIndex: 'resourceName',
},
{
title: '所属科室',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '进行中',
align: 'center',
dataIndex: 'ongoing',
},
{
title: '咨询统计',
align: 'center',
dataIndex: 'total',
},
{
title: '图文统计',
align: 'center',
dataIndex: 'graphic',
},
{
title: '视频统计',
align: 'center',
dataIndex: 'audioVideo',
},
{
title: '满意度',
align: 'center',
dataIndex: 'satisfaction',
},
{
title: '24小时回复率',
align: 'center',
dataIndex: 'rate',
},
{
title: '热度',
align: 'center',
dataIndex: 'degreeHeat',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '专家姓名',
field: 'doctorName',
component: 'Input',
},
{
label: '选择时间',
field: 'date',
component: 'RangePicker',
componentProps: ({ formModel }) => {
return {
valueType: 'Date',
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => BODY_CONTAINER,
'onUpdate:value': (value) => {
if (value) {
formModel.startTime = value[0];
formModel.endTime = value[1];
} else {
formModel.startTime = null;
formModel.endTime = null;
}
},
style: {
width: '100%',
},
};
},
},
{
label: '所属医院',
field: 'resourceId',
component: 'ApiSelect',
componentProps: {
// @ts-ignore
api: selectResourceList,
resultField: 'list',
labelField: 'name',
valueField: 'id',
immediate: true,
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
},
},
{
label: '所属科室',
field: 'departmentId',
component: 'JTreeDepartment',
componentProps: () => {
return {
allDep: true,
placeholder: '请选择科室',
showSearch: true,
treeNodeFilterProp: 'label',
};
},
},
{
label: '',
field: 'startTime',
component: 'Input',
show: false,
},
{
label: '',
field: 'endTime',
component: 'Input',
show: false,
},
];
//表单数据
export const formSchema: FormSchema[] = [];
/**
* 流程表单调用这个方法获取formSchema
*/
export function getBpmFormSchema(): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,35 @@
<template>
<!--引用表格-->
<BasicTable @register="registerTable" />
</template>
<script lang="ts" name="consultation-statistics" setup>
import { BasicTable } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './statistics.data';
import { list } from './statistics.api';
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_doctor',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 120,
fixed: 'right',
},
showActionColumn: false,
},
});
const [registerTable] = tableContext;
</script>
@@ -0,0 +1,50 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
export enum Api {
list = '/health-im/sync/group/list',
terminate = '/health-im/sync/group/terminate',
deleteOne = '/health-im/sync/group/delete/',
}
/**
* 列表接口
* @param params
*/
export const list = (params: any) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params: any, handleSuccess: Function) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 终止
* @param id
* @param handleSuccess
*/
export const terminate = (id: string, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认结束',
content: '是否强制结束同步',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.post({ url: Api.terminate + '/' + id }).then(() => {
handleSuccess();
});
},
});
};
@@ -0,0 +1,70 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//0-未开始 1-正在同步 2-同步结束 3-同步错误
export const stateMap = new Map([
[0, '未开始'],
[1, '正在同步'],
[2, '同步结束'],
[3, '同步错误'],
]);
export const columns: BasicColumn[] = [
{
title: '群组ID',
dataIndex: 'groupId',
},
{
title: '同步开始时间',
dataIndex: 'startTime',
},
{
title: '同步结束时间',
dataIndex: 'endTime',
},
{
title: '同步状态',
dataIndex: 'syncStatus',
customRender: ({ record }) => {
return stateMap.get(record?.['syncStatus']) || '';
},
},
{
title: '错误信息',
dataIndex: 'errorMsg',
},
{
title: '获取总消息数',
dataIndex: 'fetchMsgCount',
},
{
title: '已存在消息数',
dataIndex: 'existMsgCount',
},
{
title: '新同步消息数',
dataIndex: 'syncMsgCount',
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'groupId',
label: '群组ID',
component: 'Input',
colProps: {
span: 8,
},
},
{
field: 'date',
label: '同步时间',
component: 'RangeDate',
},
{
field: 'syncStatus',
label: '同步状态',
component: 'Select',
componentProps: () => {
return {
options: Array.from(stateMap).map((item) => ({ label: item[1], value: item[0] })),
};
},
},
];
@@ -0,0 +1,73 @@
<template>
<BasicTable :rowSelection="rowSelection" @register="registerTable">
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</template>
<script lang="ts" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from '/@/views/consult/groupRecordSynchronization/groupRecordSynchronization.data';
import { deleteOne, list, terminate } from '/@/views/consult/groupRecordSynchronization/groupRecordSynchronization.api';
import { message } from 'ant-design-vue';
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '群组记录同步',
api: list,
columns: columns,
canResize: false,
showIndexColumn: true,
formConfig: {
labelWidth: 100,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [['date', ['startTimeStart', 'startTimeEnd'], 'YYYY-MM-DD']],
},
beforeFetch: (par) => {
if (par['startTimeStart']) {
par['startTimeStart'] = par['startTimeStart'] + ' 00:00:00';
par['startTimeEnd'] = par['startTimeEnd'] + ' 23:59:59';
}
return par;
},
actionColumn: {
width: 150,
fixed: 'right',
},
},
});
const [registerTable, { reload, getForm, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
function handleClose(record: Recordable) {
if (record?.syncStatus == '2') {
return message.warn('当前已同步结束!');
}
terminate(record.id, handleSuccess);
}
function handleDelete(record: Recordable) {
deleteOne(record.id, handleSuccess);
}
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '强制结束',
onClick: handleClose.bind(null, record),
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
</script>
<style lang="less" scoped></style>
@@ -0,0 +1,173 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
:showFooter="showFooter"
destroyOnClose
:title="title"
:maskClosable="false"
:width="adaptiveWidth"
@ok="handleSubmit"
>
<div class="form-container">
<div class="title"><strong>基本信息</strong></div>
<BasicForm @register="registerForm" />
<!-- <div class="title"><strong>扩展信息</strong></div>-->
<!-- <BasicForm @register="registerFormData" />-->
<!-- <div class="title"><strong>其它信息</strong></div>-->
<!-- <BasicForm @register="registerFormOther" />-->
</div>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema, formSchemaData, formSchemaOther } from '../conHelper.data';
import { saveOrUpdate } from '../conHelper.api';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
import { dealPassword } from '/@/views/system/user/user.data';
const { adaptiveWidth } = useDrawerAdaptiveWidth();
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref<boolean>(true);
const showFooter = ref<boolean>(true);
const state = ref({});
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
schemas: formSchema,
showActionButtonGroup: false,
labelWidth: 100,
});
//表单配置扩展
const [
registerFormData,
{ setProps: setPropsData, setFieldsValue: setFieldsValueData, validate: validateData, clearValidate: clearValidateData },
] = useForm({
schemas: formSchemaData,
showActionButtonGroup: false,
labelWidth: 100,
});
//表单配置其他
const [
registerFormOther,
{ setProps: setPropsOther, setFieldsValue: setFieldsValueOther, validate: validateOther, clearValidate: clearValidateOther },
] = useForm({
schemas: formSchemaOther,
showActionButtonGroup: false,
labelWidth: 100,
});
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
state.value = data.record;
showFooter.value = data.showFooter;
//重置表单
await resetFields();
setDrawerProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
await updateSchema([{ field: 'password', ifShow: false }]);
//表单赋值
await setFieldsValue({
...data.record.conHelper,
...data.record.sysUserModel,
secondDepartment: data.record.conHelper.secondDepartId,
thirdDepartment: data.record.conHelper.thirdDepartId,
status: data.record.conHelper.status + '',
});
await setFieldsValueData({
...data.record.conHelper,
...data.record.sysUserModel,
});
await setFieldsValueOther({
...data.record.conHelper,
...data.record.sysUserModel,
});
await clearValidate();
await clearValidateData();
await clearValidateOther();
} else {
await updateSchema([
{
field: 'password',
ifShow: true,
},
]);
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
// await setPropsData({ disabled: !data?.showFooter });
// await setPropsOther({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let data = {
...(await validate()),
// ...(await validateData()),
// ...(await validateOther()),
};
let passwordObj = {};
if (!isUpdate.value) {
passwordObj = {
password: dealPassword(data?.password),
};
}
//调整数据参数
let setData = {
conHelper: {
id: data?.id,
goodAt: data?.goodAt,
orgCode: data?.orgCode,
helperName: data?.helperName,
status: data?.status || 1,
},
sysUserModel: {
id: data?.id,
avatar: data?.avatar,
email: data?.email,
idCard: data?.idCard,
phone: data?.phone,
sex: data?.sex,
username: data?.username,
...passwordObj,
secondDepartment: data?.secondDepartment,
thirdDepartment: data?.thirdDepartment,
},
};
setDrawerProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(setData, isUpdate.value);
//关闭弹窗
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
.form-container {
overflow: auto;
}
.title {
box-sizing: border-box;
color: #333;
font-weight: bold;
padding: 12px 16px;
}
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,114 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800">
<div class="history-modal">
<BasicForm
:labelWidth="80"
:schemas="schedulingSearchSchema"
:baseColProps="colOptions"
:actionColOptions="colOptions"
@submit="handleSubmit"
@reset="handleReset"
/>
<a-table
:columns="schedulingColumns"
:pagination="paginationState"
@change="changeTable"
:data-source="customState.customData"
border
size="small"
/>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import BasicForm from '/@/components/Form/src/BasicForm.vue';
import { schedulingColumns, schedulingSearchSchema, SchedulingState } from '/@/views/consult/helper/conHelper.data';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { reactive, ref } from 'vue';
import { schedulingRecord } from '/@/views/consult/helper/conHelper.api';
const title = '历史排班记录';
const colOptions = {
offset: 0,
span: 12,
xs: 12, // <576px
sm: 12, // ≥576px
md: 12, // ≥768px
lg: 12, // ≥992px
xl: 12, // ≥1200px
xxl: 12, // ≥1600px
};
let paginationState = ref({
current: 1,
pageSize: 10,
showSizeChanger: false,
total: 0,
showQuickJumper: true,
size: 'small',
showTotal: (total: number) => ' 共 ' + total / 2 + ' 条数据',
});
const customState = reactive<SchedulingState>({
form: {
date: '',
type: '',
},
customData: [],
apiData: [],
});
const [registerModal, { setModalProps }] = useModalInner(() => {
setModalProps({ showCancelBtn: false, showOkBtn: false });
getCustomList();
});
function getCustomList(pageNo?: number) {
const pageSize = paginationState.value.pageSize === 10 ? 5 : paginationState.value.pageSize / 2;
const params = {
...customState.form,
pageNo: pageNo ?? paginationState.value.current,
pageSize: pageSize,
};
schedulingRecord(params).then((res) => {
const { current, total, records = [] } = res;
function dealRes() {
let arr: any[] = [];
records?.map((item: any) => {
let amUser = item?.detail[0].dataList?.map((p) => p.userName) || [];
let pmUser = item?.detail[1].dataList?.map((p) => p.userName) || [];
arr.push({ ...item?.detail[0], helper: amUser.join(', '), time: item?.time });
arr.push({ ...item?.detail[1], helper: pmUser.join(', '), time: item?.time });
});
return arr;
}
customState.customData = dealRes();
customState.apiData = records;
paginationState.value = {
...paginationState.value,
current,
total: total * 2,
};
});
}
/**
* @Description:页码改变事件
* @date 2023/6/20
* @param pagination
*/
const changeTable = (pagination: { current: number; pageSize: number }) => {
paginationState.value.current = pagination.current;
paginationState.value.pageSize = pagination.pageSize;
getCustomList();
};
function handleSubmit(val) {
customState.form = val;
getCustomList(1);
}
function handleReset() {
customState.form = {};
getCustomList(1);
}
</script>
<style scoped lang="less">
.history-modal {
height: 60vh;
min-height: 60vh;
overflow: auto;
}
</style>
@@ -0,0 +1,168 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @cancel="handleCancel">
<a-table :columns="columns" :data-source="state.columnData" :pagination="false" border size="small">
<template #bodyCell="{ column, record, text }">
<template v-if="column.dataIndex === 'departmentName'">
<div class="editable-cell">
<div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
<a-select
v-model:value="editableData[record.key].selectVal"
:options="selectOptions"
mode="multiple"
placeholder="请选择值班人员"
:fieldNames="fieldNames"
optionLabelProp="userName"
style="min-width: 200px"
/>
</div>
<div v-else class="editable-cell-text-wrapper">
{{ text || ' ' }}
</div>
</div>
</template>
<template v-if="column.key === 'action'">
<a v-if="!editableData[record.key]" @click.prevent="edit(record.key)">编辑</a>
<span v-else>
<a @click.prevent="save(record.key)">保存 </a>
<a style="margin-left: 10px" @click.prevent="noSave(record.key)">取消</a>
</span>
</template>
</template>
</a-table>
</BasicModal>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { columns } from '../scheduling.data';
import { getHelperIds, list, updateHelper } from '../scheduling.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
//设置标题
const title = '排班设置';
const fieldNames = { label: 'userName', value: 'id' };
const state = ref({
columnData: [],
});
const checkKey = ref('');
let editableData = reactive({});
const selectOptions = ref([]);
const [registerModal, { setModalProps, closeModal }] = useModalInner(() => {
setModalProps({ showCancelBtn: false, showOkBtn: false });
initOption();
initData();
});
function initOption() {
getHelperIds({}).then((res) => {
selectOptions.value = res;
});
}
function dealData(data) {
data.map((item, i) => {
item.key = `${i}`;
});
return data;
}
async function initData() {
let res = await list({});
state.value.columnData = dealData(res);
}
function edit(key) {
let row = state.value.columnData.filter((item) => key === item.key)[0];
const names = row?.departmentName.split(',');
let values = getValues(names);
editableData[key] = { row: row, selectVal: values };
checkKey.value = key;
}
function getLabel(selectVal) {
let labels = [];
selectOptions.value.forEach((item) => {
if (selectVal.includes(item[fieldNames.value])) {
labels.push(item[fieldNames.label]);
}
});
return labels.join(',');
}
function getValues(labels) {
let values = [];
selectOptions.value.forEach((item) => {
if (labels.includes(item[fieldNames.label])) {
values.push(item[fieldNames.value]);
}
});
return values;
}
async function updateData(params) {
if (params) {
await updateHelper(params);
}
}
function save(key: string) {
const { selectVal } = editableData[key];
let labels = getLabel(selectVal);
const params = {
type: editableData[key]?.row?.type,
week: editableData[key]?.row?.week,
userIds: editableData[key].selectVal.join(','),
userNames: getLabel(editableData[key].selectVal),
};
Object.assign(state.value.columnData.filter((item) => key === item.key)[0], {
...editableData[key].row,
departmentName: labels,
});
updateData(params);
delete editableData[key];
}
function noSave(key) {
editableData[key].selectVal = state.value.columnData[key]?.list?.map((item) => item.userId) || [];
delete editableData[key];
}
function clearData() {
state.value.columnData = [];
editableData = {};
checkKey.value = '';
}
//表单提交事件
function handleCancel() {
try {
if (checkKey.value !== '') {
noSave(checkKey.value);
}
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
clearData();
}
}
defineExpose({});
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
:deep(table tr, table th, table td) {
border-right: 1px solid #f0f0f0 !important;
border-bottom: 1px solid #f0f0f0 !important;
}
</style>
+91
View File
@@ -0,0 +1,91 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/conHelper/list',
save = '/health-consultation/conHelper/add',
edit = '/health-consultation/conHelper/edit',
deleteOne = '/health-consultation/conHelper/delete',
deleteBatch = '/health-consultation/conHelper/deleteBatch',
importExcel = '/health-consultation/conHelper/importExcel',
exportXls = '/health-consultation/conHelper/exportXls',
queryById = '/health-consultation/conHelper/queryById',
schedulingRecord = '/health-consultation/consultation/conHelperSchedulingHistory/list',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* @description 获取小助手信息详情
* @param params 请求参数
* */
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
// 排班记录
export const schedulingRecord = (params) => defHttp.get({ url: Api.schedulingRecord, params });
+404
View File
@@ -0,0 +1,404 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { getFamaleDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { h, ref } from 'vue';
import { EyeOutlined } from '@ant-design/icons-vue';
import { Image } from 'ant-design-vue';
import { render } from '/@/utils/common/renderUtils';
import { checkPassword } from '/@/hooks/checkPassword/checkPassword';
//列表数据
export const columns: BasicColumn[] = [
{
title: '用户账号',
align: 'center',
width: 200,
dataIndex: 'userAccount',
},
{
title: '姓名',
align: 'center',
width: 200,
dataIndex: 'userName',
},
{
title: '头像',
align: 'center',
dataIndex: 'faceUrl',
width: 100,
customRender: ({ text, record }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getFamaleDefaultImage(record.sex),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
{
title: '身份证号',
align: 'center',
dataIndex: 'idCard',
width: 200,
},
{
title: '手机号',
align: 'center',
width: 200,
dataIndex: 'phone',
},
{
title: '账号状态',
align: 'center',
dataIndex: 'status',
width: 100,
customRender: ({ text }) => {
if (!text) return '';
if (text == '1') {
return '正常';
} else {
return '冻结';
}
},
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '姓名',
field: 'userName',
component: 'Input',
},
{
label: '手机号',
field: 'phoneOrIdCard',
component: 'Input',
},
{
label: '账号状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
// @ts-ignore//@ts-ignore
dictCode: 'freeze_status',
},
},
{
label: '',
field: 'secondOrgId',
component: 'Input',
show: false,
},
];
const orgCode = ref('');
//表单数据
export const formSchema: FormSchema[] = [
// 主键Id
{
label: '',
field: 'id',
show: false,
component: 'Input',
},
{
label: '用户账号',
field: 'username',
component: 'Input',
componentProps: ({ formModel }) => {
return {
autocomplete: 'off',
onInput: (event) => {
const target = event.target;
formModel.username = target.value.replace(/[^\w]/g, '');
},
};
},
required: true,
rules: [{ required: true, message: '请输入用户账号' }],
},
{
label: '账号状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
// @ts-ignore//@ts-ignore
dictCode: 'freeze_status',
},
show: ({ values }) => {
return values.id;
},
defaultValue: '1',
},
{
label: '密码',
field: 'password',
component: 'StrengthMeter',
componentProps: {
// @ts-ignore
autocomplete: 'new-password',
readOnly: true,
onfocus: (field) => {
field.target.readOnly = false;
},
},
dynamicRules: () => {
return [
{
required: true,
validator: (_, value) => {
let { message } = checkPassword(value);
if (message === 'ok') {
return Promise.resolve();
} else {
return Promise.reject(message);
}
},
trigger: 'blur',
},
];
},
show: ({ values }) => {
return !values.id;
},
},
{
label: '姓名',
field: 'helperName',
component: 'Input',
required: true,
},
{
field: 'sex',
component: 'JDictSelectTag',
label: '性别',
componentProps: {
//@ts-ignore
dictCode: 'sex2',
type: 'radio',
},
rules: [{ required: true, message: '请选择性别' }],
},
{
field: 'avatar',
label: '头像',
component: 'JImageUpload',
componentProps: {
// @ts-ignore
maxCount: 1,
},
// rules: [{ required: true, message: '请选择头像' }],
},
{
label: '手机号',
field: 'phone',
component: 'Input',
rules: [{ required: true, pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' }],
},
{
label: '身份证号',
field: 'idCard',
component: 'Input',
rules: [{ required: true, pattern: /^\d{18}$/, message: '身份证号格式有误' }],
},
{
field: 'email',
label: '邮箱',
component: 'Input',
rules: [{ required: false, type: 'email', message: '邮箱格式不正确', trigger: 'blur' }],
componentProps: {
// @ts-ignore
placeholder: '请输入邮箱',
},
},
{
label: '',
field: 'orgCode',
component: 'Input',
// @ts-ignore
componentProps: ({ formModel }) => {
formModel.orgCode = orgCode.value;
},
show: false,
},
];
//扩展数据
export const formSchemaData: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '学历',
field: 'qualification',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'education',
},
},
{
label: '出生年月',
field: 'birthday',
component: 'DatePicker',
componentProps: {
// @ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
},
},
{
label: '毕业院校',
field: 'graduateSchool',
component: 'Input',
},
{
field: 'graduationTime',
label: '毕业时间',
component: 'DatePicker',
componentProps: {
// @ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
},
},
{
label: '民族',
field: 'nationality',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'nation',
},
},
{
label: '籍贯',
field: 'nativePlace',
component: 'Input',
},
{
label: '联系地址',
field: 'address',
component: 'Input',
},
];
//扩展数据
export const formSchemaOther: FormSchema[] = [
{
field: 'workTime',
label: '参加工作时间',
component: 'DatePicker',
componentProps: {
// @ts-ignore
showTime: false,
valueFormat: 'YYYY-MM-DD',
},
},
{
label: '擅长',
field: 'excel',
component: 'InputTextArea',
},
{
label: '职业经历',
field: 'practicingExperience',
component: 'InputTextArea',
},
{
label: '获奖或论文',
field: 'awardsAndPapers',
component: 'InputTextArea',
},
{
label: '工作经历',
field: 'workHistory',
component: 'InputTextArea',
},
];
// 排班记录
export const schedulingSearchSchema: FormSchema[] = [
{
label: '排班时间',
field: 'date',
component: 'RangePicker',
componentProps: ({ formModel }) => {
return {
showTime: false,
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => document.body,
onChange: ([start, end]) => {
formModel.startTime = start;
formModel.endTime = end;
},
};
},
},
{
label: '开始时间',
field: 'startTime',
component: 'Input',
show: false,
},
{
label: '结束时间',
field: 'endTime',
component: 'Input',
show: false,
},
];
// 排班列表
export const schedulingColumns: BasicColumn[] = [
{
title: '排班时间',
dataIndex: 'time',
key: 'time',
width: 120,
align: 'center',
customCell: (_, index) => {
if (index % 2 === 0) {
return {
rowSpan: 2,
};
} else {
return {
rowSpan: 0,
};
}
},
},
{
title: '班次',
width: 80,
dataIndex: 'type',
align: 'center',
customRender: ({ text }) => text && render.renderDict(text, 'am_pm'),
},
{
title: '值班人员',
key: 'helper',
dataIndex: 'helper',
},
];
export interface SchedulingState {
form: any;
customData: any[];
apiData: any[];
}
/**
* 流程表单调用这个方法获取formSchema
*/
export function getBpmFormSchema(): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
+181
View File
@@ -0,0 +1,181 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'consultation:con_helper:add'" @click="handleAdd" preIcon="ant-design:plus-outlined">
新增
</a-button>
<a-button type="primary" @click="handleScheduling"> 排班设置 </a-button>
<a-button @click="handleSchedulingRecord"> 历史排班记录 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!-- 表单 -->
<ConHelperModal @register="registerDrawer" @success="handleSuccess" />
<!-- 排班设置 -->
<s-modal @register="registerScheModal" @success="handleSuccess" />
<!-- 重置密码 -->
<RestPass @register="restPassModal" />
<!-- 历史排班记录-->
<historical-scheduling-records @register="registerScheduling" />
</div>
</template>
<script lang="ts" name="consultation-conHelper" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConHelperModal from './components/conHelperModal.vue';
import SModal from './components/schedulingModal.vue';
import { columns, searchFormSchema } from './conHelper.data';
import { deleteOne, list, queryById } from './conHelper.api';
import { lockUser } from '/@/views/system/user/hospitalDoctor/HospitalDoctor.api';
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
import { useDrawer } from '/@/components/Drawer';
import HistoricalSchedulingRecords from '/@/views/consult/helper/components/historicalSchedulingRecords.vue';
const [registerDrawer, { openDrawer }] = useDrawer();
//注册model
const [registerScheModal, { openModal: openScheModal }] = useModal();
const [restPassModal, { openModal: restPaddModel }] = useModal();
const [registerScheduling, { openModal: openScheduling }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_helper',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 140,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openDrawer(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
let res = await queryById({ id: record.id });
openDrawer(true, {
record: res,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
/**
* 排班事件
*/
function handleScheduling() {
openScheModal(true, {
isUpdate: false,
showFooter: true,
});
}
// 历史排班记录
function handleSchedulingRecord() {
openScheduling(true, {
isUpdate: false,
showFooter: false,
});
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'consultation:con_helper:edit',
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'consultation:con_helper:delete',
},
];
}
// 重置密码
function restPass(record: Recordable) {
restPaddModel(true, {
record: record.id,
isUpdate: true,
showFooter: false,
});
}
// 1 锁定用户 2 激活用户
function handleUserOpt(record: Recordable, type: any) {
const params = {
userId: record.id,
status: type,
};
lockUser(params, type, handleSuccess);
}
/**
* 下拉操作栏
*/
function getDropDownAction(record: Recordable) {
return [
{
label: '重置密码',
onClick: restPass.bind(null, record),
},
{
label: '锁定用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 2),
},
{
label: '激活用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 1),
},
];
}
</script>
<style lang="less" scoped>
:deep(.ant-popover-buttons) {
display: flex !important;
}
</style>
@@ -0,0 +1,77 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
//排班
list = '/health-consultation/conHelperScheduling/list',
save = '/health-consultation/conHelperScheduling/add',
edit = '/health-consultation/conHelperScheduling/edit',
deleteOne = '/health-consultation/conHelperScheduling/delete',
deleteBatch = '/health-consultation/conHelperScheduling/deleteBatch',
importExcel = '/health-consultation/conHelperScheduling/importExcel',
exportXls = '/health-consultation/conHelperScheduling/exportXls',
getHelperIds = '/health-consultation/conHelper/getHelperIds',
updateHelper = '/health-consultation/conHelperScheduling/updateHelperCustom',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
export const getHelperIds = (params) => defHttp.get({ url: Api.getHelperIds, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
export const updateHelper = (params) => defHttp.post({ url: Api.updateHelper, params });
@@ -0,0 +1,68 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '星期',
dataIndex: 'week_dictText',
key: 'week_dictText',
width: 80,
align: 'center',
customCell: (_, index: number) => {
if (index % 2 === 0) {
return {
rowSpan: 2,
};
} else {
return {
rowSpan: 0,
};
}
},
},
{
title: '班次',
width: 80,
align: 'center',
dataIndex: 'type_dictText',
},
{
title: '值班人员',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
align: 'center',
width: 120,
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '视频咨询时间',
field: 'scheduling_date',
component: 'Input',
required: true,
},
{
label: '在线时间',
field: 'password',
component: 'StrengthMeter',
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,67 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit" :maskClosable="false">
<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 '../conNotice.data';
import { saveOrUpdate } from '../conNotice.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = 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,
});
}
await clearValidate();
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
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>
+98
View File
@@ -0,0 +1,98 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conNotice/list',
save = '/health-consultation/consultation/conNotice/add',
edit = '/health-consultation/consultation/conNotice/edit',
deleteOne = '/health-consultation/consultation/conNotice/delete',
deleteBatch = '/health-consultation/consultation/conNotice/deleteBatch',
importExcel = '/health-consultation/consultation/conNotice/importExcel',
exportXls = '/health-consultation/consultation/conNotice/exportXls',
queryById = '/health-consultation/consultation/conNotice/queryById',
status = '/health-consultation/api/consult/notice/on',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
params['status'] = '2';
return defHttp.post({ url: url, params });
};
/**
* @Description:获取详情
* @date 2023/6/19
* @param: {id}
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/**
* 启用/禁用
*/
export const changeStatus = (params, data, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '提示',
content: `启用将会设置其他【${data.noticeType === 0 ? '图文咨询' : '视频咨询'}】须知为禁用,是否确定启用?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.post({ url: Api.status, params }).then(() => {
handleSuccess();
});
},
});
};
+106
View File
@@ -0,0 +1,106 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '标题',
align: 'center',
dataIndex: 'title',
},
{
title: '咨询类型',
align: 'center',
dataIndex: 'type_dictText',
},
{
title: '创建时间',
align: 'center',
dataIndex: 'createTime',
customRender: ({ text }): string => {
return !text ? '' : text.length > 10 ? text.substring(0, 10) : text;
},
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
return text && text == '1' ? '启用' : '禁用';
},
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '标题',
field: 'title',
component: 'Input',
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
options: [
{ label: '启用', value: '1' },
{ label: '禁用', value: '2' },
],
},
},
{
label: '咨询类型',
field: 'noticeType',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'notice_type',
},
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '标题',
field: 'title',
component: 'Input',
required: true,
},
{
label: '咨询类型',
field: 'noticeType',
component: 'JDictSelectTag',
componentProps: {
//@ts-ignore
dictCode: 'notice_type',
},
required: true,
},
{
label: '咨询须知',
field: 'content',
component: 'JEditor',
required: true,
},
];
/**
* 流程表单调用这个方法获取formSchema
*/
export function getBpmFormSchema(): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
+136
View File
@@ -0,0 +1,136 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="null">
<!--插槽:table标题-->
<template #tableTitle>
<a-button v-auth="auth.add" type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)"
>下载
</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<con-notice-modal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conNotice" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConNoticeModal from './components/conNoticeModal.vue';
import { columns, searchFormSchema } from './conNotice.data';
import { list, deleteOne, queryById, changeStatus } from './conNotice.api';
import { downloadFile } from '/@/utils/common/renderUtils';
const auth = {
add: 'consultation:con_notice:add',
edit: 'consultation:con_notice:edit',
deleteOne: 'consultation:con_notice:delete',
deleteBatch: '',
};
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '咨询须知',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 160,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
let res = await queryById({ id: record.id });
openModal(true, {
record: res,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
/**
* @Description:启用
* @date 2023/8/29
* @param record
*/
function handleStatus(record: Recordable) {
changeStatus({ id: record.id }, record, handleSuccess);
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: auth.edit,
},
{
label: '启用',
onClick: handleStatus.bind(null, record),
disabled: record.status === '1',
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: auth.deleteOne,
},
];
}
</script>
<style lang="less" scoped>
:deep(.ant-popover-buttons) {
display: flex !important;
}
</style>
@@ -0,0 +1,109 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
showFooter
destroyOnClose
width="40%"
:title="isUpdate ? '修改就医信息' : `新增就医信息`"
@ok="handleSubmit"
>
<BasicForm @register="registerForm">
<template #name="{ model }">
<div style="display: flex">
<a-input placeholder="请选择用户" v-model:value="userItem['realName']" :disabled="true" />
<a-button type="primary" style="margin-left: 10px" @click="choose"> 选择 </a-button>
</div>
<div>
<div style="display: flex; margin-top: 10px">
<div style="flex: 1">单位{{ userItem?.secondName || '--' }}</div>
<div style="flex: 1">部门{{ userItem?.thirdName || '--' }}</div>
</div>
<div style="display: flex; margin-top: 10px">
<div>工号{{ userItem?.workNo || '--' }}</div>
</div>
</div>
</template>
</BasicForm>
</BasicDrawer>
<ChooseSome :width="800" @register="registerUserDrawer" @select-some="onSelectUserOk" title="选择指定员工" :tableprops="tableContext" />
</template>
<script setup lang="ts">
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
import { useDrawer, useDrawerInner } from '/@/components/Drawer';
import { BasicForm, useForm } from '/@/components/Form';
import { formSchema, tableContext } from '/@/views/consult/offlineTreatment/offlineTreatment.data';
import ChooseSome from '/@/views/compoents/chooseSome/index.vue';
import { ref } from 'vue';
import { addApi, editApi } from '/@/views/consult/offlineTreatment/offlineTreatment.api';
const userItem = ref<object>({});
const isUpdate = ref<boolean>(false);
const emit = defineEmits(['success']);
const [registerForm, { setFieldsValue, validate, resetFields, clearValidate }] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
const [registerUserDrawer, { openDrawer: openChooseDrawer }] = useDrawer();
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
await resetFields();
isUpdate.value = data?.isUpdate;
if (isUpdate.value) {
userItem.value = {
id: data?.record.userId,
realName: data?.record.realName,
secondName: data?.record?.secondDeptName,
thirdName: data?.record?.thirdDeptName,
workNo: data?.record?.workNo,
orgCode: data?.record?.orgCode,
};
await setFieldsValue(data?.record);
await clearValidate();
}
});
async function handleSubmit() {
try {
setDrawerProps({ confirmLoading: true });
const val = await validate();
if (isUpdate.value) {
await editApi(val);
} else {
await addApi(val);
}
userItem.value = {};
closeDrawer();
emit('success');
} catch (err: any) {
} finally {
setDrawerProps({ confirmLoading: false });
}
}
function choose() {
openChooseDrawer(true, {
selectedRowKeys: [JSON.stringify(userItem.value)],
});
}
function onSelectUserOk(e: any) {
try {
e = JSON.parse(e);
} catch (err: any) {
e = {};
}
userItem.value = e;
setFieldsValue({
userId: e?.id,
realName: e?.realName,
orgCode: e?.orgCode,
});
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,35 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/emergency/treatment/list',
add = '/health-consultation/emergency/treatment/add',
edit = '/health-consultation/emergency/treatment/edit',
delB = '/health-consultation/emergency/treatment/deleteBatch',
del = '/health-consultation/emergency/treatment/delete',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params });
export const addApi = (params: any) => defHttp.post({ url: Api.add, params });
export const editApi = (params: any) => defHttp.post({ url: Api.edit, params });
export const delApi = (params: any, handleSuccess: any, type: any) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: async () => {
if (type === '0') {
return defHttp.delete({ url: Api.del, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
}
return defHttp.post({ url: Api.delB, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
@@ -0,0 +1,202 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { orgSearchInfo } from '/@/utils/orgSearchInfo';
import { ref } from 'vue';
import { employListApi } from '/@/views/emergency/communication/components/commApi';
import { mColumns } from '/@/views/healthMonitor/healMonitorManage/monitorToll/toolManagement/toolManagement.data';
import { userFormSchema } from '/@/views/interveneNew/diabetes/intervene/terminalManagement/terminalManagement.data';
export const columns: BasicColumn[] = [
{
title: '单位名称',
dataIndex: 'secondDeptName',
},
{
title: '部门名称',
dataIndex: 'thirdDeptName',
},
{
title: '员工姓名',
dataIndex: 'realName',
},
{
title: '员工工号',
dataIndex: 'workNo',
},
{
title: '就诊医院',
dataIndex: 'hospital',
},
{
title: '就诊医生',
dataIndex: 'doctor',
},
{
title: '疾病名称',
dataIndex: 'disease',
},
{
title: '患病程度',
dataIndex: 'diseaseLevel',
},
{
title: '就医日期',
dataIndex: 'treatmentDate',
},
];
export const searchForm: FormSchema[] = [
...orgSearchInfo(),
{
field: 'realName',
label: '员工姓名',
component: 'Input',
},
{
field: 'workNo',
label: '员工工号',
component: 'Input',
},
{
field: 'disease',
label: '疾病名称',
component: 'Input',
},
{
field: 'diseaseLevel',
label: '患病程度',
component: 'Input',
},
{
field: 'rangeDate',
label: '就医日期',
component: 'RangeDate',
componentProps: () => {
return {
valueFormat: 'YYYY-MM-DD',
format: 'YYYY-MM-DD',
style: {
width: '100%',
},
};
},
},
];
export const formSchema: FormSchema[] = [
{
field: 'realName',
label: '员工信息',
component: 'Input',
slot: 'name',
},
{
field: 'hospital',
label: '就诊医院',
component: 'Input',
},
{
field: 'doctor',
label: '就诊医生',
component: 'Input',
},
{
field: 'disease',
label: '疾病名称',
component: 'Input',
required: true,
},
{
field: 'diseaseLevel',
label: '患病程度',
component: 'Input',
required: true,
},
{
field: 'treatmentDate',
label: '就医日期',
component: 'DatePicker',
componentProps: () => ({ valueFormat: 'YYYY-MM-DD', format: 'YYYY-MM-DD', style: { width: '100%' } }),
required: true,
},
{
field: 'memo',
label: '备注',
component: 'InputTextArea',
componentProps: () => ({ rows: 5 }),
},
{
field: 'orgCode',
label: '',
component: 'Input',
show: false,
},
{
field: 'userId',
label: '',
component: 'Input',
show: false,
},
{
field: 'id',
label: '',
component: 'Input',
show: false,
},
];
export const tableContext = ref({
tableProps: {
title: '选择指定员工',
api: employListApi,
columns: [
...mColumns.slice(0, 2),
{
title: '员工工号',
dataIndex: ['extension', 'empNo'],
align: 'center',
},
],
canResize: false,
clearSelectOnPageChange: false,
formConfig: {
labelWidth: 63,
schemas: userFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
baseColProps: {
xs: 8,
sm: 8,
md: 8,
lg: 8,
xl: 12,
xxl: 12,
},
actionColOptions: {
span: 24,
offset: 0,
xs: 8,
sm: 8,
md: 8,
lg: 8,
xl: 12,
xxl: 12,
},
},
beforeFetch: (params) => {
// params['orgCode'] = user?.userInfo?.orgCode?.substring(0, 6);
return params;
},
showActionColumn: false,
rowKey: (record: Recordable) =>
JSON.stringify({
id: record.id,
realName: record.realname,
secondName: record?.secondDepart?.departName,
thirdName: record?.threeDepart?.departName,
workNo: record?.workNo,
orgCode: record?.orgCode,
}),
},
});
@@ -0,0 +1,99 @@
<template>
<div>
<BasicTable @register="registerTable" :row-selection="rowSelection">
<template #tableTitle>
<a-button v-auth="`offline-treatment:add-button`" type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined">
新增
</a-button>
<a-button v-auth="`offline-treatment:add-button`" type="primary" @click="handleDeleteB" preIcon="ant-design:plus-outlined">
批量删除
</a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</div>
<add-drawer @register="registerDrawer" @success="handleSuccess" />
</template>
<script setup lang="ts" name="consult-offlineTreatment-offlineTreatment">
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchForm } from '/@/views/consult/offlineTreatment/offlineTreatment.data';
import { delApi, listApi } from '/@/views/consult/offlineTreatment/offlineTreatment.api';
import { useDrawer } from '/@/components/Drawer';
import AddDrawer from '/@/views/consult/offlineTreatment/components/addDrawer.vue';
import { message } from 'ant-design-vue';
const [registerDrawer, { openDrawer }] = useDrawer();
const { tableContext } = useListPage({
tableProps: {
title: '就医记录',
api: listApi,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchForm,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [['rangeDate', ['startDate', 'endDate']]],
},
actionColumn: {
width: 160,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { selectedRowKeys, rowSelection }] = tableContext;
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
},
];
}
function handleAdd() {
openDrawer(true, {
isUpdate: false,
});
}
function handleEdit(record: Recordable) {
openDrawer(true, {
isUpdate: true,
record,
});
}
function handleDelete(record: Recordable) {
try {
delApi({ id: record?.id }, handleSuccess, '0');
} catch (e) {
console.log(e);
}
}
function handleDeleteB() {
if (selectedRowKeys.value.length === 0) return message.warn('请至少选择一条数据');
try {
delApi({ ids: selectedRowKeys.value }, handleSuccess, '1');
} catch (e) {
console.log(e);
}
}
function handleSuccess() {
reload();
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,72 @@
<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 '../conPhrases.data';
import { saveOrUpdate } from '../conPhrases.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,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
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>
@@ -0,0 +1,71 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/phrases/list',
save = '/health-consultation/consultation/phrases/add',
edit = '/health-consultation/consultation/phrases/edit',
deleteOne = '/health-consultation/consultation/phrases/delete',
deleteBatch = '/health-consultation/consultation/phrases/deleteBatch',
importExcel = '/health-consultation/consultation/phrases/importExcel',
exportXls = '/health-consultation/consultation/phrases/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () =>
defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
}),
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,152 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
import { useUserStoreWithOut } from '/@/store/modules/user';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
dataIndex: 'number',
width: 70,
},
{
title: '常用语内容',
align: 'center',
dataIndex: 'content',
},
{
title: '所属角色',
align: 'center',
dataIndex: 'personType',
customRender: ({ text }) => {
return render.renderDict(text, 'p_type');
},
},
{
title: '所属用户',
align: 'center',
dataIndex: 'userName',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '0') {
return '启用';
} else {
return '禁用';
}
},
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '常用语',
field: 'content',
component: 'Input',
},
{
label: '角色',
field: 'personType',
component: 'JDictSelectTag',
componentProps: {
// @ts-ignore
options: [
{
label: '员工',
value: '1',
},
{
label: '小助手',
value: '2',
},
{
label: '专家',
value: '3',
},
],
},
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
// @ts-ignore
dictCode: 'switch_status',
},
},
];
const userId = useUserStoreWithOut().getUserInfo.id;
//表单数据
export const formSchema: FormSchema[] = [
{
label: '所属角色',
field: 'personType',
component: 'JDictSelectTag',
rules: [{ required: true, message: '请选择人员类型' }],
componentProps: {
// @ts-ignore
options: [
{
label: '员工',
value: '1',
},
{
label: '小助手',
value: '2',
},
{
label: '专家',
value: '3',
},
],
getPopupContainer: () => document.body,
},
},
{
label: '常用语内容',
field: 'content',
component: 'InputTextArea',
rules: [{ required: true, message: '请输入常用语内容' }],
},
{
label: '所属用户',
field: 'userId',
component: 'Input',
show: (formSchema) => formSchema.field === userId,
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
// @ts-ignore
dictCode: 'switch_status',
getPopupContainer: () => document.body,
},
defaultValue: '0',
rules: [{ required: true, message: '请选择状态' }],
},
{
label: '序号',
field: 'number',
component: 'InputNumber',
},
{
label: '主键隐藏字段,目前写死为ID',
field: 'id',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
*/
export function getBpmFormSchema(): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,108 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="null">
<!--插槽:table标题-->
<template #tableTitle>
<a-button v-auth="auth.add" type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<con-phrases-modal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conPhrases" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConPhrasesModal from './components/conPhrasesModal.vue';
import { columns, searchFormSchema } from './conPhrases.data';
import { list, deleteOne } from './conPhrases.api';
const auth = {
add: 'consultation:con_phrases:add',
edit: 'consultation:con_phrases:edit',
deleteOne: 'consultation:con_phrases:delete',
};
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_phrases',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: auth.edit,
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: auth.deleteOne,
},
];
}
</script>
@@ -0,0 +1,5 @@
export interface Position {
lng: string | number;
lat: string | number;
[k: string]: any;
}
@@ -0,0 +1,290 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
destroyOnClose
:title="title"
:width="1000"
@ok="handleSubmit"
@cancel="clearData"
:maskClosable="false"
>
<div class="map-search-box">
<a-select
v-model:value="intValue"
allow-clear
show-search
id="tipinput"
placeholder="请输入地点"
style="width: 400px"
:default-active-first-option="false"
:show-arrow="true"
:filter-option="false"
:not-found-content="null"
@search="onSearch"
:options="panelList"
@change="handleChange"
/>
<a-button type="primary" @click="onButtonSearch"> 搜索 </a-button>
</div>
<div id="panel" style="display: none"></div>
<!--查询列表,高德地图api绑定id-->
<div class="list" id="list"> </div>
<div style="height: 62vh" id="container"></div>
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import AMapLoader from '@amap/amap-jsapi-loader';
import { ref, reactive, nextTick } from 'vue';
import positionIcon from '/@/assets/images/position.png';
import { debounce } from 'lodash-es';
import { Position } from '/@/views/consult/resource/components/Map';
import mapKey from '/@/utils/mapKey';
import { message } from 'ant-design-vue';
let geocoder = ref();
const emit = defineEmits(['getPosition']);
let BasicMap = null;
let SelfMap = null;
const isCLickMap = ref(false);
let positionRef = ref({
lng: '',
lat: '',
handleItem: {
pname: '',
},
});
let mapState = reactive({
marker: null,
});
let adr = ref('');
let position: Position = positionRef.value;
const title = '地图选点';
const intValue = ref('');
let placeSearch = ref();
let panelList = ref([]);
//表单赋值
const [registerModal, { closeModal }] = useModalInner(async (data) => {
let { longitude: lng, latitude: lat, address } = data?.record || {};
position.lng = lng;
position.lat = lat;
intValue.value = address || '';
adr.value = address || '';
isCLickMap.value = false;
initMap();
});
function initMap() {
AMapLoader.load({
key: mapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
// version: '1.4.15', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.Geocoder'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
})
.then((AMap) => {
SelfMap = AMap;
//设置地图容器id
BasicMap = new AMap.Map('container', {
viewMode: '3D', //是否为3D地图模式
zoom: 12, //初始化地图级别
center: [108.95, 34.33], //初始化地图中心点位置
resizeEnable: true,
});
// 注册搜索插件
bindSearch(AMap);
bindEvent();
// 注册坐标转地址
geocoder.value = new AMap.Geocoder({
city: '', //城市设为北京,默认:“全国”
radius: 1000, //范围,默认:500
});
if (position.lng) {
panTo(position);
addMarker(position);
}
})
.catch((e) => {
console.log(e);
});
}
/**
* @Description:输入框查询位置
* @date 2023/7/8
*/
function onSearch(value: string) {
//关键字查询
if (value) {
intValue.value = value;
searchRes(value);
}
}
const searchRes = debounce((value) => {
placeSearch.value.search(value, (status: string, result: any) => {
console.log(result);
if (status === 'complete') {
panelList.value = result.poiList.pois.map((item) => ({ label: item.name, value: item.name, ...item }));
}
});
}, 500);
function onButtonSearch() {
onSearch(intValue.value);
}
/**
* @Description下拉框选择事件
* @date 2023/8/30
*/
function handleChange(value: any, option: any) {
panTo(option.location);
setMapZoom(15);
let { lng, lat } = option.location;
let handleItem = panelList.value?.find((item) => item['id'] === intValue.value) ?? { pname: '' };
// adr.value = value;
positionRef.value = { lng, lat, handleItem };
addMarker(option.location);
}
function addMarker(position: Position) {
if (!position.lng) {
return;
}
try {
removeMarker();
mapState.marker = new SelfMap.Marker({
icon: positionIcon,
position: [position.lng, position.lat],
offset: [-18, -32],
maxZoom: 14,
});
mapState && (mapState.marker as any).setMap(BasicMap);
} catch {}
}
function panTo(position: Position) {
if (!position.lng) {
return;
}
BasicMap && (BasicMap as any).panTo([position.lng, position.lat]);
}
function setMapZoom(zoom: number) {
if (zoom) BasicMap && (BasicMap as any).setZoom(zoom);
}
function removeMarker() {
if (mapState.marker) {
(mapState.marker as any).setMap(null);
mapState.marker = null;
}
}
/**
* @Description:经纬度转地址文本信息
* @date 2023/8/30
*/
function lngLatToAddress({ lng, lat }) {
if (!lng) return '';
return new Promise((resolve, reject) => {
let str = [lng, lat];
geocoder.value?.getAddress(str, function (status, result) {
if (status === 'complete' && result?.regeocode) {
let address = result?.regeocode?.formattedAddress;
return resolve(address);
} else {
return reject(new Error('Address not found'));
}
});
});
}
// 地图点击事件添加marker 更新地址
async function mapClick(e: any) {
let { lng, lat } = e['lnglat'];
position.lng = lng;
position.lat = lat;
try {
addMarker(e['lnglat']);
let res = await lngLatToAddress(e['lnglat']);
await nextTick(() => {
intValue.value = res as string;
adr.value = res as string;
positionRef.value = {
lng,
lat,
handleItem: {
pname: res as string,
},
};
isCLickMap.value = true;
});
} catch {}
}
function bindEvent() {
BasicMap && (BasicMap as any).on('click', mapClick);
}
// 搜索插件
function bindSearch(AMap) {
AMap.plugin(['AMap.PlaceSearch'], function () {
//构造地点查询类
placeSearch.value = new AMap.PlaceSearch({
pageSize: 20, // 单页显示结果条数
pageIndex: 1, // 页码
map: BasicMap, // 展现结果的地图实例
// citylimit: false, //是否强制限制在设置的城市内搜索
panel: 'panel', // 结果列表将在此容器中进行展示。
autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
});
// 为marker注册点击事件
placeSearch.value.on('markerClick', function (e: any) {
mapClick(e.event);
});
});
}
async function handleSubmit() {
console.log(getPosition());
const { lat, lng } = getPosition();
if (!lat && !lng) return message.warning('尚未选点');
if (!isCLickMap.value && !lat && !lng) return message.warning('尚未选点');
emit('getPosition', getPosition());
clearData();
closeModal();
}
function clearData() {
intValue.value = '';
}
function getPosition() {
return {
...positionRef.value,
handleItem: {
pname: adr.value,
},
};
}
defineExpose({
getPosition,
});
</script>
<style scoped lang="less">
:deep(.amap-icon) {
img {
width: 34px !important;
height: 34px !important;
}
}
.map-search-box {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.15);
}
.panel-list {
position: absolute;
background-color: white;
max-height: 90%;
overflow-y: auto;
top: 70px;
left: 13px;
width: 280px;
z-index: 9;
.panel-item {
padding: 10px 5px;
color: #999;
line-height: 20px;
}
}
</style>
@@ -0,0 +1,138 @@
<template>
<BasicDrawer
:showFooter="showFooter"
v-bind="$attrs"
@register="registerDrawer"
destroyOnClose
:title="title"
:width="800"
@ok="handleSubmit"
:maskClosable="false"
>
<div style="height: 65vh">
<BasicForm @register="registerForm">
<template #address="{ model }">
<a-input style="width: 82%" v-model:value="model['address']" :disabled="true" />
<a-button style="margin-left: 10px" @click="viewMap" :disabled="!showFooter">查看地图</a-button>
</template>
</BasicForm>
</div>
<Map @register="registerMap" :state="state" ref="map" @get-position="getPosition" />
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { useModal } from '/@/components/Modal';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../conResource.data';
import { saveOrUpdate } from '../conResource.api';
import Map from '/@/views/consult/resource/components/Map.vue';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const state = ref();
const showFooter = ref(false);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//设置标题
const title = ref<string>('');
//表单赋值
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
showFooter.value = data.showFooter;
title.value = data.title;
//重置表单
await resetFields();
setDrawerProps({
confirmLoading: false,
showCancelBtn: !!data?.showFooter,
showOkBtn: !!data?.showFooter,
});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
let customAddress = `${data.record?.longitude}${data.record?.latitude}`;
//表单赋值
let { conDepartmentList, conDoctorList, conSicksList } = data.record;
await setFieldsValue({
...data.record,
customAddress,
conDepartmentList: conDepartmentList && conDepartmentList.split(','),
conDoctorList: conDoctorList && conDoctorList.split(','),
conSicksList: conSicksList && conSicksList.split(','),
});
state.value = data.record;
} else {
state.value = {};
}
await clearValidate();
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
const [registerMap, { openModal }] = useModal();
function viewMap() {
openModal(true, {
record: state.value,
});
}
async function getPosition(val) {
let { pname, cityname, adname, address, name } = val.handleItem || '';
let nameList = [pname, cityname, adname, address, name];
let str = '';
nameList.map((item) => {
if (item !== undefined) {
str += item;
}
});
await setFieldsValue({
latitude: val.lat,
longitude: val.lng,
customAddress: `${val.lng}${val.lat}`,
address: str,
});
state.value = {
...state.value,
latitude: val.lat,
longitude: val.lng,
};
}
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
const params = {
...state.value,
...values,
// type: values.type,
level: values.level,
conDepartmentList: values.conDepartmentList,
conSicksList: values.conSicksList,
conDoctorList: values.conDoctorList,
};
setDrawerProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(params, isUpdate.value);
//关闭弹窗
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,117 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/conResource/list',
save = '/health-consultation/conResource/add',
edit = '/health-consultation/conResource/edit',
deleteOne = '/health-consultation/conResource/delete',
deleteBatch = '/health-consultation/conResource/deleteBatch',
importExcel = '/health-consultation/conResource/importExcel',
exportXls = '/health-consultation/conResource/exportXls',
departmentList = '/health-consultation/conSicks/listByDept',
doctor = '/health-consultation/conDoctor/getFreeDoctorByDept',
detail = '/health-consultation/conResource/queryById',
selectDepartmentList = '/health-consultation/conResource/selectDepartmentList',
topping = '/health-consultation/conResource/updateTop',
selectSickListByDepartmentId = '/health-consultation/conSicks/selectSickListByDepartmentId',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
//根据科室获取所有闲置专家
export const getDoctor = (params) => defHttp.post({ url: Api.doctor, params });
// 根据科室list查询疾病
export const getDepartmentList = (params) => defHttp.post({ url: Api.departmentList, params });
//获取详情数据
export const getDetail = (params) => defHttp.get({ url: Api.detail, params });
/**
* @Description:
* @date 2023/6/19
* @param:
*/
export const selectDepartmentList = (params) => defHttp.post({ url: Api.selectDepartmentList, params });
export const selectSickListByDepartmentId = (params) => defHttp.get({ url: Api.selectSickListByDepartmentId, params });
// 是否置顶
export const getTopping = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '提示',
content: `是否将该条数据${params.type === '0' ? '取消置顶' : '置顶'}`,
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.get({ url: Api.topping, params }).then(() => {
handleSuccess();
});
},
});
};
@@ -0,0 +1,363 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { getDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { h } from 'vue';
import { EyeOutlined } from '@ant-design/icons-vue';
import { Image } from 'ant-design-vue';
import { render } from '/@/utils/common/renderUtils';
import { getDoctor } from '/@/views/consult/resource/conResource.api';
import { message } from 'ant-design-vue';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '医院名称',
align: 'center',
dataIndex: 'resourceName',
},
{
title: '主图',
align: 'center',
dataIndex: 'img',
width: 100,
customRender: ({ text }) => {
return h(Image, {
placeholder: true,
src: getFileAccessHttpUrl(text),
height: 50,
width: 50,
fallback: getDefaultImage(),
previewMask: () => {
return h(EyeOutlined, {
style: {
color: 'white',
},
});
},
});
},
},
{
title: '详细地址',
align: 'center',
dataIndex: 'address',
},
// {
// title: '医院类型',
// align: 'center',
// dataIndex: 'type',
// customRender: ({ text }) => {
// return render.renderDict(text, 'h_type');
// },
// },
{
title: '医院级别',
align: 'center',
dataIndex: 'level',
customRender: ({ text }) => {
return render.renderDict(text, 'hospital_level');
},
},
{
title: '是否置顶',
align: 'center',
dataIndex: 'tfTop',
customRender: ({ text }) => (text === 1 ? '是' : '否'),
},
{
title: '排序',
align: 'center',
dataIndex: 'sort',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
customRender: ({ text }) => {
if (text == '1') {
return '正常';
} else {
return '冻结';
}
},
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '医院名称',
field: 'resourceName',
component: 'Input',
},
{
label: '地址',
field: 'province',
component: 'Input',
},
{
label: '医院级别',
field: 'level',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'hospital_level',
},
},
// {
// label: '医院类型',
// field: 'type',
// component: 'JDictSelectTag',
// componentProps: {
// dictCode: 'h_type',
// },
// },
];
const validate = async (_rule, value) => {
if (!value) {
return Promise.reject('请选择状态');
} else {
return Promise.resolve();
}
};
const departmentValidator = async (_rule, value) => {
if (!value || !value.length) {
return Promise.reject('请选择院内科室');
}
return Promise.resolve();
};
//表单数据
export const formSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '医院名称',
field: 'resourceName',
component: 'Input',
required: true,
rules: [{ required: true, message: '请输入医院名称!' }],
},
// {
// label: '医院类型',
// field: 'type',
// component: 'JDictSelectTag',
// componentProps: {
// dictCode: 'h_type',
// },
// rules: [{ required: true }],
// },
{
label: '医院级别',
field: 'level',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'hospital_level',
},
rules: [{ required: true }],
},
{
label: '医院主图',
field: 'img',
component: 'JImageUpload',
componentProps: {
fileMax: 1,
},
rules: [{ required: false }],
},
{
label: '状态',
field: 'status',
component: 'JDictSelectTag',
componentProps: {
options: [
{
value: 1,
label: '正常',
},
{
value: 2,
label: '冻结',
},
],
type: 'radio',
},
rules: [{ required: true, validator: validate, trigger: 'blur' }],
},
{
label: '医院简介',
field: 'synopsis',
component: 'InputTextArea',
componentProps: {
showCount: true,
rows: 4,
},
rules: [{ required: true }],
},
{
label: '医院介绍',
field: 'resourceDetail',
component: 'JEditor',
rules: [{ required: true }],
},
{
label: '地址',
field: 'customAddress',
component: 'Input',
slot: 'customAddress',
rules: [{ required: true }],
ifShow: false,
},
{
label: '详细地址',
field: 'address',
component: 'Input',
slot: 'address',
rules: [{ required: true }],
},
{
label: '',
field: 'longitude',
component: 'Input',
show: false,
},
{
label: '',
field: 'latitude',
component: 'Input',
show: false,
},
{
label: '院内科室',
field: 'conDepartmentList',
component: 'JTreeDepartment',
componentProps: ({ formModel }) => {
return {
valueField: 'departmentId',
field: 'departmentId',
allDep: true,
multiple: true,
immediate: true,
treeCheckable: true,
treeCheckStrictly: true,
showCheckedStrategy: 'SHOW_ALL',
checkFather: true,
onChange: (val) => {
if (!val) {
formModel.conSicksList = [];
formModel.conDoctorList = [];
}
},
};
},
rules: [{ required: true, trigger: 'blur', validator: departmentValidator }],
},
{
label: '主治疾病',
field: 'conSicksList',
component: 'ApiSelect',
show: false,
// componentProps: ({ formModel }) => {
// let departmentId = '1321354';
// if (formModel?.conDepartmentList) {
// if (Array.isArray(formModel?.conDepartmentList)) {
// departmentId = formModel?.conDepartmentList?.join(',');
// } else {
// departmentId = formModel?.conDepartmentList;
// }
// }
// return {
// api: getDepartmentList,
// params: {
// departmentId: departmentId,
// isDoctor: false, //通用接口标识 是否是医生的情况下查询疾病
// },
// labelField: 'sicksName',
// valueField: 'id',
// mode: 'multiple',
// immediate: true,
// onFocus: () => {
// if (!formModel?.conDepartmentList || formModel?.conDepartmentList.length < 1) {
// return message.warn('请先选择院内科室');
// }
// },
// disabledInitValue: true,
// getPopupContainer: () => document.body,
// };
// },
},
{
label: '院内专家',
field: 'conDoctorList',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
let departmentId = '1321354';
if (formModel?.conDepartmentList) {
if (Array.isArray(formModel?.conDepartmentList)) {
departmentId = formModel?.conDepartmentList?.join(',');
} else {
departmentId = formModel?.conDepartmentList;
}
}
return {
api: getDoctor,
params: {
departmentId: departmentId,
isAdd: !formModel.id,
},
labelField: 'doctorName',
valueField: 'id',
mode: 'multiple',
immediate: true,
onFocus: () => {
if (!formModel?.conDepartmentList || formModel?.conDepartmentList.length < 1) {
return message.warn('请先选择院内科室');
}
},
// disabledInitValue: true,
getPopupContainer: () => document.body,
disabled: true,
};
},
ifShow: ({ values }) => values?.id,
},
{
label: '是否置顶',
field: 'tfTop',
component: 'RadioGroup',
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param _formData
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
@@ -0,0 +1,205 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="auth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button v-auth="auth.deleteBatch" type="primary" preIcon="ant-design:delete-outlined" @click="batchHandleDelete">
批量删除</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> -->
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
<template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)"
>下载</a-button
>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConResourceModal @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conResource" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useDrawer } from '/@/components/Drawer';
import { useListPage } from '/@/hooks/system/useListPage';
import ConResourceModal from './components/conResourceModal.vue';
import { columns, searchFormSchema } from './conResource.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, getDetail, getTopping } from './conResource.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { message } from 'ant-design-vue';
//注册model
const [registerDrawer, { openDrawer }] = useDrawer();
const auth = {
deleteBatch: 'consultation:con_resource:deleteBatch',
add: 'consultation:con_resource:add',
};
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_resource',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 220,
fixed: 'right',
},
},
exportConfig: {
name: 'con_resource',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openDrawer(true, {
isUpdate: false,
showFooter: true,
title: '新增',
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
let data = await getDetail({ id: record.id });
openDrawer(true, {
record: { ...data, ...record },
isUpdate: true,
showFooter: true,
title: '编辑',
});
}
/**
* 详情
*/
async function handleDetail(record: Recordable) {
let data = await getDetail({ id: record.id });
openDrawer(true, {
record: { ...data, ...record },
isUpdate: true,
showFooter: false,
title: '详情',
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/*
* 置顶 1置顶 0 取消置顶
* */
async function handleTopping(record) {
await getTopping({ type: record.tfTop === 0 ? '1' : '0', id: record.id }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: `${record.tfTop === 0 ? '置顶' : '取消置顶'}`,
onClick: handleTopping.bind(null, record),
},
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'consultation:con_resource:edit',
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'consultation:con_resource:delete',
},
];
}
/**
* 下拉操作栏
*/
// function getDropDownAction(record){
// return [
// {
// label: '详情',
// onClick: handleDetail.bind(null, record),
// }, {
// label: '删除',
// popConfirm: {
// title: '是否确认删除',
// confirm: handleDelete.bind(null, record),
// }
// }
// ]
// }
</script>
<style lang="less" scoped>
:deep(.ant-popover-buttons) {
display: flex !important;
}
</style>
@@ -0,0 +1,49 @@
<template>
<BasicModal @register="registerModal" :title="isUpdate ? '修改休息时间' : '新增休息时间'" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import BasicForm from '/@/components/Form/src/BasicForm.vue';
import { useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import { useForm } from '/@/components/Form';
import { schema } from '/@/views/consult/restTime/restTime.data';
import { addApi, editApi } from '/@/views/consult/restTime/restTime.api';
const emit = defineEmits(['success']);
const isUpdate = ref(false);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
isUpdate.value = data?.isUpdate;
await resetFields();
if (data?.isUpdate) {
await setFieldsValue({ ...data?.record });
}
await clearValidate();
});
const [registerForm, { setFieldsValue, clearValidate, resetFields, validate }] = useForm({
labelWidth: 120,
schemas: schema,
showActionButtonGroup: false,
});
async function handleSubmit() {
try {
const value = await validate();
setModalProps({ confirmLoading: true });
if (isUpdate.value) {
await editApi(value);
} else {
await addApi(value);
}
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,45 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conHelperRest/list',
add = '/health-consultation/consultation/conHelperRest/add',
edit = '/health-consultation/consultation/conHelperRest/edit',
delete = '/health-consultation/consultation/conHelperRest/delete',
deleteBatch = '/health-consultation/consultation/conHelperRest/deleteBatch',
}
export const listApi = (params = {}) => defHttp.get({ url: Api.list, params });
export const addApi = (params = {}) => defHttp.post({ url: Api.add, params });
export const editApi = (params = {}) => defHttp.put({ url: Api.edit, params });
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await defHttp.delete({ url: Api.delete, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
export const deleteB = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
@@ -0,0 +1,65 @@
import { FormSchema } from '/@/components/Form';
import { BasicColumn } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: '开始时间',
dataIndex: 'startTime',
width: 200,
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 200,
},
{
title: '自动回复内容',
dataIndex: 'replyContent',
},
];
export const schema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '休息开始时间',
field: 'startTime',
component: 'TimePicker',
componentProps: () => {
return {
style: {
width: '100%',
},
format: 'HH:mm',
valueFormat: 'HH:mm',
getPopupContainer: () => document.body,
};
},
required: true,
},
{
label: '休息结束时间',
field: 'endTime',
component: 'TimePicker',
componentProps: () => {
return {
style: {
width: '100%',
},
format: 'HH:mm',
valueFormat: 'HH:mm',
getPopupContainer: () => document.body,
};
},
required: true,
},
{
label: '自动回复内容',
field: 'replyContent',
component: 'InputTextArea',
required: true,
},
];
+89
View File
@@ -0,0 +1,89 @@
<template>
<div class="outer-d">
<BasicTable @register="registerTable" :row-selection="rowSelection">
<template #tableTitle>
<a-button type="primary" v-auth="'consultation:con_helper_rest:add'" @click="handleAdd" preIcon="ant-design:plus-outlined">
新增
</a-button>
<a-button type="primary" v-auth="" @click="deleteBatch" preIcon="ant-design:delete-outlined"> 批量删除 </a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<rest-time-modal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { deleteB, deleteOne, listApi } from '/@/views/consult/restTime/restTime.api';
import { columns } from '/@/views/consult/restTime/restTime.data';
import RestTimeModal from '/@/views/consult/restTime/component/restTimeModal.vue';
import { useModal } from '/@/components/Modal';
import { TableAction } from '/@/components/Table';
const [registerModal, { openModal }] = useModal();
const { tableContext } = useListPage({
tableProps: {
title: '小助手休息时间',
api: listApi,
columns,
tableSetting: {
redo: true,
},
canResize: false,
useSearchForm: false,
actionColumn: {
width: 220,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { selectedRowKeys, rowSelection }] = tableContext;
function handleAdd() {
openModal(true, {
isUpdate: false,
});
}
function deleteBatch() {
deleteB({ ids: selectedRowKeys.value }, handleSuccess);
}
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
function getTableAction(record: Recordable) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'consultation:con_helper_rest:edit',
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: 'consultation:con_helper_rest:delete',
},
];
}
function handleEdit(record: Recordable) {
openModal(true, {
isUpdate: true,
record,
});
}
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
</script>
<style scoped lang="less">
.outer-d {
padding: 10px;
}
</style>
@@ -0,0 +1,304 @@
<template>
<BasicDrawer @register="registerDrawer" width="60%" @ok="handleSubmit">
<BasicForm @register="registerForm" />
<div class="line-d">
<div style="font-size: 16px; font-weight: bold">结算费用设置</div>
<a-button type="primary" @click="addCostList">添加</a-button>
</div>
<div class="list-d">
<div class="list-line-d" style="border: none; font-weight: bold; margin: 0">
<div class="item-d">费用类型</div>
<div class="item-d">结算对象</div>
<div class="item-d">图文咨询费用</div>
<div class="item-d">音视频咨询费用</div>
<div class="item-d-s"></div>
</div>
<template v-for="(item, index) in costList">
<div class="list-line-d" :style="{ marginTop: index === 0 ? 0 : '10px' }">
<div class="item-d">
<a-select
:disabled="index < 2"
v-model:value="item['costType']"
:options="[
{
label: '职称',
value: 1,
},
{
label: '单独设置',
value: 2,
},
]"
/>
</div>
<div class="item-d">
<JDictSelectTag
placeholder="请选择结算对象"
:disabled="index < 2"
:show-choose-option="false"
dict-code="z_doct_lev"
v-model:value="item['titleId']"
/>
</div>
<div class="item-d"> <a-input placeholder="请输入" v-model:value="item['textFee']" /> </div>
<div class="item-d"> <a-input placeholder="请输入" v-model:value="item['videoFee']" /> </div>
<div class="item-d-s">
<delete-outlined v-if="index > 1" style="cursor: pointer" @click="() => costList.splice(index, 1)" />
</div>
</div>
</template>
</div>
<div class="line-d">
<div style="font-size: 16px; font-weight: bold">结算周期设置</div>
</div>
<div class="range-date-d">
<a-range-picker
v-model:value="settingDate"
picker="month"
fotmat="YYYY-MM"
valueFormat="YYYY-MM"
style="width: 50%"
@change="changeRangeDate"
/>
</div>
<div class="list-d">
<div class="list-line-d-b" style="border: none; font-weight: bold; margin: 0">
<div class="item-d">月份</div>
<div class="item-d">范围类型</div>
<div class="item-d">自定义范围</div>
</div>
<template v-for="(item, index) in settingList">
<div class="list-line-d-b" :style="{ marginTop: index === 0 ? 0 : '10px' }">
<div class="item-d"> {{ item?.yearValue }}-{{ item?.monthValue }} </div>
<div class="item-d">
<JDictSelectTag
placeholder="请选择结范围"
:show-choose-option="false"
:string-to-number="true"
:options="[
{
label: '自然月',
value: 1,
},
{
label: '自定义起止时间',
value: 2,
},
]"
v-model:value="item['periodType']"
/>
</div>
<div class="item-d">
<a-range-picker
v-if="item['periodType'] !== 1"
v-model:value="item['settingDate']"
fotmat="YYYY-MM-DD"
valueFormat="YYYY-MM-DD"
style="width: 70%"
/>
</div>
</div>
</template>
</div>
</BasicDrawer>
</template>
<script setup lang="ts">
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { useDrawerInner } from '/@/components/Drawer';
import { schemas } from '/@/views/consult/settlement/settlement.data';
import { DeleteOutlined } from '@ant-design/icons-vue';
import { ref } from 'vue';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import dayjs from 'dayjs';
import { addApi, editApi } from '/@/views/consult/settlement/settlement.api';
const isUpdate = ref(false);
const costList = ref<any[]>([]);
const settingDate = ref(null);
const settingList = ref<any[]>([]);
const emit = defineEmits(['success']);
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
settingDate.value = null;
await resetFields();
setDrawerProps({
title: data.isUpdate ? '编辑结算配置' : '新增结算配置',
showFooter: true,
});
costList.value = [
{
costType: 1,
titleId: '2',
textFee: '',
videoFee: '',
},
{
costType: 1,
titleId: '4',
textFee: '',
videoFee: '',
},
];
settingList.value = [];
isUpdate.value = data.isUpdate;
console.log(data.record);
if (data.isUpdate) {
await setFieldsValue({
...data.record,
});
costList.value = JSON.parse(JSON.stringify(data?.record?.configCosts)) || [];
settingList.value = data?.record?.configPeriods
? data?.record?.configPeriods.map((item) => {
item['settingDate'] = [item.startDate, item.endDate];
return item;
})
: [];
if (settingList.value.length > 0) {
console.log(settingList.value);
settingDate.value = [
dayjs(settingList.value[0].startDate).format('YYYY-MM'),
dayjs(settingList.value[settingList.value.length - 1].startDate).format('YYYY-MM'),
];
}
}
});
function addCostList() {
costList.value.push({
costType: 1,
titleId: undefined,
textFee: '',
videoFee: '',
});
}
function changeRangeDate(v) {
settingList.value = [];
let c = dayjs(v[1]).diff(dayjs(v[0]), 'month');
let d = null;
for (let i = 0; i <= c; i++) {
d = dayjs(v[0]).add(i, 'month');
settingList.value.push({
periodType: 1,
settleId: '',
yearValue: d.format('YYYY') * 1,
monthValue: d.format('MM') * 1,
settingDate: [d.startOf('month'), d.endOf('month')],
startDate: '',
endDate: '',
});
}
console.log(settingList.value);
}
async function handleSubmit() {
try {
await setDrawerProps({
confirmLoading: true,
});
let values = await validate();
let params = {
...values,
configCosts: costList.value,
configPeriods: settingList.value.map((item) => {
item.startDate = dayjs(item.settingDate[0]).format('YYYY-MM-DD');
item.endDate = dayjs(item.settingDate[1]).format('YYYY-MM-DD');
return item;
}),
};
if (isUpdate.value) {
await editApi(params);
} else {
await addApi(params);
}
closeDrawer();
emit('success');
} catch (e) {
console.log(e);
} finally {
await setDrawerProps({
confirmLoading: false,
});
}
}
const [registerForm, { setFieldsValue, validate, resetFields }] = useForm({
schemas,
showActionButtonGroup: false,
});
</script>
<style scoped lang="less">
.line-d {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eaeaea;
padding-bottom: 10px;
}
.list-line-d,
.list-line-d-b {
display: flex;
border: 1px solid #eaeaea;
padding: 5px;
margin-top: 10px;
> div {
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0;
}
.item-d {
width: calc((100% - 50px) / 4);
}
.item-d-s {
width: 50px;
}
}
.list-line-d-b {
> :nth-child(1) {
width: 100px;
}
> :nth-child(2) {
width: 180px;
}
> :nth-child(3) {
width: calc(100% - 280px);
}
}
.list-d {
:deep(.ant-select) {
width: 90%;
}
:deep(.ant-input) {
width: 80%;
margin-right: 5%;
}
}
.range-date-d {
margin-top: 5px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
@@ -0,0 +1,97 @@
<template>
<div>
<a-button
style="margin-bottom: 10px"
type="primary"
@click="
() => {
emit('toOtherPages', props.commeStatus);
console.log(props.commeStatus);
}
"
>
<template #icon><left-outlined /></template>
返回
</a-button>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary">下载</a-button>
</template>
</BasicTable>
</div>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { consultColumn } from '/@/views/consult/settlement/settlement.data';
import { watch } from 'vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { itemApi } from '/@/views/consult/settlement/settlement.api';
const props = defineProps({
id: {
type: String,
default: () => '',
},
detailId: {
type: String,
default: () => '',
},
monthId: {
type: String,
default: () => '',
},
commeStatus: {
type: String,
default: () => '',
},
});
watch(
() => props.id,
(v) => {
reload({ page: 1 });
}
);
watch(
() => props.monthId,
(v) => {
if (v) {
reload({ page: 1 });
}
}
);
const emit = defineEmits(['toOtherPages']);
const { tableContext } = useListPage({
tableProps: {
api: itemApi,
columns: consultColumn,
tableSetting: {
redo: true,
},
beforeFetch: (params) => {
params['settleId'] = props.detailId;
params['resultId'] = props.monthId;
if (props.id) params['periodId'] = props.id;
return params;
},
showActionColumn: false,
canResize: false,
useSearchForm: false,
immediate: false,
actionColumn: {
width: 220,
fixed: 'right',
},
},
});
function toConsult() {
emit('toOtherPages', 'consult');
}
const [registerTable, { reload }, {}] = tableContext;
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,82 @@
<template>
<div>
<a-button style="margin-bottom: 10px" type="primary" @click="() => emit('toOtherPages', 'settlement')">
<template #icon><left-outlined /></template>
返回
</a-button>
<BasicTable @register="registerTable">
<!-- <template #tableTitle>-->
<!-- <a-button type="primary">下载</a-button>-->
<!-- </template>-->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'a'">
<a-button class="list-button" type="link" @click="toMonth(record)">月详情</a-button>
<a-button class="list-button" type="link" @click="toConsult(record)">咨询详情</a-button>
</template>
</template>
</BasicTable>
</div>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { detailColumn } from '/@/views/consult/settlement/settlement.data';
import { watch } from 'vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { resultApi } from '/@/views/consult/settlement/settlement.api';
const props = defineProps({
id: {
type: String,
default: () => '',
},
});
watch(
() => props.id,
(v) => {
if (v) {
reload({ page: 1 });
}
}
);
const emit = defineEmits(['toOtherPages', 'toConsult']);
const { tableContext } = useListPage({
tableProps: {
api: resultApi,
columns: detailColumn,
beforeFetch: (params) => {
params['settleId'] = props.id;
return params;
},
tableSetting: {
redo: true,
},
showActionColumn: false,
canResize: false,
useSearchForm: false,
immediate: false,
actionColumn: {
width: 220,
fixed: 'right',
},
},
});
function toMonth(record: Recordable) {
emit('toOtherPages', 'month', record?.id);
}
function toConsult(record: Recordable) {
emit('toOtherPages', 'consult', record?.id);
emit('toConsult', 'detail', record?.id);
}
const [registerTable, { reload }, {}] = tableContext;
</script>
<style scoped lang="less">
.list-button {
padding: 0 3px;
}
</style>
@@ -0,0 +1,79 @@
<template>
<div>
<a-button style="margin-bottom: 10px" type="primary" @click="() => emit('toOtherPages', 'detail')">
<template #icon><left-outlined /></template>
返回
</a-button>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary">下载</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'a'">
<a-button class="list-button" type="link" @click="toConsult(record)">咨询详情</a-button>
</template>
</template>
</BasicTable>
</div>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { monthColumn } from '/@/views/consult/settlement/settlement.data';
import { watch } from 'vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import { itemApi, periodApi } from '/@/views/consult/settlement/settlement.api';
const props = defineProps({
id: {
type: String,
default: () => '',
},
detailId: {
type: String,
default: () => '',
},
});
watch(
() => props.id,
(v) => {
if (v) {
reload({ page: 1 });
}
}
);
const emit = defineEmits(['toOtherPages', 'toConsult']);
const { tableContext } = useListPage({
tableProps: {
api: periodApi,
columns: monthColumn,
tableSetting: {
redo: true,
},
beforeFetch: (params) => {
params['settleId'] = props.detailId;
params['resultId'] = props.id;
return params;
},
showActionColumn: false,
canResize: false,
useSearchForm: false,
immediate: false,
actionColumn: {
width: 220,
fixed: 'right',
},
},
});
function toConsult(record: Recordable) {
emit('toConsult', 'month');
emit('toOtherPages', 'consult', record?.id);
}
const [registerTable, { reload }, {}] = tableContext;
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,21 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-consultation/settle/config/list',
detail = '/health-consultation/settle/config',
result = '/health-consultation/settle/result',
item = '/health-consultation/settle/result/item',
period = '/health-consultation/settle/result/period',
generate = '/health-consultation/settle/generate/',
add = '/health-consultation/settle/config/add',
edit = '/health-consultation/settle/config/edit',
}
export const listApi = (params = {}) => defHttp.get({ url: Api.list, params });
export const resultApi = (params = {}) => defHttp.get({ url: Api.result, params });
export const itemApi = (params = {}) => defHttp.get({ url: Api.item, params });
export const periodApi = (params = {}) => defHttp.get({ url: Api.period, params });
export const generateApi = (id) => defHttp.post({ url: Api.generate + id }, { isReturnNativeResponse: true });
export const addApi = (params = {}) => defHttp.post({ url: Api.add, params });
export const editApi = (params = {}) => defHttp.post({ url: Api.edit, params });
export const detailApi = (params = {}) => defHttp.get({ url: Api.detail + `/${params.id}` });
@@ -0,0 +1,267 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { getName } from '/@/views/interveneNew/compoents/utils';
import dayjs from 'dayjs';
const status = [
{
label: '待开始',
value: '0',
},
{
label: '结算中',
value: '1',
},
{
label: '已完成',
value: '2',
},
{
label: '结算异常',
value: '3',
},
{
label: '队列中',
value: '5',
},
];
export const schemas: FormSchema[] = [
{
label: '结算名称',
field: 'settleName',
component: 'Input',
required: true,
},
{
label: '是否产生通讯费用',
field: 'hasComFee',
component: 'RadioGroup',
defaultValue: true,
componentProps: () => {
return {
options: [
{
label: '是',
value: true,
},
{
label: '否',
value: false,
},
],
};
},
},
{
label: '通讯费',
field: 'comFeePerMonth',
component: 'Input',
componentProps: () => {
return {
suffix: '元',
};
},
ifShow: ({ values }) => values.hasComFee,
required: true,
},
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
export const columns: BasicColumn[] = [
{
title: '结算名称',
dataIndex: 'settleName',
},
{
title: '结算起始月份',
dataIndex: '2',
customRender: ({ record }) => {
return (
record?.configPeriods &&
record?.configPeriods.length > 0 &&
record?.configPeriods[0]?.yearValue + '-' + record?.configPeriods[0]?.monthValue
);
},
},
{
title: '结算结束月份',
dataIndex: '3',
customRender: ({ record }) => {
return (
record?.configPeriods &&
record?.configPeriods.length > 0 &&
record?.configPeriods[record?.configPeriods.length - 1]?.yearValue +
'-' +
record?.configPeriods[record?.configPeriods.length - 1]?.monthValue
);
},
},
{
title: '结算状态',
dataIndex: 'settleStatus',
customRender: ({ text }) => {
return getName(text, status);
},
},
{
title: '创建时间',
dataIndex: 'createTime',
},
{
title: '结算开始时间',
dataIndex: 'lastGenerateTime',
},
{
title: '结算完成时间',
dataIndex: 'lastFinishedTime',
},
{
title: '错误信息',
dataIndex: 'errorMsg',
defaultHidden: true,
},
{
title: '结算操作',
dataIndex: 'a',
fixed: 'right',
width: 220,
},
{
title: '配置操作',
dataIndex: 'b',
fixed: 'right',
width: 150,
},
];
export const detailColumn: BasicColumn[] = [
{
title: '专家编号',
dataIndex: 'doctorNo',
},
{
title: '专家名称',
dataIndex: 'doctorName',
},
{
title: '专家职称',
dataIndex: 'doctorTitle_dictText',
},
{
title: '医院',
dataIndex: 'hospitalName',
},
{
title: '单价(元)',
dataIndex: 'doctorPrice',
},
{
title: '回复次数',
dataIndex: 'consultCount',
},
{
title: '通讯费(元)',
dataIndex: 'comFee',
},
{
title: '咨询费(元)',
dataIndex: 'consultFee',
},
{
title: '结算费(元)',
dataIndex: 'settleInvoice',
},
{
title: '操作',
dataIndex: 'a',
width: 150,
},
];
export const monthColumn: BasicColumn[] = [
...detailColumn.slice(0, 5),
{
title: '结算年份',
dataIndex: 'yearValue',
},
{
title: '结算月份',
dataIndex: 'monthValue',
},
...detailColumn.slice(5),
];
export const consultColumn: BasicColumn[] = [
{
title: '专家编号',
dataIndex: 'doctorNo',
},
{
title: '专家名称',
dataIndex: 'doctorName',
},
{
title: '专家职称',
dataIndex: 'doctorTitle_dictText',
},
{
title: '医院',
dataIndex: 'hospitalName',
},
{
title: '科室',
dataIndex: 'departmentName',
},
{
title: '单位',
dataIndex: 'secondDepartName',
},
{
title: '部门',
dataIndex: 'userDepartName',
},
{
title: '员工名称',
dataIndex: 'userName',
},
{
title: '身份证号',
dataIndex: 'userIdCard',
},
{
title: '生日',
dataIndex: 'userBirthday',
customRender: ({ text }) => {
return text && dayjs(text).format('YYYY-MM-DD');
},
},
{
title: '年龄',
dataIndex: 'userAge',
width: 80,
},
{
title: '性别',
dataIndex: 'userSex_dictText',
width: 80,
},
{
title: '用户咨询时间',
dataIndex: 'userConsultTime',
},
{
title: '专家首次回复时间',
dataIndex: 'doctorFirstReplyTime',
},
{
title: '订单状态',
dataIndex: 'sessionStatus_dictText',
},
];
export const pageStatus = [{}];
+159
View File
@@ -0,0 +1,159 @@
<template>
<div style="padding: 10px">
<BasicTable v-show="pageStatus === 'settlement'" @register="registerTable">
<template #tableTitle>
<a-button type="primary" @click="addD">新增</a-button>
<a-button type="primary" @click="() => reload({ page: 1 })">刷新</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'a'">
<a-button class="list-button" type="link" @click="generate(record)">生成结算</a-button>
<a-button class="list-button" type="link" @click="toPage('detail', record)">查看报表</a-button>
<a-button class="list-button" type="link" @click="downLoadReport(record)">报表下载</a-button>
</template>
<template v-if="column.dataIndex === 'b'">
<a-button class="list-button" type="link" @click="updateSetting(record)">编辑配置</a-button>
<a-button class="list-button" type="link">删除</a-button>
</template>
</template>
</BasicTable>
<settlement-detail
v-show="pageStatus === 'detail'"
@to-other-pages="detailPage"
:id="detailId"
@to-consult="
(path, id) => {
toConsult = path;
consultId = '';
monthId = id;
}
"
/>
<settlement-moth
v-show="pageStatus === 'month'"
@to-other-pages="MonthPage"
:id="monthId"
:detailId="detailId"
@to-consult="(path) => (toConsult = path)"
/>
<settlement-consult
v-show="pageStatus === 'consult'"
:comme-status="toConsult"
:monthId="monthId"
:detailId="detailId"
:id="consultId"
@to-other-pages="(path) => (pageStatus = path)"
/>
<add-drawer @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from '/@/views/consult/settlement/settlement.data';
import { ref } from 'vue';
import SettlementDetail from '/@/views/consult/settlement/components/settlementDetail.vue';
import SettlementMoth from '/@/views/consult/settlement/components/settlementMoth.vue';
import SettlementConsult from '/@/views/consult/settlement/components/settlementConsult.vue';
import AddDrawer from '/@/views/consult/settlement/components/addDrawer.vue';
import { useDrawer } from '/@/components/Drawer';
import { generateApi, listApi } from '/@/views/consult/settlement/settlement.api';
import { message } from 'ant-design-vue';
import { getFileAccessHttpUrlDown } from '/@/utils/common/compUtils';
const pageStatus = ref('settlement'); // 当前页面是哪个 settlement为list页 detail为详情页 month为月明细页 consult为咨询明细页
const detailId = ref('');
const monthId = ref('');
const consultId = ref('');
const toConsult = ref('');
const [registerDrawer, { openDrawer }] = useDrawer();
const { tableContext } = useListPage({
tableProps: {
api: listApi,
columns,
tableSetting: {
redo: true,
},
showActionColumn: false,
canResize: false,
useSearchForm: false,
actionColumn: {
width: 220,
fixed: 'right',
},
},
});
function toPage(path, record) {
if (!record?.statementFile) {
return message.warn('当前未生成报表,请先生成报表');
}
pageStatus.value = path;
detailId.value = record.id;
}
function downLoadReport(record: Recordable) {
if (!record?.statementFile) {
return message.warn('当前未生成报表,请先生成报表');
}
window.open(getFileAccessHttpUrlDown(record?.statementFile));
}
function updateSetting(record: Recordable) {
openDrawer(true, {
isUpdate: true,
record,
});
}
function MonthPage(path, id) {
pageStatus.value = path;
if (path === 'consult') consultId.value = id;
}
function detailPage(path, id) {
pageStatus.value = path;
if (path === 'settlement') {
return;
} else {
consultId.value = '';
monthId.value = '';
}
if (path === 'month') {
monthId.value = id;
} else {
consultId.value = id;
}
}
function addD() {
openDrawer(true, {
isUpdate: false,
});
}
async function generate(record: Recordable) {
const { data } = await generateApi(record?.id);
if (data.code === 200) {
message.info('结算生成中,请稍后刷新');
} else {
message.warn(data.message);
}
handleSuccess();
}
function handleSuccess() {
reload();
}
const [registerTable, { reload }, {}] = tableContext;
</script>
<style scoped lang="less">
.list-button {
padding: 0 3px;
}
</style>
@@ -0,0 +1,80 @@
<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 '../conSicks.data';
import { saveOrUpdate } from '../conSicks.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = useForm({
//labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
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)) {
if (!data.record.departmentId) {
//表单赋值
await setFieldsValue({
...data.record,
});
} else {
//表单赋值
await setFieldsValue({
...data.record,
value: data.record.departmentId,
status: data.record.status + '',
});
}
await clearValidate();
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
const params = {
...values,
// departmentId: values.departmentId.split(','),
};
//提交表单
await saveOrUpdate(params, 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>
+103
View File
@@ -0,0 +1,103 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/conSicks/list',
save = '/health-consultation/conSicks/add',
edit = '/health-consultation/conSicks/edit',
deleteOne = '/health-consultation/conSicks/delete',
deleteBatch = '/health-consultation/conSicks/deleteBatch',
importExcel = '/health-consultation/conSicks/importExcel',
exportXls = '/health-consultation/conSicks/exportXls',
toTop = '/health-consultation/conSicks/updateTop',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* @Description:置顶
* @date 2023/7/7
* @param:
*/
export const toTop = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '提示',
content: `是否将该条数据${params.type === '0' ? '取消置顶' : '置顶'}`,
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.get({ url: Api.toTop, params }).then(() => {
handleSuccess();
});
},
});
};
+130
View File
@@ -0,0 +1,130 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '疾病名称',
align: 'center',
dataIndex: 'sicksName',
},
{
title: '关联科室',
align: 'center',
dataIndex: 'departmentName',
},
{
title: '是否置顶',
align: 'center',
dataIndex: 'tfTop',
customRender: ({ text }) => (text === 1 ? '是' : '否'),
},
{
title: '排序',
align: 'center',
dataIndex: 'sort',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '疾病名称',
field: 'sicksName',
component: 'Input',
},
{
label: '科室名称',
field: 'departmentId',
component: 'JTreeDepartment',
componentProps: {
// api: getOfficeList,
// resultField: 'list',
// labelField: 'name',
// valueField: 'id',
// immediate: false,
// onChange: (e) => {
// // console.log('selected:', e);
// },
placeholder: '请选择科室',
allDep: true,
getPopupContainer: () => document.body,
showSearch: true,
treeNodeFilterProp: 'label',
},
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '疾病名称',
field: 'sicksName',
component: 'Input',
rules: [{ required: true, message: '请输入疾病名称' }],
},
{
label: '关联科室',
field: 'departmentId',
component: 'JTreeDepartment',
componentProps: {
valueField: 'departmentId',
field: 'departmentId',
allDep: true,
immediate: true,
getPopupContainer: () => document.body,
showSearch: true,
filterTreeNode: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.departmentName.toLowerCase().indexOf(str) >= 0;
},
},
dynamicRules: () => {
return [
{
required: true,
validator: (value) => {
if (!value) {
return Promise.reject('请选择科室');
}
return Promise.resolve();
},
},
];
},
},
{
label: '是否置顶',
field: 'tfTop',
component: 'RadioGroup',
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
},
{
label: '排序',
field: 'sort',
component: 'InputNumber',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
/**
* 流程表单调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
+138
View File
@@ -0,0 +1,138 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="null">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="auth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增 </a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
<template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)"
>下载
</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<ConSicksModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script lang="ts" name="consultation-conSicks" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import ConSicksModal from './components/conSicksModal.vue';
import { columns, searchFormSchema } from './conSicks.data';
import { list, deleteOne, getImportUrl, getExportUrl, toTop } from './conSicks.api';
import { downloadFile } from '/@/utils/common/renderUtils';
const auth = {
add: 'consultation:con_sicks:add',
edit: 'consultation:con_sicks:edit',
deleteOne: 'consultation:con_sicks:delete',
};
//注册model
const [registerModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: 'con_sicks',
api: list,
columns,
canResize: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: true,
fieldMapToNumber: [],
fieldMapToTime: [],
},
actionColumn: {
width: 170,
fixed: 'right',
},
},
exportConfig: {
name: 'con_sicks',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload }, { selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
function handleDelete(record: Recordable) {
deleteOne({ id: record.id }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* @Description:置顶
* @date 2023/7/8
*/
async function moveTop(record: Recordable) {
toTop({ type: record.tfTop === 0 ? '1' : '0', id: record.id }, handleSuccess);
}
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: `${record.tfTop === 0 ? '置顶' : '取消置顶'}`,
onClick: moveTop.bind(null, record),
},
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: auth.edit,
},
{
label: '删除',
onClick: handleDelete.bind(null, record),
auth: auth.deleteOne,
},
];
}
</script>
@@ -0,0 +1,90 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<div class="title"><strong>咨询信息</strong></div>
<BasicForm @register="registerForm" />
<div class="title"><strong>订单信息</strong></div>
<BasicForm @register="registerFormOrder" />
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, defineExpose, ref, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema, formSchemaOrder } from '../doctorForHelper.data';
import { saveOrUpdate } from '../doctorForHelper.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: 12 },
});
//表单配置
const [registerFormOrder, { setProps: setPropsOrder, setFieldsValue: setFieldsValueOrder }] = useForm({
//labelWidth: 150,
schemas: formSchemaOrder,
showActionButtonGroup: false,
baseColProps: { span: 12 },
});
//表单赋值
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,
});
await setFieldsValueOrder({
...data.record,
});
}
// 隐藏底部时禁用整个表单
await setProps({ disabled: !data?.showFooter });
await setPropsOrder({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '编辑' : '咨询详情'));
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
function handleRecordTable(record) {
console.log(record);
}
defineExpose({
handleRecordTable,
});
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,67 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-consultation/consultation/conSession/list',
save = '/health-consultation/consultation/conSession/add',
edit = '/health-consultation/consultation/conSession/edit',
deleteOne = '/health-consultation/consultation/conSession/delete',
deleteBatch = '/health-consultation/consultation/conSession/deleteBatch',
importExcel = '/health-consultation/consultation/conSession/importExcel',
exportXls = '/health-consultation/consultation/conSession/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => {
params.sessionType = '3';
// params.fromType = '1';
return defHttp.get({ url: Api.list, params });
};
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};

Some files were not shown because too many files have changed in this diff Show More