diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 8448820c..7bd776d2 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -46,8 +46,12 @@ "dayjs": "catalog:", "markdown-it": "^14.1.0", "pinia": "catalog:", + "sortablejs": "catalog:", "vue": "catalog:", "vue-router": "catalog:", "xlsx": "^0.18.5" + }, + "devDependencies": { + "@types/sortablejs": "catalog:" } } diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts index ab681720..a2207aac 100644 --- a/apps/web-antd/src/api/core/auth.ts +++ b/apps/web-antd/src/api/core/auth.ts @@ -3,13 +3,15 @@ import { baseRequestClient, requestClient } from '#/api/request'; export namespace AuthApi { /** 登录接口参数 */ export interface LoginParams { - login_method?: 'phone' | 'login_account' | 'job_number'; // 登录方式类型 + account: string; password?: string; - username?: string; - phone?: string; // 手机号(当login_method为phone时) code?: string; - login_account?: string; // 登录账号(当login_method为login_account时) - job_number?: string; // 工号(当login_method为job_number时) + /** @deprecated 兼容旧客户端 */ + login_method?: 'phone' | 'login_account' | 'job_number'; + username?: string; + phone?: string; + login_account?: string; + job_number?: string; } /** 登录接口返回值 */ @@ -24,13 +26,8 @@ export namespace AuthApi { /** 发送验证码返回结果 */ export interface SendCodeResult { - code?: string; // 开发环境返回验证码 - accounts?: Array<{ - id: number; - login_account: string; - job_number: string; - }>; // 如果手机号绑定多个账号,返回账号列表 - need_select?: boolean; // 是否需要选择账号/工号 + code?: string; + need_select?: boolean; } } @@ -38,12 +35,20 @@ export namespace AuthApi { * 登录 */ export async function loginApi(data: AuthApi.LoginParams) { - const params: any = { - login_method: data.login_method || 'phone', // 添加默认值 + if (data.account) { + return requestClient.post('login', { + account: data.account, + password: data.password, + code: data.code, + }); + } + + const params: Record = { + login_method: data.login_method || 'phone', password: data.password, code: data.code, }; - + if (data.login_method === 'phone') { params.phone = data.phone || data.username; } else if (data.login_method === 'login_account') { @@ -51,23 +56,32 @@ export async function loginApi(data: AuthApi.LoginParams) { } else if (data.login_method === 'job_number') { params.job_number = data.job_number; } - + return requestClient.post('login', params); } + /** * 发送验证码 */ export async function sendVerificationCode(params: { + account?: string; + password?: string; login_method?: 'phone' | 'login_account' | 'job_number'; username?: string; phone?: string; login_account?: string; job_number?: string; }) { - const requestParams: any = { + if (params.account) { + return requestClient.post('send-verification-code', { + account: params.account, + }); + } + + const requestParams: Record = { login_method: params.login_method || 'phone', }; - + if (params.login_method === 'phone') { requestParams.phone = params.phone || params.username; } else if (params.login_method === 'login_account') { @@ -75,8 +89,11 @@ export async function sendVerificationCode(params: { } else if (params.login_method === 'job_number') { requestParams.job_number = params.job_number; } - - return requestClient.post('send-verification-code', requestParams); + + return requestClient.post( + 'send-verification-code', + requestParams, + ); } /** diff --git a/apps/web-antd/src/components/form/components/avatar.vue b/apps/web-antd/src/components/form/components/avatar.vue index 0296d042..8186d853 100644 --- a/apps/web-antd/src/components/form/components/avatar.vue +++ b/apps/web-antd/src/components/form/components/avatar.vue @@ -5,6 +5,10 @@ import { Upload } from 'ant-design-vue'; // 导入上传相关API和工具函数 import { uploadFile } from '#/api/core/upload'; import { uploadToOss } from '#/utils/oss-upload'; +import { + beforeImageUpload, + resolveUploadFile, +} from '#/utils/use-image-upload-pending'; import { preferences } from '@vben/preferences'; import { Icon } from '#/components/icon'; @@ -24,28 +28,29 @@ const mValue = useVModel(props, 'value', emits, { }); /** * 自定义上传请求处理函数 - * + * * 该函数根据preferences配置的上传方式,选择使用OSS直传或后端上传: * - 'direct': OSS直传模式,文件直接从浏览器上传到阿里云OSS * - 'backend': 后端上传模式,文件先上传到后端服务器,再由后端上传到OSS - * + * * 上传方式说明: * 1. OSS直传(direct): * - 调用uploadToOss函数,直接上传到OSS * - 减少服务器负载,上传速度更快 - * + * * 2. 后端上传(backend): * - 调用uploadFile API,通过后端服务器上传 * - 兼容现有功能,所有上传逻辑由后端统一处理 - * + * * 兼容性处理: * - 两种上传方式返回的数据结构保持一致:{ url: string } * - 上传成功后,将URL赋值给mValue,触发组件更新 - * + * * @param e 上传事件对象,包含file字段(文件对象) */ const customRequest = async (e: any) => { try { + const actualFile = resolveUploadFile(e.file as File); // 从preferences中读取上传方式配置 // uploadMethod可能的值:'direct'(OSS直传)或 'backend'(后端上传) const uploadMethod = preferences.app.uploadMethod || 'direct'; @@ -57,13 +62,13 @@ const customRequest = async (e: any) => { // OSS直传模式:文件直接从浏览器上传到OSS // uploadToOss函数会处理签名获取、文件上传等所有逻辑 data = await uploadToOss({ - file: e.file as File, + file: actualFile, }); } else { // 后端上传模式:文件先上传到后端服务器,再由后端上传到OSS // uploadFile函数会将文件发送到后端API(/upload/image) data = await uploadFile({ - file: e.file, + file: actualFile, }); } @@ -83,6 +88,7 @@ const handleRemove = (e: Event) => { - - - diff --git a/apps/web-antd/src/views/doctor/doctor-reception/components/PrescriptionDetail.vue b/apps/web-antd/src/views/doctor/doctor-reception/components/PrescriptionDetail.vue index 0166b5c5..96390e54 100644 --- a/apps/web-antd/src/views/doctor/doctor-reception/components/PrescriptionDetail.vue +++ b/apps/web-antd/src/views/doctor/doctor-reception/components/PrescriptionDetail.vue @@ -99,6 +99,18 @@ const getImageSource = (imageString) => { } else { return `data:image/jpeg;base64,${imageString}`; // 使用Base64格式 } +}; + +function parseRecipeContent(content: string | Record) { + if (!content) return {}; + if (typeof content === 'string') { + try { + return JSON.parse(content); + } catch { + return {}; + } + } + return content; } @@ -187,20 +199,24 @@ const getImageSource = (imageString) => {
-
- {{ - JSON.parse(recipe.content).name || - JSON.parse(recipe.content).drug_name - }} - {{ JSON.parse(recipe.content).number - }}{{ JSON.parse(recipe.content).unit?.name }} -
- {{ JSON.parse(recipe.content).useWay }} +
使用方法: {{ recipe.instruction }}
@@ -357,6 +373,13 @@ const getImageSource = (imageString) => { font-weight: 500; } +.drug-spec { + font-size: 12px; + color: #666; + margin-left: 8px; + font-weight: normal; +} + .preparation-info { color: #666; margin-top: 12px; diff --git a/apps/web-antd/src/views/doctor/settings/index.vue b/apps/web-antd/src/views/doctor/settings/index.vue index 4aa756ba..a13a6160 100644 --- a/apps/web-antd/src/views/doctor/settings/index.vue +++ b/apps/web-antd/src/views/doctor/settings/index.vue @@ -21,8 +21,6 @@ import SignatureModal from './components/SignatureModal.vue'; import { Button, Card, - Collapse, - CollapsePanel, Empty, Input, InputGroup, @@ -189,6 +187,10 @@ const getDrugNames = (recipes: any[], nameField = 'drug_name') => { return recipes.map((item) => item[nameField] || item.name).join('、'); }; +const formatField = (value?: string | null) => (value?.trim() ? value : '—'); + +const getDrugCount = (recipes: any[]) => recipes?.length || 0; + // ==================== 常用方编辑弹窗 ==================== const [EditCommonPrescriptionModals, EditCommonPrescriptionModalApi] = @@ -553,50 +555,61 @@ const handleViewSignature = () => {

西药常用方

- - +
-
-

- 临床诊断: - {{ item.clinical_diagnose }} -

-

- 医嘱:{{ item.doctor_order }} -

-

- 药品: - {{ getDrugNames(commonPrescriptionData.west[index]) }} -

-
- - - - -
+
+ {{ + item.name || `西药处方 ${index + 1}` + }} + {{ getDrugCount(commonPrescriptionData.west[index]) }}种药品
- - +
+ + + + +
+
+
+

+ 临床诊断: + {{ formatField(item.clinical_diagnose) }} +

+

+ 医嘱: + {{ formatField(item.doctor_order) }} +

+

+ 药品: + {{ getDrugNames(commonPrescriptionData.west[index]) }} +

+
+
@@ -610,62 +623,73 @@ const handleViewSignature = () => {

中药常用方

- - +
-
-

- 临床诊断: - {{ item.clinical_diagnose }} -

-

- 医嘱:{{ item.doctor_order }} -

-

- 调配方式: - {{ item.rule_type === 1 ? '自制剂' : '委托调剂' }} - 剂量:{{ item.dosage || 7 }}天/剂 - 频次:{{ item.day_dosage || 2 }}次/天 -

-

- 委托调剂配置: - 制剂:{{ item.process_rule_name }} - 煎法:{{ item.child_process_rule_name }} - 备注:{{ item.process_rule_note_name }} -

-

- 药品: - {{ getDrugNames(commonPrescriptionData.chinese[index], 'drug_name') }} -

-
- - - - -
+
+ {{ + item.name || `中药处方 ${index + 1}` + }} + {{ getDrugCount(commonPrescriptionData.chinese[index]) }}种药品
- - +
+ + + + +
+
+
+

+ 临床诊断: + {{ formatField(item.clinical_diagnose) }} +

+

+ 医嘱: + {{ formatField(item.doctor_order) }} +

+

+ 调配方式: + {{ item.rule_type === 1 ? '自制剂' : '委托调剂' }} + 剂量:{{ item.dosage || 7 }}天/剂 + 频次:{{ item.day_dosage || 2 }}次/天 +

+

+ 委托调剂配置: + 制剂:{{ item.process_rule_name }} + 煎法:{{ item.child_process_rule_name }} + 备注:{{ item.process_rule_note_name }} +

+

+ 药品: + {{ getDrugNames(commonPrescriptionData.chinese[index], 'drug_name') }} +

+
+
@@ -679,50 +703,61 @@ const handleViewSignature = () => {

颗粒药常用方

- - +
-
-

- 临床诊断: - {{ item.clinical_diagnose }} -

-

- 医嘱:{{ item.doctor_order }} -

-

- 药品: - {{ getDrugNames(commonPrescriptionData.granular[index], 'name') }} -

-
- - - - -
+
+ {{ + item.name || `颗粒药处方 ${index + 1}` + }} + {{ getDrugCount(commonPrescriptionData.granular[index]) }}种药品
- - +
+ + + + +
+
+
+

+ 临床诊断: + {{ formatField(item.clinical_diagnose) }} +

+

+ 医嘱: + {{ formatField(item.doctor_order) }} +

+

+ 药品: + {{ getDrugNames(commonPrescriptionData.granular[index], 'name') }} +

+
+
diff --git a/apps/web-antd/src/views/finance/withdrawal/api/settlement.ts b/apps/web-antd/src/views/finance/withdrawal/api/settlement.ts index 485bd0ba..0c38d63f 100644 --- a/apps/web-antd/src/views/finance/withdrawal/api/settlement.ts +++ b/apps/web-antd/src/views/finance/withdrawal/api/settlement.ts @@ -4,8 +4,24 @@ const prefix = 'settlement/'; * 分页查询用户列表 * @param data */ +/** 结算记录(yii_ledger_log,正确分页) */ +export async function getSettlementLedgerLogList(data: any) { + return requestClient.get(`${prefix}ledger-log-list`, { params: data }); +} + +/** @deprecated 请使用 getSettlementLedgerLogList */ export async function getSettlementList(data: any) { - return requestClient.get(`${prefix}list`, { params: data }); + return getSettlementLedgerLogList(data); +} + +/** 结算明细(yii_ledger) */ +export async function getSettlementLedgerDetail(params: { + ledger_log_id?: number; + order_id?: number; + order_type?: number; + fee_type?: number; +}) { + return requestClient.get(`${prefix}ledger-detail`, { params }); } /** * 分页查询用户列表 diff --git a/apps/web-antd/src/views/finance/withdrawal/config/settlement-table.ts b/apps/web-antd/src/views/finance/withdrawal/config/settlement-table.ts index 21075b66..e3ebc1a4 100644 --- a/apps/web-antd/src/views/finance/withdrawal/config/settlement-table.ts +++ b/apps/web-antd/src/views/finance/withdrawal/config/settlement-table.ts @@ -1,6 +1,9 @@ import type { VxeGridProps } from '#/adapter/vxe-table'; -import {getSettlementItemsList, getSettlementList} from '#/views/finance/withdrawal/api/settlement'; +import { + getSettlementItemsList, + getSettlementLedgerLogList, +} from '#/views/finance/withdrawal/api/settlement'; interface RowType { id: string; @@ -89,10 +92,10 @@ export const gridOptions3: VxeGridProps = { // { type: 'checkbox', width: 60 }, // { field: 'id', align: 'left', title: 'ID', width: 100 }, { field: 'order_no', title: '订单号' }, - { field: 'user_id', title: '名称', slots: { default: 'user_id' } }, - { field: 'money', title: '分账金额' }, - // { field: 'fee_type_txt', title: '费用类型' }, - // { field: 'status_txt', title: '结算状态' }, + { field: 'fee_type_txt', title: '费用类型' }, + { field: 'amount', title: '金额' }, + { field: 'type_txt', title: '类型' }, + { field: 'content', title: '说明' }, { field: 'created_at', title: '创建时间' }, { type: 'html', title: '操作', slots: { default: 'action' } }, ], @@ -102,7 +105,7 @@ export const gridOptions3: VxeGridProps = { ajax: { // 请求后端接口方法 query: async ({ page }, formValues) => { - return await getSettlementList({ + return await getSettlementLedgerLogList({ page: page.currentPage, pageSize: page.pageSize, ...formValues, diff --git a/apps/web-antd/src/views/log/api-log/components/modal.vue b/apps/web-antd/src/views/log/api-log/components/modal.vue index efb097c3..0d333d49 100644 --- a/apps/web-antd/src/views/log/api-log/components/modal.vue +++ b/apps/web-antd/src/views/log/api-log/components/modal.vue @@ -7,6 +7,10 @@ import { Descriptions } from 'ant-design-vue'; import { Icon } from '#/components/icon'; import { getIcon } from '#/util/tool'; +import { + getApiOpLogOperatorLabel, + getApiOpLogPlatformLabel, +} from '../config/platform'; @@ -43,10 +47,7 @@ const [Modal, modalApi] = useVbenModal({ > {{ data.id }} - {{ data?.admin?.nick_name || '获取失败' }} - {{ data?.admin?.nick_name || '获取失败' }} - {{ data?.user?.nickname || '获取失败' }} - 获取失败 + {{ getApiOpLogOperatorLabel(data) }} {{ data.url }} @@ -62,11 +63,8 @@ const [Modal, modalApi] = useVbenModal({ {{ data.result_code }} - - 平台 - 诊所 - 小程序 - 获取失败 + + {{ getApiOpLogPlatformLabel(data.platform_type) }} diff --git a/apps/web-antd/src/views/log/api-log/config/platform.ts b/apps/web-antd/src/views/log/api-log/config/platform.ts new file mode 100644 index 00000000..fe765cf4 --- /dev/null +++ b/apps/web-antd/src/views/log/api-log/config/platform.ts @@ -0,0 +1,35 @@ +export const API_OP_LOG_PLATFORM_OPTIONS = [ + { label: '后台', value: 0 }, + { label: '用户移动端', value: 1 }, + { label: '医生移动端', value: 2 }, + { label: '互医内部接口', value: 3 }, +] as const; + +export function getApiOpLogPlatformLabel( + platformType: number | string | null | undefined, +): string { + const value = Number(platformType); + const found = API_OP_LOG_PLATFORM_OPTIONS.find((item) => item.value === value); + return found?.label ?? '历史/未知'; +} + +export function getApiOpLogOperatorLabel(row: { + platform_type?: number; + admin?: { nick_name?: string }; + user?: { nickname?: string }; +}): string { + const type = row.platform_type; + if (type === 3) { + return '互医中转'; + } + if (type === 2) { + return row?.user?.nickname || row?.admin?.nick_name || '-'; + } + if (type === 1) { + return row?.user?.nickname || '获取失败'; + } + if (type === 0) { + return row?.admin?.nick_name || '获取失败'; + } + return row?.admin?.nick_name || row?.user?.nickname || '-'; +} diff --git a/apps/web-antd/src/views/log/api-log/config/search.ts b/apps/web-antd/src/views/log/api-log/config/search.ts index 004103f1..3ad18dba 100644 --- a/apps/web-antd/src/views/log/api-log/config/search.ts +++ b/apps/web-antd/src/views/log/api-log/config/search.ts @@ -1,5 +1,7 @@ import type { VbenFormProps } from '#/adapter/form'; +import { API_OP_LOG_PLATFORM_OPTIONS } from './platform'; + // import dayjs from 'dayjs'; export const formOptions: VbenFormProps = { @@ -18,6 +20,16 @@ export const formOptions: VbenFormProps = { fieldName: 'router', label: 'api接口', }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: [...API_OP_LOG_PLATFORM_OPTIONS], + placeholder: '全部', + }, + fieldName: 'platform_type', + label: '接口来源', + }, { component: 'RangePicker', componentProps: { diff --git a/apps/web-antd/src/views/log/api-log/config/table.ts b/apps/web-antd/src/views/log/api-log/config/table.ts index 36e065b9..d47420d1 100644 --- a/apps/web-antd/src/views/log/api-log/config/table.ts +++ b/apps/web-antd/src/views/log/api-log/config/table.ts @@ -32,6 +32,12 @@ export const gridOptions: VxeGridProps = { slots: { default: 'admin' }, }, { field: 'url', title: '访问路由' }, + { + field: 'platform_type', + title: '接口来源', + slots: { default: 'platform_type' }, + width: 120, + }, { field: 'ip', title: '用户IP地址' }, { field: 'ip_address', title: 'IP归属地' }, { field: 'controller', title: '访问控制器' }, diff --git a/apps/web-antd/src/views/log/api-log/index.vue b/apps/web-antd/src/views/log/api-log/index.vue index 756290e9..f52f6949 100644 --- a/apps/web-antd/src/views/log/api-log/index.vue +++ b/apps/web-antd/src/views/log/api-log/index.vue @@ -11,6 +11,10 @@ import { TableAction } from '#/components/table-action'; import FormModalDemo from './components/modal.vue'; import { formOptions } from './config/search'; import { gridOptions } from './config/table'; +import { + getApiOpLogOperatorLabel, + getApiOpLogPlatformLabel, +} from './config/platform'; import { getIcon } from '#/util/tool'; import {Icon} from "#/components/icon"; @@ -46,10 +50,10 @@ const showModal = (data = {}, isUpdate = false) => { 访问失败 +