This commit is contained in:
Your Name
2026-03-11 09:49:47 +08:00
parent 02ae537b4c
commit 38ad60f4bb
290 changed files with 36917 additions and 123 deletions
+731
View File
@@ -0,0 +1,731 @@
<!-- 订单列表 -->
<template>
<div class="order-list">
<el-card class="!border-none" shadow="never">
<!-- 搜索表单 -->
<el-form class="ls-form" :model="queryParams" inline>
<el-form-item class="w-[280px]" label="订单号">
<el-input
v-model="queryParams.order_no"
placeholder="请输入订单号"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item class="w-[280px]" label="患者信息">
<el-input
v-model="queryParams.patient_keyword"
placeholder="患者姓名/手机号"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item class="w-[280px]" label="订单类型">
<el-select v-model="queryParams.order_type" placeholder="请选择" clearable>
<el-option label="挂号费" :value="1" />
<el-option label="问诊费" :value="2" />
<el-option label="药品费用" :value="3" />
</el-select>
</el-form-item>
<el-form-item class="w-[280px]" label="订单状态">
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
<el-option label="待支付" :value="1" />
<el-option label="已支付" :value="2" />
<el-option label="已取消" :value="3" />
<el-option label="已退款" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<daterange-picker
v-model:startTime="queryParams.create_time_start"
v-model:endTime="queryParams.create_time_end"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<export-data
class="ml-2.5"
:fetch-fun="orderExport"
:params="queryParams"
:page-size="pager.size"
/>
<el-button
v-perms="['order.order/create']"
type="success"
class="ml-2.5"
@click="handleCreate"
>
+ 创建订单
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card class="!border-none mt-4" shadow="never">
<el-table
v-loading="pager.loading"
:data="pager.lists"
size="large"
>
<el-table-column label="订单号" prop="order_no" min-width="150" />
<el-table-column label="患者信息" min-width="150">
<template #default="{ row }">
<div>{{ row.patient_name }}</div>
<div class="text-xs text-gray-400">{{ row.patient_phone }}</div>
</template>
</el-table-column>
<el-table-column label="创建人" prop="creator_name" min-width="120" />
<el-table-column label="订单类型" width="100">
<template #default="{ row }">
<el-tag :type="getOrderTypeTag(row.order_type)">
{{ getOrderTypeText(row.order_type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="金额" width="100">
<template #default="{ row }">
<span class="text-red-500 font-semibold">¥{{ row.amount }}</span>
</template>
</el-table-column>
<el-table-column label="支付方式" width="100">
<template #default="{ row }">
{{ getPaymentMethodText(row.payment_method) }}
</template>
</el-table-column>
<el-table-column label="订单状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusTag(row.status)">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="支付时间" min-width="160">
<template #default="{ row }">
{{ row.payment_time || '-' }}
</template>
</el-table-column>
<el-table-column label="创建时间" min-width="160">
<template #default="{ row }">
{{ row.create_time }}
</template>
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['order.order/detail']"
type="primary"
link
@click="handleDetail(row)"
>
详情
</el-button>
<el-button
v-if="row.status === 1"
v-perms="['order.order/pay']"
type="success"
link
@click="handlePay(row)"
>
支付
</el-button>
<el-button
v-if="row.status === 2"
v-perms="['order.order/refund']"
type="warning"
link
@click="handleRefund(row)"
>
退款
</el-button>
<el-button
v-if="row.status === 1"
v-perms="['order.order/cancel']"
type="danger"
link
@click="handleCancel(row)"
>
取消
</el-button>
<el-button
v-perms="['order.order/delete']"
type="danger"
link
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog
v-model="detailVisible"
title="订单详情"
width="700px"
>
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="订单号">
{{ detailData.order_no }}
</el-descriptions-item>
<el-descriptions-item label="订单状态">
<el-tag :type="getStatusTag(detailData.status)">
{{ getStatusText(detailData.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="患者姓名">
{{ detailData.patient_name }}
</el-descriptions-item>
<el-descriptions-item label="患者电话">
{{ detailData.patient_phone }}
</el-descriptions-item>
<el-descriptions-item label="创建人">
{{ detailData.creator_name }}
</el-descriptions-item>
<el-descriptions-item label="订单类型">
<el-tag :type="getOrderTypeTag(detailData.order_type)">
{{ getOrderTypeText(detailData.order_type) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="订单金额">
<span class="text-red-500 font-semibold">¥{{ detailData.amount }}</span>
</el-descriptions-item>
<el-descriptions-item label="支付方式">
{{ getPaymentMethodText(detailData.payment_method) }}
</el-descriptions-item>
<el-descriptions-item label="支付时间">
{{ detailData.payment_time || '-' }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ detailData.create_time }}
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ detailData.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
<!-- 订单详情项 -->
<el-divider>订单详情</el-divider>
<el-table :data="detailData?.details || []" size="small">
<el-table-column label="关联类型" prop="related_type" width="100">
<template #default="{ row }">
{{ getRelatedTypeText(row.related_type) }}
</template>
</el-table-column>
<el-table-column label="数量" prop="quantity" width="80" />
<el-table-column label="单价" width="100">
<template #default="{ row }">
¥{{ row.unit_price }}
</template>
</el-table-column>
<el-table-column label="总价" width="100">
<template #default="{ row }">
¥{{ row.total_price }}
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 支付弹窗 -->
<el-dialog
v-model="payDialogVisible"
title="订单支付"
width="500px"
>
<el-form :model="payForm" label-width="100px">
<el-form-item label="订单号">
<span>{{ payForm.order_no }}</span>
</el-form-item>
<el-form-item label="订单金额">
<span class="text-red-500 font-semibold">¥{{ payForm.amount }}</span>
</el-form-item>
<el-form-item label="支付方式" required>
<el-radio-group v-model="payForm.payment_method">
<el-radio label="alipay">支付宝</el-radio>
<el-radio label="wechat">微信</el-radio>
<el-radio label="bank">银行卡</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="payDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmPay" :loading="payLoading">确认支付</el-button>
</template>
</el-dialog>
<!-- 创建订单弹窗 -->
<el-dialog
v-model="createDialogVisible"
title="创建订单"
width="600px"
@close="resetCreateForm"
>
<el-form
ref="createFormRef"
:model="createForm"
:rules="createRules"
label-width="100px"
>
<el-form-item label="患者" prop="patient_id">
<el-select
v-model="createForm.patient_id"
placeholder="请选择患者"
filterable
remote
:remote-method="searchPatients"
:loading="patientLoading"
>
<el-option
v-for="item in patientList"
:key="item.id"
:label="`${item.nickname} (${item.mobile})`"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="订单类型" prop="order_type">
<el-select v-model="createForm.order_type" placeholder="请选择">
<el-option label="挂号费" :value="1" />
<el-option label="问诊费" :value="2" />
<el-option label="药品费用" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="订单金额" prop="amount">
<el-input-number
v-model="createForm.amount"
:min="0"
:step="0.01"
:precision="2"
placeholder="请输入金额"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="createForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注(可选)"
/>
</el-form-item>
<el-form-item label="订单详情">
<div class="w-full">
<el-button
type="primary"
link
@click="addOrderDetail"
>
+ 添加订单项
</el-button>
<el-table :data="createForm.details" class="mt-3">
<el-table-column label="关联类型" width="120">
<template #default="{ row }">
<el-select
v-model="row.related_type"
placeholder="请选择"
size="small"
>
<el-option label="挂号" value="appointment" />
<el-option label="问诊" value="diagnosis" />
<el-option label="药品" value="medicine" />
</el-select>
</template>
</el-table-column>
<el-table-column label="关联ID" width="100">
<template #default="{ row }">
<el-input
v-model.number="row.related_id"
type="number"
placeholder="ID"
size="small"
/>
</template>
</el-table-column>
<el-table-column label="数量" width="80">
<template #default="{ row }">
<el-input-number
v-model="row.quantity"
:min="1"
size="small"
/>
</template>
</el-table-column>
<el-table-column label="单价" width="100">
<template #default="{ row, $index }">
<el-input-number
v-model="row.unit_price"
:min="0"
:step="0.01"
:precision="2"
size="small"
@change="updateDetailTotal($index)"
/>
</template>
</el-table-column>
<el-table-column label="总价" width="100">
<template #default="{ row }">
<span>{{ (row.quantity * row.unit_price).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{ row, $index }">
<el-button
type="danger"
link
size="small"
@click="removeOrderDetail($index)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitCreateOrder" :loading="createLoading">
创建订单
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="orderList">
import { usePaging } from '@/hooks/usePaging'
import { orderLists, orderDetail, orderPay, orderCancel, orderDelete, orderRefund, orderExport, orderCreate, searchPatients as searchPatientsAPI } from '@/api/order'
import feedback from '@/utils/feedback'
const queryParams = reactive({
order_no: '',
patient_keyword: '',
order_type: '',
status: '',
create_time_start: '',
create_time_end: ''
})
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: orderLists,
params: queryParams
})
// 搜索
const handleSearch = () => {
resetPage()
}
// 重置
const handleReset = () => {
resetParams()
}
const detailVisible = ref(false)
const detailData = ref<any>(null)
const payDialogVisible = ref(false)
const payLoading = ref(false)
const payForm = ref({
order_id: 0,
order_no: '',
amount: 0,
payment_method: 'alipay'
})
// 创建订单相关
const createDialogVisible = ref(false)
const createFormRef = ref()
const createLoading = ref(false)
const patientLoading = ref(false)
const patientList = ref<any[]>([])
const createForm = reactive({
patient_id: '',
order_type: '',
amount: 0,
remark: '',
details: [] as any[]
})
const createRules = {
patient_id: [{ required: true, message: '请选择患者', trigger: 'change' }],
order_type: [{ required: true, message: '请选择订单类型', trigger: 'change' }],
amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }]
}
// 搜索患者
const searchPatients = async (query: string) => {
if (!query) {
patientList.value = []
return
}
try {
patientLoading.value = true
const res = await searchPatientsAPI({
keyword: query,
page_no: 1,
page_size: 10
})
patientList.value = res?.lists || []
} catch (error) {
console.error('搜索患者失败:', error)
} finally {
patientLoading.value = false
}
}
// 添加订单项
const addOrderDetail = () => {
createForm.details.push({
related_type: '',
related_id: '',
quantity: 1,
unit_price: 0,
total_price: 0
})
}
// 删除订单项
const removeOrderDetail = (index: number) => {
createForm.details.splice(index, 1)
}
// 更新订单项总价
const updateDetailTotal = (index: number) => {
const detail = createForm.details[index]
detail.total_price = detail.quantity * detail.unit_price
}
// 重置创建表单
const resetCreateForm = () => {
createForm.patient_id = ''
createForm.order_type = ''
createForm.amount = 0
createForm.remark = ''
createForm.details = []
createFormRef.value?.clearValidate()
}
// 打开创建订单弹窗
const handleCreate = () => {
resetCreateForm()
createDialogVisible.value = true
}
// 提交创建订单
const submitCreateOrder = async () => {
try {
await createFormRef.value?.validate()
if (createForm.details.length === 0) {
feedback.msgWarning('请至少添加一个订单项')
return
}
createLoading.value = true
// 计算总金额
const totalAmount = createForm.details.reduce((sum, detail) => {
return sum + (detail.quantity * detail.unit_price)
}, 0)
const params = {
patient_id: createForm.patient_id,
order_type: createForm.order_type,
amount: totalAmount,
remark: createForm.remark,
details: createForm.details.map(detail => ({
related_type: detail.related_type,
related_id: detail.related_id,
quantity: detail.quantity,
unit_price: detail.unit_price,
total_price: detail.quantity * detail.unit_price
}))
}
await orderCreate(params)
feedback.msgSuccess('订单创建成功')
createDialogVisible.value = false
getLists()
} catch (error) {
console.error('创建订单失败:', error)
} finally {
createLoading.value = false
}
}
// 获取订单类型文本
const getOrderTypeText = (type: number) => {
const typeMap: Record<number, string> = {
1: '挂号费',
2: '问诊费',
3: '药品费用'
}
return typeMap[type] || '未知'
}
// 获取订单类型标签类型
const getOrderTypeTag = (type: number): 'success' | 'info' | 'warning' => {
const tagMap: Record<number, 'success' | 'info' | 'warning'> = {
1: 'success',
2: 'info',
3: 'warning'
}
return tagMap[type] || 'info'
}
// 获取订单状态文本
const getStatusText = (status: number) => {
const statusMap: Record<number, string> = {
1: '待支付',
2: '已支付',
3: '已取消',
4: '已退款'
}
return statusMap[status] || '未知'
}
// 获取订单状态标签类型
const getStatusTag = (status: number): 'warning' | 'success' | 'info' | 'danger' => {
const tagMap: Record<number, 'warning' | 'success' | 'info' | 'danger'> = {
1: 'warning',
2: 'success',
3: 'info',
4: 'danger'
}
return tagMap[status] || 'info'
}
// 获取支付方式文本
const getPaymentMethodText = (method: string | null) => {
if (!method) return '-'
const methodMap: Record<string, string> = {
alipay: '支付宝',
wechat: '微信',
bank: '银行卡'
}
return methodMap[method] || '未知'
}
// 获取关联类型文本
const getRelatedTypeText = (type: string) => {
const typeMap: Record<string, string> = {
appointment: '挂号',
diagnosis: '问诊',
medicine: '药品'
}
return typeMap[type] || '未知'
}
// 查看详情
const handleDetail = async (row: any) => {
try {
const res = await orderDetail({ id: row.id })
detailData.value = res
detailVisible.value = true
} catch (error) {
console.error('获取详情失败:', error)
}
}
// 支付订单
const handlePay = (row: any) => {
payForm.value = {
order_id: row.id,
order_no: row.order_no,
amount: row.amount,
payment_method: 'alipay'
}
payDialogVisible.value = true
}
// 确认支付
const confirmPay = async () => {
try {
payLoading.value = true
await orderPay({
id: payForm.value.order_id,
payment_method: payForm.value.payment_method
})
feedback.msgSuccess('支付成功')
payDialogVisible.value = false
getLists()
} catch (error) {
console.error('支付失败:', error)
} finally {
payLoading.value = false
}
}
// 取消订单
const handleCancel = async (row: any) => {
try {
await feedback.confirm('确定要取消该订单吗?')
await orderCancel({ id: row.id })
feedback.msgSuccess('取消成功')
getLists()
} catch (error) {
// 用户取消操作
}
}
// 退款订单
const handleRefund = async (row: any) => {
try {
await feedback.confirm('确定要退款该订单吗?')
await orderRefund({ id: row.id })
feedback.msgSuccess('退款成功')
getLists()
} catch (error) {
// 用户取消操作
}
}
// 删除订单
const handleDelete = async (row: any) => {
try {
await feedback.confirm('确定要删除该订单吗?此操作不可恢复')
await orderDelete({ id: row.id })
feedback.msgSuccess('删除成功')
getLists()
} catch (error) {
// 用户取消操作
}
}
onMounted(() => {
getLists()
})
</script>
<style scoped lang="scss">
.order-list {
padding: 20px;
}
</style>
+24 -8
View File
@@ -193,7 +193,7 @@
v-perms="['doctor.appointment/prescription']"
type="success"
link
@click="handleCreatePrescription(row)"
@click="handleChat(row)"
>
聊天
</el-button>
@@ -271,6 +271,7 @@
<script setup lang="ts">
import { usePaging } from '@/hooks/usePaging'
import { appointmentLists, cancelAppointment, completeAppointment, appointmentDetail } from '@/api/doctor'
import { getCallSignature } from '@/api/tcm'
import feedback from '@/utils/feedback'
import EditPopup from '../diagnosis/edit.vue'
import VideoCall from '@/components/video-call/index.vue'
@@ -459,8 +460,8 @@ const handleGroupVideoCall = (row: any) => {
})
}
// 创建处方单
const handleCreatePrescription = async (row: any) => {
// 聊天
const handleChat = async (row: any) => {
if (!row.patient_id) {
feedback.msgWarning('患者信息不完整')
return
@@ -472,11 +473,26 @@ const handleCreatePrescription = async (row: any) => {
return
}
chatDialogRef.value?.open({
patientId: row.patient_id,
patientName: row.patient_name,
diagnosisId: row.diagnosis_id || row.id // 使用诊单ID,如果没有则使用预约ID
})
try {
// 获取聊天签名信息
const res = await getCallSignature({
patient_id: row.patient_id,
diagnosis_id: row.diagnosis_id || row.id
})
console.log('获取聊天签名成功:', res)
// 直接打开聊天对话框,传入必要的参数
chatDialogRef.value?.open({
patientId: row.patient_id,
patientName: row.patient_name,
diagnosisId: row.diagnosis_id || row.id,
signatureData: res // 传入签名数据
})
} catch (error: any) {
console.error('获取聊天签名失败:', error)
feedback.msgError(error.message || '获取聊天签名失败')
}
}
loadData()
+2 -2
View File
@@ -894,7 +894,7 @@ defineExpose({
}
</style>
<style lang="scss">
<style lang="scss" scoped>
/* 全局样式,用于提高弹出层的z-index */
.high-z-index {
z-index: 3000 !important;
@@ -906,7 +906,7 @@ defineExpose({
}
.el-overlay {
z-index: 1499 !important;
z-index: 1499 ;
}
/* 确保日期选择器和下拉选择器的弹出层在最上层 */