更新
This commit is contained in:
@@ -125,6 +125,18 @@ export function myPatientCancelAppointment(params: { id: number }) {
|
||||
return request.post({ url: '/firstvisit.myPatient/cancelAppointment', params })
|
||||
}
|
||||
|
||||
export function myPatientAssistants() {
|
||||
return request.get({ url: '/firstvisit.myPatient/assistants' })
|
||||
}
|
||||
|
||||
export function myPatientAssign(params: { id: number; assistant_id: number; is_inherit?: 0 | 1 }) {
|
||||
return request.post({ url: '/firstvisit.myPatient/assign', params })
|
||||
}
|
||||
|
||||
export function myPatientFillIdCard(params: { id: number; id_card: string }) {
|
||||
return request.post({ url: '/firstvisit.myPatient/fillIdCard', params })
|
||||
}
|
||||
|
||||
export interface FirstVisitConversionParams {
|
||||
time_type: 'today' | 'yesterday' | 'week' | 'month' | 'quarter' | 'year'
|
||||
dept_id?: number
|
||||
|
||||
@@ -146,12 +146,28 @@
|
||||
<el-table-column label="诊单日期" width="118">
|
||||
<template #default="{ row }">{{ row.diagnosis_date_text || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="285" fixed="right">
|
||||
<el-table-column label="操作" min-width="430" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="row-actions">
|
||||
<el-button v-if="canEditDiagnosis" type="primary" link @click="openDiagnosis(row)">诊单</el-button>
|
||||
<el-button v-else-if="canReadDiagnosis" type="primary" link @click="openReadonlyDiagnosis(row)">查看</el-button>
|
||||
<el-button v-if="canBookAppointment" type="primary" link @click="openAppointment(row)">预约</el-button>
|
||||
<el-button
|
||||
v-if="canAssignPatient"
|
||||
type="warning"
|
||||
link
|
||||
@click="openAssignDialog(row)"
|
||||
>
|
||||
{{ Number(row.assistant_id) > 0 ? '重新指派' : '指派' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canFillIdCard && !Number(row.has_id_card)"
|
||||
type="warning"
|
||||
link
|
||||
@click="openFillIdCardDialog(row)"
|
||||
>
|
||||
补全身份证
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canShowDiagnosisQRCode(row)"
|
||||
type="primary"
|
||||
@@ -196,6 +212,84 @@
|
||||
<edit-popup ref="editRef" @success="refreshPage" />
|
||||
<appointment-popup ref="appointmentRef" api-scene="my_patient" @success="refreshPage" />
|
||||
|
||||
<el-dialog
|
||||
v-model="assignDialogVisible"
|
||||
title="指派医助"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form :model="assignForm" label-width="88px">
|
||||
<el-form-item label="患者">
|
||||
<span class="dialog-patient-name">{{ currentActionPatient?.patient_name || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前助理">
|
||||
<span>{{ currentActionPatient?.assistant_name || '未分配' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择医助" required>
|
||||
<el-select
|
||||
v-model="assignForm.assistant_id"
|
||||
class="dialog-full-width"
|
||||
filterable
|
||||
clearable
|
||||
:loading="assistantOptionsLoading"
|
||||
placeholder="请选择当前范围内的医助"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in assistantOptions"
|
||||
:key="item.id"
|
||||
:label="assistantOptionLabel(item)"
|
||||
:value="Number(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="继承">
|
||||
<div class="inherit-field">
|
||||
<el-checkbox v-model="assignForm.is_inherit">作为继承指派</el-checkbox>
|
||||
<small>用于区分继承关系,指派操作仍会完整记录。</small>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="assignDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="assignLoading" @click="submitAssign">确定指派</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="fillIdCardDialogVisible"
|
||||
title="补全身份证号"
|
||||
width="430px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="resetFillIdCardForm"
|
||||
>
|
||||
<el-form
|
||||
ref="fillIdCardFormRef"
|
||||
:model="fillIdCardForm"
|
||||
:rules="fillIdCardRules"
|
||||
label-width="88px"
|
||||
>
|
||||
<el-form-item label="患者">
|
||||
<span class="dialog-patient-name">{{ fillIdCardForm.patient_name || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="id_card">
|
||||
<el-input
|
||||
v-model="fillIdCardForm.id_card"
|
||||
maxlength="18"
|
||||
clearable
|
||||
show-word-limit
|
||||
placeholder="请输入15或18位身份证号"
|
||||
@keyup.enter="submitFillIdCard"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="fillIdCardDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="fillIdCardLoading" @click="submitFillIdCard">
|
||||
提交并更新年龄
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="qrcodeDialogVisible"
|
||||
title="诊单二维码"
|
||||
@@ -233,7 +327,13 @@ import { Calendar, Clock, Loading, Lock, Refresh, Search } from '@element-plus/i
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import { hasPermission } from '@/utils/perm'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { myPatientCancelAppointment, myPatientLists } from '@/api/first_visit'
|
||||
import {
|
||||
myPatientAssign,
|
||||
myPatientAssistants,
|
||||
myPatientCancelAppointment,
|
||||
myPatientFillIdCard,
|
||||
myPatientLists
|
||||
} from '@/api/first_visit'
|
||||
import { generateMiniProgramQrcode } from '@/api/tcm'
|
||||
import { getWeappConfig } from '@/api/channel/weapp'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@@ -259,6 +359,23 @@ const qrcodeDialogVisible = ref(false)
|
||||
const qrcodeLoading = ref(false)
|
||||
const qrcodeUrl = ref('')
|
||||
const currentQRCodePatient = ref<any>(null)
|
||||
const assignDialogVisible = ref(false)
|
||||
const assignLoading = ref(false)
|
||||
const assistantOptionsLoading = ref(false)
|
||||
const assistantOptions = ref<any[]>([])
|
||||
const currentActionPatient = ref<any>(null)
|
||||
const assignForm = reactive({
|
||||
assistant_id: null as number | null,
|
||||
is_inherit: false
|
||||
})
|
||||
const fillIdCardDialogVisible = ref(false)
|
||||
const fillIdCardLoading = ref(false)
|
||||
const fillIdCardFormRef = ref<any>()
|
||||
const fillIdCardForm = reactive({
|
||||
id: 0,
|
||||
patient_name: '',
|
||||
id_card: ''
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
keyword: '',
|
||||
@@ -301,6 +418,8 @@ const scopeLabel = computed(() => pager.extend?.scope?.label || '按权限加载
|
||||
const canEditDiagnosis = computed(() => hasPermission(['tcm.diagnosis/edit']))
|
||||
const canReadDiagnosis = computed(() => hasPermission(['tcm.diagnosis/readonlyDetail']))
|
||||
const canBookAppointment = computed(() => hasPermission(['tcm.diagnosis/guahao']))
|
||||
const canAssignPatient = computed(() => hasPermission(['tcm.diagnosis/assign']))
|
||||
const canFillIdCard = computed(() => hasPermission(['tcm.diagnosis/edit']))
|
||||
const workspaceLoading = computed(() => {
|
||||
if (activeWorkspace.value === 'orders') return Boolean(orderPanelRef.value?.loading)
|
||||
if (activeWorkspace.value === 'progress') return Boolean(progressPanelRef.value?.loading)
|
||||
@@ -399,6 +518,115 @@ function openAppointment(row: any) {
|
||||
})
|
||||
}
|
||||
|
||||
function assistantOptionLabel(item: any) {
|
||||
const name = String(item?.name || item?.account || `医助${item?.id || ''}`)
|
||||
const departments = String(item?.dept_names || '').trim()
|
||||
return departments ? `${name} · ${departments}` : name
|
||||
}
|
||||
|
||||
async function loadAssistantOptions() {
|
||||
assistantOptionsLoading.value = true
|
||||
try {
|
||||
const result = await myPatientAssistants()
|
||||
assistantOptions.value = Array.isArray(result) ? result : []
|
||||
} catch (error: any) {
|
||||
assistantOptions.value = []
|
||||
feedback.msgError(error?.msg || '医助列表加载失败')
|
||||
} finally {
|
||||
assistantOptionsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function openAssignDialog(row: any) {
|
||||
currentActionPatient.value = row
|
||||
assignForm.assistant_id = Number(row.assistant_id) > 0 ? Number(row.assistant_id) : null
|
||||
assignForm.is_inherit = false
|
||||
assignDialogVisible.value = true
|
||||
if (assistantOptions.value.length === 0) {
|
||||
await loadAssistantOptions()
|
||||
}
|
||||
}
|
||||
|
||||
async function submitAssign() {
|
||||
const diagnosisId = Number(currentActionPatient.value?.diagnosis_id || currentActionPatient.value?.id)
|
||||
if (diagnosisId <= 0) {
|
||||
feedback.msgWarning('患者诊单信息不完整')
|
||||
return
|
||||
}
|
||||
if (!assignForm.assistant_id) {
|
||||
feedback.msgWarning('请选择医助')
|
||||
return
|
||||
}
|
||||
|
||||
assignLoading.value = true
|
||||
try {
|
||||
await myPatientAssign({
|
||||
id: diagnosisId,
|
||||
assistant_id: Number(assignForm.assistant_id),
|
||||
is_inherit: assignForm.is_inherit ? 1 : 0
|
||||
})
|
||||
feedback.msgSuccess('指派成功')
|
||||
assignDialogVisible.value = false
|
||||
await getLists()
|
||||
} catch (error: any) {
|
||||
feedback.msgError(error?.msg || '指派失败')
|
||||
} finally {
|
||||
assignLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const validateIdCard = (_rule: any, value: string, callback: (error?: Error) => void) => {
|
||||
const idCard = String(value || '').trim()
|
||||
if (!idCard) {
|
||||
callback(new Error('请输入身份证号'))
|
||||
return
|
||||
}
|
||||
const valid18 = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(idCard)
|
||||
const valid15 = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/.test(idCard)
|
||||
callback(valid18 || valid15 ? undefined : new Error('请输入15或18位有效身份证号'))
|
||||
}
|
||||
|
||||
const fillIdCardRules = {
|
||||
id_card: [{ required: true, validator: validateIdCard, trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function openFillIdCardDialog(row: any) {
|
||||
fillIdCardForm.id = Number(row.diagnosis_id || row.id)
|
||||
fillIdCardForm.patient_name = String(row.patient_name || '')
|
||||
fillIdCardForm.id_card = ''
|
||||
fillIdCardDialogVisible.value = true
|
||||
}
|
||||
|
||||
function resetFillIdCardForm() {
|
||||
fillIdCardForm.id = 0
|
||||
fillIdCardForm.patient_name = ''
|
||||
fillIdCardForm.id_card = ''
|
||||
fillIdCardFormRef.value?.clearValidate?.()
|
||||
}
|
||||
|
||||
async function submitFillIdCard() {
|
||||
if (fillIdCardLoading.value) return
|
||||
try {
|
||||
await fillIdCardFormRef.value?.validate?.()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
fillIdCardLoading.value = true
|
||||
try {
|
||||
await myPatientFillIdCard({
|
||||
id: fillIdCardForm.id,
|
||||
id_card: fillIdCardForm.id_card.trim()
|
||||
})
|
||||
feedback.msgSuccess('补全成功,年龄已自动更新')
|
||||
fillIdCardDialogVisible.value = false
|
||||
await getLists()
|
||||
} catch (error: any) {
|
||||
feedback.msgError(error?.msg || '补全身份证失败')
|
||||
} finally {
|
||||
fillIdCardLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function canShowDiagnosisQRCode(row: any) {
|
||||
return canBookAppointment.value
|
||||
&& Number(row.appointment_id) > 0
|
||||
@@ -850,6 +1078,25 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dialog-patient-name {
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.inherit-field {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
|
||||
small {
|
||||
color: #98a2b3;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -66,6 +66,22 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">今日新增业绩</div>
|
||||
<div class="metric-value metric-value--money">
|
||||
{{ formatMoney(dashboard.performance.today_amount) }}
|
||||
</div>
|
||||
<div class="metric-foot" :class="comparisonClass(dashboard.performance.today_compare_rate)">
|
||||
<template v-if="dashboard.performance.today_compare_rate !== null">
|
||||
<el-icon v-if="dashboard.performance.today_compare_rate >= 0"><CaretTop /></el-icon>
|
||||
<el-icon v-else><CaretBottom /></el-icon>
|
||||
{{ dashboard.performance.today_compare_label }}
|
||||
{{ formatSignedPercent(dashboard.performance.today_compare_rate) }}
|
||||
</template>
|
||||
<template v-else>昨日暂无可比数据</template>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">昨日新增业绩</div>
|
||||
<div class="metric-value metric-value--money">
|
||||
@@ -336,6 +352,9 @@ const createInitialDashboard = () => ({
|
||||
month_amount: 0,
|
||||
month_compare_rate: null as number | null,
|
||||
month_compare_label: '',
|
||||
today_amount: 0,
|
||||
today_compare_rate: null as number | null,
|
||||
today_compare_label: '',
|
||||
yesterday_amount: 0,
|
||||
yesterday_compare_rate: null as number | null,
|
||||
yesterday_compare_label: '',
|
||||
@@ -347,6 +366,7 @@ const createInitialDashboard = () => ({
|
||||
interview_count: 0,
|
||||
completed_order_count: 0,
|
||||
completed_order_amount: 0,
|
||||
paid_appointment_count: 0,
|
||||
paid_appointment_rate: 0,
|
||||
interview_receive_rate: 0,
|
||||
comparisons: {
|
||||
@@ -447,7 +467,7 @@ const todayMetrics = computed(() => [
|
||||
key: 'appointmentRate',
|
||||
label: '付费挂号率',
|
||||
value: formatPercent(dashboard.today.paid_appointment_rate),
|
||||
hint: '付费挂号数 / 加粉数',
|
||||
hint: `付费挂号 ${formatNumber(dashboard.today.paid_appointment_count)} / 加粉数`,
|
||||
comparisons: [dashboard.today.comparisons.paid_appointment_rate],
|
||||
},
|
||||
{
|
||||
@@ -750,7 +770,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.performance-strip {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user