Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08290b329e | ||
|
|
1d66f84bd3 | ||
|
|
a2d9c1a03f | ||
|
|
d79db88349 | ||
|
|
9f5fb650af | ||
|
|
15b4339c90 | ||
|
|
7ba9dabdb4 | ||
|
|
611f3dcd5c | ||
|
|
d3388b160e | ||
|
|
f83d4c06cb | ||
|
|
4941ea3e21 | ||
|
|
e35696e153 | ||
|
|
e1df584ed5 | ||
|
|
928f75e016 | ||
|
|
37fa160c24 | ||
|
|
0eaacbb4ce | ||
|
|
1e8b5c4646 | ||
|
|
fabaa84373 | ||
|
|
da017bdf20 | ||
|
|
5d94ffab3e | ||
|
|
796ca12fb8 | ||
|
|
c644f2554f | ||
|
|
e83dac756d | ||
|
|
0c1709d589 | ||
|
|
a3892001d3 | ||
|
|
7add364e20 | ||
|
|
0ebea74fde | ||
|
|
15cf6324f6 |
@@ -66,6 +66,16 @@ export function appointmentLists(params: any) {
|
||||
return request.get({ url: '/doctor.appointment/lists', params })
|
||||
}
|
||||
|
||||
/** 后台编辑挂号(预约日期/时段/类型/状态/备注/医助) */
|
||||
export function appointmentAdminEdit(params: any) {
|
||||
return request.post({ url: '/doctor.appointment/edit', params })
|
||||
}
|
||||
|
||||
/** 批量修改挂号渠道来源(与 edit 共用 doctor.appointment/edit 权限) */
|
||||
export function appointmentBatchEditChannel(params: any) {
|
||||
return request.post({ url: '/doctor.appointment/batchEditChannel', params })
|
||||
}
|
||||
|
||||
// 获取挂号详情
|
||||
export function appointmentDetail(params: any) {
|
||||
return request.get({ url: '/doctor.appointment/detail', params })
|
||||
|
||||
@@ -103,3 +103,79 @@ export function doctorDailyStatsOverview(params: {
|
||||
}) {
|
||||
return request.get({ url: '/stats.doctorDailyStats/overview', params })
|
||||
}
|
||||
|
||||
/** 提成结算接口计算较重,单独放宽超时(默认多为 60s) */
|
||||
const COMMISSION_SETTLEMENT_TIMEOUT_MS = 120000
|
||||
|
||||
/** 提成结算业绩(独立于业绩看板 yejiStats) */
|
||||
export function commissionSettlementOverview(params: {
|
||||
settlement_month: string
|
||||
/** 与 tcm.prescriptionOrder/lists 同源:create_time between */
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
/** 默认 3=履约完成,与列表 fulfillment_status 一致 */
|
||||
fulfillment_status?: number
|
||||
/** 传 1 时仅统计 is_system_auto=1;显式时段下默认不传(含手动) */
|
||||
require_system_auto_prescription?: 0 | 1
|
||||
dept_ids?: number[] | string
|
||||
channel_code?: string
|
||||
}) {
|
||||
return request.get(
|
||||
{ url: '/stats.commissionSettlement/overview', params, timeout: COMMISSION_SETTLEMENT_TIMEOUT_MS },
|
||||
{ ignoreCancelToken: true }
|
||||
)
|
||||
}
|
||||
|
||||
export function commissionSettlementDeptOptions() {
|
||||
return request.get({ url: '/stats.commissionSettlement/deptOptions' })
|
||||
}
|
||||
|
||||
export function commissionSettlementChannelOptions() {
|
||||
return request.get({ url: '/stats.commissionSettlement/channelOptions' })
|
||||
}
|
||||
|
||||
/** 提成核对:订单明细分页 bucket: 空|current|deferred;appt_channel_value 可与 assistant_id/doctor_id 组合(0=未匹配挂号渠道) */
|
||||
export function commissionSettlementOrderLines(params: {
|
||||
settlement_month: string
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
fulfillment_status?: number
|
||||
require_system_auto_prescription?: 0 | 1
|
||||
dept_ids?: number[] | string
|
||||
channel_code?: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
bucket?: string
|
||||
assistant_id?: number
|
||||
doctor_id?: number
|
||||
appt_channel_value?: number
|
||||
}) {
|
||||
return request.get(
|
||||
{ url: '/stats.commissionSettlement/orderLines', params, timeout: COMMISSION_SETTLEMENT_TIMEOUT_MS },
|
||||
{ ignoreCancelToken: true }
|
||||
)
|
||||
}
|
||||
|
||||
export function commissionSettlementConfirmStatus(params: {
|
||||
settlement_month: string
|
||||
dept_ids?: number[] | string
|
||||
channel_code?: string
|
||||
}) {
|
||||
return request.get({ url: '/stats.commissionSettlement/confirmStatus', params })
|
||||
}
|
||||
|
||||
export function commissionSettlementSaveReconcile(params: Record<string, any>) {
|
||||
return request.post({ url: '/stats.commissionSettlement/saveReconcile', params })
|
||||
}
|
||||
|
||||
export function commissionSettlementConfirmFinalize(params: Record<string, any>) {
|
||||
return request.post(
|
||||
{ url: '/stats.commissionSettlement/confirmFinalize', params, timeout: COMMISSION_SETTLEMENT_TIMEOUT_MS },
|
||||
{ ignoreCancelToken: true }
|
||||
)
|
||||
}
|
||||
|
||||
/** 撤回「确定本期业绩」:清除顺延结转,状态变为可再次核对/确定 */
|
||||
export function commissionSettlementConfirmRevoke(params: Record<string, any>) {
|
||||
return request.post({ url: '/stats.commissionSettlement/confirmRevoke', params })
|
||||
}
|
||||
|
||||
@@ -319,6 +319,16 @@ export function prescriptionEdit(params: any) {
|
||||
return request.post({ url: '/tcm.prescription/edit', params })
|
||||
}
|
||||
|
||||
/** 仅修正处方笺患者姓名、手机号与性别(不改审核状态),写入业务订单日志表 */
|
||||
export function prescriptionPatchPatient(params: {
|
||||
id: number
|
||||
patient_name: string
|
||||
phone: string
|
||||
gender: number
|
||||
}) {
|
||||
return request.post({ url: '/tcm.prescription/patchPatient', params })
|
||||
}
|
||||
|
||||
// 删除处方
|
||||
export function prescriptionDelete(params: { id: number }) {
|
||||
return request.post({ url: '/tcm.prescription/delete', params })
|
||||
@@ -363,6 +373,11 @@ export function prescriptionOrderLists(params: any) {
|
||||
return request.get({ url: '/tcm.prescriptionOrder/lists', params })
|
||||
}
|
||||
|
||||
/** 处方业务订单导出(export=1 预估条数,export=2 下载 Excel) */
|
||||
export function prescriptionOrderExport(params: any) {
|
||||
return request.get({ url: '/tcm.prescriptionOrder/export', params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 诊单下可关联的支付单(已支付 zyt_order;已占用且未撤回的会排除;编辑时传 prescription_order_id 保留当前单已选)。
|
||||
* 服务端仅返回创建时间在 2026-04-20(含)之后的支付单;编辑时本单已关联的旧单仍会出现在列表中。
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<el-button>导出</el-button>
|
||||
</template>
|
||||
<div>
|
||||
<p v-if="props.exportHint" class="text-sm text-gray-500 mb-3 leading-relaxed">{{ props.exportHint }}</p>
|
||||
<el-form ref="formRef" :model="formData" label-width="120px" :rules="formRules">
|
||||
<el-form-item label="数据量:">
|
||||
预计导出{{ exportData.count }}条数据, 共{{ exportData.sum_page }}页,每页{{
|
||||
@@ -79,6 +80,11 @@ const props = defineProps({
|
||||
fetchFun: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
/** 可选:导出弹窗内提示文案(如说明与列表筛选一致) */
|
||||
exportHint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const popupRef = shallowRef<InstanceType<typeof Popup>>()
|
||||
|
||||
@@ -0,0 +1,582 @@
|
||||
<template>
|
||||
<div class="guahao-list">
|
||||
<el-card class="!border-none" shadow="never">
|
||||
<el-form class="mb-[-16px]" :model="queryParams" :inline="true" @submit.prevent>
|
||||
<el-form-item label="预约日期">
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.start_date"
|
||||
v-model:endTime="queryParams.end_date"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="患者姓名">
|
||||
<el-input
|
||||
v-model="queryParams.patient_name"
|
||||
placeholder="模糊搜索"
|
||||
clearable
|
||||
class="!w-[160px]"
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="医生">
|
||||
<el-input
|
||||
v-model="queryParams.doctor_name"
|
||||
placeholder="模糊搜索"
|
||||
clearable
|
||||
class="!w-[140px]"
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="医助">
|
||||
<el-select
|
||||
v-model="queryParams.assistant_id"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-[180px]"
|
||||
>
|
||||
<el-option
|
||||
v-for="a in assistantOptions"
|
||||
:key="a.id"
|
||||
:label="a.name + (a.account ? ` (${a.account})` : '')"
|
||||
:value="a.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable class="!w-[130px]">
|
||||
<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="渠道">
|
||||
<el-select
|
||||
v-model="queryParams.channel_source"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-[200px]"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
:key="String(item.value)"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||
<el-button @click="resetFilter">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<div class="flex flex-wrap items-center gap-3 mb-4">
|
||||
<el-button
|
||||
v-perms="['doctor.appointment/edit']"
|
||||
type="primary"
|
||||
:disabled="selectedRows.length === 0"
|
||||
@click="openBatchChannelDialog"
|
||||
>
|
||||
批量修改渠道
|
||||
</el-button>
|
||||
<span v-if="selectedRows.length > 0" class="text-sm text-gray-500">
|
||||
已选 {{ selectedRows.length }} 条
|
||||
</span>
|
||||
</div>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="pager.loading"
|
||||
row-key="id"
|
||||
:data="pager.lists"
|
||||
size="large"
|
||||
stripe
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" reserve-selection />
|
||||
<el-table-column label="ID" prop="id" width="72" align="center" />
|
||||
<el-table-column label="诊单/患者" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<div class="text-sm">
|
||||
<div class="font-medium">{{ row.patient_name || '—' }}</div>
|
||||
<div class="text-gray-500">{{ maskPhone(row.patient_phone) }}</div>
|
||||
<div class="text-xs text-gray-400">诊单 #{{ row.diagnosis_id ?? row.patient_id }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="医生" prop="doctor_name" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="医助" prop="assistant_name" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="预约时间" min-width="128">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.appointment_date }}</div>
|
||||
<div class="text-gray-500">{{ formatHm(row.appointment_time) }} · {{ row.period_desc }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="appointment_type_desc" width="100" />
|
||||
<el-table-column label="渠道" min-width="130" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<div class="text-sm">{{ row.channel_source_desc || '—' }}</div>
|
||||
<div v-if="row.channel_source_detail" class="text-xs text-gray-400 truncate">
|
||||
{{ row.channel_source_detail }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="96" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="
|
||||
row.status === 1 ? 'success' : row.status === 2 ? 'info' : row.status === 3 ? 'primary' : 'danger'
|
||||
"
|
||||
size="small"
|
||||
effect="light"
|
||||
>
|
||||
{{ row.status_desc }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="100" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button v-perms="['doctor.appointment/edit']" type="primary" link @click="openEdit(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="editVisible" title="编辑挂号" width="560px" destroy-on-close @closed="resetEditForm">
|
||||
<el-form :model="editForm" label-width="96px">
|
||||
<el-form-item label="预约日期" required>
|
||||
<el-date-picker
|
||||
v-model="editForm.appointment_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="预约时间" required>
|
||||
<el-time-picker
|
||||
v-model="editForm.appointment_time"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm"
|
||||
placeholder="时间"
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="时段" required>
|
||||
<el-radio-group v-model="editForm.period">
|
||||
<el-radio-button label="morning">上午</el-radio-button>
|
||||
<el-radio-button label="afternoon">下午</el-radio-button>
|
||||
<el-radio-button label="all">全天</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="问诊类型" required>
|
||||
<el-select v-model="editForm.appointment_type" class="!w-full">
|
||||
<el-option label="视频问诊" value="video" />
|
||||
<el-option label="图文问诊" value="text" />
|
||||
<el-option label="电话问诊" value="phone" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道来源" required>
|
||||
<el-select
|
||||
v-model="editForm.channel_source"
|
||||
placeholder="请选择渠道来源"
|
||||
filterable
|
||||
class="!w-full"
|
||||
@change="onChannelSourceChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
:key="String(item.value)"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="needsChannelSourceDetail" label="自媒体补充" required>
|
||||
<el-input
|
||||
v-model="editForm.channel_source_detail"
|
||||
maxlength="128"
|
||||
show-word-limit
|
||||
placeholder="请输入自媒体相关补充内容"
|
||||
clearable
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" required>
|
||||
<el-select v-model="editForm.status" class="!w-full">
|
||||
<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="挂号医助">
|
||||
<el-select
|
||||
v-model="editForm.assistant_id"
|
||||
placeholder="不修改可留空"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-full"
|
||||
>
|
||||
<el-option
|
||||
v-for="a in assistantOptions"
|
||||
:key="a.id"
|
||||
:label="a.name + (a.account ? ` (${a.account})` : '')"
|
||||
:value="a.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="editForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="editVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="editSaving" @click="submitEdit">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="batchChannelVisible"
|
||||
title="批量修改渠道"
|
||||
width="520px"
|
||||
destroy-on-close
|
||||
@closed="resetBatchChannelForm"
|
||||
>
|
||||
<p class="text-sm text-gray-600 mb-4">将对已选 {{ selectedRows.length }} 条挂号写入同一渠道来源(仅改渠道,不改其它字段)。</p>
|
||||
<el-form :model="batchChannelForm" label-width="96px">
|
||||
<el-form-item label="渠道来源" required>
|
||||
<el-select
|
||||
v-model="batchChannelForm.channel_source"
|
||||
placeholder="请选择渠道来源"
|
||||
filterable
|
||||
class="!w-full"
|
||||
@change="onBatchChannelSourceChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
:key="String(item.value)"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="needsBatchChannelSourceDetail" label="自媒体补充" required>
|
||||
<el-input
|
||||
v-model="batchChannelForm.channel_source_detail"
|
||||
maxlength="128"
|
||||
show-word-limit
|
||||
placeholder="请输入自媒体相关补充内容"
|
||||
clearable
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="batchChannelVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="batchChannelSaving" @click="submitBatchChannel">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="consumerPrescriptionGuahao">
|
||||
import DaterangePicker from '@/components/daterange-picker/index.vue'
|
||||
import { getDictData } from '@/api/app'
|
||||
import { appointmentAdminEdit, appointmentBatchEditChannel, appointmentLists } from '@/api/doctor'
|
||||
import { getAssistants } from '@/api/tcm'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
const queryParams = reactive({
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
patient_name: '',
|
||||
doctor_name: '',
|
||||
assistant_id: undefined as number | undefined,
|
||||
status: '' as number | '',
|
||||
channel_source: '' as string
|
||||
})
|
||||
|
||||
const queryInit = {
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
patient_name: '',
|
||||
doctor_name: '',
|
||||
assistant_id: undefined as number | undefined,
|
||||
status: '' as number | '',
|
||||
channel_source: ''
|
||||
}
|
||||
|
||||
const { pager, getLists, resetPage } = usePaging({
|
||||
fetchFun: appointmentLists,
|
||||
params: queryParams
|
||||
})
|
||||
|
||||
const assistantOptions = ref<Array<{ id: number; name: string; account?: string }>>([])
|
||||
|
||||
const tableRef = ref<{ clearSelection?: () => void } | null>(null)
|
||||
const selectedRows = ref<Record<string, unknown>[]>([])
|
||||
|
||||
function onSelectionChange(rows: Record<string, unknown>[]) {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
/** 与诊单预约弹窗一致:这些字典 name 需填「自媒体补充」 */
|
||||
const CHANNEL_NAMES_REQUIRING_SELF_MEDIA_DETAIL = new Set([
|
||||
'自媒体4H',
|
||||
'自媒体3Q',
|
||||
'自媒体3H',
|
||||
'自媒体2H',
|
||||
'自媒体2Q'
|
||||
])
|
||||
|
||||
function channelNameRequiresSelfMediaDetail(name: string) {
|
||||
return CHANNEL_NAMES_REQUIRING_SELF_MEDIA_DETAIL.has(String(name ?? '').trim())
|
||||
}
|
||||
|
||||
const channelOptions = ref<Array<{ name: string; value: string }>>([])
|
||||
|
||||
const selectedChannelDictName = computed(() => {
|
||||
const v = editForm.channel_source
|
||||
if (v === '' || v == null) return ''
|
||||
const row = channelOptions.value.find((item: { value: string }) => String(item.value) === String(v))
|
||||
return row ? String(row.name ?? '').trim() : ''
|
||||
})
|
||||
|
||||
const needsChannelSourceDetail = computed(() => channelNameRequiresSelfMediaDetail(selectedChannelDictName.value))
|
||||
|
||||
const batchSelectedChannelDictName = computed(() => {
|
||||
const v = batchChannelForm.channel_source
|
||||
if (v === '' || v == null) return ''
|
||||
const row = channelOptions.value.find((item: { value: string }) => String(item.value) === String(v))
|
||||
return row ? String(row.name ?? '').trim() : ''
|
||||
})
|
||||
|
||||
const needsBatchChannelSourceDetail = computed(() =>
|
||||
channelNameRequiresSelfMediaDetail(batchSelectedChannelDictName.value)
|
||||
)
|
||||
|
||||
function onBatchChannelSourceChange(val: string) {
|
||||
const row = channelOptions.value.find((item: { value: string }) => String(item.value) === String(val))
|
||||
const name = row ? String(row.name ?? '').trim() : ''
|
||||
if (!channelNameRequiresSelfMediaDetail(name)) {
|
||||
batchChannelForm.channel_source_detail = ''
|
||||
}
|
||||
}
|
||||
|
||||
const batchChannelVisible = ref(false)
|
||||
const batchChannelSaving = ref(false)
|
||||
const batchChannelForm = reactive({
|
||||
channel_source: '' as string,
|
||||
channel_source_detail: '' as string
|
||||
})
|
||||
|
||||
function openBatchChannelDialog() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
feedback.msgWarning('请先勾选挂号记录')
|
||||
return
|
||||
}
|
||||
batchChannelForm.channel_source = ''
|
||||
batchChannelForm.channel_source_detail = ''
|
||||
batchChannelVisible.value = true
|
||||
}
|
||||
|
||||
function resetBatchChannelForm() {
|
||||
batchChannelForm.channel_source = ''
|
||||
batchChannelForm.channel_source_detail = ''
|
||||
}
|
||||
|
||||
async function submitBatchChannel() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
feedback.msgWarning('请先勾选挂号记录')
|
||||
return
|
||||
}
|
||||
if (!batchChannelForm.channel_source) {
|
||||
feedback.msgError('请选择渠道来源')
|
||||
return
|
||||
}
|
||||
if (needsBatchChannelSourceDetail.value && !batchChannelForm.channel_source_detail.trim()) {
|
||||
feedback.msgError('请填写自媒体补充说明')
|
||||
return
|
||||
}
|
||||
batchChannelSaving.value = true
|
||||
try {
|
||||
const ids = selectedRows.value.map((r) => Number(r.id))
|
||||
await appointmentBatchEditChannel({
|
||||
ids,
|
||||
channel_source: batchChannelForm.channel_source,
|
||||
channel_source_detail: batchChannelForm.channel_source_detail.trim()
|
||||
})
|
||||
feedback.msgSuccess('批量修改成功')
|
||||
batchChannelVisible.value = false
|
||||
tableRef.value?.clearSelection?.()
|
||||
selectedRows.value = []
|
||||
getLists()
|
||||
} finally {
|
||||
batchChannelSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onChannelSourceChange(val: string) {
|
||||
const row = channelOptions.value.find((item: { value: string }) => String(item.value) === String(val))
|
||||
const name = row ? String(row.name ?? '').trim() : ''
|
||||
if (!channelNameRequiresSelfMediaDetail(name)) {
|
||||
editForm.channel_source_detail = ''
|
||||
}
|
||||
}
|
||||
|
||||
const editVisible = ref(false)
|
||||
const editSaving = ref(false)
|
||||
const editForm = reactive({
|
||||
id: 0,
|
||||
appointment_date: '',
|
||||
appointment_time: '' as string,
|
||||
period: 'morning' as 'morning' | 'afternoon' | 'all',
|
||||
appointment_type: 'video',
|
||||
status: 1,
|
||||
remark: '',
|
||||
assistant_id: undefined as number | undefined,
|
||||
channel_source: '' as string,
|
||||
channel_source_detail: '' as string
|
||||
})
|
||||
|
||||
function maskPhone(phone?: string) {
|
||||
const p = String(phone || '')
|
||||
if (p.length >= 11) {
|
||||
return p.slice(0, 3) + '****' + p.slice(-4)
|
||||
}
|
||||
return p || '—'
|
||||
}
|
||||
|
||||
function formatHm(t?: string) {
|
||||
const s = String(t || '')
|
||||
return s.length >= 5 ? s.slice(0, 5) : s || '—'
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
Object.assign(queryParams, queryInit)
|
||||
resetPage()
|
||||
}
|
||||
|
||||
function rowPeriod(row: Record<string, unknown>): 'morning' | 'afternoon' | 'all' {
|
||||
const v = String(row.period ?? row.type ?? 'morning')
|
||||
if (v === 'afternoon' || v === 'all') {
|
||||
return v
|
||||
}
|
||||
return 'morning'
|
||||
}
|
||||
|
||||
async function loadAssistants() {
|
||||
try {
|
||||
const res: any = await getAssistants()
|
||||
assistantOptions.value = res?.lists ?? res ?? []
|
||||
} catch {
|
||||
assistantOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function loadChannelOptions() {
|
||||
try {
|
||||
const data: any = await getDictData({ type: 'channels' })
|
||||
const rows = (data?.channels || []).filter((row: { status?: number }) => row.status !== 0)
|
||||
rows.sort((a: { sort?: number; id?: number }, b: { sort?: number; id?: number }) => {
|
||||
const ds = Number(b?.sort ?? 0) - Number(a?.sort ?? 0)
|
||||
if (ds !== 0) return ds
|
||||
return Number(b?.id ?? 0) - Number(a?.id ?? 0)
|
||||
})
|
||||
channelOptions.value = rows
|
||||
} catch {
|
||||
channelOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function openEdit(row: Record<string, unknown>) {
|
||||
editForm.id = Number(row.id)
|
||||
editForm.appointment_date = String(row.appointment_date || '')
|
||||
editForm.appointment_time = formatHm(String(row.appointment_time || ''))
|
||||
editForm.period = rowPeriod(row)
|
||||
editForm.appointment_type = String(row.appointment_type || 'video')
|
||||
editForm.status = Number(row.status)
|
||||
editForm.remark = String(row.remark || '')
|
||||
const raw = row.appointment_assistant_id
|
||||
editForm.assistant_id =
|
||||
raw !== undefined && raw !== null && raw !== '' ? Number(raw) : undefined
|
||||
editForm.channel_source = String(row.channel_source ?? row.channels ?? '')
|
||||
editForm.channel_source_detail = String(row.channel_source_detail ?? '')
|
||||
editVisible.value = true
|
||||
}
|
||||
|
||||
function resetEditForm() {
|
||||
editForm.id = 0
|
||||
editForm.appointment_date = ''
|
||||
editForm.appointment_time = ''
|
||||
editForm.period = 'morning'
|
||||
editForm.appointment_type = 'video'
|
||||
editForm.status = 1
|
||||
editForm.remark = ''
|
||||
editForm.assistant_id = undefined
|
||||
editForm.channel_source = ''
|
||||
editForm.channel_source_detail = ''
|
||||
}
|
||||
|
||||
async function submitEdit() {
|
||||
if (!editForm.appointment_date) {
|
||||
feedback.msgError('请选择预约日期')
|
||||
return
|
||||
}
|
||||
if (!editForm.appointment_time) {
|
||||
feedback.msgError('请选择预约时间')
|
||||
return
|
||||
}
|
||||
if (!editForm.channel_source) {
|
||||
feedback.msgError('请选择渠道来源')
|
||||
return
|
||||
}
|
||||
if (needsChannelSourceDetail.value && !editForm.channel_source_detail.trim()) {
|
||||
feedback.msgError('请填写自媒体补充说明')
|
||||
return
|
||||
}
|
||||
editSaving.value = true
|
||||
try {
|
||||
const payload: Record<string, unknown> = {
|
||||
id: editForm.id,
|
||||
appointment_date: editForm.appointment_date,
|
||||
appointment_time: editForm.appointment_time,
|
||||
period: editForm.period,
|
||||
appointment_type: editForm.appointment_type,
|
||||
status: editForm.status,
|
||||
remark: editForm.remark,
|
||||
assistant_id: editForm.assistant_id ?? '',
|
||||
channel_source: editForm.channel_source,
|
||||
channel_source_detail: editForm.channel_source_detail.trim()
|
||||
}
|
||||
await appointmentAdminEdit(payload)
|
||||
feedback.msgSuccess('保存成功')
|
||||
editVisible.value = false
|
||||
getLists()
|
||||
} finally {
|
||||
editSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadAssistants()
|
||||
loadChannelOptions()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
getLists()
|
||||
})
|
||||
|
||||
getLists()
|
||||
</script>
|
||||
@@ -216,6 +216,15 @@
|
||||
<el-button type="primary" link @click="handleView(row)" v-perms="['cf.prescription/read']">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="Number(row.void_status) !== 1"
|
||||
type="primary"
|
||||
link
|
||||
v-perms="['tcm.prescription/patchPatient']"
|
||||
@click="openPatchPatientDialog(row)"
|
||||
>
|
||||
改姓名手机
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!Number(row.has_prescription_order) && Number(row.void_status) !== 1"
|
||||
type="success"
|
||||
@@ -548,12 +557,20 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者姓名" prop="patient_name">
|
||||
<el-input v-model="editForm.patient_name" placeholder="请输入患者姓名" />
|
||||
<el-input
|
||||
v-model="editForm.patient_name"
|
||||
placeholder="请输入患者姓名"
|
||||
:disabled="editMode === 'edit'"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="门诊号" prop="visit_no">
|
||||
<el-input v-model="editForm.visit_no" placeholder="自动生成或手动输入" />
|
||||
<el-input
|
||||
v-model="editForm.visit_no"
|
||||
placeholder="自动生成或手动输入"
|
||||
:disabled="editMode === 'edit'"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -972,6 +989,48 @@
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 修正处方笺姓名 / 手机号(zyt_tcm_prescription,写入订单日志) -->
|
||||
<el-dialog
|
||||
v-model="patchPatientVisible"
|
||||
title="修正姓名、性别与手机号"
|
||||
width="440px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
@closed="resetPatchPatientForm"
|
||||
>
|
||||
<el-form
|
||||
ref="patchPatientFormRef"
|
||||
:model="patchPatientForm"
|
||||
:rules="patchPatientRules"
|
||||
label-width="88px"
|
||||
>
|
||||
<el-form-item label="处方编号">
|
||||
<span class="text-gray-700">#{{ patchPatientForm.id || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="患者姓名" prop="patient_name">
|
||||
<el-input v-model="patchPatientForm.patient_name" maxlength="50" show-word-limit placeholder="处方笺展示姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-radio-group v-model="patchPatientForm.gender">
|
||||
<el-radio :label="1">男</el-radio>
|
||||
<el-radio :label="0">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="patchPatientForm.phone" maxlength="20" placeholder="处方笺展示手机号" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<p class="text-xs text-gray-500 -mt-2 mb-2">
|
||||
仅更新处方表 patient_name、gender、phone,不改变审核状态;记录写入业务订单操作日志(有关联订单时可在此订单日志中查看)。
|
||||
</p>
|
||||
<template #footer>
|
||||
<el-button @click="patchPatientVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="patchPatientSubmitLoading" @click="submitPatchPatient">
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 从消费者处方创建业务订单(zyt_tcm_prescription_order,与支付单 zyt_order 分离) -->
|
||||
<el-dialog
|
||||
v-model="createOrderVisible"
|
||||
@@ -1436,6 +1495,7 @@ import {
|
||||
prescriptionLists,
|
||||
prescriptionAdd,
|
||||
prescriptionEdit,
|
||||
prescriptionPatchPatient,
|
||||
prescriptionDelete,
|
||||
prescriptionAudit,
|
||||
prescriptionDetail,
|
||||
@@ -1523,6 +1583,66 @@ const auditTargetId = ref(0)
|
||||
const auditRemark = ref('')
|
||||
const auditLoading = ref(false)
|
||||
|
||||
/** 修正处方笺显示用姓名 / 手机号 */
|
||||
const patchPatientVisible = ref(false)
|
||||
const patchPatientSubmitLoading = ref(false)
|
||||
const patchPatientFormRef = ref<FormInstance>()
|
||||
const patchPatientForm = reactive({
|
||||
id: 0,
|
||||
patient_name: '',
|
||||
gender: 1 as 0 | 1,
|
||||
phone: ''
|
||||
})
|
||||
const patchPatientRules: FormRules = {
|
||||
patient_name: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
|
||||
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
||||
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function openPatchPatientDialog(row: Record<string, unknown>) {
|
||||
patchPatientForm.id = Number(row.id) || 0
|
||||
patchPatientForm.patient_name = String(row.patient_name ?? '').trim()
|
||||
const g = Number(row.gender)
|
||||
patchPatientForm.gender = g === 0 || g === 1 ? (g as 0 | 1) : 1
|
||||
patchPatientForm.phone = String(row.phone ?? '').trim()
|
||||
patchPatientVisible.value = true
|
||||
}
|
||||
|
||||
function resetPatchPatientForm() {
|
||||
patchPatientForm.id = 0
|
||||
patchPatientForm.patient_name = ''
|
||||
patchPatientForm.gender = 1
|
||||
patchPatientForm.phone = ''
|
||||
patchPatientFormRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
async function submitPatchPatient() {
|
||||
const form = patchPatientFormRef.value
|
||||
if (!form) return
|
||||
try {
|
||||
await form.validate()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
patchPatientSubmitLoading.value = true
|
||||
try {
|
||||
await prescriptionPatchPatient({
|
||||
id: patchPatientForm.id,
|
||||
patient_name: patchPatientForm.patient_name.trim(),
|
||||
phone: patchPatientForm.phone.trim(),
|
||||
gender: patchPatientForm.gender
|
||||
})
|
||||
feedback.msgSuccess('已保存')
|
||||
patchPatientVisible.value = false
|
||||
getLists()
|
||||
} catch (e: unknown) {
|
||||
const msg = e && typeof e === 'object' && 'msg' in e ? String((e as { msg?: string }).msg) : ''
|
||||
feedback.msgError(msg || '保存失败')
|
||||
} finally {
|
||||
patchPatientSubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 从消费者处方创建订单 */
|
||||
const createOrderVisible = ref(false)
|
||||
/** 创建订单分步向导:0 患者与收货 / 1 服务与支付单 / 2 金额与确认 */
|
||||
|
||||
@@ -263,9 +263,22 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="w-[160px]" label="服务渠道">
|
||||
<el-select v-model="queryParams.service_channel" clearable placeholder="全部" class="!w-full">
|
||||
<el-option label="未指派" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
<export-data
|
||||
v-perms="['tcm.prescriptionOrder/export']"
|
||||
class="ml-2.5"
|
||||
:fetch-fun="prescriptionOrderExport"
|
||||
:params="prescriptionOrderExportParams"
|
||||
:page-size="pager.size"
|
||||
export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:按诊单关联患者取最近一条挂号的渠道字典与细分。"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@@ -426,7 +439,37 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="开方人" prop="doctor_name" width="90" show-overflow-tooltip />
|
||||
<el-table-column label="创建人" prop="creator_name" width="90" show-overflow-tooltip />
|
||||
|
||||
<el-table-column min-width="128" show-overflow-tooltip>
|
||||
<template #header>
|
||||
<span>二中心·指派</span>
|
||||
<el-tooltip
|
||||
placement="top"
|
||||
max-width="320"
|
||||
content="与诊单「指派记录」快照一致:仅当本条业务单在操作瞬间为快照中的「关联业务单」(创建人+创建时间命中)时显示。绿色=新医助在名称含「二中心」的部门及其下级;灰色=系统自动释放诊单医助。"
|
||||
>
|
||||
<el-icon class="ml-0.5 align-middle text-gray-400 cursor-help"><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<template v-if="!Number(row.po_assign_snapshot_hit)">
|
||||
<span class="text-gray-400">—</span>
|
||||
</template>
|
||||
<template v-else-if="Number(row.po_assign_is_release)">
|
||||
<el-tag type="info" size="small">已释放</el-tag>
|
||||
</template>
|
||||
<template v-else-if="Number(row.po_assign_to_er_center)">
|
||||
<el-tag type="success" size="small">
|
||||
{{ row.po_assign_to_assistant_name || ('医助#' + row.po_assign_to_assistant_id) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="warning" size="small" effect="plain">
|
||||
非二中心 {{ row.po_assign_to_assistant_name || ('医助#' + row.po_assign_to_assistant_id) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="310" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center gap-1 flex-wrap">
|
||||
@@ -452,7 +495,7 @@
|
||||
>修改单号</el-button>
|
||||
<el-button
|
||||
v-if="canShipRow(row)"
|
||||
v-perms="['tcm.prescriptionOrder/ship']"
|
||||
v-perms="['tcm.prescriptionOrder/ship', 'tcm.prescriptionOrder/submitGancaoRecipel']"
|
||||
type="primary"
|
||||
link
|
||||
@click="openShip(row)"
|
||||
@@ -563,7 +606,7 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canShipRow(detailData)"
|
||||
v-perms="['tcm.prescriptionOrder/ship']"
|
||||
v-perms="['tcm.prescriptionOrder/ship', 'tcm.prescriptionOrder/submitGancaoRecipel']"
|
||||
type="primary"
|
||||
size="small"
|
||||
plain
|
||||
@@ -621,6 +664,41 @@
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<!-- 物流轨迹含拒收/退回等:抽屉内强提示 -->
|
||||
<el-alert
|
||||
v-if="detailLogisticsUrgent.show && String(detailData?.tracking_number || '').trim()"
|
||||
type="error"
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4 !items-start ring-2 ring-red-600/90 shadow-lg"
|
||||
title="物流异常:轨迹中出现拒收 / 退回等字样,请优先跟进处理"
|
||||
>
|
||||
<template #default>
|
||||
<div class="text-sm leading-relaxed space-y-1.5">
|
||||
<div v-if="detailLogisticsUrgent.keywords.length" class="flex flex-wrap gap-1.5 items-center">
|
||||
<span class="text-red-100 text-xs">命中关键词:</span>
|
||||
<el-tag
|
||||
v-for="k in detailLogisticsUrgent.keywords"
|
||||
:key="k"
|
||||
type="danger"
|
||||
effect="plain"
|
||||
size="small"
|
||||
class="!border-white/40 !text-white"
|
||||
>
|
||||
{{ k }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<ul v-if="detailLogisticsUrgent.lines.length" class="list-disc pl-5 space-y-1 text-red-50">
|
||||
<li v-for="(line, idx) in detailLogisticsUrgent.lines" :key="idx">{{ line }}</li>
|
||||
</ul>
|
||||
<div class="text-xs text-red-100/90 pt-1 border-t border-white/15">
|
||||
请核实是否需改址重发、拦截退件或协调患者签收,并在备注中记录处理结果。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<div class="bg-gray-50/80 border border-gray-100 rounded-lg p-5 mb-5 shadow-sm">
|
||||
<el-steps :active="workflowActiveStep" align-center finish-status="success">
|
||||
<el-step title="业务订单创建" :description="formatTime(detailData.create_time)" />
|
||||
@@ -1126,10 +1204,25 @@
|
||||
<el-card
|
||||
v-if="String(detailData.tracking_number || '').trim()"
|
||||
shadow="never"
|
||||
class="po-panel po-panel-logistics border-gray-100"
|
||||
class="po-panel po-panel-logistics"
|
||||
:class="
|
||||
detailLogisticsUrgent.show
|
||||
? '!border-2 !border-red-500 shadow-md shadow-red-500/10'
|
||||
: 'border-gray-100'
|
||||
"
|
||||
>
|
||||
<template #header>
|
||||
<span class="font-medium text-[15px]">物流轨迹</span>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="font-medium text-[15px]">物流轨迹</span>
|
||||
<el-tag
|
||||
v-if="detailLogisticsUrgent.show"
|
||||
type="danger"
|
||||
effect="dark"
|
||||
size="small"
|
||||
>
|
||||
拒收/退回 · 待处理
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<p class="po-logistics-tip text-gray-400 text-xs mb-3 font-normal">
|
||||
优先读库,无记录时走快递100。顺丰会校验单号+电话(快递100要求完整手机号更易通过);默认预填订单收货手机,若仍提示验证码错误请改成与顺丰面单一致的号码。
|
||||
@@ -1200,7 +1293,14 @@
|
||||
:timestamp="t.time"
|
||||
placement="top"
|
||||
>
|
||||
<span :class="idx === 0 ? 'text-gray-800 font-medium' : 'text-gray-500'">{{ t.context }}</span>
|
||||
<span
|
||||
:class="[
|
||||
idx === 0 ? 'text-gray-800 font-medium' : 'text-gray-500',
|
||||
logisticsTraceLineUrgent(t.context)
|
||||
? '!text-red-700 font-semibold bg-red-50 px-1.5 py-0.5 rounded'
|
||||
: ''
|
||||
]"
|
||||
>{{ t.context }}</span>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else description="暂无轨迹节点记录" :image-size="64" />
|
||||
@@ -1282,7 +1382,7 @@
|
||||
<!-- 编辑(分步向导与「创建业务订单」弹窗统一) -->
|
||||
<el-dialog
|
||||
v-model="editVisible"
|
||||
title="编辑业务订单"
|
||||
:title="editGancaoLogisticsOnlyMode ? '修改物流信息(甘草订单)' : '编辑业务订单'"
|
||||
width="940px"
|
||||
top="4vh"
|
||||
:close-on-click-modal="false"
|
||||
@@ -1305,18 +1405,33 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<el-steps
|
||||
:active="editOrderStep"
|
||||
finish-status="success"
|
||||
align-center
|
||||
class="create-order-steps"
|
||||
<el-alert
|
||||
v-if="editGancaoLogisticsOnlyMode"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
title="订单已提交甘草,仅可修改快递单号与承运商;地址、金额等变更请先走取消流程。"
|
||||
>
|
||||
<el-step title="患者与收货" description="诊单与物流地址" />
|
||||
<el-step title="服务与支付单" description="套餐与关联收款" />
|
||||
<el-step title="金额与确认" description="费用与备注" />
|
||||
</el-steps>
|
||||
<template v-if="editGancaoDisplayNo" #default>
|
||||
<div class="text-sm text-gray-700 mt-1">甘草处方单号:{{ editGancaoDisplayNo }}</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<p class="create-order-step-lead">{{ editOrderStepLead }}</p>
|
||||
<template v-if="!editGancaoLogisticsOnlyMode">
|
||||
<el-steps
|
||||
:active="editOrderStep"
|
||||
finish-status="success"
|
||||
align-center
|
||||
class="create-order-steps"
|
||||
>
|
||||
<el-step title="患者与收货" description="诊单与物流地址" />
|
||||
<el-step title="服务与支付单" description="套餐与关联收款" />
|
||||
<el-step title="金额与确认" description="费用与备注" />
|
||||
</el-steps>
|
||||
|
||||
<p class="create-order-step-lead">{{ editOrderStepLead }}</p>
|
||||
</template>
|
||||
|
||||
<el-form
|
||||
ref="editFormRef"
|
||||
@@ -1325,6 +1440,24 @@
|
||||
label-width="108px"
|
||||
class="create-order-form"
|
||||
>
|
||||
<template v-if="editGancaoLogisticsOnlyMode">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="物流设置">
|
||||
<div class="flex gap-2 w-full">
|
||||
<el-select v-model="editForm.express_company" placeholder="承运商" class="w-[140px] shrink-0">
|
||||
<el-option label="自动识别" value="auto" />
|
||||
<el-option label="顺丰速运" value="sf" />
|
||||
<el-option label="京东快递" value="jd" />
|
||||
<el-option label="极兔速递" value="jt" />
|
||||
</el-select>
|
||||
<el-input v-model="editForm.tracking_number" maxlength="80" placeholder="快递单号" class="flex-1 min-w-0" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template v-if="!editGancaoLogisticsOnlyMode">
|
||||
<div v-show="editOrderStep === 0" class="create-order-step-panel">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@@ -1471,7 +1604,17 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="服务渠道">
|
||||
<el-input v-model="editForm.service_channel" placeholder="可选" maxlength="100" />
|
||||
<el-select
|
||||
v-model="editForm.service_channel"
|
||||
class="w-full"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
clearable
|
||||
placeholder="可选或自定义输入"
|
||||
>
|
||||
<el-option label="未指派" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -1648,12 +1791,13 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="create-order-footer">
|
||||
<div class="create-order-footer__hint">
|
||||
<div v-if="!editGancaoLogisticsOnlyMode" class="create-order-footer__hint">
|
||||
<span class="create-order-footer__step">第 {{ editOrderStep + 1 }} / 3 步</span>
|
||||
<span v-if="editOrderStep === 2 && editDepositMin > 0" class="create-order-footer__warn">
|
||||
订单金额与关联支付单合计须 ≥ ¥{{ formatMoney(editDepositMin) }}
|
||||
@@ -1661,17 +1805,19 @@
|
||||
</div>
|
||||
<div class="create-order-footer__actions">
|
||||
<el-button size="large" @click="editVisible = false">取消</el-button>
|
||||
<el-button v-if="editOrderStep > 0" size="large" @click="goEditOrderPrevStep">上一步</el-button>
|
||||
<template v-if="!editGancaoLogisticsOnlyMode">
|
||||
<el-button v-if="editOrderStep > 0" size="large" @click="goEditOrderPrevStep">上一步</el-button>
|
||||
<el-button
|
||||
v-if="editOrderStep < 2"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="goEditOrderNextStep"
|
||||
>
|
||||
下一步
|
||||
</el-button>
|
||||
</template>
|
||||
<el-button
|
||||
v-if="editOrderStep < 2"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="goEditOrderNextStep"
|
||||
>
|
||||
下一步
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="editOrderStep === 2"
|
||||
v-if="editGancaoLogisticsOnlyMode || editOrderStep === 2"
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="editSaving"
|
||||
@@ -1771,6 +1917,15 @@
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-alert
|
||||
v-if="shipForm.ship_mode === 'gancao'"
|
||||
title="甘草药方由甘草侧履约配送,无需填写快递单号。点击下方按钮将提交处方至甘草药管家(与「上传药方」相同)。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
/>
|
||||
<el-alert
|
||||
v-else
|
||||
title="确认后订单状态将变更为「已发货」,请核对快递信息无误。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
@@ -1784,27 +1939,33 @@
|
||||
<el-radio label="direct">药房直发</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="承运商">
|
||||
<el-select v-model="shipForm.express_company" class="w-full">
|
||||
<el-option label="自动识别" value="auto" />
|
||||
<el-option label="顺丰速运" value="sf" />
|
||||
<el-option label="京东快递" value="jd" />
|
||||
<el-option label="极兔速递" value="jt" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="快递单号">
|
||||
<el-input
|
||||
v-model="shipForm.tracking_number"
|
||||
maxlength="80"
|
||||
placeholder="请输入快递单号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<template v-if="shipForm.ship_mode === 'direct'">
|
||||
<el-form-item label="承运商">
|
||||
<el-select v-model="shipForm.express_company" class="w-full">
|
||||
<el-option label="自动识别" value="auto" />
|
||||
<el-option label="顺丰速运" value="sf" />
|
||||
<el-option label="京东快递" value="jd" />
|
||||
<el-option label="极兔速递" value="jt" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="快递单号">
|
||||
<el-input
|
||||
v-model="shipForm.tracking_number"
|
||||
maxlength="80"
|
||||
placeholder="请输入快递单号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="shipVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="shipSaving" @click="submitShip">
|
||||
确认发货
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="shipSaving || (shipForm.ship_mode === 'gancao' && gancaoSubmitId === shipRowId)"
|
||||
@click="submitShip"
|
||||
>
|
||||
{{ shipForm.ship_mode === 'gancao' ? '确认并上传药方' : '确认发货' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -1815,21 +1976,56 @@
|
||||
width="480px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
@closed="completeOrderId = 0"
|
||||
@closed="onCompleteOrderDialogClosed"
|
||||
>
|
||||
<p class="text-sm text-gray-600 mb-3">请选择本单结案状态。选择「已完成」时,实付将按已关联的支付单金额汇总。</p>
|
||||
<el-select
|
||||
v-model="completeFulfillmentStatus"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in completeOrderStatusOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div v-loading="completeOrderLogisticsLoading" class="min-h-[52px]">
|
||||
<el-alert
|
||||
v-if="completeOrderLogisticsUrgent.show"
|
||||
type="error"
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-3 !items-start ring-2 ring-red-600/70"
|
||||
title="物流异常:轨迹含拒收/退回等,请先核实处理再结案"
|
||||
>
|
||||
<template #default>
|
||||
<div class="text-xs leading-relaxed space-y-1.5">
|
||||
<div v-if="completeOrderLogisticsUrgent.keywords.length" class="flex flex-wrap gap-1 items-center">
|
||||
<span class="text-red-100/90">命中:</span>
|
||||
<el-tag
|
||||
v-for="k in completeOrderLogisticsUrgent.keywords"
|
||||
:key="k"
|
||||
type="danger"
|
||||
effect="plain"
|
||||
size="small"
|
||||
class="!border-white/35 !text-white"
|
||||
>
|
||||
{{ k }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<ul v-if="completeOrderLogisticsUrgent.lines.length" class="list-disc pl-4 space-y-0.5 text-red-50">
|
||||
<li v-for="(line, idx) in completeOrderLogisticsUrgent.lines" :key="idx">{{ line }}</li>
|
||||
</ul>
|
||||
<div class="text-red-100/85 border-t border-white/15 pt-1.5">
|
||||
确认已协调拒收/退回后续(重发、退款、改址等)后再完成订单。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<p class="text-sm text-gray-600 mb-3">请选择本单结案状态。选择「已完成」时,实付将按已关联的支付单金额汇总。</p>
|
||||
<el-select
|
||||
v-model="completeFulfillmentStatus"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in completeOrderStatusOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="completeOrderDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="completeOrderSubmitting" @click="submitCompleteOrder">
|
||||
@@ -2165,7 +2361,7 @@
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">
|
||||
药房备注:{{ rxPharmacyRemarkText }}
|
||||
</p>
|
||||
<p v-if="rxOutPelletText" class="rx-text-warn">
|
||||
<p v-if="rxOutPelletText && prescriptionViewData?.prescription_type !== '饮片'" class="rx-text-warn">
|
||||
出丸:{{ rxOutPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -2375,7 +2571,7 @@
|
||||
<script lang="ts" setup name="prescriptionOrderList">
|
||||
import { computed, onMounted, reactive, ref, nextTick, watch, defineAsyncComponent } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { Refresh, Loading, ArrowDown, InfoFilled, Search, Calendar, Document, Link as LinkIcon, Wallet } from '@element-plus/icons-vue'
|
||||
import { Refresh, Loading, ArrowDown, InfoFilled, QuestionFilled, Search, Calendar, Document, Link as LinkIcon, Wallet } from '@element-plus/icons-vue'
|
||||
import DaterangePicker from '@/components/daterange-picker/index.vue'
|
||||
import {
|
||||
prescriptionOrderAuditPayment,
|
||||
@@ -2383,6 +2579,7 @@ import {
|
||||
prescriptionOrderDetail,
|
||||
prescriptionOrderEdit,
|
||||
prescriptionOrderLists,
|
||||
prescriptionOrderExport,
|
||||
prescriptionOrderPaidPayOrders,
|
||||
prescriptionOrderWithdraw,
|
||||
prescriptionOrderLogisticsTrace,
|
||||
@@ -2407,6 +2604,7 @@ import { getDictData } from '@/api/app'
|
||||
import { deptAll } from '@/api/org/department'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { hasPermission } from '@/utils/perm'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@@ -2667,6 +2865,8 @@ const queryParams = reactive({
|
||||
payment_slip_audit_status: '' as number | '',
|
||||
/** 供货方式:甘草(已传甘草药方单号)/ 自营(无甘草单号) */
|
||||
supply_mode: '' as '' | 'gancao' | 'self',
|
||||
/** 服务渠道:'' 不限;'0' 未指派(库内 '' 或 '0') */
|
||||
service_channel: '' as '' | '0',
|
||||
/** 列表排除履约已取消(4):重点看板「待处方审核」等用 */
|
||||
exclude_fulfillment_cancelled: 0 as number
|
||||
})
|
||||
@@ -2731,21 +2931,35 @@ function handleFocusBoardSearch(key: 'pendingRx' | 'pendingPay' | 'pendingShip'
|
||||
resetPage()
|
||||
}
|
||||
|
||||
async function fetchLists(params: Record<string, unknown>) {
|
||||
function normalizePrescriptionOrderListQuery(params: Record<string, unknown>): Record<string, unknown> {
|
||||
const p: Record<string, unknown> = { ...params }
|
||||
if (p.prescription_id === '' || p.prescription_id === undefined) {
|
||||
delete p.prescription_id
|
||||
} else {
|
||||
p.prescription_id = Number(p.prescription_id)
|
||||
}
|
||||
if (p.fulfillment_status === '' || p.fulfillment_status === undefined) {
|
||||
if (p.fulfillment_status === '' || p.fulfillment_status === undefined || p.fulfillment_status === null) {
|
||||
delete p.fulfillment_status
|
||||
} else {
|
||||
p.fulfillment_status = Number(p.fulfillment_status)
|
||||
}
|
||||
if (p.prescription_audit_status === '' || p.prescription_audit_status === undefined) {
|
||||
if (
|
||||
p.prescription_audit_status === '' ||
|
||||
p.prescription_audit_status === undefined ||
|
||||
p.prescription_audit_status === null
|
||||
) {
|
||||
delete p.prescription_audit_status
|
||||
} else {
|
||||
p.prescription_audit_status = Number(p.prescription_audit_status)
|
||||
}
|
||||
if (p.payment_slip_audit_status === '' || p.payment_slip_audit_status === undefined) {
|
||||
if (
|
||||
p.payment_slip_audit_status === '' ||
|
||||
p.payment_slip_audit_status === undefined ||
|
||||
p.payment_slip_audit_status === null
|
||||
) {
|
||||
delete p.payment_slip_audit_status
|
||||
} else {
|
||||
p.payment_slip_audit_status = Number(p.payment_slip_audit_status)
|
||||
}
|
||||
if (!p.supply_mode || p.supply_mode === '') {
|
||||
delete p.supply_mode
|
||||
@@ -2769,12 +2983,17 @@ async function fetchLists(params: Record<string, unknown>) {
|
||||
if (!String(p.patient_keyword || '').trim()) delete p.patient_keyword
|
||||
if (!String(p.express_keyword || '').trim()) delete p.express_keyword
|
||||
if (!p.express_company || p.express_company === '') delete p.express_company
|
||||
if (p.service_channel === '' || p.service_channel === undefined || p.service_channel === null) {
|
||||
delete p.service_channel
|
||||
} else {
|
||||
p.service_channel = String(p.service_channel).trim()
|
||||
if (p.service_channel === '') delete p.service_channel
|
||||
}
|
||||
if (Number(p.exclude_fulfillment_cancelled) !== 1) delete p.exclude_fulfillment_cancelled
|
||||
if (!String(p.start_time || '').trim() || !String(p.end_time || '').trim()) {
|
||||
delete p.start_time
|
||||
delete p.end_time
|
||||
} else {
|
||||
// 当前页面按“日”筛选:请求接口时展开为整天区间,避免结束日只落在 00:00:00。
|
||||
const st = String(p.start_time || '').trim()
|
||||
const et = String(p.end_time || '').trim()
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(st) && /^\d{4}-\d{2}-\d{2}$/.test(et)) {
|
||||
@@ -2782,9 +3001,16 @@ async function fetchLists(params: Record<string, unknown>) {
|
||||
p.end_time = `${et} 23:59:59`
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
async function fetchLists(params: Record<string, unknown>) {
|
||||
const p = normalizePrescriptionOrderListQuery({ ...params })
|
||||
return prescriptionOrderLists(p)
|
||||
}
|
||||
|
||||
const prescriptionOrderExportParams = computed(() => normalizePrescriptionOrderListQuery({ ...queryParams }))
|
||||
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: fetchLists,
|
||||
params: queryParams
|
||||
@@ -2980,6 +3206,7 @@ function handleReset() {
|
||||
queryParams.prescription_audit_status = ''
|
||||
queryParams.payment_slip_audit_status = ''
|
||||
queryParams.supply_mode = ''
|
||||
queryParams.service_channel = ''
|
||||
queryParams.exclude_fulfillment_cancelled = 0
|
||||
resetParams()
|
||||
}
|
||||
@@ -3083,14 +3310,21 @@ function canEditRow(row: {
|
||||
gancao_reciperl_order_no?: string | null
|
||||
gancao_submit_time?: number | null
|
||||
}) {
|
||||
// 已发货(5)、已签收(6)、已完成(3)、已取消(4) 不可编辑
|
||||
const fs = Number(row.fulfillment_status)
|
||||
if (fs !== 1 && fs !== 2) return false
|
||||
// 已成功提交甘草(已生成甘草处方单号 或 提交时间 > 0)不允许再编辑
|
||||
// 已完成(3)、已取消(4) 不可编辑
|
||||
if (fs === 3 || fs === 4) return false
|
||||
|
||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
const gcTime = Number(row.gancao_submit_time || 0)
|
||||
if (gcNo !== '' || gcTime > 0) return false
|
||||
return true
|
||||
const gcLocked = gcNo !== '' || gcTime > 0
|
||||
|
||||
// 甘草已提交:仅允许在待双审/履约中/已发货/进行中下修改快递(已签收 6 不可改单号;后端 edit 亦拦截)
|
||||
if (gcLocked) {
|
||||
return [1, 2, 5, 7].includes(fs)
|
||||
}
|
||||
|
||||
// 未提交甘草:仅待双审(1)、履约中(2)可全量编辑
|
||||
return fs === 1 || fs === 2
|
||||
}
|
||||
|
||||
function canRxAudit(row: { prescription_audit_status?: number; fulfillment_status?: number }) {
|
||||
@@ -3159,9 +3393,8 @@ function canCompleteRow(row: { fulfillment_status?: number; payment_slip_audit_s
|
||||
}
|
||||
|
||||
function canQuickTrackRow(row: { fulfillment_status?: number }) {
|
||||
// 已发货(5) / 已签收(6) 后如需修改快递信息可单独使用此功能
|
||||
const fs = Number(row.fulfillment_status)
|
||||
return fs === 5 || fs === 6
|
||||
// 仅已发货(5) 可修改快递单号;已签收(6) 不可再改
|
||||
return Number(row.fulfillment_status) === 5
|
||||
}
|
||||
|
||||
function canUpdateAmount(row: { id?: number; fulfillment_status?: number } | null | undefined) {
|
||||
@@ -3534,6 +3767,75 @@ const logisticsTraceList = computed(() => {
|
||||
return p.traces as Array<{ time: string; context: string }>
|
||||
})
|
||||
|
||||
/** 物流轨迹中需人工立即跟进的常见异常关键词 */
|
||||
const LOGISTICS_URGENT_KEYWORDS = [
|
||||
'拒收',
|
||||
'拒签',
|
||||
'退件',
|
||||
'客户拒收',
|
||||
'拦截退回',
|
||||
'退回发件',
|
||||
'派件退回',
|
||||
'无人签收',
|
||||
'退回快件'
|
||||
]
|
||||
|
||||
function logisticsStringHasUrgentKeyword(s: unknown): boolean {
|
||||
const t = String(s ?? '')
|
||||
return LOGISTICS_URGENT_KEYWORDS.some((k) => t.includes(k))
|
||||
}
|
||||
|
||||
function logisticsTraceLineUrgent(context: unknown): boolean {
|
||||
return logisticsStringHasUrgentKeyword(context)
|
||||
}
|
||||
|
||||
/** 根据单次拉取的物流 payload 判断是否含拒收/退回等(详情抽屉、完成订单弹窗共用) */
|
||||
function analyzeLogisticsPayloadUrgent(p: Record<string, any> | null | undefined): {
|
||||
show: boolean
|
||||
keywords: string[]
|
||||
lines: string[]
|
||||
} {
|
||||
if (!p) {
|
||||
return { show: false, keywords: [], lines: [] }
|
||||
}
|
||||
const traces = Array.isArray(p.traces)
|
||||
? (p.traces as Array<{ time?: string; context?: string }>)
|
||||
: []
|
||||
const chunks: string[] = []
|
||||
if (p.state_text) chunks.push(String(p.state_text))
|
||||
if (p.hint) chunks.push(String(p.hint))
|
||||
for (const row of traces) {
|
||||
if (row.context) chunks.push(String(row.context))
|
||||
}
|
||||
const joined = chunks.join('\n')
|
||||
const keywords = LOGISTICS_URGENT_KEYWORDS.filter((k) => joined.includes(k))
|
||||
if (!keywords.length) {
|
||||
return { show: false, keywords: [], lines: [] }
|
||||
}
|
||||
const lines: string[] = []
|
||||
for (const row of traces) {
|
||||
const ctx = String(row.context || '')
|
||||
if (logisticsStringHasUrgentKeyword(ctx)) {
|
||||
lines.push(ctx)
|
||||
if (lines.length >= 5) break
|
||||
}
|
||||
}
|
||||
if (!lines.length && logisticsStringHasUrgentKeyword(p.state_text)) {
|
||||
lines.push(`最新状态:${p.state_text}`)
|
||||
}
|
||||
if (!lines.length && logisticsStringHasUrgentKeyword(p.hint)) {
|
||||
lines.push(String(p.hint))
|
||||
}
|
||||
return { show: true, keywords, lines }
|
||||
}
|
||||
|
||||
/** 详情抽屉:轨迹/状态中是否出现拒收等字样,用于顶部与物流卡片强提示 */
|
||||
const detailLogisticsUrgent = computed(() => analyzeLogisticsPayloadUrgent(logisticsTracePayload.value))
|
||||
|
||||
const completeOrderLogisticsPayload = ref<Record<string, any> | null>(null)
|
||||
const completeOrderLogisticsLoading = ref(false)
|
||||
const completeOrderLogisticsUrgent = computed(() => analyzeLogisticsPayloadUrgent(completeOrderLogisticsPayload.value))
|
||||
|
||||
function expressCompanyLabel(v: unknown) {
|
||||
const s = String(v || '').toLowerCase()
|
||||
if (s === 'sf') return '顺丰速运'
|
||||
@@ -3728,6 +4030,10 @@ const editSaving = ref(false)
|
||||
const editOrderStep = ref(0)
|
||||
/** 顶部「关联处方」卡片数据(来自业务订单详情中的 prescription) */
|
||||
const editOrderPrescription = ref<Record<string, any> | null>(null)
|
||||
/** 甘草 SCM 已提交:弹窗仅展示并提交快递单号与承运商 */
|
||||
const editGancaoLogisticsOnlyMode = ref(false)
|
||||
/** 用于提示文案展示甘草处方单号 */
|
||||
const editGancaoDisplayNo = ref('')
|
||||
|
||||
const editOrderStepLead = computed(() => {
|
||||
const texts = [
|
||||
@@ -3906,6 +4212,8 @@ const editRules = computed<FormRules>(() => {
|
||||
function resetEditOrderDialog() {
|
||||
editOrderStep.value = 0
|
||||
editOrderPrescription.value = null
|
||||
editGancaoLogisticsOnlyMode.value = false
|
||||
editGancaoDisplayNo.value = ''
|
||||
editFormRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
@@ -4025,7 +4333,12 @@ async function openEdit(row: { id: number }) {
|
||||
editForm.pay_order_ids = Array.isArray(pids) ? pids.map((x: unknown) => Number(x)).filter((n) => !Number.isNaN(n)) : []
|
||||
editForm.internal_cost =
|
||||
d.internal_cost != null && d.internal_cost !== '' ? Number(d.internal_cost) : undefined
|
||||
|
||||
|
||||
const gcNo = String(d.gancao_reciperl_order_no || '').trim()
|
||||
const gcTime = Number(d.gancao_submit_time || 0)
|
||||
editGancaoLogisticsOnlyMode.value = gcNo !== '' || gcTime > 0
|
||||
editGancaoDisplayNo.value = gcNo || ''
|
||||
|
||||
await loadEditPaidOrders(editForm.diagnosis_id, editForm.id)
|
||||
|
||||
// BUG FIX: NextTick 清理刚展开时意外触发的金额下限表单校验红字
|
||||
@@ -4041,8 +4354,14 @@ async function openEdit(row: { id: number }) {
|
||||
}
|
||||
|
||||
async function submitEdit() {
|
||||
if (!editFormRef.value || editOrderStep.value !== 2) return
|
||||
await editFormRef.value.validate()
|
||||
if (!editFormRef.value) return
|
||||
const gancaoLogisticsOnly = editGancaoLogisticsOnlyMode.value
|
||||
if (!gancaoLogisticsOnly && editOrderStep.value !== 2) return
|
||||
if (!gancaoLogisticsOnly) {
|
||||
await editFormRef.value.validate()
|
||||
} else {
|
||||
editFormRef.value.clearValidate()
|
||||
}
|
||||
editSaving.value = true
|
||||
try {
|
||||
const payload: Record<string, unknown> = {
|
||||
@@ -4244,6 +4563,61 @@ async function testGancaoPreviewFromDetail() {
|
||||
if (d && d.success) {
|
||||
gancaoPreviewData.value = d
|
||||
gancaoPreviewDrawerVisible.value = true
|
||||
|
||||
// 将价格信息「总计」(药材成本 + 制作费 + 物流费) 写入 internal_cost(需财务字段权限,与后台 canViewInternalCost 一致)
|
||||
if (canViewFinanceFields() && d.fee) {
|
||||
const internalTotal = calculateTotalFee(d.fee)
|
||||
try {
|
||||
const resDetail: any = await prescriptionOrderDetail({ id: detailData.value.id })
|
||||
const od = resDetail?.data ?? resDetail
|
||||
if (od?.id) {
|
||||
const pkgRaw = od.service_package
|
||||
const servicePackageStr = Array.isArray(pkgRaw)
|
||||
? pkgRaw.join(',')
|
||||
: String(pkgRaw || '')
|
||||
await prescriptionOrderEdit({
|
||||
id: od.id,
|
||||
recipient_name: od.recipient_name || '',
|
||||
recipient_phone: od.recipient_phone || '',
|
||||
shipping_province: od.shipping_province || '',
|
||||
shipping_city: od.shipping_city || '',
|
||||
shipping_district: od.shipping_district || '',
|
||||
shipping_address: od.shipping_address || '',
|
||||
is_follow_up: od.is_follow_up ? 1 : 0,
|
||||
medication_days:
|
||||
od.medication_days != null && String(od.medication_days).trim() !== ''
|
||||
? od.medication_days
|
||||
: '',
|
||||
dose_unit: od.dose_unit || '剂',
|
||||
dose_count: od.dose_count > 0 ? od.dose_count : 1,
|
||||
prev_staff: od.prev_staff || '',
|
||||
service_channel: od.service_channel || '',
|
||||
service_package: servicePackageStr,
|
||||
express_company: od.express_company || 'auto',
|
||||
tracking_number: od.tracking_number || '',
|
||||
fee_type: Number(od.fee_type) || 3,
|
||||
amount: Number(od.amount) || 0,
|
||||
remark_extra: od.remark_extra || '',
|
||||
remark_assistant: od.remark_assistant || '',
|
||||
internal_cost: internalTotal,
|
||||
pay_order_ids: Array.isArray(od.pay_order_ids) ? od.pay_order_ids : []
|
||||
})
|
||||
feedback.msgSuccess('内部成本已按预报价总计更新')
|
||||
getLists()
|
||||
if (detailVisible.value && Number(detailData.value?.id) === Number(od.id)) {
|
||||
try {
|
||||
const r: any = await prescriptionOrderDetail({ id: od.id })
|
||||
const nd = r?.data ?? r ?? null
|
||||
if (nd) detailData.value = nd
|
||||
} catch {
|
||||
detailData.value = { ...detailData.value, internal_cost: internalTotal }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* 拦截器已提示;仍保留抽屉中的预报价结果 */
|
||||
}
|
||||
}
|
||||
//feedback.msgSuccess('预下单测试成功')
|
||||
} else {
|
||||
// feedback.msgError(d?.error || '预下单失败')
|
||||
@@ -4359,8 +4733,8 @@ async function submitQuickTrack() {
|
||||
if (nd) detailData.value = nd
|
||||
} catch { /* 静默 */ }
|
||||
}
|
||||
// 履约中状态且刚填单号,提示是否立即发货
|
||||
if (canShipRow({ fulfillment_status: 2, tracking_number: quickTrackForm.tracking_number })) {
|
||||
// 仅保存前为履约中(2)时才询问确认发货;已发货(5)/已签收(6)等修改单号不再弹窗
|
||||
if (canShipRow({ fulfillment_status: Number(d.fulfillment_status) })) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`快递单号「${quickTrackForm.tracking_number}」已保存,是否立即确认发货?`,
|
||||
@@ -4389,13 +4763,31 @@ const shipForm = reactive({
|
||||
|
||||
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
||||
shipRowId.value = row.id
|
||||
shipForm.ship_mode = String(row.ship_mode || 'gancao') || 'gancao'
|
||||
const tn = String(row.tracking_number ?? '').trim()
|
||||
const sm = String(row.ship_mode ?? '').trim()
|
||||
if (sm === 'direct' || sm === 'gancao') {
|
||||
shipForm.ship_mode = sm
|
||||
} else if (tn) {
|
||||
// 已填单号时默认走药房直发(如快捷填单号后「确认发货」)
|
||||
shipForm.ship_mode = 'direct'
|
||||
} else {
|
||||
shipForm.ship_mode = 'gancao'
|
||||
}
|
||||
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||
shipForm.tracking_number = String(row.tracking_number || '')
|
||||
shipVisible.value = true
|
||||
}
|
||||
|
||||
async function submitShip() {
|
||||
if (shipForm.ship_mode !== 'direct') {
|
||||
if (!hasPermission(['tcm.prescriptionOrder/submitGancaoRecipel'])) {
|
||||
feedback.msgError('暂无上传甘草药方权限,请改用「药房直发」并填写快递单号,或联系管理员开通权限')
|
||||
return
|
||||
}
|
||||
shipVisible.value = false
|
||||
await confirmSubmitGancaoRecipel({ id: shipRowId.value })
|
||||
return
|
||||
}
|
||||
if (!shipForm.tracking_number.trim()) {
|
||||
feedback.msgError('请先填写快递单号再确认发货')
|
||||
return
|
||||
@@ -4404,7 +4796,7 @@ async function submitShip() {
|
||||
try {
|
||||
await prescriptionOrderShip({
|
||||
id: shipRowId.value,
|
||||
ship_mode: shipForm.ship_mode === 'direct' ? 'direct' : 'gancao',
|
||||
ship_mode: 'direct',
|
||||
express_company: shipForm.express_company || 'auto',
|
||||
tracking_number: shipForm.tracking_number.trim()
|
||||
})
|
||||
@@ -4441,6 +4833,37 @@ const completeOrderId = ref(0)
|
||||
const completeFulfillmentStatus = ref<number>(3)
|
||||
const completeOrderSubmitting = ref(false)
|
||||
|
||||
function onCompleteOrderDialogClosed() {
|
||||
completeOrderId.value = 0
|
||||
completeOrderLogisticsPayload.value = null
|
||||
completeOrderLogisticsLoading.value = false
|
||||
}
|
||||
|
||||
async function fetchCompleteOrderLogisticsForDialog(row: {
|
||||
id: number
|
||||
express_company?: unknown
|
||||
recipient_phone?: unknown
|
||||
}) {
|
||||
completeOrderLogisticsLoading.value = true
|
||||
completeOrderLogisticsPayload.value = null
|
||||
try {
|
||||
const digits = String(row.recipient_phone || '').replace(/\D/g, '')
|
||||
const params: { id: number; express_company?: string; phone_tail?: string } = {
|
||||
id: row.id,
|
||||
express_company: String(row.express_company || 'auto') || 'auto'
|
||||
}
|
||||
if (digits.length >= 4) {
|
||||
params.phone_tail = digits
|
||||
}
|
||||
const res: any = await prescriptionOrderLogisticsTrace(params)
|
||||
completeOrderLogisticsPayload.value = (res?.data ?? res) as Record<string, any>
|
||||
} catch {
|
||||
completeOrderLogisticsPayload.value = null
|
||||
} finally {
|
||||
completeOrderLogisticsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function submitCompleteOrder() {
|
||||
const id = completeOrderId.value
|
||||
if (!id) return
|
||||
@@ -4603,10 +5026,19 @@ async function submitAddPayOrder() {
|
||||
}
|
||||
|
||||
// ─── 完成订单(已发货/已签收 + 支付审核通过)────────────────────────────────────────────
|
||||
function confirmComplete(row: { id: number }) {
|
||||
function confirmComplete(row: {
|
||||
id: number
|
||||
tracking_number?: unknown
|
||||
express_company?: unknown
|
||||
recipient_phone?: unknown
|
||||
}) {
|
||||
completeOrderId.value = row.id
|
||||
completeFulfillmentStatus.value = 3
|
||||
completeOrderLogisticsPayload.value = null
|
||||
completeOrderDialogVisible.value = true
|
||||
if (String(row.tracking_number || '').trim()) {
|
||||
void fetchCompleteOrderLogisticsForDialog(row)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 撤回处方审核 ────────────────────────────────────────────
|
||||
@@ -4795,6 +5227,8 @@ const rxTypeText = computed(() => {
|
||||
const v = prescriptionViewData.value as any
|
||||
if (!v) return '—'
|
||||
const t = v.prescription_type || '浓缩水丸'
|
||||
/** 饮片不匹配「丸|散|膏|片」,避免误显示「浓缩丸-饮片」;类型字段直接展示饮片 */
|
||||
if (t === '饮片') return '饮片'
|
||||
return /丸|散|膏|片/.test(t) ? `浓缩丸-${t}` : t
|
||||
})
|
||||
|
||||
|
||||
@@ -3052,14 +3052,18 @@ function canEditRow(row: {
|
||||
gancao_reciperl_order_no?: string | null
|
||||
gancao_submit_time?: number | null
|
||||
}) {
|
||||
// 已发货(5)、已签收(6)、已完成(3)、已取消(4) 不可编辑
|
||||
const fs = Number(row.fulfillment_status)
|
||||
if (fs !== 1 && fs !== 2) return false
|
||||
// 已成功提交甘草(已生成甘草处方单号 或 提交时间 > 0)不允许再编辑
|
||||
if (fs === 3 || fs === 4) return false
|
||||
|
||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
const gcTime = Number(row.gancao_submit_time || 0)
|
||||
if (gcNo !== '' || gcTime > 0) return false
|
||||
return true
|
||||
const gcLocked = gcNo !== '' || gcTime > 0
|
||||
|
||||
if (gcLocked) {
|
||||
return [1, 2, 5, 7].includes(fs)
|
||||
}
|
||||
|
||||
return fs === 1 || fs === 2
|
||||
}
|
||||
|
||||
function canRxAudit(row: { prescription_audit_status?: number; fulfillment_status?: number }) {
|
||||
@@ -3128,9 +3132,7 @@ function canCompleteRow(row: { fulfillment_status?: number; payment_slip_audit_s
|
||||
}
|
||||
|
||||
function canQuickTrackRow(row: { fulfillment_status?: number }) {
|
||||
// 已发货(5) / 已签收(6) 后如需修改快递信息可单独使用此功能
|
||||
const fs = Number(row.fulfillment_status)
|
||||
return fs === 5 || fs === 6
|
||||
return Number(row.fulfillment_status) === 5
|
||||
}
|
||||
|
||||
function canUploadGancaoRow(row: {
|
||||
@@ -4312,8 +4314,8 @@ async function submitQuickTrack() {
|
||||
if (nd) detailData.value = nd
|
||||
} catch { /* 静默 */ }
|
||||
}
|
||||
// 履约中状态且刚填单号,提示是否立即发货
|
||||
if (canShipRow({ fulfillment_status: 2, tracking_number: quickTrackForm.tracking_number })) {
|
||||
// 仅保存前为履约中(2)时才询问确认发货;已发货(5)/已签收(6)等修改单号不再弹窗
|
||||
if (canShipRow({ fulfillment_status: Number(d.fulfillment_status) })) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`快递单号「${quickTrackForm.tracking_number}」已保存,是否立即确认发货?`,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -102,6 +102,18 @@
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="添加时间">
|
||||
<el-date-picker
|
||||
v-model="addTimeRange"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
clearable
|
||||
class="!w-[280px]"
|
||||
@change="onAddTimeRangeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
v-model="queryParams.tag_ids"
|
||||
@@ -557,12 +569,34 @@ const syncSettings = reactive({
|
||||
interval: 3600
|
||||
})
|
||||
|
||||
const queryParams = reactive<{ name: string; follow_user: string; tag_ids: string[] }>({
|
||||
const queryParams = reactive<{
|
||||
name: string
|
||||
follow_user: string
|
||||
tag_ids: string[]
|
||||
add_time_start: string
|
||||
add_time_end: string
|
||||
}>({
|
||||
name: '',
|
||||
follow_user: '',
|
||||
tag_ids: []
|
||||
tag_ids: [],
|
||||
add_time_start: '',
|
||||
add_time_end: ''
|
||||
})
|
||||
|
||||
/** 与列表「添加时间」列口径一致(库内 external_first_add_time / create_time) */
|
||||
const addTimeRange = ref<[string, string] | null>(null)
|
||||
|
||||
function onAddTimeRangeChange(val: [string, string] | null) {
|
||||
if (val && val.length === 2 && val[0] && val[1]) {
|
||||
queryParams.add_time_start = val[0]
|
||||
queryParams.add_time_end = val[1]
|
||||
} else {
|
||||
queryParams.add_time_start = ''
|
||||
queryParams.add_time_end = ''
|
||||
}
|
||||
resetPage()
|
||||
}
|
||||
|
||||
// ── 标签维度(筛选下拉 + 抽屉面板共用同一份数据) ──────────────────────────
|
||||
interface TagItem {
|
||||
tag_id: string
|
||||
@@ -793,6 +827,9 @@ function handleReset() {
|
||||
queryParams.name = ''
|
||||
queryParams.follow_user = ''
|
||||
queryParams.tag_ids = []
|
||||
queryParams.add_time_start = ''
|
||||
queryParams.add_time_end = ''
|
||||
addTimeRange.value = null
|
||||
resetParams()
|
||||
}
|
||||
|
||||
|
||||
@@ -290,12 +290,12 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="yeji-table__foot yeji-table__foot--left">
|
||||
<span>诊金单位:元 · 被指派数与部门业绩表同口径 · 接诊率 = 诊金 ÷ 进线(元/进线)</span>
|
||||
<span>诊金单位:元 · 诊金/接诊诊单/复诊均按「订单创建人 = 该医助」归属,与部门「合计业绩 / 接诊诊单 / 复诊」同口径 · 被指派数与部门业绩表同口径 · 接诊率 = 诊金 ÷ 进线(元/进线)</span>
|
||||
<span v-if="lb.er_center_subtree">
|
||||
· 复诊列为二中心口径(同一诊单业务单序列第 2 笔起),点击数字查看对应业务订单
|
||||
</span>
|
||||
<span>
|
||||
· 预约诊单:预约日期在统计区间内,状态含已预约/已完成/已过号(不含已取消),医助归属与接诊单数一致;各科组展示全部医助。
|
||||
· 预约诊单:预约日期在统计区间内,状态含已预约/已完成/已过号(不含已取消),医助归属与接诊单数一致(按挂号 assistant_id 优先,否则诊单 assistant_id);各科组展示全部医助。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -370,7 +370,7 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
仅<b>名称含「二中心」</b>的部门及其组织下级:与本表区间一致,按患者业务单 <b>create_time</b> 排序,<b>第 1 笔不计</b>;第 2 笔起计入合计,并分列复诊2、复诊三…(含本部门及下级汇总)。订单条件同「合计业绩」列(非取消)。非二中心子树部门为 0。
|
||||
仅<b>名称含「二中心」</b>的部门及其组织下级:候选订单为有诊单医助、且 <b>fulfillment_status</b> 非 4/9/10(<b>NULL 计入</b>)的业务单,按患者 <b>create_time</b> 排序,<b>第 1 笔不计</b>;第 2 笔起计入合计,分列复诊2、复诊三…。<b>归属</b>按订单创建人的人事部门,与<b>合计业绩</b>列同口径。非二中心子树部门为 0。
|
||||
</div>
|
||||
</template>
|
||||
<el-icon class="col-info"><InfoFilled /></el-icon>
|
||||
@@ -395,8 +395,8 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 300px; line-height: 1.7; font-size: 12px">
|
||||
按订单<b>创建时间</b>统计(剔除履约已取消),金额与<b>接诊诊单</b>列一致:<b>创建人</b>人事部门优先,无创建人时回退诊单<b>医助</b>;落在本展示行(含下级)即计入,同表多行命中时取最深的展示部门。<br />
|
||||
<b>不受渠道筛选影响</b>。与侧栏按部门打开的业务订单列表可对齐。<br />
|
||||
按订单<b>创建时间</b>统计(剔除履约已取消/拒收/退款 4·9·10),金额与<b>接诊诊单</b>列一致:仅按<b>订单创建人</b>的人事部门落在本展示行(含下级)计入,多行命中时取最深的展示部门。与<b>医助排行榜诊金</b>同口径。<br />
|
||||
<b>不受渠道筛选影响</b>。与侧栏按部门打开的业务订单列表对齐。<br />
|
||||
点击金额可查看对应业务订单明细(侧栏默认「合计业绩」全量列表)。
|
||||
</div>
|
||||
</template>
|
||||
@@ -409,7 +409,7 @@
|
||||
<template #content>
|
||||
<div style="max-width: 340px; line-height: 1.7; font-size: 12px">
|
||||
「{{ tb.channel_name }}」渠道业绩(与卡片标题渠道一致)。与「处方订单列表」业绩<b>同口径</b>:按订单
|
||||
<b>create_time</b> 归日,剔除履约已取消(4)。<br />
|
||||
<b>create_time</b> 归日,剔除履约状态 4(已取消)/9(拒收)/10(退款)。<br />
|
||||
渠道判定用 EXISTS:该患者有任一挂号 <b>channels 命中字典 + status=3</b>(不限挂号时点)。<br />
|
||||
点击金额可查看该部门本渠道订单明细(侧栏「渠道筛选」列表)。
|
||||
</div>
|
||||
@@ -445,8 +445,12 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
<b>业务订单条数</b>(非取消):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ≠ 4</b>;与列表筛选
|
||||
<<<<<<< HEAD
|
||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>(<b>NULL 计入</b>);按<b>订单创建人</b>的人事部门落在该部门子树即计入(表格多行命中时取最深的展示部门)。与<b>合计业绩 / 医助排行榜接诊诊单</b>同口径。选定渠道时本列仍为全量。
|
||||
=======
|
||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>;与列表筛选
|
||||
<b>assistant_dept_id</b> 时一致——<b>创建人</b>人事部门优先,无创建人则诊单 <b>医助</b>;落在该部门子树即计入(表格多行命中时取最深的展示部门)。与侧栏勾选「与表格业绩对齐」时的集合可能略有差异。选定渠道时本列仍为全量。
|
||||
>>>>>>> master
|
||||
</div>
|
||||
</template>
|
||||
<el-icon class="col-info"><InfoFilled /></el-icon>
|
||||
@@ -457,7 +461,7 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
「{{ tb.channel_name }}」渠道成交单数;与渠道业绩同源:区间内 <b>非取消业务订单</b> 条数,按订单
|
||||
「{{ tb.channel_name }}」渠道成交单数;与渠道业绩同源:区间内 <b>计业绩的业务订单</b> 条数(剔除 4/9/10),按订单
|
||||
<b>create_time</b> 归日;渠道判定同业绩列。
|
||||
</div>
|
||||
</template>
|
||||
@@ -970,7 +974,7 @@
|
||||
>
|
||||
<span class="yeji-order-drawer__stat-label">
|
||||
业务订单金额合计
|
||||
<span class="yeji-order-drawer__stat-note">(不含履约已取消,与上方业绩口径一致)</span>
|
||||
<span class="yeji-order-drawer__stat-note">(不含履约 4·9·10,与上方业绩口径一致)</span>
|
||||
</span>
|
||||
<span class="yeji-order-drawer__stat-value yeji-order-drawer__stat-value--money">
|
||||
¥{{ formatOrderDrawerMoney(orderDrawerTotalAmount) }}
|
||||
@@ -1232,6 +1236,7 @@
|
||||
<el-table-column prop="patient_name" label="患者" min-width="92" show-overflow-tooltip />
|
||||
<el-table-column prop="patient_phone" label="手机" min-width="116" show-overflow-tooltip />
|
||||
<el-table-column prop="doctor_name" label="接诊医生" width="92" show-overflow-tooltip />
|
||||
<el-table-column prop="assistant_name" label="医助" width="88" show-overflow-tooltip />
|
||||
<el-table-column label="诊单ID" width="84" align="right">
|
||||
<template #default="{ row }">{{ formatInt(row.diagnosis_id ?? 0) }}</template>
|
||||
</el-table-column>
|
||||
@@ -1314,6 +1319,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { Search, RefreshRight, InfoFilled } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import vCharts from 'vue-echarts'
|
||||
@@ -2075,7 +2081,9 @@ async function loadDoctorDailyStats() {
|
||||
if (res?.start_date && res?.end_date) {
|
||||
doctorDailyRange.value = { start: res.start_date, end: res.end_date }
|
||||
}
|
||||
} catch (_e) {
|
||||
} catch (e: unknown) {
|
||||
// 与业绩主接口并发时,同源请求键相同会触发 axios 重复请求取消;勿清空状态以免覆盖后到的成功结果
|
||||
if (axios.isCancel(e)) return
|
||||
doctorDailyRows.value = []
|
||||
doctorDailyTotal.value = {}
|
||||
doctorDailyRange.value = null
|
||||
@@ -2124,7 +2132,7 @@ type OrderDrawerFilter =
|
||||
}
|
||||
|
||||
const orderDrawerFilter = ref<OrderDrawerFilter | null>(null)
|
||||
/** 列表接口 extend,含 stats_order_amount_performance(与当前筛选、不含已取消口径一致) */
|
||||
/** 列表接口 extend,含 stats_order_amount_performance(与当前筛选一致:剔除履约 4/9/10) */
|
||||
const orderDrawerExtend = ref<Record<string, any> | null>(null)
|
||||
|
||||
const orderDrawerTotalAmount = computed(() => {
|
||||
@@ -2472,7 +2480,8 @@ async function loadLeaderboard(range: { start: string; end: string }) {
|
||||
range_note: res.range_note || '',
|
||||
leaderboards: res.leaderboards || [],
|
||||
}
|
||||
} catch (_e) {
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
leaderboardBlock.value = null
|
||||
} finally {
|
||||
leaderboardsLoading.value = false
|
||||
@@ -2527,8 +2536,10 @@ async function loadData() {
|
||||
}))
|
||||
const ymd = formatLocalYmd(new Date())
|
||||
await loadLeaderboard({ start: ymd, end: ymd })
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (canViewDoctorDailyStats.value) {
|
||||
@@ -2636,18 +2647,17 @@ function buildOrderDrawerListParams(): Record<string, any> | null {
|
||||
const params: Record<string, any> = {
|
||||
start_time: `${f.start} 00:00:00`,
|
||||
end_time: `${f.end} 23:59:59`,
|
||||
/** 与业绩列口径一致:不列出履约已取消(4) */
|
||||
/** 与业绩列口径一致:不列出履约 4(已取消)/9(拒收)/10(退款) */
|
||||
exclude_fulfillment_cancelled: 1,
|
||||
/** 侧栏展示约诊数等 extend 字段 */
|
||||
/**
|
||||
* 业绩看板入口标志:后端 PrescriptionOrderLists::applyDoctorAssistantFilters 看到此标志后,
|
||||
* assistant_id / assistant_dept_id 一律按订单创建人收窄(与表格诊金/合计业绩/复诊同口径),
|
||||
* 而非默认的「创建人 ∪ 诊单医助」并集。同时启用 extend 字段(约诊数、合计业绩对照)。
|
||||
*/
|
||||
yeji_order_drawer: 1,
|
||||
}
|
||||
if (f.mode === 'dept') {
|
||||
params.assistant_dept_id = f.deptId
|
||||
/** 与 YejiStatsLogic 最深展示行 + 创建人优先归属一致 */
|
||||
if (f.yejiTableRowDeptIds && f.yejiTableRowDeptIds.length > 0) {
|
||||
params.yeji_table_row_dept_ids = f.yejiTableRowDeptIds.join(',')
|
||||
}
|
||||
/** 勿传 yeji_drawer_match_table_performance:该参数按「业绩归因」医助收窄,与看板列表摊行不一致 */
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
params.dept_ids = selectedDeptIds.value.join(',')
|
||||
}
|
||||
@@ -2733,8 +2743,10 @@ async function fetchOrderDrawerPage() {
|
||||
) {
|
||||
await hydrateOrderDrawerSplitPerfCache()
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载业务订单失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载业务订单失败')
|
||||
orderDrawerLists.value = []
|
||||
orderDrawerCount.value = 0
|
||||
orderDrawerExtend.value = null
|
||||
@@ -2756,7 +2768,7 @@ function openOrderDrawerByDept(tb: YejiTable, row: YejiRow, opts?: { listTab?: '
|
||||
yejiTableRowDeptIds: tb.rows.map(r => r.dept_id).filter((id): id is number => typeof id === 'number' && id > 0),
|
||||
}
|
||||
orderDrawerTitle.value = `业务订单 · ${row.dept_name}`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 与「接诊诊单」同口径:**创建人**人事部门优先,无创建人则诊单医助;同表多行间取最深归属 · 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 与「合计业绩 / 接诊诊单」同口径:订单创建人人事部门 ∈ 本部门子树 · 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2780,7 +2792,7 @@ function openOrderDrawerByYejiTableScope(
|
||||
yejiTableRowDeptIds: tb.rows.map(r => r.dept_id).filter((id): id is number => typeof id === 'number' && id > 0),
|
||||
}
|
||||
orderDrawerTitle.value = '业务订单 · 当前展示合计'
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 已选展示部门 1 个根节点(含其下全部展示行)· 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 已选展示部门 1 个根节点(含其下全部展示行)· 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2837,7 +2849,7 @@ function openOrderDrawerByAssistant(row: LeaderboardPack['leaderboards'][0]['row
|
||||
end,
|
||||
}
|
||||
orderDrawerTitle.value = `业务订单 · ${row.name}`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 诊单医助 · 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${row.name}(与排行榜诊金同口径)· 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2861,7 +2873,7 @@ function openOrderDrawerByAssistantErCenterRevisit(
|
||||
}
|
||||
const slotLbl = revisitSlot === 0 ? '复诊合计' : yejiRevisitSlotColumnTitle(revisitSlot)
|
||||
orderDrawerTitle.value = `业务订单 · ${assistant.name} · ${slotLbl}`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 诊单医助 · 仅二中心复诊口径 · ${slotLbl} · 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${assistant.name}(与部门表 / 排行榜复诊同口径)· 仅二中心复诊口径 · ${slotLbl} · 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2974,8 +2986,10 @@ async function openYejiRevisitDeptBreakdown(tb: YejiTable, row: YejiRow, revisit
|
||||
const res: any = await yejiStatsRevisitBreakdown(p as any)
|
||||
revisitBreakdownRows.value = Array.isArray(res?.rows) ? res.rows : []
|
||||
revisitBreakdownNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载复诊拆解失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载复诊拆解失败')
|
||||
revisitBreakdownRows.value = []
|
||||
revisitBreakdownNote.value = ''
|
||||
} finally {
|
||||
@@ -3014,8 +3028,10 @@ async function openUnassignedBreakdown(tb: YejiTable) {
|
||||
const res: any = await yejiStatsUnassignedBreakdown(p as any)
|
||||
unassignedDialogRows.value = Array.isArray(res?.rows) ? res.rows : []
|
||||
unassignedDialogNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载未归属拆解失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载未归属拆解失败')
|
||||
unassignedDialogRows.value = []
|
||||
unassignedDialogNote.value = ''
|
||||
} finally {
|
||||
@@ -3056,8 +3072,10 @@ async function fetchLeadLinesPage() {
|
||||
leadLinesRows.value = Array.isArray(res?.lists) ? res.lists : []
|
||||
leadLinesCount.value = Number(res?.count ?? 0)
|
||||
leadLinesApiNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载进线明细失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载进线明细失败')
|
||||
leadLinesRows.value = []
|
||||
leadLinesCount.value = 0
|
||||
leadLinesApiNote.value = ''
|
||||
@@ -3129,8 +3147,10 @@ async function fetchAppointmentLinesPage() {
|
||||
appointmentLinesRows.value = Array.isArray(res?.lists) ? res.lists : []
|
||||
appointmentLinesCount.value = Number(res?.count ?? 0)
|
||||
appointmentLinesApiNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载挂号明细失败')
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载挂号明细失败')
|
||||
appointmentLinesRows.value = []
|
||||
appointmentLinesCount.value = 0
|
||||
appointmentLinesApiNote.value = ''
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
>
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
:key="item.value"
|
||||
:key="String(item.value)"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
@@ -470,7 +470,11 @@ const loadChannelOptions = async () => {
|
||||
if (ds !== 0) return ds
|
||||
return Number(b?.id ?? 0) - Number(a?.id ?? 0)
|
||||
})
|
||||
channelOptions.value = rows
|
||||
// 统一为字符串,避免字典 value 为数字时 el-select 与表单校验不一致导致看似选了但实际未绑定
|
||||
channelOptions.value = rows.map((row: any) => ({
|
||||
...row,
|
||||
value: row?.value != null && row.value !== '' ? String(row.value) : ''
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error('加载渠道来源失败:', e)
|
||||
channelOptions.value = []
|
||||
@@ -693,7 +697,7 @@ const handleConfirm = async () => {
|
||||
appointment_time: form.appointmentTime,
|
||||
appointment_type: form.appointmentType,
|
||||
remark: form.remark,
|
||||
channel_source: form.channel_source,
|
||||
channel_source: String(form.channel_source ?? ''),
|
||||
channel_source_detail: form.channel_source_detail.trim()
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ const load = async () => {
|
||||
await loadChannels()
|
||||
const res = await appointmentLists({
|
||||
patient_id: props.diagnosisId,
|
||||
/** 诊单维度拉挂号:后端豁免医生/医助与数据范围收窄 */
|
||||
diag_scope_relax: 1,
|
||||
page_no: 1,
|
||||
page_size: 500
|
||||
})
|
||||
|
||||
@@ -683,7 +683,7 @@ const bloodTrendOption = computed(() => ({
|
||||
name: '空腹血糖',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
connectNulls: false,
|
||||
connectNulls: true,
|
||||
data: bloodTrendSeries.value.fasting,
|
||||
symbolSize: 7,
|
||||
itemStyle: {
|
||||
@@ -698,7 +698,7 @@ const bloodTrendOption = computed(() => ({
|
||||
name: '餐后血糖',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
connectNulls: false,
|
||||
connectNulls: true,
|
||||
data: bloodTrendSeries.value.postprandial,
|
||||
symbolSize: 7,
|
||||
itemStyle: {
|
||||
@@ -719,7 +719,8 @@ function formatColumnDate(date: string) {
|
||||
function parseTrendNumber(value: unknown): number | null {
|
||||
if (value === '' || value == null) return null
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
if (!Number.isFinite(parsed) || parsed === 0) return null
|
||||
return parsed
|
||||
}
|
||||
|
||||
function getCell(metric: string, date: string): { value: string; isHigh: boolean; hasRecord: boolean } {
|
||||
|
||||
@@ -274,6 +274,7 @@
|
||||
<el-descriptions-item label="用药疗程">{{ detailData.medication_days ? detailData.medication_days + ' 天' : '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="上次医护">{{ detailData.prev_staff || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="服务渠道">{{ detailData.service_channel || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="服务套餐">{{ formatServicePackage(detailData.service_package) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="费用类别">{{ feeTypeText(detailData.fee_type) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="快递单号">{{ detailData.tracking_number || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="快递公司">{{ expressCompanyLabel(detailData.express_company) }}</el-descriptions-item>
|
||||
@@ -332,7 +333,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, watch, ref } from 'vue'
|
||||
import { computed, watch, ref, onMounted } from 'vue'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import {
|
||||
prescriptionOrderLists,
|
||||
@@ -341,6 +342,7 @@ import {
|
||||
prescriptionOrderLogisticsTrace,
|
||||
prescriptionOrderPaidPayOrders
|
||||
} from '@/api/tcm'
|
||||
import { getDictData } from '@/api/app'
|
||||
import { hasPermission } from '@/utils/perm'
|
||||
import feedback from '@/utils/feedback'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@@ -563,6 +565,39 @@ function feeTypeText(t: number | undefined) {
|
||||
return m[Number(t)] ?? '—'
|
||||
}
|
||||
|
||||
// 服务套餐字典选项(与 order_list.vue 数据源一致:dict 类型 server_order)
|
||||
const servicePackageOptions = ref<Array<{ name: string; value: string }>>([])
|
||||
|
||||
async function loadServicePackageOptions() {
|
||||
try {
|
||||
const data: any = await getDictData({ type: 'server_order' })
|
||||
const list = (data?.server_order || []) as Array<{ name: string; value: string; status?: number }>
|
||||
servicePackageOptions.value = list.filter((item) => item.status !== 0)
|
||||
} catch {
|
||||
servicePackageOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function formatServicePackage(value: unknown): string {
|
||||
if (value === null || value === undefined || value === '') return '—'
|
||||
let packages: string[] = []
|
||||
if (Array.isArray(value)) {
|
||||
packages = value.map((v) => String(v)).filter((v) => v !== '')
|
||||
} else if (typeof value === 'string') {
|
||||
packages = value.split(',').map((v) => v.trim()).filter((v) => v !== '')
|
||||
}
|
||||
if (packages.length === 0) return '—'
|
||||
const names = packages.map((val) => {
|
||||
const opt = servicePackageOptions.value.find((o) => o.value === val)
|
||||
return opt ? opt.name : val
|
||||
})
|
||||
return names.join('、')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadServicePackageOptions()
|
||||
})
|
||||
|
||||
function expressCompanyLabel(v: unknown) {
|
||||
const s = String(v || '').toLowerCase()
|
||||
if (s === 'sf') return '顺丰速运'
|
||||
|
||||
@@ -797,6 +797,32 @@ function buildTcmDiagnosisListRequestPayload(req: Record<string, unknown>): Reco
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 待分配「订单月份」:未选月份时,未进入 Tab 前默认当月(与 handlePendingAssignTabClick 一致);
|
||||
* 在 Tab 内清空月份表示不限月份。
|
||||
*/
|
||||
function resolvePendingAssignOrderMonthForRequest(): string {
|
||||
const trimmed = String(formData.pending_assign_order_month ?? '').trim()
|
||||
if (trimmed !== '') return trimmed
|
||||
if (formData.pending_assign === '1') return ''
|
||||
return dayjs().format('YYYY-MM')
|
||||
}
|
||||
|
||||
/** 待分配角标 count 请求:与列表同条件,且去掉其它顶部 Tab 残留(如默认「当天挂号」) */
|
||||
function buildPendingAssignCountPayload(): Record<string, unknown> {
|
||||
return buildTcmDiagnosisListRequestPayload({
|
||||
...formData,
|
||||
page_no: 1,
|
||||
page_size: 1,
|
||||
pending_assign: 1,
|
||||
appointment_date: '',
|
||||
has_appointment: '',
|
||||
pending_booking: '',
|
||||
completed_appointment: '',
|
||||
pending_assign_order_month: resolvePendingAssignOrderMonthForRequest()
|
||||
} as Record<string, unknown>) as Record<string, unknown>
|
||||
}
|
||||
|
||||
const fetchTcmDiagnosisListsForPaging = (req: Record<string, unknown>) =>
|
||||
tcmDiagnosisLists(buildTcmDiagnosisListRequestPayload(req) as any)
|
||||
|
||||
@@ -925,16 +951,7 @@ const fetchDateCounts = async () => {
|
||||
tcmDiagnosisLists({ page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists({ has_appointment: 0, page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists({ completed_appointment: 1, page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists(
|
||||
buildTcmDiagnosisListRequestPayload({
|
||||
pending_assign: 1,
|
||||
page_no: 1,
|
||||
page_size: 1,
|
||||
pending_assign_order_month: formData.pending_assign_order_month,
|
||||
pending_assign_keyword: formData.pending_assign_keyword,
|
||||
keyword: formData.keyword
|
||||
}) as any
|
||||
)
|
||||
tcmDiagnosisLists(buildPendingAssignCountPayload() as any)
|
||||
])
|
||||
dateCounts.value = {
|
||||
[yesterdayStr.value]: yesterday?.count ?? 0,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -64,6 +64,40 @@ class AppointmentController extends BaseAdminController
|
||||
return $this->dataLists(new AppointmentLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台编辑挂号记录(消费者处方-挂号列表等,perms: doctor.appointment/edit)
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('adminEdit');
|
||||
$post = $this->request->post();
|
||||
if (array_key_exists('assistant_id', $post)) {
|
||||
$params['assistant_id'] = $post['assistant_id'];
|
||||
}
|
||||
$result = AppointmentLogic::adminEdit($params, $this->adminId, $this->adminInfo);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('保存成功');
|
||||
}
|
||||
|
||||
/** 批量修改挂号渠道来源(消费者处方-挂号列表) */
|
||||
public function batchEditChannel()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('batchEditChannel');
|
||||
$post = $this->request->post();
|
||||
if (\array_key_exists('channel_source_detail', $post)) {
|
||||
$params['channel_source_detail'] = $post['channel_source_detail'];
|
||||
}
|
||||
$result = AppointmentLogic::adminBatchEditChannel($params, $this->adminId, $this->adminInfo);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success((string) ($result['msg'] ?? '操作成功'), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情
|
||||
* @return \think\response\Json
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
|
||||
namespace app\adminapi\controller\stats;
|
||||
|
||||
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 提成结算业绩(独立于业绩看板 yejiStats 接口)
|
||||
|
||||
*
|
||||
|
||||
* - GET stats.commissionSettlement/overview
|
||||
|
||||
* - GET stats.commissionSettlement/orderLines
|
||||
|
||||
* - GET stats.commissionSettlement/confirmStatus
|
||||
|
||||
* - POST stats.commissionSettlement/saveReconcile
|
||||
|
||||
* - POST stats.commissionSettlement/confirmFinalize
|
||||
|
||||
* - POST stats.commissionSettlement/confirmRevoke
|
||||
|
||||
* - GET stats.commissionSettlement/deptOptions
|
||||
|
||||
* - GET stats.commissionSettlement/channelOptions
|
||||
|
||||
*/
|
||||
|
||||
class CommissionSettlementController extends BaseAdminController
|
||||
|
||||
{
|
||||
|
||||
public function overview()
|
||||
|
||||
{
|
||||
|
||||
@set_time_limit(120);
|
||||
|
||||
$params = $this->request->get();
|
||||
|
||||
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementOverview($params, $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function orderLines()
|
||||
|
||||
{
|
||||
|
||||
@set_time_limit(120);
|
||||
|
||||
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementOrderLines($this->request->get(), $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function confirmStatus()
|
||||
|
||||
{
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementConfirmStatus($this->request->get(), $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function saveReconcile()
|
||||
|
||||
{
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementSaveReconcile($this->request->post(), $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function confirmFinalize()
|
||||
|
||||
{
|
||||
|
||||
@set_time_limit(120);
|
||||
|
||||
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementConfirmFinalize($this->request->post(), $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function confirmRevoke()
|
||||
|
||||
{
|
||||
|
||||
return $this->data(YejiStatsLogic::commissionSettlementConfirmRevoke($this->request->post(), $this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function deptOptions()
|
||||
|
||||
{
|
||||
|
||||
return $this->data(YejiStatsLogic::deptOptions($this->adminId, $this->adminInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function channelOptions()
|
||||
|
||||
{
|
||||
|
||||
return $this->data(YejiStatsLogic::channelOptions(false));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,27 @@ class PrescriptionController extends BaseAdminController
|
||||
return $this->success('编辑成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 修正处方患者姓名手机性别
|
||||
*/
|
||||
public function patchPatient()
|
||||
{
|
||||
$params = (new PrescriptionValidate())->post()->goCheck('patchPatient');
|
||||
$ok = PrescriptionLogic::patchPatientContact(
|
||||
(int) $params['id'],
|
||||
(string) $params['patient_name'],
|
||||
(string) $params['phone'],
|
||||
(int) $params['gender'],
|
||||
(int) $this->adminId,
|
||||
$this->adminInfo
|
||||
);
|
||||
if (!$ok) {
|
||||
return $this->fail(PrescriptionLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('已更新');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除处方
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,14 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
return $this->dataLists(new PrescriptionOrderLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处方业务订单导出(与 lists 相同筛选条件,Excel;参数 export=1 预估 export=2 下载)
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
return $this->dataLists(new PrescriptionOrderLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定诊单下可关联的支付单(待支付/已支付,创建/编辑业务订单时多选关联)
|
||||
*/
|
||||
|
||||
@@ -109,17 +109,30 @@ class AuthMiddleware
|
||||
*/
|
||||
private function matchPermissionAlias(string $accessUri, array $adminUris): bool
|
||||
{
|
||||
if (!in_array('tcm.diagnosis/dailyrecord', $adminUris, true)) {
|
||||
return false;
|
||||
if (in_array('tcm.diagnosis/dailyrecord', $adminUris, true)
|
||||
&& in_array($accessUri, [
|
||||
'tcm.diagnosistodo/lists',
|
||||
'tcm.diagnosistodo/add',
|
||||
'tcm.diagnosistodo/cancel',
|
||||
'tcm.diagnosis/trackingnotes',
|
||||
'tcm.diagnosis/addtrackingnote',
|
||||
], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array($accessUri, [
|
||||
'tcm.diagnosistodo/lists',
|
||||
'tcm.diagnosistodo/add',
|
||||
'tcm.diagnosistodo/cancel',
|
||||
'tcm.diagnosis/trackingnotes',
|
||||
'tcm.diagnosis/addtrackingnote',
|
||||
], true);
|
||||
// 导出与列表共用 PrescriptionOrderLists 数据域;角色漏勾「导出订单」子权限时仍返回 权限不足
|
||||
if ($accessUri === 'tcm.prescriptionorder/export'
|
||||
&& in_array('tcm.prescriptionorder/lists', $adminUris, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 挂号列表批量改渠道:与单条编辑同一数据域,复用 doctor.appointment/edit
|
||||
if ($accessUri === 'doctor.appointment/batcheditchannel'
|
||||
&& in_array('doctor.appointment/edit', $adminUris, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,12 +28,9 @@ abstract class BaseAdminDataLists extends BaseDataLists
|
||||
protected array $adminInfo;
|
||||
protected int $adminId;
|
||||
|
||||
public function __construct()
|
||||
protected function initAdminIdentity(): void
|
||||
{
|
||||
parent::__construct();
|
||||
$this->adminInfo = $this->request->adminInfo;
|
||||
$this->adminId = $this->request->adminId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,9 +7,11 @@ use app\common\model\DiagnosisViewRecord;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\model\tcm\Prescription;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\model\dict\DictData;
|
||||
use app\common\lists\ListsExtendInterface;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
use app\common\lists\Traits\HasDataScopeFilter;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 医生预约列表
|
||||
@@ -20,6 +22,16 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
{
|
||||
use HasDataScopeFilter;
|
||||
|
||||
/**
|
||||
* 诊单编辑/详情「挂号记录」Tab:按诊单 ID 拉全量挂号,不做医生/医助角色收窄与数据范围过滤。
|
||||
* 须同时传 patient_id(挂号表存的是诊单 id)与本开关,避免列表页被滥用拓宽可见范围。
|
||||
*/
|
||||
private function appointmentListsScopeRelaxedForDiagnosis(): bool
|
||||
{
|
||||
return (int) ($this->params['diag_scope_relax'] ?? 0) === 1
|
||||
&& (int) ($this->params['patient_id'] ?? 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据隔离:医生或医助(u.assistant_id = diag.assistant_id)命中可见集合;progress_board 场景放开(看板跨医生查看)
|
||||
*/
|
||||
@@ -28,6 +40,9 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
if ($progressBoard) {
|
||||
return;
|
||||
}
|
||||
if ($this->appointmentListsScopeRelaxedForDiagnosis()) {
|
||||
return;
|
||||
}
|
||||
if (!$this->dataScopeShouldApply()) {
|
||||
return;
|
||||
}
|
||||
@@ -43,6 +58,63 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
$inList = implode(',', $ids);
|
||||
$query->whereRaw("(a.doctor_id IN ({$inList}) OR u.assistant_id IN ({$inList}))");
|
||||
}
|
||||
|
||||
/**
|
||||
* 按医助筛选:诊单指派医助或挂号记录上的医助任一命中即可
|
||||
*
|
||||
* @param mixed $query
|
||||
*/
|
||||
private function applyAssistantIdFilter($query): void
|
||||
{
|
||||
$aid = (int) ($this->params['assistant_id'] ?? 0);
|
||||
if ($aid <= 0) {
|
||||
return;
|
||||
}
|
||||
$query->where(function ($q) use ($aid): void {
|
||||
$q->where('u.assistant_id', $aid)->whereOr('a.assistant_id', $aid);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道筛选:与 AppointmentLogic 一致,兼容仅有 channel_source、仅有 channels、或两者皆有的表结构
|
||||
*
|
||||
* @param mixed $query
|
||||
*/
|
||||
private function applyChannelSourceFilter($query, string $chFilter): void
|
||||
{
|
||||
if ($chFilter === '') {
|
||||
return;
|
||||
}
|
||||
$tblFields = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = \is_array($tblFields) ? $tblFields : [];
|
||||
$hasChannelSource = \in_array('channel_source', $cols, true);
|
||||
$hasChannels = \in_array('channels', $cols, true);
|
||||
if (!$hasChannelSource && !$hasChannels) {
|
||||
return;
|
||||
}
|
||||
|
||||
$query->where(function ($q) use ($chFilter, $hasChannelSource, $hasChannels): void {
|
||||
if ($hasChannelSource && $hasChannels) {
|
||||
$q->where('a.channel_source', '=', $chFilter)
|
||||
->whereOr('a.channels', '=', $chFilter);
|
||||
if (is_numeric($chFilter)) {
|
||||
$q->whereOr('a.channels', '=', (int) $chFilter);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if ($hasChannelSource) {
|
||||
$q->where('a.channel_source', '=', $chFilter);
|
||||
|
||||
return;
|
||||
}
|
||||
$q->where('a.channels', '=', $chFilter);
|
||||
if (is_numeric($chFilter)) {
|
||||
$q->whereOr('a.channels', '=', (int) $chFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
@@ -76,6 +148,9 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
$this->searchWhere[] = ['a.status', '=', $this->params['status']];
|
||||
}
|
||||
|
||||
// 渠道字典 value(命中 channel_source 或 legacy channels)
|
||||
$chFilter = isset($this->params['channel_source']) ? trim((string) $this->params['channel_source']) : '';
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$this->searchWhere[] = ['a.appointment_date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
@@ -109,11 +184,15 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||||
->leftJoin('admin ad', 'a.doctor_id = ad.id')
|
||||
->leftJoin('admin asst', 'u.assistant_id = asst.id')
|
||||
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, u.gender as gender, u.age as age, u.weight as weight, u.height as height, u.assistant_id as assistant_id, ad.name as doctor_name, asst.name as assistant_name, u.id as diagnosis_id');
|
||||
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, u.gender as gender, u.age as age, u.weight as weight, u.height as height, u.assistant_id as assistant_id, ad.name as doctor_name, asst.name as assistant_name, u.id as diagnosis_id, a.assistant_id as appointment_assistant_id');
|
||||
if ($this->searchWhere !== []) {
|
||||
$query->where($this->searchWhere);
|
||||
}
|
||||
|
||||
$this->applyAssistantIdFilter($query);
|
||||
|
||||
$this->applyChannelSourceFilter($query, $chFilter);
|
||||
|
||||
// 是否确认诊单:1=已确认 0=未确认
|
||||
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
|
||||
$confirmed = (int)$this->params['diagnosis_confirmed'];
|
||||
@@ -128,7 +207,8 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
|
||||
// 面诊进度看板:可切换查看各医生挂号,不按当前账号角色收窄
|
||||
$progressBoard = (int) ($this->params['progress_board'] ?? 0) === 1;
|
||||
if (!$progressBoard) {
|
||||
$diagScopeRelax = $this->appointmentListsScopeRelaxedForDiagnosis();
|
||||
if (!$progressBoard && !$diagScopeRelax) {
|
||||
// 如果是医生角色(role_id=1),只显示挂自己号的预约
|
||||
if (in_array(1, $roleIds)) {
|
||||
$query->where('a.doctor_id', $this->adminId);
|
||||
@@ -198,6 +278,9 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$channelNameByValue = DictData::where('type_value', 'channels')->column('name', 'value');
|
||||
|
||||
foreach ($lists as &$item) {
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
@@ -214,6 +297,24 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
];
|
||||
$item['appointment_type_desc'] = $typeMap[$item['appointment_type']] ?? '未知';
|
||||
|
||||
$periodRaw = (string) ($item['period'] ?? ($item['type'] ?? ''));
|
||||
$periodMap = [
|
||||
'morning' => '上午',
|
||||
'afternoon' => '下午',
|
||||
'all' => '全天',
|
||||
];
|
||||
$item['period_desc'] = $periodMap[$periodRaw] ?? ($periodRaw !== '' ? $periodRaw : '—');
|
||||
|
||||
$srcKey = trim((string) ($item['channel_source'] ?? ''));
|
||||
if ($srcKey === '' && isset($item['channels']) && $item['channels'] !== '' && $item['channels'] !== null) {
|
||||
$srcKey = trim((string) $item['channels']);
|
||||
}
|
||||
if ($srcKey !== '') {
|
||||
$item['channel_source_desc'] = (string) ($channelNameByValue[$srcKey] ?? $channelNameByValue[(string) (int) $srcKey] ?? $srcKey);
|
||||
} else {
|
||||
$item['channel_source_desc'] = '—';
|
||||
}
|
||||
|
||||
$item['diagnosis_confirmed'] = isset($confirmedMap[$item['diagnosis_id'] ?? 0]) ? 1 : 0;
|
||||
$item['has_prescription'] = isset($prescribedDiagnosisIds[$item['diagnosis_id'] ?? 0]) ? 1 : 0;
|
||||
|
||||
@@ -253,6 +354,7 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
if ($applyStatusFilter && isset($this->params['status']) && $this->params['status'] !== '') {
|
||||
$query->where('a.status', '=', $this->params['status']);
|
||||
}
|
||||
$chFilter = isset($this->params['channel_source']) ? trim((string) $this->params['channel_source']) : '';
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$query->whereBetween('a.appointment_date', [$this->params['start_date'], $this->params['end_date']]);
|
||||
} elseif (!empty($this->params['start_date'])) {
|
||||
@@ -269,6 +371,10 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
$query->where('a.doctor_id', '=', (int) $this->params['doctor_id']);
|
||||
}
|
||||
|
||||
$this->applyAssistantIdFilter($query);
|
||||
|
||||
$this->applyChannelSourceFilter($query, $chFilter);
|
||||
|
||||
if ((int) ($this->params['exclude_cancelled'] ?? 0) === 1) {
|
||||
$sf = $this->params['status'] ?? '';
|
||||
if ($sf === '' || (int) $sf !== 2) {
|
||||
@@ -288,7 +394,8 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
}
|
||||
|
||||
$progressBoard = (int) ($this->params['progress_board'] ?? 0) === 1;
|
||||
if (!$progressBoard) {
|
||||
$diagScopeRelax = $this->appointmentListsScopeRelaxedForDiagnosis();
|
||||
if (!$progressBoard && !$diagScopeRelax) {
|
||||
if (in_array(1, $roleIds)) {
|
||||
$query->where('a.doctor_id', $this->adminId);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,23 @@ class CustomerLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
}
|
||||
}
|
||||
|
||||
// 添加时间:与 lists 排序口径一致(external_first_add_time 优先,0 则 create_time)
|
||||
$addStart = trim((string) ($this->params['add_time_start'] ?? ''));
|
||||
$addEnd = trim((string) ($this->params['add_time_end'] ?? ''));
|
||||
$effExpr = 'COALESCE(NULLIF(external_first_add_time, 0), create_time)';
|
||||
if ($addStart !== '') {
|
||||
$t = strtotime($addStart . ' 00:00:00');
|
||||
if ($t !== false) {
|
||||
$query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' >= ?', [$t]);
|
||||
}
|
||||
}
|
||||
if ($addEnd !== '') {
|
||||
$t = strtotime($addEnd . ' 23:59:59');
|
||||
if ($t !== false) {
|
||||
$query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' <= ?', [$t]);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\adminapi\logic\tcm\PrescriptionOrderLogic;
|
||||
use app\common\enum\ExportEnum;
|
||||
use app\common\lists\ListsExcelInterface;
|
||||
use app\common\lists\ListsExtendInterface;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
use app\common\lists\Traits\HasDataScopeFilter;
|
||||
@@ -23,7 +25,7 @@ use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use think\db\Query;
|
||||
|
||||
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
|
||||
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface, ListsExcelInterface
|
||||
{
|
||||
use HasDataScopeFilter;
|
||||
|
||||
@@ -102,12 +104,13 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$this->applyPatientIdFilter($query);
|
||||
$this->applyExpressCompanyFilter($query);
|
||||
$this->applyExpressKeywordFilter($query);
|
||||
$this->applyServiceChannelFilter($query);
|
||||
$this->applySupplyModeFilter($query);
|
||||
if (!$this->shouldBypassListVisibilityForDiagnosisEdit()) {
|
||||
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
||||
$this->applyDataScopeForPrescriptionOrder($query);
|
||||
}
|
||||
// 业绩看板侧栏等:不展示履约已取消(4),与 stats 业绩统计口径一致
|
||||
// 业绩看板侧栏等:不展示履约 4/9/10,与 stats 业绩口径一致
|
||||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||||
}
|
||||
@@ -117,6 +120,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
|
||||
/**
|
||||
* 业绩看板:仅列出二中心复诊口径下的业务订单(须同时传 assistant_id、start_time、end_time;revisit_slot=0 为全部复诊分项合计)。
|
||||
* admin 维度按订单创建人 o.creator_id(与部门表复诊列、排行榜复诊列同口径)。
|
||||
*/
|
||||
private function applyYejiErCenterRevisitOrderFilter($query): void
|
||||
{
|
||||
@@ -138,7 +142,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
if ($t0 === false || $t1 === false) {
|
||||
return;
|
||||
}
|
||||
$ids = DeptLogic::listErCenterRevisitOrderIdsForAssistant(
|
||||
$ids = DeptLogic::listErCenterRevisitOrderIdsForCreator(
|
||||
$assistantId,
|
||||
$slot,
|
||||
(int) $t0,
|
||||
@@ -197,6 +201,24 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$query->whereRaw("(($po) OR ($et))");
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务渠道:未指派传 service_channel=0 时命中「空串」或「0」(varchar)
|
||||
*/
|
||||
private function applyServiceChannelFilter(Query $query): void
|
||||
{
|
||||
$raw = $this->params['service_channel'] ?? '';
|
||||
if ($raw === '' || $raw === null) {
|
||||
return;
|
||||
}
|
||||
$v = trim((string) $raw);
|
||||
if ($v === '0') {
|
||||
$query->whereRaw("(TRIM(IFNULL(`service_channel`,'')) = '' OR `service_channel` = '0')");
|
||||
|
||||
return;
|
||||
}
|
||||
$query->where('service_channel', '=', $v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 供货方式:甘草(已上传甘草药方单号)/ 自营(无甘草单号,走药房直发等)
|
||||
*
|
||||
@@ -377,7 +399,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$this->applyDataScopeForPrescriptionOrder($query);
|
||||
}
|
||||
}
|
||||
// 与看板「合计业绩」一致:NULL 安全剔除履约已取消(4),勿用 `<>` 以免误丢 NULL 状态行
|
||||
// 与看板「合计业绩」一致:NULL 安全剔除履约 4/9/10
|
||||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||||
}
|
||||
@@ -473,7 +495,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
if ($prescriptionIds !== []) {
|
||||
$prescriptions = \app\common\model\tcm\Prescription::whereIn('id', $prescriptionIds)
|
||||
->whereNull('delete_time')
|
||||
->field(['id', 'creator_id', 'doctor_name', 'phone'])
|
||||
->field(['id', 'creator_id', 'doctor_name', 'phone', 'assistant_id'])
|
||||
->select()
|
||||
->toArray();
|
||||
$doctorIds = [];
|
||||
@@ -486,6 +508,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
'doctor_id' => $doctorId,
|
||||
'doctor_name' => $doctorName,
|
||||
'phone' => (string) ($rx['phone'] ?? ''),
|
||||
'assistant_id' => (int) ($rx['assistant_id'] ?? 0),
|
||||
];
|
||||
if ($doctorId > 0) {
|
||||
$doctorIds[] = $doctorId;
|
||||
@@ -511,7 +534,20 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
if ($diagIdsForAssist !== []) {
|
||||
$assistantByDiag = Diagnosis::whereIn('id', $diagIdsForAssist)->whereNull('delete_time')->column('assistant_id', 'id');
|
||||
}
|
||||
$assistantIds = array_values(array_unique(array_filter(array_map('intval', array_values($assistantByDiag)))));
|
||||
$assistantIds = [];
|
||||
foreach (array_values($assistantByDiag) as $v) {
|
||||
$vid = (int) $v;
|
||||
if ($vid > 0) {
|
||||
$assistantIds[] = $vid;
|
||||
}
|
||||
}
|
||||
foreach ($prescriptionCreators as $pc) {
|
||||
$aid = (int) ($pc['assistant_id'] ?? 0);
|
||||
if ($aid > 0) {
|
||||
$assistantIds[] = $aid;
|
||||
}
|
||||
}
|
||||
$assistantIds = array_values(array_unique($assistantIds));
|
||||
$assistantNames = $assistantIds !== []
|
||||
? \app\common\model\auth\Admin::whereIn('id', $assistantIds)->column('name', 'id')
|
||||
: [];
|
||||
@@ -536,25 +572,33 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$creatorId = (int) ($item['creator_id'] ?? 0);
|
||||
$item['creator_name'] = $creatorId > 0 ? ($creatorNames[$creatorId] ?? '') : '';
|
||||
|
||||
// 添加医助(来自诊单 assistant_id)
|
||||
// 医助:与处方列表一致优先关联处方 assistant_id,否则诊单 assistant_id(见 PrescriptionOrderLogic::resolveAssistantAdminIdForPrescriptionOrderRow)
|
||||
$diagIdForAssist = (int) ($item['diagnosis_id'] ?? 0);
|
||||
$assistantIdForItem = (int) ($assistantByDiag[$diagIdForAssist] ?? 0);
|
||||
$diagAst = (int) ($assistantByDiag[$diagIdForAssist] ?? 0);
|
||||
$rxId = (int) ($item['prescription_id'] ?? 0);
|
||||
$rxAst = $rxId > 0 ? (int) (($prescriptionCreators[$rxId] ?? [])['assistant_id'] ?? 0) : 0;
|
||||
$assistantIdForItem = PrescriptionOrderLogic::resolveAssistantAdminIdForPrescriptionOrderRow($rxAst, $diagAst);
|
||||
$item['assistant_id'] = $assistantIdForItem;
|
||||
$item['assistant_name'] = $assistantIdForItem > 0
|
||||
? ($assistantNames[$assistantIdForItem] ?? '')
|
||||
: '';
|
||||
|
||||
// 添加开方人姓名、处方登记手机(与业务订单收货手机比对用)
|
||||
$rxId = (int) ($item['prescription_id'] ?? 0);
|
||||
$item['doctor_name'] = $rxId > 0 ? ($prescriptionCreators[$rxId]['doctor_name'] ?? '') : '';
|
||||
$item['prescription_phone'] = $rxId > 0 ? ($prescriptionCreators[$rxId]['phone'] ?? '') : '';
|
||||
}
|
||||
unset($item);
|
||||
|
||||
$this->appendPrescriptionOrderAssignSnapshotErCenterFlags($lists);
|
||||
|
||||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) === 1) {
|
||||
PrescriptionOrderLogic::appendLinkedAppointmentsToListRows($lists, $this->params);
|
||||
}
|
||||
|
||||
if ((int) $this->export === ExportEnum::EXPORT) {
|
||||
PrescriptionOrderLogic::appendPrescriptionOrderListExportRows($lists, $this->adminInfo);
|
||||
}
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
@@ -569,13 +613,13 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
'stats_order_amount' => $s['order_amount'],
|
||||
'stats_order_amount_not_cancelled' => $s['order_amount_not_cancelled'],
|
||||
'stats_order_amount_cancelled' => $s['order_amount_cancelled'],
|
||||
/** 业绩:业务订单金额,剔除履约已取消(fulfillment_status=4),其余状态全部计入 */
|
||||
'stats_order_amount_performance' => $s['order_amount_not_cancelled'],
|
||||
/** 业绩:业务订单金额,剔除履约已取消/拒收/退款(4/9/10),NULL 与其它状态计入 */
|
||||
'stats_order_amount_performance' => $s['order_amount_performance'] ?? $s['order_amount_not_cancelled'],
|
||||
'stats_linked_pay_amount' => $s['linked_pay_amount'],
|
||||
'stats_linked_pay_amount_not_cancelled' => $s['linked_pay_amount_not_cancelled'],
|
||||
'stats_linked_pay_amount_cancelled' => $s['linked_pay_amount_cancelled'],
|
||||
/** 业绩-关联实付:剔除履约已取消订单的关联支付金额 */
|
||||
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_not_cancelled'],
|
||||
/** 业绩-关联实付:剔除履约 4/9/10 订单的关联支付金额 */
|
||||
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_performance'] ?? $s['linked_pay_amount_not_cancelled'],
|
||||
'stats_time_start' => $s['label_start'],
|
||||
'stats_time_end' => $s['label_end'],
|
||||
/** 统计口径:all=支付审核白名单全量;assistant=医助仅诊单协助业绩;restricted=与列表同域 */
|
||||
@@ -598,6 +642,40 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
return $base;
|
||||
}
|
||||
|
||||
public function setFileName(): string
|
||||
{
|
||||
return '处方业务订单';
|
||||
}
|
||||
|
||||
public function setExcelFields(): array
|
||||
{
|
||||
return [
|
||||
'export_fulfillment_status_text' => '履约状态',
|
||||
'export_order_time' => '创建时间',
|
||||
'doctor_name' => '医生姓名',
|
||||
'export_patient_name' => '患者姓名',
|
||||
'export_patient_gender' => '性别',
|
||||
'export_patient_age' => '年龄',
|
||||
'export_patient_phone' => '手机号',
|
||||
'assistant_name' => '医助名字',
|
||||
'export_center_label' => '一中心还是二中心',
|
||||
'export_assistant_dept' => '医助部门',
|
||||
'export_channel' => '渠道',
|
||||
'export_guahao_channel_source' => '自媒体渠道(挂号渠道来源)',
|
||||
'export_medication_form' => '药品形态',
|
||||
'export_medication_days' => '天数',
|
||||
'export_amount' => '总金额',
|
||||
'export_paid_amount' => '已付金额',
|
||||
'export_agency_collect' => '代收金额',
|
||||
'export_tracking_number' => '快递单号',
|
||||
'export_supply_mode' => '甘草还是自营',
|
||||
'export_gancao_prescription_cost' => '处方成本',
|
||||
'export_first_visit_assistant' => '初诊医助',
|
||||
'export_rx_audit_time' => '审核时间',
|
||||
'export_rx_auditor' => '审核人',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 业绩看板侧栏专用:约诊/接诊条数,与 YejiStatsLogic::applyConsultFiltersAlignedWithAppointmentLists 同口径
|
||||
* (doctor_appointment.status=3,appointment_date 落入区间,有效医助 COALESCE(挂号.assistant_id,诊单.assistant_id))。
|
||||
@@ -692,7 +770,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$t1 = $tmp;
|
||||
}
|
||||
$q = $this->buildBaseQueryForStats(false)->whereBetween('create_time', [$t0, $t1]);
|
||||
// 全量「合计业绩」与看板列同口径:始终剔除履约已取消(4),不依赖 exclude 参数
|
||||
// 全量「合计业绩」与看板列同口径:始终剔除履约 4/9/10,不依赖 exclude 参数
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, '');
|
||||
$amount = round((float) (clone $q)->sum('amount'), 2);
|
||||
$count = (int) (clone $q)->count();
|
||||
@@ -714,7 +792,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($pendingRxQuery, '');
|
||||
|
||||
return [
|
||||
// 处方审核待处理(不含履约已取消)
|
||||
// 处方审核待处理(不含履约 4/9/10)
|
||||
'pending_rx' => (int) $pendingRxQuery->where('prescription_audit_status', 0)->count(),
|
||||
// 支付审核待处理(前提:处方已通过)
|
||||
'pending_pay' => (int) (clone $query)->where('prescription_audit_status', 1)->where('payment_slip_audit_status', 0)->count(),
|
||||
@@ -760,8 +838,8 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
}
|
||||
|
||||
$q = $this->buildBaseQueryForStats()->whereBetween('create_time', [$t0, $t1]);
|
||||
$orderTotal = round((float) (clone $q)->sum('amount'), 2);
|
||||
$orderCancelled = round((float) (clone $q)->where('fulfillment_status', 4)->sum('amount'), 2);
|
||||
$orderTotal = round((float) (clone $q)->sum('amount'), 2);
|
||||
$orderCancelled = round((float) (clone $q)->where('fulfillment_status', 4)->sum('amount'), 2);
|
||||
$orderNotCancelled = round($orderTotal - $orderCancelled, 2);
|
||||
|
||||
$idsAll = (clone $q)->column('id');
|
||||
@@ -769,7 +847,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
return $id > 0;
|
||||
}));
|
||||
if ($idsAll === []) {
|
||||
return $this->statsExtendPayload(
|
||||
$empty = $this->statsExtendPayload(
|
||||
$orderTotal,
|
||||
$orderNotCancelled,
|
||||
$orderCancelled,
|
||||
@@ -779,6 +857,12 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$l0,
|
||||
$l1
|
||||
);
|
||||
$qPerfEmpty = clone $q;
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($qPerfEmpty, '');
|
||||
$empty['order_amount_performance'] = round((float) $qPerfEmpty->sum('amount'), 2);
|
||||
$empty['linked_pay_amount_performance'] = 0.0;
|
||||
|
||||
return $empty;
|
||||
}
|
||||
$idsCancel = (clone $q)->where('fulfillment_status', 4)->column('id');
|
||||
$idsCancel = array_values(array_filter(array_map('intval', $idsCancel), static function (int $id): bool {
|
||||
@@ -790,7 +874,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$linkedNotCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsNotCancelled);
|
||||
$linkedCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsCancel);
|
||||
|
||||
return $this->statsExtendPayload(
|
||||
$base = $this->statsExtendPayload(
|
||||
$orderTotal,
|
||||
$orderNotCancelled,
|
||||
$orderCancelled,
|
||||
@@ -800,6 +884,21 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$l0,
|
||||
$l1
|
||||
);
|
||||
|
||||
$qPerf = clone $q;
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($qPerf, '');
|
||||
$base['order_amount_performance'] = round((float) $qPerf->sum('amount'), 2);
|
||||
|
||||
$idsExcludedPerf = (clone $q)
|
||||
->whereIn('fulfillment_status', YejiStatsLogic::PRESCRIPTION_ORDER_FULFILLMENT_EXCLUDED_FROM_PERFORMANCE)
|
||||
->column('id');
|
||||
$idsExcludedPerf = array_values(array_filter(array_map('intval', $idsExcludedPerf), static function (int $id): bool {
|
||||
return $id > 0;
|
||||
}));
|
||||
$idsPerf = array_values(array_diff($idsAll, $idsExcludedPerf));
|
||||
$base['linked_pay_amount_performance'] = $this->sumLinkedPayForPrescriptionOrderIds($idsPerf);
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,7 +906,11 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
*/
|
||||
private function statsZeroPayload(): array
|
||||
{
|
||||
return $this->statsExtendPayload(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, '', '');
|
||||
$z = $this->statsExtendPayload(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, '', '');
|
||||
$z['order_amount_performance'] = 0.0;
|
||||
$z['linked_pay_amount_performance'] = 0.0;
|
||||
|
||||
return $z;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -870,16 +973,23 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$query->whereExists("SELECT 1 FROM {$rxTbl} rx WHERE rx.id = {$poTbl}.prescription_id AND rx.delete_time IS NULL AND rx.creator_id = {$doctorId}");
|
||||
}
|
||||
|
||||
/** 业绩看板入口(yeji_order_drawer=1)所有筛选统一按订单创建人;其它入口(处方订单列表 / 患者跟进等)保留旧口径(创建人 ∪ 诊单医助) */
|
||||
$yejiPanel = (int) ($this->params['yeji_order_drawer'] ?? 0) === 1;
|
||||
|
||||
if (isset($this->params['assistant_id']) && (int) $this->params['assistant_id'] > 0) {
|
||||
$assistantId = (int) $this->params['assistant_id'];
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$query->where(function ($q) use ($poTbl, $diagTbl, $assistantId) {
|
||||
$q->where('creator_id', $assistantId);
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$assistantId})"
|
||||
);
|
||||
});
|
||||
if ($yejiPanel) {
|
||||
$query->where('creator_id', $assistantId);
|
||||
} else {
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$query->where(function ($q) use ($poTbl, $diagTbl, $assistantId) {
|
||||
$q->where('creator_id', $assistantId);
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$assistantId})"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) {
|
||||
@@ -911,7 +1021,8 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
YejiStatsLogic::applyPrescriptionOrderListAlignedWinnerDeptFilter(
|
||||
$query,
|
||||
(int) $this->params['assistant_dept_id'],
|
||||
$yejiTableRowDeptIds
|
||||
$yejiTableRowDeptIds,
|
||||
$this->params['dept_ids'] ?? null
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -927,16 +1038,31 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
return;
|
||||
}
|
||||
$inList = implode(',', $deptIds);
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$adTbl = (new AdminDept())->getTable();
|
||||
$query->whereRaw(
|
||||
YejiStatsLogic::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poTbl, $diagTbl, $adTbl, $inList)
|
||||
);
|
||||
if ($yejiPanel) {
|
||||
/** 业绩看板部门侧栏:仅订单创建人人事部门 ∈ 子树(与「合计业绩」/「接诊诊单」/「复诊」聚合同口径) */
|
||||
$query->whereExists(
|
||||
"SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = `{$poTbl}`.`creator_id` AND adc.`dept_id` IN ({$inList})"
|
||||
);
|
||||
} else {
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$query->where(function ($q) use ($poTbl, $diagTbl, $adTbl, $inList) {
|
||||
$q->whereExists(
|
||||
"SELECT 1 FROM `{$adTbl}` ad INNER JOIN `{$diagTbl}` dg ON ad.admin_id = dg.assistant_id AND dg.delete_time IS NULL "
|
||||
. "WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` AND ad.dept_id IN ({$inList})"
|
||||
);
|
||||
$q->whereExists(
|
||||
"SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = `{$poTbl}`.`creator_id` AND adc.`dept_id` IN ({$inList})",
|
||||
'OR'
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 业绩看板侧栏:只保留「业绩归属医助」落在给定 admin_id 集合的订单(与 YejiStatsLogic::sqlPerformanceAttributionAdminExpr 一致)。
|
||||
* 业绩看板侧栏:只保留「业绩归属医助」落在给定 admin_id 集合的订单
|
||||
* (与 YejiStatsLogic::sqlPerformanceAttributionAdminExpr 一致——按订单创建人)。
|
||||
*
|
||||
* @param int[] $adminIds
|
||||
*/
|
||||
@@ -950,18 +1076,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
|
||||
return;
|
||||
}
|
||||
$poTbl = (new PrescriptionOrder())->getTable();
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$rxTbl = (new Prescription())->getTable();
|
||||
$inList = implode(',', $adminIds);
|
||||
$query->whereRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg "
|
||||
. "LEFT JOIN `{$rxTbl}` rx ON rx.`id` = `{$poTbl}`.`prescription_id` AND rx.`delete_time` IS NULL "
|
||||
. "WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` AND dg.`delete_time` IS NULL AND `{$poTbl}`.`diagnosis_id` > 0 AND "
|
||||
. '(CASE WHEN dg.`assistant_id` > 0 THEN dg.`assistant_id` '
|
||||
. "WHEN `{$poTbl}`.`creator_id` > 0 AND (rx.`id` IS NULL OR NOT (`{$poTbl}`.`creator_id` <=> rx.`creator_id`)) THEN `{$poTbl}`.`creator_id` "
|
||||
. 'ELSE 0 END) IN (' . $inList . '))'
|
||||
);
|
||||
$query->whereIn('creator_id', $adminIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1031,4 +1146,118 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
. ")"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表行标注:指派日志快照(related_po_creator_id + related_po_create_time)是否指向本业务单,
|
||||
* 以及该次操作的新医助(to_assistant_id)是否归属「二中心」部门子树(与 DeptLogic / 业绩看板一致)。
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $lists
|
||||
*/
|
||||
private function appendPrescriptionOrderAssignSnapshotErCenterFlags(array &$lists): void
|
||||
{
|
||||
if ($lists === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$diagIds = [];
|
||||
foreach ($lists as $row) {
|
||||
$d = (int) ($row['diagnosis_id'] ?? 0);
|
||||
if ($d > 0) {
|
||||
$diagIds[$d] = true;
|
||||
}
|
||||
}
|
||||
$diagIdList = array_keys($diagIds);
|
||||
$latestSnapLogByTriple = [];
|
||||
if ($diagIdList !== []) {
|
||||
$logRows = Db::name('tcm_diagnosis_assign_log')
|
||||
->whereIn('diagnosis_id', $diagIdList)
|
||||
->field(['id', 'diagnosis_id', 'to_assistant_id', 'related_po_creator_id', 'related_po_create_time'])
|
||||
->order('id', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($logRows as $log) {
|
||||
$d = (int) ($log['diagnosis_id'] ?? 0);
|
||||
$rcp = (int) ($log['related_po_creator_id'] ?? 0);
|
||||
$rpct = (int) ($log['related_po_create_time'] ?? 0);
|
||||
if ($d <= 0 || $rcp <= 0 || $rpct <= 0) {
|
||||
continue;
|
||||
}
|
||||
$tripleKey = $d . ':' . $rcp . ':' . $rpct;
|
||||
if (!isset($latestSnapLogByTriple[$tripleKey])) {
|
||||
$latestSnapLogByTriple[$tripleKey] = $log;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$toAssistantIdsHit = [];
|
||||
foreach ($lists as $item) {
|
||||
$diagId = (int) ($item['diagnosis_id'] ?? 0);
|
||||
$creatorId = (int) ($item['creator_id'] ?? 0);
|
||||
$poCt = (int) ($item['create_time'] ?? 0);
|
||||
$tripleKey = $diagId . ':' . $creatorId . ':' . $poCt;
|
||||
$log = $latestSnapLogByTriple[$tripleKey] ?? null;
|
||||
if (\is_array($log)) {
|
||||
$tid = (int) ($log['to_assistant_id'] ?? 0);
|
||||
if ($tid > 0) {
|
||||
$toAssistantIdsHit[$tid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$toAssistantIdList = array_keys($toAssistantIdsHit);
|
||||
|
||||
$erDeptSet = DeptLogic::getErCenterSubtreeDeptIdSet();
|
||||
$adminErCenter = [];
|
||||
if ($toAssistantIdList !== [] && $erDeptSet !== []) {
|
||||
$adRows = AdminDept::whereIn('admin_id', $toAssistantIdList)
|
||||
->field(['admin_id', 'dept_id'])
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($adRows as $ad) {
|
||||
$aid = (int) ($ad['admin_id'] ?? 0);
|
||||
$did = (int) ($ad['dept_id'] ?? 0);
|
||||
if ($aid > 0 && $did > 0 && isset($erDeptSet[$did])) {
|
||||
$adminErCenter[$aid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nameMap = [];
|
||||
if ($toAssistantIdList !== []) {
|
||||
$nameMap = \app\common\model\auth\Admin::whereIn('id', $toAssistantIdList)->column('name', 'id');
|
||||
}
|
||||
|
||||
foreach ($lists as &$item) {
|
||||
$diagId = (int) ($item['diagnosis_id'] ?? 0);
|
||||
$creatorId = (int) ($item['creator_id'] ?? 0);
|
||||
$poCt = (int) ($item['create_time'] ?? 0);
|
||||
|
||||
$item['po_assign_snapshot_hit'] = 0;
|
||||
$item['po_assign_to_assistant_id'] = 0;
|
||||
$item['po_assign_to_assistant_name'] = '';
|
||||
$item['po_assign_to_er_center'] = 0;
|
||||
$item['po_assign_is_release'] = 0;
|
||||
|
||||
if ($diagId <= 0 || $creatorId <= 0 || $poCt <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tripleKey = $diagId . ':' . $creatorId . ':' . $poCt;
|
||||
$log = $latestSnapLogByTriple[$tripleKey] ?? null;
|
||||
if (!\is_array($log)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item['po_assign_snapshot_hit'] = 1;
|
||||
$toId = (int) ($log['to_assistant_id'] ?? 0);
|
||||
$item['po_assign_to_assistant_id'] = $toId;
|
||||
if ($toId <= 0) {
|
||||
$item['po_assign_is_release'] = 1;
|
||||
continue;
|
||||
}
|
||||
$item['po_assign_to_assistant_name'] = (string) ($nameMap[$toId] ?? '');
|
||||
$item['po_assign_to_er_center'] = isset($adminErCenter[$toId]) ? 1 : 0;
|
||||
}
|
||||
unset($item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
namespace app\adminapi\logic\dept;
|
||||
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminDept;
|
||||
@@ -212,28 +213,31 @@ class DeptLogic extends BaseLogic
|
||||
->join('tcm_diagnosis dg', 'dg.id = o.diagnosis_id AND dg.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.diagnosis_id', '>', 0)
|
||||
->where('dg.assistant_id', '>', 0)
|
||||
->whereRaw('NOT (o.fulfillment_status <=> ?)', [4]);
|
||||
->where('dg.assistant_id', '>', 0);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, 'o');
|
||||
if ($orderStartTs !== null && $orderEndTs !== null) {
|
||||
$q->where('o.create_time', 'between', [$orderStartTs, $orderEndTs]);
|
||||
}
|
||||
|
||||
return $q
|
||||
->field(['o.diagnosis_id', 'o.create_time', 'o.id', 'dg.assistant_id'])
|
||||
->field(['o.diagnosis_id', 'o.create_time', 'o.id', 'dg.assistant_id', 'o.creator_id'])
|
||||
->order(['o.diagnosis_id' => 'asc', 'o.create_time' => 'asc', 'o.id' => 'asc'])
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 二中心子树:各部门复诊 rollup;同时返回按诊单医助聚合的分项(供医助排行榜)。
|
||||
* 二中心子树:各部门复诊 rollup(按订单创建人 creator_id 归属)。
|
||||
* 候选订单仍要求 dg.assistant_id > 0(保持「er-center 案例 = 有医助的诊单」的定义),
|
||||
* 但**复诊归属**改为订单创建人——leaf 部门归属取 creator_id 在二中心子树内的 canonical 部门,
|
||||
* 并产出 by_creator_slots 供医助排行榜复诊列复用,与诊金 / 接诊诊单口径完全一致。
|
||||
*
|
||||
* @param list<int> $subtreeDeptIds
|
||||
*
|
||||
* @return array{
|
||||
* totals: array<int, int>,
|
||||
* slots: array<int, array<int, int>>,
|
||||
* by_assistant_slots: array<int, array<int, int>>
|
||||
* by_creator_slots: array<int, array<int, int>>
|
||||
* }
|
||||
*/
|
||||
private static function computeErCenterRevisitFullPack(
|
||||
@@ -249,12 +253,12 @@ class DeptLogic extends BaseLogic
|
||||
$emptyTotals[$did] = 0;
|
||||
}
|
||||
if ($subtreeDeptIds === []) {
|
||||
return ['totals' => [], 'slots' => [], 'by_assistant_slots' => []];
|
||||
return ['totals' => [], 'slots' => [], 'by_creator_slots' => []];
|
||||
}
|
||||
$subtreeSet = array_fill_keys($subtreeDeptIds, true);
|
||||
$orderRows = self::fetchErCenterRevisitCandidateOrders($orderStartTs, $orderEndTs);
|
||||
if ($orderRows === []) {
|
||||
return ['totals' => $emptyTotals, 'slots' => $emptySlots, 'by_assistant_slots' => []];
|
||||
return ['totals' => $emptyTotals, 'slots' => $emptySlots, 'by_creator_slots' => []];
|
||||
}
|
||||
|
||||
$byPatient = [];
|
||||
@@ -266,29 +270,30 @@ class DeptLogic extends BaseLogic
|
||||
$byPatient[$pid][] = $r;
|
||||
}
|
||||
|
||||
$assistantNeed = [];
|
||||
$creatorNeed = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
$n = \count($orders);
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
if ($aid > 0) {
|
||||
$assistantNeed[$aid] = true;
|
||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||
if ($cid > 0) {
|
||||
$creatorNeed[$cid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$assistantIds = array_keys($assistantNeed);
|
||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree($assistantIds, $subtreeSet);
|
||||
$creatorIds = array_keys($creatorNeed);
|
||||
/** 复用 buildAssistantCanonicalDeptInSubtree —— 该方法实际是 admin → 子树内 canonical 部门,与 admin 角色无关 */
|
||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree($creatorIds, $subtreeSet);
|
||||
|
||||
/** @var array<int, array<int, int>> $leafByDeptSlot dept_id => [ slot => cnt ],slot≥2 */
|
||||
$leafByDeptSlot = [];
|
||||
/** @var array<int, array<int, int>> $byAssistantSlot */
|
||||
$byAssistantSlot = [];
|
||||
/** @var array<int, array<int, int>> $byCreatorSlot 复诊按订单创建人聚合(与诊金/接诊诊单口径一致) */
|
||||
$byCreatorSlot = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
$n = \count($orders);
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$slot = $i + 1;
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
$deptId = $canonicalDept[$aid] ?? null;
|
||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||
$deptId = $canonicalDept[$cid] ?? null;
|
||||
if ($deptId === null || !isset($subtreeSet[$deptId])) {
|
||||
continue;
|
||||
}
|
||||
@@ -296,11 +301,11 @@ class DeptLogic extends BaseLogic
|
||||
$leafByDeptSlot[$deptId] = [];
|
||||
}
|
||||
$leafByDeptSlot[$deptId][$slot] = ($leafByDeptSlot[$deptId][$slot] ?? 0) + 1;
|
||||
if ($aid > 0) {
|
||||
if (!isset($byAssistantSlot[$aid])) {
|
||||
$byAssistantSlot[$aid] = [];
|
||||
if ($cid > 0) {
|
||||
if (!isset($byCreatorSlot[$cid])) {
|
||||
$byCreatorSlot[$cid] = [];
|
||||
}
|
||||
$byAssistantSlot[$aid][$slot] = ($byAssistantSlot[$aid][$slot] ?? 0) + 1;
|
||||
$byCreatorSlot[$cid][$slot] = ($byCreatorSlot[$cid][$slot] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,16 +318,16 @@ class DeptLogic extends BaseLogic
|
||||
$rollupTotals[$id] = array_sum($rollupSlots[$id] ?? []);
|
||||
}
|
||||
|
||||
foreach ($byAssistantSlot as $aid => $sm) {
|
||||
foreach ($byCreatorSlot as $cid => $sm) {
|
||||
if ($sm !== []) {
|
||||
ksort($byAssistantSlot[$aid], SORT_NUMERIC);
|
||||
ksort($byCreatorSlot[$cid], SORT_NUMERIC);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'totals' => $rollupTotals,
|
||||
'slots' => $rollupSlots,
|
||||
'by_assistant_slots' => $byAssistantSlot,
|
||||
'by_creator_slots' => $byCreatorSlot,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -345,11 +350,11 @@ class DeptLogic extends BaseLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 二中心子树内:各医助成交的复诊业务单笔数(合计 + 复诊2/3/…分项),口径与部门 rollup 同源。
|
||||
* 二中心子树复诊:按订单创建人 creator_id 聚合(与部门 rollup 同源,与诊金/接诊诊单口径一致)。
|
||||
*
|
||||
* @return array{totals: array<int, int>, slots: array<int, array<int, int>>}
|
||||
*/
|
||||
public static function getErCenterRevisitCountsByAssistant(?int $orderStartTs = null, ?int $orderEndTs = null): array
|
||||
public static function getErCenterRevisitCountsByCreator(?int $orderStartTs = null, ?int $orderEndTs = null): array
|
||||
{
|
||||
$erRoots = self::findErCenterRootDeptIds();
|
||||
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
||||
@@ -357,42 +362,36 @@ class DeptLogic extends BaseLogic
|
||||
return ['totals' => [], 'slots' => []];
|
||||
}
|
||||
$pack = self::computeErCenterRevisitFullPack($subtreeIds, $orderStartTs, $orderEndTs);
|
||||
$by = $pack['by_assistant_slots'];
|
||||
$by = $pack['by_creator_slots'];
|
||||
$totals = [];
|
||||
$slots = [];
|
||||
foreach ($by as $aid => $slotMap) {
|
||||
$aid = (int) $aid;
|
||||
$totals[$aid] = (int) array_sum($slotMap);
|
||||
$slots[$aid] = $slotMap;
|
||||
foreach ($by as $cid => $slotMap) {
|
||||
$cid = (int) $cid;
|
||||
$totals[$cid] = (int) array_sum($slotMap);
|
||||
$slots[$cid] = $slotMap;
|
||||
}
|
||||
|
||||
return ['totals' => $totals, 'slots' => $slots];
|
||||
}
|
||||
|
||||
/**
|
||||
* 某医助在区间内、计入二中心复诊的业务订单 id(用于看板侧栏按复诊笔数下钻)。
|
||||
* 二中心子树复诊:列出某「订单创建人」在区间内、复诊序列(slot≥2)命中的业务订单 id(用于看板侧栏按复诊笔数下钻)。
|
||||
* $revisitSlot = 0 表示累计全部复诊(第 2 笔起);≥2 时仅该分项。
|
||||
*
|
||||
* @return list<int>
|
||||
*/
|
||||
public static function listErCenterRevisitOrderIdsForAssistant(
|
||||
int $assistantId,
|
||||
public static function listErCenterRevisitOrderIdsForCreator(
|
||||
int $creatorId,
|
||||
int $revisitSlot,
|
||||
int $orderStartTs,
|
||||
int $orderEndTs
|
||||
): array {
|
||||
if ($assistantId <= 0 || $orderStartTs <= 0 || $orderEndTs < $orderStartTs) {
|
||||
if ($creatorId <= 0 || $orderStartTs <= 0 || $orderEndTs < $orderStartTs) {
|
||||
return [];
|
||||
}
|
||||
if ($revisitSlot < 0 || $revisitSlot === 1) {
|
||||
return [];
|
||||
}
|
||||
$erRoots = self::findErCenterRootDeptIds();
|
||||
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
||||
if ($subtreeIds === []) {
|
||||
return [];
|
||||
}
|
||||
$subtreeSet = array_fill_keys($subtreeIds, true);
|
||||
$orderRows = self::fetchErCenterRevisitCandidateOrders($orderStartTs, $orderEndTs);
|
||||
if ($orderRows === []) {
|
||||
return [];
|
||||
@@ -405,31 +404,16 @@ class DeptLogic extends BaseLogic
|
||||
}
|
||||
$byPatient[$pid][] = $r;
|
||||
}
|
||||
$assistantNeed = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
$n = \count($orders);
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
if ($aid > 0) {
|
||||
$assistantNeed[$aid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree(array_keys($assistantNeed), $subtreeSet);
|
||||
$ids = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
$n = \count($orders);
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$slot = $i + 1;
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
if ($aid !== $assistantId) {
|
||||
continue;
|
||||
}
|
||||
if ($revisitSlot > 0 && $slot !== $revisitSlot) {
|
||||
continue;
|
||||
}
|
||||
$dcan = $canonicalDept[$aid] ?? null;
|
||||
if ($dcan === null || !isset($subtreeSet[$dcan])) {
|
||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||
if ($cid !== $creatorId) {
|
||||
continue;
|
||||
}
|
||||
$oid = (int) ($orders[$i]['id'] ?? 0);
|
||||
@@ -443,8 +427,8 @@ class DeptLogic extends BaseLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 某展示部门行(组织架构上含下级 rollup)内,二中心复诊按诊单医助拆解笔数。
|
||||
* 与看板行内「复诊」列口径一致:诊单下订单序列第 2 笔起;医助 canonical 部门须落在二中心子树且在该部门行的子树内。
|
||||
* 某展示部门行(组织架构上含下级 rollup)内,二中心复诊按**订单创建人**拆解笔数。
|
||||
* 与看板行内「复诊」列口径一致:订单序列第 2 笔起;创建人 canonical 部门须落在二中心子树且在该部门行的子树内。
|
||||
*
|
||||
* @return array{rows: list<array{admin_id: int, name: string, order_count: int}>}
|
||||
*/
|
||||
@@ -484,17 +468,17 @@ class DeptLogic extends BaseLogic
|
||||
}
|
||||
$byPatient[$pid][] = $r;
|
||||
}
|
||||
$assistantNeed = [];
|
||||
$creatorNeed = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
$n = \count($orders);
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
if ($aid > 0) {
|
||||
$assistantNeed[$aid] = true;
|
||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||
if ($cid > 0) {
|
||||
$creatorNeed[$cid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree(array_keys($assistantNeed), $erSubtreeSet);
|
||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree(array_keys($creatorNeed), $erSubtreeSet);
|
||||
/** @var array<int, int> $counts */
|
||||
$counts = [];
|
||||
foreach ($byPatient as $orders) {
|
||||
@@ -504,15 +488,15 @@ class DeptLogic extends BaseLogic
|
||||
if ($revisitSlot > 0 && $slot !== $revisitSlot) {
|
||||
continue;
|
||||
}
|
||||
$aid = (int) ($orders[$i]['assistant_id'] ?? 0);
|
||||
if ($aid <= 0) {
|
||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||
if ($cid <= 0) {
|
||||
continue;
|
||||
}
|
||||
$canon = $canonicalDept[$aid] ?? null;
|
||||
$canon = $canonicalDept[$cid] ?? null;
|
||||
if ($canon === null || !isset($erSubtreeSet[$canon]) || !isset($descFlip[$canon])) {
|
||||
continue;
|
||||
}
|
||||
$counts[$aid] = ($counts[$aid] ?? 0) + 1;
|
||||
$counts[$cid] = ($counts[$cid] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
if ($counts === []) {
|
||||
@@ -527,10 +511,10 @@ class DeptLogic extends BaseLogic
|
||||
->column('name', 'id');
|
||||
}
|
||||
$rows = [];
|
||||
foreach ($counts as $aid => $cnt) {
|
||||
foreach ($counts as $cid => $cnt) {
|
||||
$rows[] = [
|
||||
'admin_id' => (int) $aid,
|
||||
'name' => (string) ($nameRows[$aid] ?? ('#' . $aid)),
|
||||
'admin_id' => (int) $cid,
|
||||
'name' => (string) ($nameRows[$cid] ?? ('#' . $cid)),
|
||||
'order_count' => (int) $cnt,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ use app\common\service\doctor\RosterSegmentService;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\DiagnosisGuahaoLog;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\api\logic\ChatNotifyLogic;
|
||||
use app\adminapi\logic\tcm\PrescriptionLogic;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
@@ -42,6 +44,61 @@ class AppointmentLogic extends BaseLogic
|
||||
return in_array($name, self::CHANNEL_NAMES_REQUIRING_SOURCE_DETAIL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $cols Db::name('doctor_appointment')->getTableFields()
|
||||
*/
|
||||
private static function assertAppointmentChannelWritable(array $cols, string $chSrc): ?string
|
||||
{
|
||||
$hasChannelSource = in_array('channel_source', $cols, true);
|
||||
$hasChannels = in_array('channels', $cols, true);
|
||||
if (!$hasChannelSource && !$hasChannels) {
|
||||
return '挂号表缺少渠道字段(channel_source 或 channels),无法保存渠道来源';
|
||||
}
|
||||
if (!$hasChannelSource && $hasChannels && $chSrc !== '' && !is_numeric($chSrc)) {
|
||||
return '当前挂号表仅有数值型渠道字段 channels,无法写入该渠道;请在数据库增加 channel_source 列';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int|string, mixed> $cols
|
||||
*/
|
||||
private static function mergeAppointmentChannelIntoRow(array &$row, array $cols, string $chSrc, string $chDetail): void
|
||||
{
|
||||
$hasChannelSource = in_array('channel_source', $cols, true);
|
||||
$hasChannelDetail = in_array('channel_source_detail', $cols, true);
|
||||
$hasChannels = in_array('channels', $cols, true);
|
||||
if ($hasChannelSource) {
|
||||
$row['channel_source'] = $chSrc;
|
||||
}
|
||||
if ($hasChannelDetail) {
|
||||
$row['channel_source_detail'] = $chDetail;
|
||||
}
|
||||
if ($hasChannels && is_numeric($chSrc)) {
|
||||
$row['channels'] = (int) $chSrc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int|string, mixed> $cols
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function filterAppointmentRowByExistingColumns(array $row, array $cols): array
|
||||
{
|
||||
$out = [];
|
||||
foreach ($row as $k => $v) {
|
||||
if (in_array((string) $k, $cols, true)) {
|
||||
$out[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @param array $params
|
||||
@@ -262,25 +319,49 @@ class AppointmentLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'patient_id' => $params['patient_id'],
|
||||
'assistant_id'=>$params['assistant_id'],
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'roster_id' => 0, // 不再关联排班表
|
||||
$cols = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = is_array($cols) ? $cols : [];
|
||||
$channelErr = self::assertAppointmentChannelWritable($cols, $chSrc);
|
||||
if ($channelErr !== null) {
|
||||
self::setError($channelErr);
|
||||
Db::rollback();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$insert = [
|
||||
'patient_id' => (int) $params['patient_id'],
|
||||
'doctor_id' => (int) $params['doctor_id'],
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'period' => $params['period'] ?? 'all',
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $params['appointment_type'] ?? 'video',
|
||||
'remark' => $params['remark'] ?? '',
|
||||
// 表字段为 channel_source(与字典 type_value=channels 的 value 一致);勿再写入不存在的 channels 列
|
||||
'channel_source' => $chSrc,
|
||||
'channel_source_detail' => $chDetail,
|
||||
'status' => 1,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
if (in_array('roster_id', $cols, true)) {
|
||||
$insert['roster_id'] = 0;
|
||||
}
|
||||
if (in_array('assistant_id', $cols, true)) {
|
||||
$insert['assistant_id'] = (int) ($params['assistant_id'] ?? 0);
|
||||
}
|
||||
$periodVal = $params['period'] ?? 'all';
|
||||
if (in_array('period', $cols, true)) {
|
||||
$insert['period'] = $periodVal;
|
||||
} elseif (in_array('type', $cols, true)) {
|
||||
$insert['type'] = $periodVal;
|
||||
}
|
||||
self::mergeAppointmentChannelIntoRow($insert, $cols, $chSrc, $chDetail);
|
||||
|
||||
$appointment = Appointment::create($data);
|
||||
$insert = self::filterAppointmentRowByExistingColumns($insert, $cols);
|
||||
$newId = (int) Db::name('doctor_appointment')->insertGetId($insert);
|
||||
if ($newId <= 0) {
|
||||
self::setError('预约创建失败');
|
||||
Db::rollback();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
|
||||
@@ -288,21 +369,21 @@ class AppointmentLogic extends BaseLogic
|
||||
$hm = substr((string) $appointmentTime, 0, 5);
|
||||
$summary = sprintf(
|
||||
'挂号 #%d:医生「%s」,%s %s',
|
||||
(int) $appointment->id,
|
||||
$newId,
|
||||
$doctorName !== '' ? $doctorName : ('ID:' . (int) $params['doctor_id']),
|
||||
(string) $params['appointment_date'],
|
||||
$hm
|
||||
);
|
||||
self::appendDiagnosisGuahaoLog(
|
||||
(int) $params['patient_id'],
|
||||
(int) $appointment->id,
|
||||
$newId,
|
||||
$operatorAdminId,
|
||||
$operatorAdminInfo,
|
||||
'create',
|
||||
$summary
|
||||
);
|
||||
|
||||
return ['id' => $appointment->id];
|
||||
return ['id' => $newId];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
@@ -787,6 +868,368 @@ class AppointmentLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 AppointmentLists 一致的可见性(不含 progress_board / diag_scope_relax)
|
||||
*/
|
||||
private static function appointmentRowManageableByAdmin(
|
||||
Appointment $appointment,
|
||||
?Diagnosis $diag,
|
||||
int $adminId,
|
||||
array $adminInfo
|
||||
): bool {
|
||||
$roleIds = array_map('intval', AdminRole::where('admin_id', $adminId)->column('role_id'));
|
||||
|
||||
if (in_array(1, $roleIds, true) && (int) $appointment->doctor_id !== $adminId) {
|
||||
return false;
|
||||
}
|
||||
if (in_array(2, $roleIds, true)) {
|
||||
$asst = $diag ? (int) $diag->assistant_id : 0;
|
||||
if ($asst !== $adminId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DataScopeService::isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
$ids = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
if ($ids === []) {
|
||||
return false;
|
||||
}
|
||||
if ($ids === null) {
|
||||
return true;
|
||||
}
|
||||
$docId = (int) $appointment->doctor_id;
|
||||
$asstId = $diag ? (int) $diag->assistant_id : 0;
|
||||
|
||||
return in_array($docId, $ids, true)
|
||||
|| ($asstId > 0 && in_array($asstId, $ids, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台编辑挂号(预约日期/时段/类型/状态/备注/医助)
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public static function adminEdit(array $params, int $adminId, array $adminInfo): bool
|
||||
{
|
||||
try {
|
||||
$id = (int) ($params['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
self::setError('参数错误');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment = Appointment::findOrEmpty($id);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$diag = Diagnosis::where('id', (int) $appointment->patient_id)->whereNull('delete_time')->find();
|
||||
|
||||
if (!self::appointmentRowManageableByAdmin($appointment, $diag ?: null, $adminId, $adminInfo)) {
|
||||
self::setError('无权限编辑');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$period = trim((string) ($params['period'] ?? ''));
|
||||
if (!in_array($period, ['morning', 'afternoon', 'all'], true)) {
|
||||
self::setError('时段无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointmentDate = trim((string) ($params['appointment_date'] ?? ''));
|
||||
if ($appointmentDate === '') {
|
||||
self::setError('预约日期不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$rawTime = trim((string) ($params['appointment_time'] ?? ''));
|
||||
if ($rawTime === '') {
|
||||
self::setError('预约时间不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
$appointmentTime = strlen($rawTime) === 5 ? $rawTime . ':00' : $rawTime;
|
||||
|
||||
$status = (int) ($params['status'] ?? 0);
|
||||
if ($status < 1 || $status > 4) {
|
||||
self::setError('状态无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointmentType = trim((string) ($params['appointment_type'] ?? ''));
|
||||
if (!in_array($appointmentType, ['video', 'text', 'phone'], true)) {
|
||||
self::setError('预约类型无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$remark = isset($params['remark']) ? trim((string) $params['remark']) : '';
|
||||
if (mb_strlen($remark) > 500) {
|
||||
self::setError('备注过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$chSrc = trim((string) ($params['channel_source'] ?? ''));
|
||||
if ($chSrc === '') {
|
||||
self::setError('请选择渠道来源');
|
||||
|
||||
return false;
|
||||
}
|
||||
$chDetail = trim((string) ($params['channel_source_detail'] ?? ''));
|
||||
$channelDictName = trim((string) (DictData::where('type_value', 'channels')
|
||||
->where('value', $chSrc)
|
||||
->value('name') ?? ''));
|
||||
if ($channelDictName !== '' && self::channelDictNameRequiresSourceDetail($channelDictName)) {
|
||||
if ($chDetail === '') {
|
||||
self::setError('请填写自媒体渠道补充信息');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mb_strlen($chDetail) > 128) {
|
||||
self::setError('渠道补充说明过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$postAssistant = array_key_exists('assistant_id', $params);
|
||||
$assistantId = null;
|
||||
if ($postAssistant) {
|
||||
$assistantRaw = $params['assistant_id'];
|
||||
if ($assistantRaw !== null && $assistantRaw !== '') {
|
||||
$aid = (int) $assistantRaw;
|
||||
$assistantId = $aid > 0 ? $aid : null;
|
||||
}
|
||||
}
|
||||
|
||||
$patientId = (int) $appointment->patient_id;
|
||||
$doctorId = (int) $appointment->doctor_id;
|
||||
|
||||
if (in_array($status, [1, 4], true)) {
|
||||
$patientDup = Appointment::where('patient_id', $patientId)
|
||||
->where('appointment_date', $appointmentDate)
|
||||
->whereIn('status', [1, 4])
|
||||
->where('id', '<>', $id)
|
||||
->find();
|
||||
if ($patientDup) {
|
||||
self::setError('该诊单在所选日期已有其他有效挂号(已预约/已过号)');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($status === 1) {
|
||||
$slotDup = Appointment::where('doctor_id', $doctorId)
|
||||
->where('appointment_date', $appointmentDate)
|
||||
->where('appointment_time', $appointmentTime)
|
||||
->where('status', 1)
|
||||
->where('id', '<>', $id)
|
||||
->find();
|
||||
if ($slotDup) {
|
||||
self::setError('该医生在所选时段已被占用');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$beforeDate = (string) ($appointment->appointment_date ?? '');
|
||||
$beforeTimeRaw = (string) ($appointment->appointment_time ?? '');
|
||||
$beforeHm = strlen($beforeTimeRaw) >= 5 ? substr($beforeTimeRaw, 0, 5) : $beforeTimeRaw;
|
||||
$snap = $appointment->toArray();
|
||||
$beforePeriod = (string) ($snap['period'] ?? $snap['type'] ?? '');
|
||||
$beforeStatus = (int) $appointment->status;
|
||||
|
||||
$tblFields = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = is_array($tblFields) ? $tblFields : [];
|
||||
$channelErr = self::assertAppointmentChannelWritable($cols, $chSrc);
|
||||
if ($channelErr !== null) {
|
||||
self::setError($channelErr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
|
||||
$saveData = [
|
||||
'appointment_date' => $appointmentDate,
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $appointmentType,
|
||||
'status' => $status,
|
||||
'remark' => $remark,
|
||||
'update_time' => time(),
|
||||
];
|
||||
if (in_array('assistant_id', $cols, true) && $postAssistant) {
|
||||
$saveData['assistant_id'] = $assistantId;
|
||||
}
|
||||
if (in_array('period', $cols, true)) {
|
||||
$saveData['period'] = $period;
|
||||
} elseif (in_array('type', $cols, true)) {
|
||||
$saveData['type'] = $period;
|
||||
}
|
||||
self::mergeAppointmentChannelIntoRow($saveData, $cols, $chSrc, $chDetail);
|
||||
|
||||
Db::name('doctor_appointment')->where('id', $id)->update($saveData);
|
||||
|
||||
Db::commit();
|
||||
|
||||
$hm = substr($appointmentTime, 0, 5);
|
||||
$summary = sprintf(
|
||||
'后台修改挂号 #%d:日期 %s %s→%s %s,时段 %s→%s,状态 %d→%d',
|
||||
$id,
|
||||
$beforeDate,
|
||||
$beforeHm,
|
||||
$appointmentDate,
|
||||
$hm,
|
||||
$beforePeriod !== '' ? $beforePeriod : '—',
|
||||
$period,
|
||||
$beforeStatus,
|
||||
$status
|
||||
);
|
||||
self::appendDiagnosisGuahaoLog($patientId, $id, $adminId, $adminInfo, 'admin_edit', $summary);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台批量修改挂号渠道(仅渠道号与可选补充)
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
* @return array{updated:int}|false
|
||||
*/
|
||||
public static function adminBatchEditChannel(array $params, int $adminId, array $adminInfo)
|
||||
{
|
||||
try {
|
||||
$idsRaw = $params['ids'] ?? [];
|
||||
if (!\is_array($idsRaw) || $idsRaw === []) {
|
||||
self::setError('请选择要修改的挂号记录');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($idsRaw as $raw) {
|
||||
$id = (int) $raw;
|
||||
if ($id > 0) {
|
||||
$ids[$id] = $id;
|
||||
}
|
||||
}
|
||||
$ids = array_values($ids);
|
||||
$maxBatch = 300;
|
||||
if (\count($ids) === 0) {
|
||||
self::setError('预约ID无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (\count($ids) > $maxBatch) {
|
||||
self::setError('单次最多修改 ' . $maxBatch . ' 条挂号');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$chSrc = trim((string) ($params['channel_source'] ?? ''));
|
||||
if ($chSrc === '') {
|
||||
self::setError('请选择渠道来源');
|
||||
|
||||
return false;
|
||||
}
|
||||
$chDetail = isset($params['channel_source_detail']) ? trim((string) $params['channel_source_detail']) : '';
|
||||
$channelDictName = trim((string) (DictData::where('type_value', 'channels')
|
||||
->where('value', $chSrc)
|
||||
->value('name') ?? ''));
|
||||
if ($channelDictName !== '' && self::channelDictNameRequiresSourceDetail($channelDictName)) {
|
||||
if ($chDetail === '') {
|
||||
self::setError('请填写自媒体渠道补充信息');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mb_strlen($chDetail) > 128) {
|
||||
self::setError('渠道补充说明过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$tblFields = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = \is_array($tblFields) ? $tblFields : [];
|
||||
$channelErr = self::assertAppointmentChannelWritable($cols, $chSrc);
|
||||
if ($channelErr !== null) {
|
||||
self::setError($channelErr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
|
||||
$updated = 0;
|
||||
foreach ($ids as $id) {
|
||||
$appointment = Appointment::findOrEmpty((int) $id);
|
||||
if ($appointment->isEmpty()) {
|
||||
Db::rollback();
|
||||
self::setError('预约记录不存在(ID ' . $id . ')');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$diag = Diagnosis::where('id', (int) $appointment->patient_id)->whereNull('delete_time')->find();
|
||||
|
||||
if (!self::appointmentRowManageableByAdmin($appointment, $diag ?: null, $adminId, $adminInfo)) {
|
||||
Db::rollback();
|
||||
self::setError('无权限修改挂号 #' . $id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$saveData = [
|
||||
'update_time' => time(),
|
||||
];
|
||||
self::mergeAppointmentChannelIntoRow($saveData, $cols, $chSrc, $chDetail);
|
||||
|
||||
$saveData = self::filterAppointmentRowByExistingColumns($saveData, $cols);
|
||||
|
||||
Db::name('doctor_appointment')->where('id', $id)->update($saveData);
|
||||
|
||||
$patientId = (int) $appointment->patient_id;
|
||||
self::appendDiagnosisGuahaoLog(
|
||||
$patientId,
|
||||
(int) $id,
|
||||
$adminId,
|
||||
$adminInfo,
|
||||
'admin_batch_channel',
|
||||
sprintf('批量修改挂号 #%d:渠道设为 %s', $id, $chSrc !== '' ? $chSrc : '—')
|
||||
);
|
||||
++$updated;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
|
||||
return [
|
||||
'updated' => $updated,
|
||||
'msg' => sprintf('已更新 %d 条挂号渠道', $updated),
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 诊单列表维度:记录谁在后台发起挂号 / 取消挂号(写入失败不影响主流程)
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\common\model\finance\AccountCost;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
@@ -1041,8 +1042,9 @@ class ConversionLogic
|
||||
->alias('po')
|
||||
->leftJoin('tcm_diagnosis dg', 'dg.id = po.diagnosis_id')
|
||||
->whereNull('po.delete_time')
|
||||
->where('po.create_time', 'between', [$startTimestamp, $endTimestamp])
|
||||
->where('po.fulfillment_status', '<>', 4)
|
||||
->where('po.create_time', 'between', [$startTimestamp, $endTimestamp]);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($businessQuery, 'po');
|
||||
$businessQuery
|
||||
->fieldRaw("{$businessSourceExpr} AS source_admin_id, SUM(po.amount) AS total_amount")
|
||||
->group($businessSourceExpr);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use think\facade\Db;
|
||||
@@ -14,7 +15,7 @@ use think\facade\Db;
|
||||
* 医生范围:<b>admin_role.role_id = 1</b> 且管理员 <b>未软删</b>(delete_time 为空);再按账号「数据范围」收窄。
|
||||
*
|
||||
* - 系统/手动开方:tcm_prescription.prescription_date ∈ [start,end];渠道/标签与业绩渠道列同源 EXISTS / 诊单标签
|
||||
* - 成交:订单 create_time、排除履约(4),按处方 creator_id;渠道/标签同 sumPerformance 口径
|
||||
* - 成交:订单 create_time、排除履约 4/9/10,按处方 creator_id;渠道/标签同 sumPerformance 口径
|
||||
* - 挂号:appointment_date ∈ [start,end];选渠道时挂号 channels 命中字典值;标签渠道时 patient_id ∈ 标签诊单集
|
||||
*/
|
||||
class DoctorDailyStatsLogic
|
||||
@@ -300,9 +301,9 @@ class DoctorDailyStatsLogic
|
||||
->alias('o')
|
||||
->join('tcm_prescription rx', 'rx.id = o.prescription_id AND rx.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->whereBetween('o.create_time', [$startTs, $endTs])
|
||||
->whereRaw('NOT (o.fulfillment_status <=> 4)')
|
||||
->whereIn('rx.creator_id', $doctorIds)
|
||||
->whereBetween('o.create_time', [$startTs, $endTs]);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, 'o');
|
||||
$q->whereIn('rx.creator_id', $doctorIds)
|
||||
->where('o.diagnosis_id', '>', 0);
|
||||
|
||||
$normCh = self::normalizeAppointmentChannelInts($appointmentChannelValues);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -436,6 +436,110 @@ class PrescriptionLogic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅修正处方笺展示用患者姓名、手机号与性别(zyt_tcm_prescription),不改变审核状态与其它字段
|
||||
*/
|
||||
public static function patchPatientContact(int $rxId, string $patientName, string $phone, int $gender, int $adminId, array $adminInfo): bool
|
||||
{
|
||||
self::$error = '';
|
||||
$prescription = Prescription::find($rxId);
|
||||
if (!$prescription) {
|
||||
self::setError('处方不存在');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!self::canViewPrescription($prescription, $adminId, $adminInfo)) {
|
||||
self::setError('无权限修改此处方');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$patientName = trim($patientName);
|
||||
$phone = trim($phone);
|
||||
if ($patientName === '') {
|
||||
self::setError('患者姓名不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (mb_strlen($patientName) > 50) {
|
||||
self::setError('患者姓名过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
if ($phone === '') {
|
||||
self::setError('手机号不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (strlen($phone) > 20) {
|
||||
self::setError('手机号过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!in_array($gender, [0, 1], true)) {
|
||||
self::setError('性别无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldName = trim((string) ($prescription->patient_name ?? ''));
|
||||
$oldPhone = trim((string) ($prescription->phone ?? ''));
|
||||
$oldGender = (int) ($prescription->gender ?? 0);
|
||||
|
||||
if ($oldName === $patientName && $oldPhone === $phone && $oldGender === $gender) {
|
||||
self::setError('信息未变更');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$genderText = static function (int $g): string {
|
||||
if ($g === 1) {
|
||||
return '男';
|
||||
}
|
||||
if ($g === 0) {
|
||||
return '女';
|
||||
}
|
||||
|
||||
return '未知';
|
||||
};
|
||||
|
||||
try {
|
||||
$prescription->save([
|
||||
'patient_name' => $patientName,
|
||||
'phone' => $phone,
|
||||
'gender' => $gender,
|
||||
]);
|
||||
|
||||
$latestPo = PrescriptionOrder::where('prescription_id', $rxId)
|
||||
->whereNull('delete_time')
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
$logOrderId = $latestPo ? (int) $latestPo->id : 0;
|
||||
|
||||
$nameFrom = $oldName === '' ? '—' : $oldName;
|
||||
$phoneFrom = $oldPhone === '' ? '—' : $oldPhone;
|
||||
$genderFrom = $genderText($oldGender);
|
||||
$genderTo = $genderText($gender);
|
||||
$summary = sprintf(
|
||||
'处方 #%d:患者姓名「%s」→「%s」,手机「%s」→「%s」,性别「%s」→「%s」',
|
||||
$rxId,
|
||||
$nameFrom,
|
||||
$patientName,
|
||||
$phoneFrom,
|
||||
$phone,
|
||||
$genderFrom,
|
||||
$genderTo
|
||||
);
|
||||
PrescriptionOrderLogic::appendRxPatientPatchLog($logOrderId, $adminId, $adminInfo, $summary);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
self::setError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除处方
|
||||
*/
|
||||
|
||||
@@ -14,11 +14,13 @@ use app\common\model\tcm\Prescription;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\model\tcm\PrescriptionOrderLog;
|
||||
use app\common\model\tcm\PrescriptionOrderPayOrder;
|
||||
use app\common\model\dict\DictData;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\common\service\ExpressTrackService;
|
||||
use app\common\service\gancao\GancaoScmRecipelService;
|
||||
use think\db\Query;
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
@@ -268,6 +270,166 @@ class PrescriptionOrderLogic
|
||||
return self::roleIntersect($adminInfo, $allow);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与业务订单列表金额统计一致:命中「统计医助」角色且非农单支付审核档时,业务侧按「仅限本人关联诊单的业绩域」收口。
|
||||
* 提成结算等对业绩归因 SQL 收窄时需与之对齐。
|
||||
*/
|
||||
public static function statsRestrictedToOwnAssistantPerformanceDomain(array $adminInfo): bool
|
||||
{
|
||||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||||
return false;
|
||||
}
|
||||
if (self::canViewOrderListStatsAllScope($adminInfo)) {
|
||||
return false;
|
||||
}
|
||||
$assistantRid = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2);
|
||||
|
||||
return $assistantRid > 0 && self::roleIntersect($adminInfo, [$assistantRid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提成结算等与列表对齐:按开方医生 / 诊单医助 ∪ 订单创建人 筛选(与 PrescriptionOrderLists::applyDoctorAssistantFilters 在非业绩抽屉场景一致)。
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public static function applyCommissionOrderDoctorAssistantFilters(Query $query, string $alias, array $params): void
|
||||
{
|
||||
if (isset($params['doctor_id']) && (int) $params['doctor_id'] > 0) {
|
||||
$doctorId = (int) $params['doctor_id'];
|
||||
$rxTbl = (new Prescription())->getTable();
|
||||
$query->whereExists(
|
||||
"SELECT 1 FROM `{$rxTbl}` rx WHERE rx.`id` = `{$alias}`.`prescription_id` "
|
||||
. "AND rx.`delete_time` IS NULL AND rx.`creator_id` = {$doctorId}"
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['assistant_id']) && (int) $params['assistant_id'] > 0) {
|
||||
$assistantId = (int) $params['assistant_id'];
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$query->where(function ($q) use ($alias, $diagTbl, $assistantId): void {
|
||||
$q->where("{$alias}.creator_id", $assistantId);
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$assistantId})"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 与处方订单列表 HasDataScopeFilter + applyCreatorOrOwnPrescriptionVisibility 等价;主业务订单别名 $alias。
|
||||
*
|
||||
* @param array<string, mixed> $params 可含 assistant_id(显式收窄时校验数据域)
|
||||
*/
|
||||
public static function applyPrescriptionOrderListAccessForCommissionQuery(
|
||||
Query $query,
|
||||
string $alias,
|
||||
int $viewerAdminId,
|
||||
array $viewerAdminInfo,
|
||||
array $params = []
|
||||
): void {
|
||||
if ($viewerAdminId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::applyCommissionOrderDoctorAssistantFilters($query, $alias, $params);
|
||||
|
||||
if (self::canSeeAllPrescriptionOrders($viewerAdminInfo)) {
|
||||
self::applyCommissionOrderDataScope($query, $alias, $viewerAdminId, $viewerAdminInfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$rxTbl = (new Prescription())->getTable();
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
|
||||
$explicitAssistant = (int) ($params['assistant_id'] ?? 0);
|
||||
$allowExplicitAssistantVisibility = false;
|
||||
if ($explicitAssistant > 0) {
|
||||
if (!DataScopeService::isEnabled()) {
|
||||
$allowExplicitAssistantVisibility = true;
|
||||
} else {
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($viewerAdminId, $viewerAdminInfo);
|
||||
if ($visibleIds === null || in_array($explicitAssistant, $visibleIds, true)) {
|
||||
$allowExplicitAssistantVisibility = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query->where(function ($q) use (
|
||||
$alias,
|
||||
$rxTbl,
|
||||
$diagTbl,
|
||||
$viewerAdminId,
|
||||
$explicitAssistant,
|
||||
$allowExplicitAssistantVisibility,
|
||||
$viewerAdminInfo
|
||||
): void {
|
||||
if (self::canViewOrdersForOwnPrescription($viewerAdminInfo)) {
|
||||
$q->where(function ($qq) use ($alias, $rxTbl, $diagTbl, $viewerAdminId): void {
|
||||
$qq->where("{$alias}.creator_id", $viewerAdminId);
|
||||
$qq->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$rxTbl}` rx WHERE rx.`id` = `{$alias}`.`prescription_id`"
|
||||
. " AND rx.`delete_time` IS NULL AND rx.`creator_id` = {$viewerAdminId})"
|
||||
);
|
||||
$qq->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$viewerAdminId})"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
$q->where(function ($qq) use ($alias, $diagTbl, $viewerAdminId): void {
|
||||
$qq->where("{$alias}.creator_id", $viewerAdminId);
|
||||
$qq->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$viewerAdminId})"
|
||||
);
|
||||
});
|
||||
}
|
||||
if ($allowExplicitAssistantVisibility) {
|
||||
$q->whereOr("{$alias}.creator_id", $explicitAssistant);
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`"
|
||||
. " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$explicitAssistant})"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
self::applyCommissionOrderDataScope($query, $alias, $viewerAdminId, $viewerAdminInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表数据域:创建人 ∈ 可见 或 诊单医助 ∈ 可见。
|
||||
*/
|
||||
private static function applyCommissionOrderDataScope(
|
||||
Query $query,
|
||||
string $alias,
|
||||
int $viewerAdminId,
|
||||
array $viewerAdminInfo
|
||||
): void {
|
||||
if (!DataScopeService::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$ids = DataScopeService::getVisibleAdminIds($viewerAdminId, $viewerAdminInfo);
|
||||
if ($ids === null) {
|
||||
return;
|
||||
}
|
||||
if ($ids === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$inList = implode(',', array_map('intval', $ids));
|
||||
$query->where(function ($q) use ($alias, $diagTbl, $inList): void {
|
||||
$q->whereIn("{$alias}.creator_id", explode(',', $inList));
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id` "
|
||||
. "AND dg.`delete_time` IS NULL AND dg.`assistant_id` IN ({$inList}))"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $row
|
||||
*/
|
||||
@@ -935,7 +1097,7 @@ class PrescriptionOrderLogic
|
||||
$phoneFrom,
|
||||
$phone
|
||||
);
|
||||
self::writeLog($prescriptionOrderId, $adminId, $adminInfo, 'patch_rx_patient', $summary);
|
||||
self::appendRxPatientPatchLog($prescriptionOrderId, $adminId, $adminInfo, $summary);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -1280,16 +1442,16 @@ class PrescriptionOrderLogic
|
||||
|
||||
return false;
|
||||
}
|
||||
// 已成功提交甘草 SCM(生成了甘草处方单号 或 submit_time>0),再改本地信息会与甘草侧不一致,禁止编辑
|
||||
if ($fs === 6) {
|
||||
self::$error = '已签收订单不可修改快递单号';
|
||||
|
||||
return false;
|
||||
}
|
||||
// 已成功提交甘草 SCM:仅允许更新快递单号与承运商(与甘草侧地址/药方等仍以取消流程为准)
|
||||
$gcOrderNo = trim((string) $order->gancao_reciperl_order_no);
|
||||
$gcSubmitTime = (int) $order->gancao_submit_time;
|
||||
if ($gcOrderNo !== '' || $gcSubmitTime > 0) {
|
||||
self::$error = sprintf(
|
||||
'订单已提交甘草(处方单号:%s),不可再编辑;如需修改请先走取消流程',
|
||||
$gcOrderNo !== '' ? $gcOrderNo : '—'
|
||||
);
|
||||
|
||||
return false;
|
||||
return self::editGancaoLogisticsOnly($order, $params, $adminId, $adminInfo);
|
||||
}
|
||||
|
||||
$medDays = $params['medication_days'] ?? null;
|
||||
@@ -1423,6 +1585,47 @@ class PrescriptionOrderLogic
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 甘草已提交后仅更新物流字段(tracking_number、express_company),忽略金额/地址等其它请求参数。
|
||||
*
|
||||
* @param array<string,mixed> $params
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
private static function editGancaoLogisticsOnly(
|
||||
PrescriptionOrder $order,
|
||||
array $params,
|
||||
int $adminId,
|
||||
array $adminInfo
|
||||
) {
|
||||
$order->tracking_number = mb_substr(trim((string) ($params['tracking_number'] ?? '')), 0, 80);
|
||||
if (array_key_exists('express_company', $params)) {
|
||||
$order->express_company = self::normalizeExpressCompany($params['express_company']);
|
||||
}
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog(
|
||||
(int) $order->id,
|
||||
$adminId,
|
||||
$adminInfo,
|
||||
'edit',
|
||||
'甘草订单已提交,仅更新快递信息(单号/承运商)'
|
||||
);
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::maskRemarkExtraIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认发货:更新快递信息并将 fulfillment_status 从 2(履约中)推进到 5(已发货)
|
||||
*
|
||||
@@ -2263,7 +2466,7 @@ class PrescriptionOrderLogic
|
||||
return $out;
|
||||
}
|
||||
|
||||
private static function fulfillmentStatusLabel(int $fs): string
|
||||
public static function fulfillmentStatusLabel(int $fs): string
|
||||
{
|
||||
$m = [
|
||||
1 => '待双审通过',
|
||||
@@ -2283,6 +2486,322 @@ class PrescriptionOrderLogic
|
||||
return $m[$fs] ?? ('状态' . (string) $fs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务订单列表/导出:「诊单医助」admin id。
|
||||
* 与 {@see PrescriptionLists} 一致优先 {@see Prescription::$assistant_id}(开方时从诊单快照到处方表);
|
||||
* 为 0 时再取诊单当前 {@see Diagnosis::$assistant_id}(兼容历史处方或未写入快照列的数据)。
|
||||
*/
|
||||
public static function resolveAssistantAdminIdForPrescriptionOrderRow(
|
||||
int $prescriptionAssistantId,
|
||||
int $diagnosisAssistantId
|
||||
): int {
|
||||
if ($prescriptionAssistantId > 0) {
|
||||
return $prescriptionAssistantId;
|
||||
}
|
||||
if ($diagnosisAssistantId > 0) {
|
||||
return $diagnosisAssistantId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出用:诊单医助在 admin_dept 中的部门路径(多部门「;」、路径内「 / 」)
|
||||
*/
|
||||
public static function formatAssistantDeptPathForExport(int $assistantAdminId): string
|
||||
{
|
||||
if ($assistantAdminId <= 0) {
|
||||
return '';
|
||||
}
|
||||
$deptIds = Db::name('admin_dept')->where('admin_id', $assistantAdminId)->column('dept_id');
|
||||
$pathSegments = [];
|
||||
foreach ($deptIds as $did) {
|
||||
$p = self::buildDeptPath((int) $did);
|
||||
if ($p !== '') {
|
||||
$pathSegments[] = $p;
|
||||
}
|
||||
}
|
||||
$pathSegments = array_values(array_unique($pathSegments));
|
||||
|
||||
return $pathSegments === [] ? '' : implode(';', $pathSegments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出用:药品形态(丸剂 / 饮片+代煎|自煎等)
|
||||
*
|
||||
* @param array<string, mixed> $rx
|
||||
*/
|
||||
public static function formatMedicationFormForExport(array $rx): string
|
||||
{
|
||||
$type = trim((string) ($rx['prescription_type'] ?? ''));
|
||||
if ($type === '') {
|
||||
return '';
|
||||
}
|
||||
if ($type === '饮片') {
|
||||
$nd = (int) ($rx['need_decoction'] ?? 0);
|
||||
|
||||
return $type . ($nd === 1 ? '(代煎)' : '(自煎)');
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出列:挂号表渠道来源展示(与 AppointmentLists channel_source_desc 同字典口径)
|
||||
*
|
||||
* @param array<string, mixed> $apRow doctor_appointment 一行
|
||||
* @param array<string, string> $channelNameByValue DictData channels value=>name
|
||||
*/
|
||||
private static function formatGuahaoChannelSourceForExport(array $apRow, array $channelNameByValue): string
|
||||
{
|
||||
$srcKey = trim((string) ($apRow['channel_source'] ?? ''));
|
||||
if ($srcKey === '' && isset($apRow['channels']) && $apRow['channels'] !== '' && $apRow['channels'] !== null) {
|
||||
$srcKey = trim((string) $apRow['channels']);
|
||||
}
|
||||
$label = '';
|
||||
if ($srcKey !== '') {
|
||||
$label = (string) ($channelNameByValue[$srcKey] ?? $channelNameByValue[(string) (int) $srcKey] ?? $srcKey);
|
||||
}
|
||||
$detail = trim((string) ($apRow['channel_source_detail'] ?? ''));
|
||||
if ($label === '' && $detail === '') {
|
||||
return '';
|
||||
}
|
||||
if ($detail !== '') {
|
||||
return $label !== '' ? "{$label}({$detail})" : $detail;
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号表渠道相关列是否存在(未加 channel_source 的老库仅查 channels 或整列留空)
|
||||
*
|
||||
* @return array{channel_source: bool, channel_source_detail: bool, channels: bool}
|
||||
*/
|
||||
private static function doctorAppointmentChannelColumnsForExport(): array
|
||||
{
|
||||
$out = [
|
||||
'channel_source' => false,
|
||||
'channel_source_detail' => false,
|
||||
'channels' => false,
|
||||
];
|
||||
try {
|
||||
$cols = Db::name('doctor_appointment')->getTableFields();
|
||||
if (!is_array($cols)) {
|
||||
return $out;
|
||||
}
|
||||
foreach (array_keys($out) as $k) {
|
||||
$out[$k] = in_array($k, $cols, true);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// keep false
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务订单列表导出:写入与 PrescriptionOrderLists::setExcelFields 对应的字段(export=2 时由列表类调用)
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $lists
|
||||
*/
|
||||
public static function appendPrescriptionOrderListExportRows(array &$lists, array $adminInfo): void
|
||||
{
|
||||
if ($lists === []) {
|
||||
return;
|
||||
}
|
||||
$diagIds = [];
|
||||
$rxIds = [];
|
||||
$poIds = [];
|
||||
foreach ($lists as $row) {
|
||||
$poIds[] = (int) ($row['id'] ?? 0);
|
||||
$d = (int) ($row['diagnosis_id'] ?? 0);
|
||||
if ($d > 0) {
|
||||
$diagIds[$d] = true;
|
||||
}
|
||||
$r = (int) ($row['prescription_id'] ?? 0);
|
||||
if ($r > 0) {
|
||||
$rxIds[$r] = true;
|
||||
}
|
||||
}
|
||||
$poIds = array_values(array_filter(array_unique($poIds), static fn (int $id): bool => $id > 0));
|
||||
$diagIdList = array_keys($diagIds);
|
||||
$rxIdList = array_keys($rxIds);
|
||||
|
||||
$diagById = [];
|
||||
if ($diagIdList !== []) {
|
||||
$diagRows = Diagnosis::whereIn('id', $diagIdList)->whereNull('delete_time')
|
||||
->field(['id', 'patient_name', 'gender', 'age', 'phone', 'assistant_id'])
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($diagRows as $dr) {
|
||||
$diagById[(int) $dr['id']] = $dr;
|
||||
}
|
||||
}
|
||||
|
||||
/** 诊单 ID => 该患者最近一条挂号(按预约日、id 倒序),用于导出「挂号渠道来源」 */
|
||||
$apptChannelByDiagId = [];
|
||||
$channelNameByValue = [];
|
||||
$chCols = self::doctorAppointmentChannelColumnsForExport();
|
||||
$needGuahaoChannel = $chCols['channel_source'] || $chCols['channels'] || $chCols['channel_source_detail'];
|
||||
if ($diagIdList !== [] && $needGuahaoChannel) {
|
||||
$selectFields = ['patient_id', 'appointment_date', 'id'];
|
||||
if ($chCols['channel_source']) {
|
||||
$selectFields[] = 'channel_source';
|
||||
}
|
||||
if ($chCols['channel_source_detail']) {
|
||||
$selectFields[] = 'channel_source_detail';
|
||||
}
|
||||
if ($chCols['channels']) {
|
||||
$selectFields[] = 'channels';
|
||||
}
|
||||
$apptRows = Appointment::whereIn('patient_id', $diagIdList)
|
||||
->field($selectFields)
|
||||
->order('appointment_date', 'desc')
|
||||
->order('id', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($apptRows as $ar) {
|
||||
$p = (int) ($ar['patient_id'] ?? 0);
|
||||
if ($p > 0 && !isset($apptChannelByDiagId[$p])) {
|
||||
$apptChannelByDiagId[$p] = $ar;
|
||||
}
|
||||
}
|
||||
if ($chCols['channel_source'] || $chCols['channels']) {
|
||||
$channelNameByValue = DictData::where('type_value', 'channels')->column('name', 'value');
|
||||
}
|
||||
}
|
||||
|
||||
$rxById = [];
|
||||
if ($rxIdList !== []) {
|
||||
$rxRows = Prescription::whereIn('id', $rxIdList)->whereNull('delete_time')
|
||||
->field(['id', 'prescription_type', 'need_decoction', 'dose_unit', 'assistant_id'])
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($rxRows as $xr) {
|
||||
$rxById[(int) $xr['id']] = $xr;
|
||||
}
|
||||
}
|
||||
|
||||
$logRows = PrescriptionOrderLog::whereIn('prescription_order_id', $poIds)
|
||||
->whereIn('action', ['audit_rx_approve', 'audit_rx_reject', 'revoke_rx_audit'])
|
||||
->field(['prescription_order_id', 'id', 'action', 'admin_name', 'create_time'])
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$auditEffectiveByPo = [];
|
||||
foreach ($logRows as $lg) {
|
||||
$pid = (int) ($lg['prescription_order_id'] ?? 0);
|
||||
if ($pid <= 0) {
|
||||
continue;
|
||||
}
|
||||
$act = (string) ($lg['action'] ?? '');
|
||||
if ($act === 'revoke_rx_audit') {
|
||||
$auditEffectiveByPo[$pid] = null;
|
||||
continue;
|
||||
}
|
||||
if ($act === 'audit_rx_approve' || $act === 'audit_rx_reject') {
|
||||
$auditEffectiveByPo[$pid] = $lg;
|
||||
}
|
||||
}
|
||||
|
||||
$assistantDeptCache = [];
|
||||
|
||||
foreach ($lists as &$item) {
|
||||
$poId = (int) ($item['id'] ?? 0);
|
||||
$fs = (int) ($item['fulfillment_status'] ?? 0);
|
||||
$item['export_fulfillment_status_text'] = self::fulfillmentStatusLabel($fs);
|
||||
$ct = (int) ($item['create_time'] ?? 0);
|
||||
$item['export_order_time'] = $ct > 0 ? date('Y-m-d H:i:s', $ct) : '';
|
||||
|
||||
$diagId = (int) ($item['diagnosis_id'] ?? 0);
|
||||
$dg = $diagById[$diagId] ?? null;
|
||||
$rxId = (int) ($item['prescription_id'] ?? 0);
|
||||
$rx = $rxById[$rxId] ?? [];
|
||||
$item['export_patient_name'] = \is_array($dg) ? (string) ($dg['patient_name'] ?? '') : '';
|
||||
if (\is_array($dg)) {
|
||||
$g = (int) ($dg['gender'] ?? 0);
|
||||
$item['export_patient_gender'] = $g === 1 ? '男' : '女';
|
||||
$item['export_patient_age'] = (string) ($dg['age'] ?? '');
|
||||
$item['export_patient_phone'] = (string) ($dg['phone'] ?? '');
|
||||
$rxAst = \is_array($rx) ? (int) ($rx['assistant_id'] ?? 0) : 0;
|
||||
$diagAst = (int) ($dg['assistant_id'] ?? 0);
|
||||
$astId = self::resolveAssistantAdminIdForPrescriptionOrderRow($rxAst, $diagAst);
|
||||
if ($astId > 0) {
|
||||
if (!isset($assistantDeptCache[$astId])) {
|
||||
$assistantDeptCache[$astId] = self::formatAssistantDeptPathForExport($astId);
|
||||
}
|
||||
$item['export_assistant_dept'] = $assistantDeptCache[$astId];
|
||||
} else {
|
||||
$item['export_assistant_dept'] = '';
|
||||
}
|
||||
} else {
|
||||
$item['export_patient_gender'] = '';
|
||||
$item['export_patient_age'] = '';
|
||||
$item['export_patient_phone'] = '';
|
||||
$item['export_assistant_dept'] = '';
|
||||
}
|
||||
|
||||
$item['export_medication_form'] = self::formatMedicationFormForExport(\is_array($rx) ? $rx : []);
|
||||
|
||||
$item['export_channel'] = (string) ($item['service_channel'] ?? '');
|
||||
$apCh = $apptChannelByDiagId[$diagId] ?? null;
|
||||
$item['export_guahao_channel_source'] = \is_array($apCh)
|
||||
? self::formatGuahaoChannelSourceForExport($apCh, $channelNameByValue)
|
||||
: '';
|
||||
|
||||
$md = $item['medication_days'] ?? null;
|
||||
$item['export_medication_days'] = $md !== null && $md !== '' ? (string) $md : '';
|
||||
|
||||
$amt = round((float) ($item['amount'] ?? 0), 2);
|
||||
$paid = round((float) ($item['linked_pay_paid_total'] ?? 0), 2);
|
||||
$item['export_amount'] = number_format($amt, 2, '.', '');
|
||||
$item['export_paid_amount'] = number_format($paid, 2, '.', '');
|
||||
$item['export_agency_collect'] = number_format(round($amt - $paid, 2), 2, '.', '');
|
||||
|
||||
$item['export_tracking_number'] = (string) ($item['tracking_number'] ?? '');
|
||||
$isGc = trim((string) ($item['gancao_reciperl_order_no'] ?? '')) !== '';
|
||||
$item['export_supply_mode'] = $isGc ? '甘草' : '自营';
|
||||
// 「处方成本」列:与列表/详情一致,取订单 internal_cost(含「测试价格」预报价写入;甘草/自营均导出)
|
||||
if (self::canViewInternalCost($adminInfo)) {
|
||||
$rawCost = $item['internal_cost'] ?? null;
|
||||
$item['export_gancao_prescription_cost'] = $rawCost !== null && $rawCost !== ''
|
||||
? number_format(round((float) $rawCost, 2), 2, '.', '')
|
||||
: '';
|
||||
} else {
|
||||
$item['export_gancao_prescription_cost'] = '';
|
||||
}
|
||||
|
||||
$isFu = (int) ($item['is_follow_up'] ?? 0) === 1;
|
||||
$item['export_first_visit_assistant'] = $isFu ? (string) ($item['prev_staff'] ?? '') : '';
|
||||
|
||||
$hit = (int) ($item['po_assign_snapshot_hit'] ?? 0);
|
||||
$rel = (int) ($item['po_assign_is_release'] ?? 0);
|
||||
$er = (int) ($item['po_assign_to_er_center'] ?? 0);
|
||||
if ($hit !== 1) {
|
||||
$item['export_center_label'] = '—';
|
||||
} elseif ($rel === 1) {
|
||||
$item['export_center_label'] = '已释放';
|
||||
} elseif ($er === 1) {
|
||||
$item['export_center_label'] = '二中心';
|
||||
} else {
|
||||
$item['export_center_label'] = '一中心';
|
||||
}
|
||||
|
||||
$paSt = (int) ($item['prescription_audit_status'] ?? 0);
|
||||
$aud = $auditEffectiveByPo[$poId] ?? null;
|
||||
if (($paSt === 1 || $paSt === 2) && \is_array($aud)) {
|
||||
$at = (int) ($aud['create_time'] ?? 0);
|
||||
$item['export_rx_audit_time'] = $at > 0 ? date('Y-m-d H:i:s', $at) : '';
|
||||
$item['export_rx_auditor'] = (string) ($aud['admin_name'] ?? '');
|
||||
} else {
|
||||
$item['export_rx_audit_time'] = '';
|
||||
$item['export_rx_auditor'] = '';
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成订单时把关联诊单的 assistant_id 清空,让患者回到「待分配」状态
|
||||
* 仅当该患者没有其它进行中的处方订单且无指派日志时才清空,避免误覆盖
|
||||
@@ -2750,4 +3269,14 @@ class PrescriptionOrderLogic
|
||||
// 忽略日志写入错误
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处方患者姓名/手机号修正日志(写入 zyt_tcm_prescription_order_log,与 patchPrescriptionPatient 同源)
|
||||
*
|
||||
* @param int $prescriptionOrderId 无关联订单时可传 0(若库表禁止则可能静默失败)
|
||||
*/
|
||||
public static function appendRxPatientPatchLog(int $prescriptionOrderId, int $adminId, array $adminInfo, string $summary): void
|
||||
{
|
||||
self::writeLog($prescriptionOrderId, $adminId, $adminInfo, 'patch_rx_patient', $summary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,14 @@ class AppointmentValidate extends BaseValidate
|
||||
'doctor_id' => 'require|integer',
|
||||
'diagnosis_id' => 'require|integer',
|
||||
'appointment_date' => 'require|date',
|
||||
'period' => 'in:morning,afternoon,all',
|
||||
'appointment_time' => 'require',
|
||||
'appointment_type' => 'in:video,text,phone',
|
||||
'period' => 'in:morning,afternoon,all',
|
||||
'appointment_type' => 'require|in:video,text,phone',
|
||||
'status' => 'require|integer|between:1,4',
|
||||
'remark' => 'max:500',
|
||||
'channel_source' => 'require',
|
||||
'channel_source_detail' => 'max:128',
|
||||
'ids' => 'require|array',
|
||||
'note_id' => 'require|integer',
|
||||
'image_type' => 'require|in:tongue_images,report_files',
|
||||
'image_path' => 'require',
|
||||
@@ -41,11 +44,15 @@ class AppointmentValidate extends BaseValidate
|
||||
'doctor_id' => '医生ID',
|
||||
'diagnosis_id' => '诊单ID',
|
||||
'appointment_date' => '预约日期',
|
||||
'period' => '时段',
|
||||
'appointment_time' => '预约时间',
|
||||
'period' => '时段',
|
||||
'appointment_type' => '预约类型',
|
||||
'status' => '状态',
|
||||
'remark' => '备注',
|
||||
'assistant_id' => '医助',
|
||||
'channel_source' => '渠道来源',
|
||||
'channel_source_detail' => '渠道补充说明',
|
||||
'ids' => '预约ID列表',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -125,4 +132,28 @@ class AppointmentValidate extends BaseValidate
|
||||
{
|
||||
return $this->only(['note_id', 'image_type', 'image_path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台挂号编辑(消费者处方-挂号列表等)
|
||||
*/
|
||||
public function sceneAdminEdit()
|
||||
{
|
||||
return $this->only([
|
||||
'id',
|
||||
'appointment_date',
|
||||
'appointment_time',
|
||||
'period',
|
||||
'appointment_type',
|
||||
'status',
|
||||
'remark',
|
||||
'channel_source',
|
||||
'channel_source_detail',
|
||||
]);
|
||||
}
|
||||
|
||||
/** 批量修改挂号渠道(权限同 doctor.appointment/edit) */
|
||||
public function sceneBatchEditChannel()
|
||||
{
|
||||
return $this->only(['ids', 'channel_source', 'channel_source_detail']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ class PrescriptionValidate extends BaseValidate
|
||||
'appointment_id' => 'number',
|
||||
'dosage_bag_count' => 'integer|between:1,5',
|
||||
'patient_name' => 'require',
|
||||
'phone' => 'require|max:20',
|
||||
'gender' => 'require|in:0,1',
|
||||
'clinical_diagnosis' => 'require',
|
||||
'herbs' => 'require|array',
|
||||
'action' => 'require|in:approve,reject',
|
||||
@@ -24,6 +26,10 @@ class PrescriptionValidate extends BaseValidate
|
||||
'dosage_bag_count.integer' => '用量袋数必须为整数',
|
||||
'dosage_bag_count.between' => '用量袋数必须在1到5袋之间',
|
||||
'patient_name.require' => '患者姓名不能为空',
|
||||
'phone.require' => '手机号不能为空',
|
||||
'phone.max' => '手机号过长',
|
||||
'gender.require' => '请选择性别',
|
||||
'gender.in' => '性别无效',
|
||||
'clinical_diagnosis.require' => '临床诊断不能为空',
|
||||
'herbs.require' => '请添加中药',
|
||||
];
|
||||
@@ -63,6 +69,11 @@ class PrescriptionValidate extends BaseValidate
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
public function scenePatchPatient()
|
||||
{
|
||||
return $this->only(['id', 'patient_name', 'phone', 'gender']);
|
||||
}
|
||||
|
||||
public function sceneAudit()
|
||||
{
|
||||
return $this->only(['id', 'action', 'remark']);
|
||||
|
||||
@@ -392,7 +392,7 @@ class GancaoCallbackController extends BaseApiController
|
||||
$nu = (string) ($ext['nu'] ?? '');
|
||||
$supplier = (string) ($ext['supplier'] ?? '');
|
||||
|
||||
if ($nu !== '') {
|
||||
if ($nu !== '' && trim((string) ($order->tracking_number ?? '')) === '') {
|
||||
$order->tracking_number = mb_substr($nu, 0, 80);
|
||||
}
|
||||
if ($shippingName !== '') {
|
||||
|
||||
@@ -18,7 +18,7 @@ use think\console\Output;
|
||||
* 配置 crontab(每 10 分钟):拉快递 100 + 按履约「已发货/已签收」核对释放诊单医助(与是否甘草单无关)
|
||||
* 0,10,20,30,40,50 * * * * cd /path/to/server && php think express:auto-update >> /dev/null 2>&1
|
||||
*
|
||||
* 已对非二中心医助执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at,
|
||||
* 已对「业务订单创建人非二中心」(或创建人为开方医生时按待释放身份判定)执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at,
|
||||
* 后续履约核对不再重复扫描(需先执行 sql/1.9.20260507/add_diagnosis_shipped_non_er_assistant_cleared_at.sql)。
|
||||
*/
|
||||
class ExpressAutoUpdate extends Command
|
||||
@@ -36,8 +36,8 @@ class ExpressAutoUpdate extends Command
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$result = ExpressTrackingService::autoUpdateBatch(500);
|
||||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(500);
|
||||
$result = ExpressTrackingService::autoUpdateBatch(1000);
|
||||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(1000);
|
||||
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ use think\console\Output;
|
||||
* 建议 crontab(每 30 分钟执行一次):
|
||||
* 0,30 * * * * cd /path/to/server && php think gancao:sync-logistics --limit=200 >> runtime/log/gancao_sync.log 2>&1
|
||||
*
|
||||
* 已对非二中心医助执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at,
|
||||
* 已对「业务订单创建人非二中心」(发货/签收自动释放规则见 ExpressTrackingService)的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at,
|
||||
* 后续履约核对不再重复扫描(需先执行 sql/1.9.20260507/add_diagnosis_shipped_non_er_assistant_cleared_at.sql)。
|
||||
*/
|
||||
class GancaoSyncLogisticsRoute extends Command
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\adminapi\logic\tcm\PrescriptionOrderLogic;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\model\tcm\PrescriptionOrderLog;
|
||||
use app\common\service\gancao\GancaoScmRecipelService;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 历史 internal_cost 回填:对每条业务订单调用甘草预下单(CTM_PREVIEW),
|
||||
* 将返回 fee 的「药材成本 + 制作费 + 物流费」合计写入 zyt_tcm_prescription_order.internal_cost。
|
||||
*
|
||||
* php think tcm:backfill-internal-cost --dry-run
|
||||
* php think tcm:backfill-internal-cost --limit=30 --sleep-ms=400
|
||||
* php think tcm:backfill-internal-cost --order-id=123
|
||||
* php think tcm:backfill-internal-cost --force
|
||||
* php think tcm:backfill-internal-cost --null-or-zero --min-id=1000
|
||||
* php think tcm:backfill-internal-cost --fulfillment-status=5,6,3
|
||||
*
|
||||
* 履约状态代码(fulfillment_status):1待双审通过 2待发货 3已完成 4已取消 5已发货 6已签收
|
||||
* 7进行中 8暂不制药 9拒收 10退款 11保留药方 12制药缓发
|
||||
*
|
||||
* 说明:使用 root 上下文仅用于绕过后台「谁能看哪张处方/订单」校验;不落管理员登录态。
|
||||
* 甘草未配置、药材无法匹配、规则拦截、缺药等会跳过并在末尾汇总。
|
||||
*/
|
||||
class TcmBackfillPrescriptionOrderInternalCost extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('tcm:backfill-internal-cost')
|
||||
->setDescription('批量用甘草预报价回填处方业务订单 internal_cost')
|
||||
->addOption('dry-run', null, Option::VALUE_NONE, '只演练不写库')
|
||||
->addOption('limit', 'l', Option::VALUE_OPTIONAL, '最多处理条数', 100)
|
||||
->addOption('order-id', null, Option::VALUE_OPTIONAL, '仅处理指定 prescription_order.id', null)
|
||||
->addOption('force', 'f', Option::VALUE_NONE, '覆盖已有 internal_cost(默认只填 NULL)')
|
||||
->addOption('null-or-zero', null, Option::VALUE_NONE, '与默认一致且同时处理 internal_cost=0 的行(仍可用 --force 覆盖任意值)')
|
||||
->addOption('min-id', null, Option::VALUE_OPTIONAL, '仅 id>=该值', null)
|
||||
->addOption('max-id', null, Option::VALUE_OPTIONAL, '仅 id<=该值', null)
|
||||
->addOption(
|
||||
'fulfillment-status',
|
||||
null,
|
||||
Option::VALUE_OPTIONAL,
|
||||
'仅处理指定履约状态,逗号分隔数字,如 5,6,3=已发货/已签收/已完成;不传则不限',
|
||||
null
|
||||
)
|
||||
->addOption('sleep-ms', null, Option::VALUE_OPTIONAL, '每条成功调用后休眠毫秒,减轻开放平台压力', 250)
|
||||
->addOption('detail', 'd', Option::VALUE_NONE, '打印每条明细');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{admin_id:int, root:int, name:string, role_id:int[]}
|
||||
*/
|
||||
private static function cliAdminInfo(): array
|
||||
{
|
||||
return [
|
||||
'admin_id' => 0,
|
||||
'root' => 1,
|
||||
'name' => 'CLI-internal-cost',
|
||||
'role_id' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $fee
|
||||
*/
|
||||
private static function totalFeeFromPreview(array $fee): float
|
||||
{
|
||||
$m = (float) ($fee['m_cost'] ?? 0);
|
||||
$p = (float) ($fee['proces_cost'] ?? 0);
|
||||
$l = (float) ($fee['lis_cost'] ?? 0);
|
||||
|
||||
return round($m + $p + $l, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 CLI 传入的履约状态列表(1–12,与 PrescriptionOrderLogic::fulfillmentStatusLabel 一致)
|
||||
*
|
||||
* @return int[] 去重后的状态码;$raw 非空但解析不到合法值时返回 null 表示调用方应报错
|
||||
*/
|
||||
private static function parseFulfillmentStatusOption(?string $raw): ?array
|
||||
{
|
||||
if ($raw === null) {
|
||||
return [];
|
||||
}
|
||||
$raw = trim($raw);
|
||||
if ($raw === '') {
|
||||
return [];
|
||||
}
|
||||
$seen = [];
|
||||
foreach (explode(',', $raw) as $part) {
|
||||
$n = (int) trim($part);
|
||||
if ($n >= 1 && $n <= 12) {
|
||||
$seen[$n] = true;
|
||||
}
|
||||
}
|
||||
$ids = array_keys($seen);
|
||||
sort($ids);
|
||||
|
||||
return $ids !== [] ? $ids : null;
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output): int
|
||||
{
|
||||
$dryRun = (bool) $input->getOption('dry-run');
|
||||
$verbose = (bool) $input->getOption('detail');
|
||||
$force = (bool) $input->getOption('force');
|
||||
$nullOrZero = (bool) $input->getOption('null-or-zero');
|
||||
$limit = max(1, (int) $input->getOption('limit'));
|
||||
$sleepMs = max(0, (int) $input->getOption('sleep-ms'));
|
||||
$onlyOrderId = $input->getOption('order-id');
|
||||
$onlyOrderId = $onlyOrderId !== null && $onlyOrderId !== '' ? (int) $onlyOrderId : null;
|
||||
$minId = $input->getOption('min-id');
|
||||
$minId = $minId !== null && $minId !== '' ? (int) $minId : null;
|
||||
$maxId = $input->getOption('max-id');
|
||||
$maxId = $maxId !== null && $maxId !== '' ? (int) $maxId : null;
|
||||
$fsRaw = $input->getOption('fulfillment-status');
|
||||
$fsRaw = $fsRaw !== null ? (string) $fsRaw : null;
|
||||
$fulfillmentStatuses = self::parseFulfillmentStatusOption($fsRaw);
|
||||
if ($fulfillmentStatuses === null) {
|
||||
$output->error('--fulfillment-status 格式无效,请传入 1–12 的数字,英文逗号分隔,例如:5,6,3');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$adminInfo = self::cliAdminInfo();
|
||||
$adminId = 0;
|
||||
|
||||
$output->writeln('========================================');
|
||||
$output->writeln('批量回填 internal_cost(甘草 CTM_PREVIEW)');
|
||||
$output->writeln('========================================');
|
||||
|
||||
if (!GancaoScmRecipelService::isConfigured()) {
|
||||
$output->error('甘草 SCM 未配置:' . GancaoScmRecipelService::whyNotConfigured());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$q = PrescriptionOrder::whereNull('delete_time')->where('prescription_id', '>', 0)->order('id', 'asc');
|
||||
|
||||
if ($fulfillmentStatuses !== []) {
|
||||
$q->whereIn('fulfillment_status', $fulfillmentStatuses);
|
||||
}
|
||||
|
||||
if ($onlyOrderId !== null && $onlyOrderId > 0) {
|
||||
$q->where('id', $onlyOrderId);
|
||||
} else {
|
||||
if (!$force) {
|
||||
if ($nullOrZero) {
|
||||
$q->whereRaw('(internal_cost IS NULL OR internal_cost = 0)');
|
||||
} else {
|
||||
$q->whereNull('internal_cost');
|
||||
}
|
||||
}
|
||||
if ($minId !== null && $minId > 0) {
|
||||
$q->where('id', '>=', $minId);
|
||||
}
|
||||
if ($maxId !== null && $maxId > 0) {
|
||||
$q->where('id', '<=', $maxId);
|
||||
}
|
||||
$q->limit($limit);
|
||||
}
|
||||
|
||||
$rows = $q->field(['id', 'order_no', 'dose_count', 'medication_days', 'internal_cost', 'fulfillment_status'])->select()->toArray();
|
||||
if ($rows === []) {
|
||||
$output->warning('没有符合条件的订单。');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$fsLabel = '';
|
||||
if ($fulfillmentStatuses !== []) {
|
||||
$parts = [];
|
||||
foreach ($fulfillmentStatuses as $code) {
|
||||
$parts[] = (string) $code . '=' . PrescriptionOrderLogic::fulfillmentStatusLabel((int) $code);
|
||||
}
|
||||
$fsLabel = ' fulfillment-status=[' . implode(',', $parts) . ']';
|
||||
}
|
||||
|
||||
$output->writeln(sprintf(
|
||||
'待处理 %d 条(dry-run=%s force=%s sleep-ms=%d%s)',
|
||||
count($rows),
|
||||
$dryRun ? 'yes' : 'no',
|
||||
$force ? 'yes' : 'no',
|
||||
$sleepMs,
|
||||
$fsLabel
|
||||
));
|
||||
|
||||
$ok = 0;
|
||||
$fail = 0;
|
||||
$skipped = 0;
|
||||
$reasons = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$id = (int) ($row['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$force && !$nullOrZero && $row['internal_cost'] !== null && $row['internal_cost'] !== '') {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$dc = (int) ($row['dose_count'] ?? 0);
|
||||
if ($dc > 0) {
|
||||
$params['dose_count'] = $dc;
|
||||
}
|
||||
$md = $row['medication_days'] ?? null;
|
||||
if ($md !== null && $md !== '' && (int) $md > 0) {
|
||||
$params['medication_days'] = (int) $md;
|
||||
}
|
||||
|
||||
$ret = PrescriptionOrderLogic::previewGancaoRecipel($id, $adminId, $adminInfo, $params);
|
||||
if ($ret === false) {
|
||||
$fail++;
|
||||
$err = PrescriptionOrderLogic::getError();
|
||||
$reasons[$err] = ($reasons[$err] ?? 0) + 1;
|
||||
if ($verbose) {
|
||||
$output->writeln("<error>#{$id} 失败:{$err}</error>");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$fee = is_array($ret['fee'] ?? null) ? $ret['fee'] : [];
|
||||
$total = self::totalFeeFromPreview($fee);
|
||||
|
||||
if ($verbose) {
|
||||
$ono = (string) ($row['order_no'] ?? '');
|
||||
$fs = (int) ($row['fulfillment_status'] ?? 0);
|
||||
$fst = PrescriptionOrderLogic::fulfillmentStatusLabel($fs);
|
||||
$output->writeln(sprintf('#%d %s [%s] → internal_cost=%.2f', $id, $ono, $fst, $total));
|
||||
}
|
||||
|
||||
if (!$dryRun) {
|
||||
PrescriptionOrder::where('id', $id)->whereNull('delete_time')->update([
|
||||
'internal_cost' => $total,
|
||||
]);
|
||||
|
||||
$log = new PrescriptionOrderLog();
|
||||
$log->prescription_order_id = $id;
|
||||
$log->admin_id = 0;
|
||||
$log->admin_name = 'CLI';
|
||||
$log->action = 'cli_backfill_internal_cost';
|
||||
$log->summary = mb_substr(sprintf('甘草预报价回填 internal_cost=%.2f', $total), 0, 500);
|
||||
$log->create_time = time();
|
||||
try {
|
||||
$log->save();
|
||||
} catch (\Throwable $e) {
|
||||
// 忽略
|
||||
}
|
||||
}
|
||||
|
||||
$ok++;
|
||||
if ($sleepMs > 0 && !$dryRun) {
|
||||
usleep($sleepMs * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln(sprintf('完成:成功 %d,失败 %d,跳过 %d', $ok, $fail, $skipped));
|
||||
if ($reasons !== []) {
|
||||
$output->writeln('失败原因统计:');
|
||||
foreach ($reasons as $msg => $cnt) {
|
||||
$output->writeln(' [' . $cnt . 'x] ' . $msg);
|
||||
}
|
||||
}
|
||||
|
||||
return $fail > 0 ? 2 : 0;
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,12 @@ abstract class BaseDataLists implements ListsInterface
|
||||
|
||||
public string $export;
|
||||
|
||||
/**
|
||||
* 管理端列表:在 request 就绪后写入 adminId/adminInfo(见 BaseAdminDataLists)
|
||||
*/
|
||||
protected function initAdminIdentity(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -66,6 +72,8 @@ abstract class BaseDataLists implements ListsInterface
|
||||
|
||||
//请求参数设置
|
||||
$this->request = request();
|
||||
// admin 列表子类在 initExport 中可能触发 count/lists,须先于 initPage/initExport 写入身份
|
||||
$this->initAdminIdentity();
|
||||
$this->params = $this->request->param();
|
||||
|
||||
//分页初始化
|
||||
|
||||
@@ -249,7 +249,7 @@ class ExpressTrackingService
|
||||
/**
|
||||
* 处方业务订单履约「已发货(5) / 已签收(6)」时:清空关联诊单(患者)医助,并写入 tcm_diagnosis_assign_log(含关联业务订单 creator_id / create_time 快照,便于核对医助创建订单时间)。
|
||||
* 快递 100 定时拉轨迹、甘草路由、甘草回调与履约核对共用同一规则。
|
||||
* 不自动清空的情形仅两种:(1)患者当前医助在「二中心」及其全部子部门(与后台部门树一致)下任职;(2)诊单已有 shipped_non_er_assistant_cleared_at 且 assistant_id 已为 0(视为已处理过,重新指派医助后可再次处理)。
|
||||
* 不自动清空的情形:(1)按业务订单 creator_id(代建场景)或回退身份判断是否在「二中心」部门子树;(2)诊单已有 shipped_non_er_assistant_cleared_at 且 assistant_id 已为 0(视为已处理);(3)指派日志存在后台人工指派:`operator_admin_id>0` 且 `to_assistant_id>0`(系统任务为 operator_admin_id=0);或 related_po_creator_id=from_assistant_id 且 to>0 的旧数据兼容。避免「系统·已发货履约核对」覆盖人工指派链。
|
||||
*
|
||||
* @param array{tracking_id?:int,tracking_number?:string,source?:string} $meta
|
||||
* @return array{
|
||||
@@ -288,36 +288,55 @@ class ExpressTrackingService
|
||||
if (!$diag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 后台曾人工指派到新医助(operator_admin_id>0 且 to>0,与系统写入的 operator=0 区分)时不再自动清空;另保留快照 related_po=from 的旧数据兼容
|
||||
if (self::diagnosisShouldSkipAutoAssistantReleaseDueToManualAssignLog($diagnosisId)) {
|
||||
Log::info('Skipped auto assistant release (shipped/signed): manual assign history exists (human operator + to_assistant_id>0, or snapshot-tied pattern)', [
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'prescription_order_id' => $orderId,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$shippedClearedAt = (int) ($diag->getAttr('shipped_non_er_assistant_cleared_at') ?? 0);
|
||||
$diagAssistantIdNow = (int) ($diag->getAttr('assistant_id') ?? 0);
|
||||
if ($shippedClearedAt > 0 && $diagAssistantIdNow === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$poCreatorId = (int) ($order->getAttr('creator_id') ?? 0);
|
||||
$rxId = (int) ($order->getAttr('prescription_id') ?? 0);
|
||||
$rxCreator = 0;
|
||||
if ($rxId > 0) {
|
||||
$rxCreator = (int) Prescription::where('id', $rxId)->whereNull('delete_time')->value('creator_id');
|
||||
}
|
||||
|
||||
// 诊单 assistant_id 与列表「医助」筛选一致:医助常代建单但诊单未写 assistant_id,此时用创建人作为待释放身份(创建人即开方医生时不采用,避免误记指派日志)
|
||||
$fromAssistantId = (int) ($diag->getAttr('assistant_id') ?? 0);
|
||||
if ($fromAssistantId <= 0) {
|
||||
$creatorId = (int) ($order->getAttr('creator_id') ?? 0);
|
||||
if ($creatorId > 0) {
|
||||
$rxId = (int) ($order->getAttr('prescription_id') ?? 0);
|
||||
$rxCreator = 0;
|
||||
if ($rxId > 0) {
|
||||
$rxCreator = (int) Prescription::where('id', $rxId)->whereNull('delete_time')->value('creator_id');
|
||||
}
|
||||
if ($rxId <= 0 || $creatorId !== $rxCreator) {
|
||||
$fromAssistantId = $creatorId;
|
||||
}
|
||||
if ($poCreatorId > 0 && ($rxId <= 0 || $poCreatorId !== $rxCreator)) {
|
||||
$fromAssistantId = $poCreatorId;
|
||||
}
|
||||
}
|
||||
if ($fromAssistantId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self::currentAssistantBelongsToErzhongxinDeptTree($fromAssistantId)) {
|
||||
Log::info('Skipped auto assistant release (shipped/signed): assistant under 二中心 dept tree', [
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'prescription_order_id' => $orderId,
|
||||
'source' => $source,
|
||||
'current_assistant_id' => $fromAssistantId,
|
||||
/** 是否因「二中心」跳过释放:优先看业务订单创建人 creator_id(代建场景);创建人即开方医生时用 fromAssistantId */
|
||||
$erZhongxinCheckAdminId = ($poCreatorId > 0 && ($rxId <= 0 || $poCreatorId !== $rxCreator))
|
||||
? $poCreatorId
|
||||
: $fromAssistantId;
|
||||
|
||||
if ($erZhongxinCheckAdminId > 0 && self::currentAssistantBelongsToErzhongxinDeptTree($erZhongxinCheckAdminId)) {
|
||||
Log::info('Skipped auto assistant release (shipped/signed): order creator (or fallback from-id) under 二中心 dept tree', [
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'prescription_order_id' => $orderId,
|
||||
'source' => $source,
|
||||
'er_zhongxin_check_admin_id' => $erZhongxinCheckAdminId,
|
||||
'prescription_order_creator_id' => $poCreatorId,
|
||||
'from_assistant_id_for_log' => $fromAssistantId,
|
||||
]);
|
||||
|
||||
return null;
|
||||
@@ -440,6 +459,39 @@ class ExpressTrackingService
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 若诊单曾被后台人工指派到具体医助,则不再执行发运/签收自动清空(避免「系统·已发货履约核对」覆盖人工链)。
|
||||
* 判定:operator_admin_id>0 且 to_assistant_id>0(人工);系统写入的释放/同步为 operator_admin_id=0。
|
||||
* 另保留 related_po_creator_id=from_assistant_id 且 to>0 的窄条件,兼容早期数据。
|
||||
*/
|
||||
private static function diagnosisShouldSkipAutoAssistantReleaseDueToManualAssignLog(int $diagnosisId): bool
|
||||
{
|
||||
if ($diagnosisId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tbl = 'tcm_diagnosis_assign_log';
|
||||
|
||||
$humanReassign = (int) Db::name($tbl)
|
||||
->where('diagnosis_id', $diagnosisId)
|
||||
->where('to_assistant_id', '>', 0)
|
||||
->where('operator_admin_id', '>', 0)
|
||||
->count();
|
||||
if ($humanReassign > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$snapshotTied = (int) Db::name($tbl)
|
||||
->where('diagnosis_id', $diagnosisId)
|
||||
->where('to_assistant_id', '>', 0)
|
||||
->where('from_assistant_id', '>', 0)
|
||||
->where('related_po_creator_id', '>', 0)
|
||||
->whereRaw('`related_po_creator_id` = `from_assistant_id`')
|
||||
->count();
|
||||
|
||||
return $snapshotTied > 0;
|
||||
}
|
||||
|
||||
private static function maybeClearDiagnosisAssistantWhenShippedPrescription(ExpressTracking $tracking): ?array
|
||||
{
|
||||
if ((string) $tracking->order_type !== 'prescription') {
|
||||
@@ -619,7 +671,7 @@ class ExpressTrackingService
|
||||
|
||||
/**
|
||||
* 按处方订单履约核对:已发货(5)/已签收(6) 时符合条件的订单调用 applyAssistantReleaseForShippedPrescriptionOrder。
|
||||
* 与快递 100 / 甘草来自哪个物流渠道无关;是否清空医助以该方法为准(仅二中心不释放;已 shipped 释放且 assistant_id=0 视为已处理)。
|
||||
* 与快递 100 / 甘草来自哪个物流渠道无关;是否清空医助以该方法为准(**二中心豁免看业务单创建人**参见 apply;已 shipped 释放且 assistant_id=0 视为已处理)。
|
||||
*
|
||||
* @return array{scanned: int, cleared: int, lines: list<string>}
|
||||
*/
|
||||
@@ -810,4 +862,237 @@ class ExpressTrackingService
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 PrescriptionOrderLogic::logisticsTrace 一致:优先库表 express_tracking + express_trace;
|
||||
* 库内算不出签收时(或库无记录),可选走快递100(与详情「刷新轨迹」同源)。
|
||||
*
|
||||
* 提成结算 orderLines 等对 SQL JOIN 无法覆盖「仅 API 有轨迹」的运单做补算。
|
||||
*
|
||||
* @param bool $queryKuaidiWhenDbUnusable true 时与 logisticsTrace 完全同路径;false 时仅数据库推导(overview 大批量避免 HTTP)
|
||||
*/
|
||||
public static function resolveSignUnixTimeForCommission(
|
||||
string $trackingNumber,
|
||||
string $expressCompany,
|
||||
string $recipientPhone,
|
||||
bool $queryKuaidiWhenDbUnusable = true
|
||||
): int {
|
||||
$num = trim($trackingNumber);
|
||||
if ($num === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheKey = $num . '|' . ($queryKuaidiWhenDbUnusable ? '1' : '0');
|
||||
static $memo = [];
|
||||
if (isset($memo[$cacheKey])) {
|
||||
return $memo[$cacheKey];
|
||||
}
|
||||
|
||||
$detail = self::loadTrackingDetailNormalized($num);
|
||||
if (is_array($detail)) {
|
||||
$fromDb = self::effectiveSignUnixFromTrackingDetail($detail);
|
||||
if ($fromDb > 0) {
|
||||
$memo[$cacheKey] = $fromDb;
|
||||
|
||||
return $fromDb;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$queryKuaidiWhenDbUnusable) {
|
||||
$memo[$cacheKey] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$ec = strtolower(trim($expressCompany));
|
||||
if ($ec === '') {
|
||||
$ec = 'auto';
|
||||
}
|
||||
$api = ExpressTrackService::query($ec, $num, $recipientPhone, '');
|
||||
$fromApi = self::effectiveSignUnixFromKuaidiPayload($api);
|
||||
$memo[$cacheKey] = $fromApi;
|
||||
|
||||
return $fromApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单号精确匹配失败时尝试 TRIM(TrackingNumber)(历史数据首尾空格)。
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private static function loadTrackingDetailNormalized(string $trackingNumber): ?array
|
||||
{
|
||||
$n = trim($trackingNumber);
|
||||
if ($n === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$first = self::getDetailByTrackingNumber($n);
|
||||
if ($first !== null) {
|
||||
return $first;
|
||||
}
|
||||
|
||||
$tracking = ExpressTracking::with(['traces' => function ($query) {
|
||||
$query->order('trace_time_stamp', 'desc');
|
||||
}])
|
||||
->whereRaw('TRIM(`tracking_number`) = ?', [$n])
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
|
||||
if (!$tracking) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $tracking->toArray();
|
||||
$data['traces'] = $tracking->traces ? $tracking->traces->toArray() : [];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 YejiStatsLogic 物流子查询中每条 express_tracking 的 GREATEST 语义对齐。
|
||||
*
|
||||
* @param array<string, mixed> $detail getDetail* / loadTrackingDetailNormalized 结果
|
||||
*/
|
||||
private static function effectiveSignUnixFromTrackingDetail(array $detail): int
|
||||
{
|
||||
$a = (int) ($detail['sign_time'] ?? 0);
|
||||
|
||||
$traces = $detail['traces'] ?? [];
|
||||
if (!is_array($traces)) {
|
||||
$traces = [];
|
||||
}
|
||||
|
||||
$b = 0;
|
||||
foreach ($traces as $tr) {
|
||||
if (!is_array($tr)) {
|
||||
continue;
|
||||
}
|
||||
if (!self::dbTraceRowLooksSigned($tr)) {
|
||||
continue;
|
||||
}
|
||||
$ts = self::traceRowToUnix($tr);
|
||||
if ($ts > 0) {
|
||||
$b = max($b, $ts);
|
||||
}
|
||||
}
|
||||
|
||||
$c = 0;
|
||||
$stateOk = ((string) ($detail['current_state'] ?? '') === ExpressTracking::STATE_SIGNED
|
||||
|| (int) ($detail['is_signed'] ?? 0) === 1);
|
||||
if ($stateOk) {
|
||||
foreach ($traces as $tr) {
|
||||
if (!is_array($tr)) {
|
||||
continue;
|
||||
}
|
||||
$ts = self::traceRowToUnix($tr);
|
||||
if ($ts > 0) {
|
||||
$c = max($c, $ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max($a, $b, $c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $tr express_trace 行
|
||||
*/
|
||||
private static function dbTraceRowLooksSigned(array $tr): bool
|
||||
{
|
||||
$code = (string) ($tr['status_code'] ?? '');
|
||||
if (in_array($code, ['3', '301', '302', '304'], true)) {
|
||||
return true;
|
||||
}
|
||||
$st = (string) ($tr['status'] ?? '');
|
||||
$ctx = (string) ($tr['trace_context'] ?? '');
|
||||
$hay = $st . ' ' . $ctx;
|
||||
foreach (['签收', '妥投', '已送达', '本人签收'] as $k) {
|
||||
if (mb_stripos($hay, $k) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $tr
|
||||
*/
|
||||
private static function traceRowToUnix(array $tr): int
|
||||
{
|
||||
$ts = (int) ($tr['trace_time_stamp'] ?? 0);
|
||||
if ($ts > 0) {
|
||||
return $ts;
|
||||
}
|
||||
$raw = trim((string) ($tr['trace_time'] ?? ''));
|
||||
if ($raw === '') {
|
||||
return 0;
|
||||
}
|
||||
$p = strtotime($raw);
|
||||
|
||||
return $p !== false ? (int) $p : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $api ExpressTrackService::query 返回值
|
||||
*/
|
||||
private static function effectiveSignUnixFromKuaidiPayload(array $api): int
|
||||
{
|
||||
$traces = $api['traces'] ?? [];
|
||||
if (!is_array($traces) || $traces === []) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$state = (string) ($api['state'] ?? '');
|
||||
$best = 0;
|
||||
|
||||
foreach ($traces as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$tstr = (string) ($row['time'] ?? '');
|
||||
$ctx = (string) ($row['context'] ?? '');
|
||||
$p = strtotime($tstr);
|
||||
$ts = $p !== false ? (int) $p : 0;
|
||||
if ($ts <= 0) {
|
||||
continue;
|
||||
}
|
||||
$signedLike = self::plainContextLooksSigned($ctx);
|
||||
if ($signedLike) {
|
||||
$best = max($best, $ts);
|
||||
}
|
||||
}
|
||||
|
||||
if ($best > 0) {
|
||||
return $best;
|
||||
}
|
||||
|
||||
if ($state === '3') {
|
||||
foreach ($traces as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$tstr = (string) ($row['time'] ?? '');
|
||||
$p = strtotime($tstr);
|
||||
$ts = $p !== false ? (int) $p : 0;
|
||||
if ($ts > 0) {
|
||||
$best = max($best, $ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $best;
|
||||
}
|
||||
|
||||
private static function plainContextLooksSigned(string $context): bool
|
||||
{
|
||||
foreach (['签收', '妥投', '已送达', '本人签收', '快件已送达'] as $k) {
|
||||
if (mb_stripos($context, $k) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ use app\common\enum\ExportEnum;
|
||||
use app\common\lists\BaseDataLists;
|
||||
use app\common\lists\ListsExcelInterface;
|
||||
use app\common\lists\ListsExtendInterface;
|
||||
use think\facade\Config;
|
||||
use think\Response;
|
||||
use think\response\Json;
|
||||
use think\exception\HttpResponseException;
|
||||
@@ -120,12 +121,16 @@ class JsonService
|
||||
{
|
||||
//获取导出信息
|
||||
if ($lists->export == ExportEnum::INFO && $lists instanceof ListsExcelInterface) {
|
||||
self::relaxLimitsForExcelExport();
|
||||
|
||||
return self::data($lists->excelInfo());
|
||||
}
|
||||
|
||||
//获取导出文件的下载链接
|
||||
if ($lists->export == ExportEnum::EXPORT && $lists instanceof ListsExcelInterface) {
|
||||
self::relaxLimitsForExcelExport();
|
||||
$exportDownloadUrl = $lists->createExcel($lists->setExcelFields(), $lists->lists());
|
||||
|
||||
return self::success('', ['url' => $exportDownloadUrl], 2);
|
||||
}
|
||||
|
||||
@@ -141,4 +146,21 @@ class JsonService
|
||||
}
|
||||
return self::success('', $data, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excel 导出:拉数 + PhpSpreadsheet 易超过默认 max_execution_time=30
|
||||
*/
|
||||
private static function relaxLimitsForExcelExport(): void
|
||||
{
|
||||
@set_time_limit(0);
|
||||
$max = Config::get('project.lists.export_max_execution_time', 600);
|
||||
$max = is_numeric($max) ? (int) $max : 600;
|
||||
if ($max > 0) {
|
||||
@ini_set('max_execution_time', (string) $max);
|
||||
}
|
||||
$mem = Config::get('project.lists.export_memory_limit', '512M');
|
||||
if (is_string($mem) && $mem !== '') {
|
||||
@ini_set('memory_limit', $mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,14 @@ final class GancaoLogisticsRouteService
|
||||
}
|
||||
|
||||
$result = $resp['result'];
|
||||
$trackingNumber = trim((string) ($result['sp_order_no'] ?? ($order->tracking_number ?? '')));
|
||||
$apiTracking = trim((string) ($result['sp_order_no'] ?? ''));
|
||||
$localTracking = trim((string) ($order->tracking_number ?? ''));
|
||||
// 业务订单已录快递单号时:不改用甘草返回的 sp_order_no,轨迹仍写入本地单号对应的运单记录
|
||||
if ($localTracking !== '') {
|
||||
$trackingNumber = mb_substr($localTracking, 0, 80);
|
||||
} else {
|
||||
$trackingNumber = mb_substr($apiTracking, 0, 80);
|
||||
}
|
||||
$spName = trim((string) ($result['sp_name'] ?? ($order->gancao_shipping_name ?? '')));
|
||||
$routeList = is_array($result['route_list'] ?? null) ? $result['route_list'] : [];
|
||||
|
||||
@@ -87,7 +94,7 @@ final class GancaoLogisticsRouteService
|
||||
|
||||
// 回写订单的快递单号 / 物流公司(如果之前空着)+ 签收时升级 fulfillment_status
|
||||
$orderDirty = false;
|
||||
if (trim((string) $order->tracking_number) === '') {
|
||||
if ($localTracking === '') {
|
||||
$order->tracking_number = mb_substr($trackingNumber, 0, 80);
|
||||
$orderDirty = true;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ return [
|
||||
'qywx:sync-msg-archive' => 'app\\command\\QywxSyncMsgArchive',
|
||||
// 甘草订单物流路由同步(GET_TASK_ROUTE_LIST)
|
||||
'gancao:sync-logistics' => 'app\\command\\GancaoSyncLogisticsRoute',
|
||||
// 历史 internal_cost:批量甘草预报价回填(CTM_PREVIEW)
|
||||
'tcm:backfill-internal-cost' => 'app\\command\\TcmBackfillPrescriptionOrderInternalCost',
|
||||
// 迁移诊单图片到医生备注表
|
||||
'migrate:images-to-doctor-note' => 'app\\command\\MigrateImagesToDoctorNote',
|
||||
// 诊单待办事项:扫描到点的待执行项并向创建人发送企业微信消息
|
||||
|
||||
@@ -47,6 +47,9 @@ return [
|
||||
'lists' => [
|
||||
'page_size_max' => 25000,//列表页查询数量限制(列表页每页数量、导出每页数量)
|
||||
'page_size' => 25, //默认每页数量
|
||||
/** Excel 导出:接口内放宽 PHP 限制(JsonService::relaxLimitsForExcelExport) */
|
||||
'export_max_execution_time' => 600,
|
||||
'export_memory_limit' => '512M',
|
||||
],
|
||||
|
||||
// 各种默认图片
|
||||
@@ -137,6 +140,16 @@ return [
|
||||
'exempt_roles' => [],
|
||||
],
|
||||
|
||||
/*
|
||||
* 提成结算 stats.commissionSettlement(YejiStatsLogic)
|
||||
* require_system_auto_prescription:true(默认)仅系统代开口径(is_system_auto=1),常与订单列表「全部已完成」条数不一致。
|
||||
* false 时与列表一致纳入手动开方单(仍需履约完成、业绩归因等其它条件)。
|
||||
* .env 可选:commission_settlement.require_system_auto = 1|0 ,未配置时读本项。
|
||||
*/
|
||||
'commission_settlement' => [
|
||||
'require_system_auto_prescription' => (int) env('commission_settlement.require_system_auto', 1) === 1,
|
||||
],
|
||||
|
||||
// 腾讯云实时音视频(TRTC)配置
|
||||
'trtc' => [
|
||||
'sdkAppId' => (int)env('trtc.sdk_app_id', 1600127710),
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import r from"./error-xZPNbzUl.js";import{f as p,ak as i,I as m,a as e,aN as s,J as o}from"./@vue/runtime-core-C6bnekPw.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-QlpZ4wdW.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const a="/admin/assets/no_perms-jDxcYpYC.png",n={class:"error404"},W=p({__name:"403",setup(c){return(_,t)=>(i(),m("div",n,[e(r,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:s(()=>[...t[0]||(t[0]=[o("div",{class:"flex justify-center"},[o("img",{class:"w-[150px] h-[150px]",src:a,alt:""})],-1)])]),_:1})]))}});export{W as default};
|
||||
import r from"./error-CbHXom6i.js";import{f as p,ak as i,I as m,a as e,aN as s,J as o}from"./@vue/runtime-core-C6bnekPw.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-QlpZ4wdW.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const a="/admin/assets/no_perms-jDxcYpYC.png",n={class:"error404"},W=p({__name:"403",setup(c){return(_,t)=>(i(),m("div",n,[e(r,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:s(()=>[...t[0]||(t[0]=[o("div",{class:"flex justify-center"},[o("img",{class:"w-[150px] h-[150px]",src:a,alt:""})],-1)])]),_:1})]))}});export{W as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import o from"./error-xZPNbzUl.js";import{f as r,ak as t,I as m,a as p}from"./@vue/runtime-core-C6bnekPw.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-QlpZ4wdW.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const i={class:"error404"},T=r({__name:"404",setup(e){return(a,s)=>(t(),m("div",i,[p(o,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{T as default};
|
||||
import o from"./error-CbHXom6i.js";import{f as r,ak as t,I as m,a as p}from"./@vue/runtime-core-C6bnekPw.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-QlpZ4wdW.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const i={class:"error404"},T=r({__name:"404",setup(e){return(a,s)=>(t(),m("div",i,[p(o,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{T as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.cell-stack[data-v-849a5abe]{display:flex;flex-direction:column;gap:2px;line-height:1.35}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.cell-stack[data-v-4de87dfa]{display:flex;flex-direction:column;gap:2px;line-height:1.35}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{M as x,N as y,r as I,d as $,L as C}from"./element-plus-FSf94q2-.js";import{a7 as D}from"./@element-plus/icons-vue-BDOO1F0I.js";import{$ as L}from"./tcm-CZik0zOd.js";import{f as T,w as P,ak as g,I as A,aP as E,G as k,aN as n,a,O as m,J as B}from"./@vue/runtime-core-C6bnekPw.js";import{Q as p}from"./@vue/shared-mAAVTE9n.js";import{y as F,n as b}from"./@vue/reactivity-DiY1c2vO.js";import{_ as M}from"./index-CLwyu9dm.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const V={class:"assign-log-panel"},K=T({__name:"AssignLogPanel",props:{diagnosisId:{}},setup(h,{expose:w}){const c=h,d=b(!1),_=b([]);function v(o){const e=o.related_po_creator_name;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(o.related_po_creator_id);return Number.isFinite(t)&&t>0?`ID:${t}`:"—"}function N(o){const e=o.related_po_create_time_text;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(o.related_po_create_time);if(!Number.isFinite(t)||t<=0)return"—";const i=new Date(t*1e3);if(Number.isNaN(i.getTime()))return"—";const r=s=>String(s).padStart(2,"0");return`${i.getFullYear()}-${r(i.getMonth()+1)}-${r(i.getDate())} ${r(i.getHours())}:${r(i.getMinutes())}:${r(i.getSeconds())}`}function u(o,e){const t=e==="from"?"from_assistant_name":"to_assistant_name",i=e==="from"?"from_assistant_id":"to_assistant_id",r=o[t];if(r!=null&&String(r).trim()!==""&&String(r)!=="—")return String(r);const s=Number(o[i]);return Number.isFinite(s)&&s>0?`ID:${s}`:"—"}const f=async()=>{if(c.diagnosisId){d.value=!0;try{const o=await L({id:c.diagnosisId}),e=Array.isArray(o)?o:[];_.value=e}catch(o){console.error(o),_.value=[]}finally{d.value=!1}}};return P(()=>c.diagnosisId,()=>{f()},{immediate:!0}),w({refresh:f}),(o,e)=>{const t=y,i=$,r=I,s=x,S=C;return g(),A("div",V,[E((g(),k(s,{data:_.value,border:"",stripe:"","empty-text":"暂无指派记录"},{default:n(()=>[a(t,{label:"操作时间",width:"175",prop:"create_time_text"}),a(t,{label:"原医助","min-width":"120"},{default:n(({row:l})=>[m(p(u(l,"from")),1)]),_:1}),a(t,{label:"新医助","min-width":"120"},{default:n(({row:l})=>[m(p(u(l,"to")),1)]),_:1}),a(t,{label:"快照·业务单创建人","min-width":"130","show-overflow-tooltip":""},{default:n(({row:l})=>[m(p(v(l)),1)]),_:1}),a(t,{label:"快照·业务单创建时间",width:"190"},{header:n(()=>[e[0]||(e[0]=B("span",null,"快照·业务单创建时间",-1)),a(r,{placement:"top",content:"related_po_create_time:触发本次操作的处方业务订单 create_time;与原文助一致时表示医助创建订单时间"},{default:n(()=>[a(i,{class:"assign-log-col-hint"},{default:n(()=>[a(F(D))]),_:1})]),_:1})]),default:n(({row:l})=>[m(p(N(l)),1)]),_:1}),a(t,{label:"操作人",width:"110",prop:"operator_name"}),a(t,{label:"操作账号",width:"120",prop:"operator_account","show-overflow-tooltip":""}),a(t,{label:"IP",width:"130",prop:"ip","show-overflow-tooltip":""})]),_:1},8,["data"])),[[S,d.value]])])}}}),yt=M(K,[["__scopeId","data-v-5465d5eb"]]);export{yt as default};
|
||||
import{M as x,N as y,r as I,d as C,L as $}from"./element-plus-DFTWCWyi.js";import{W as D}from"./@element-plus/icons-vue-HOEUG8sr.js";import{a0 as L}from"./tcm-B5X1iJji.js";import{f as T,w as P,ak as g,I as A,aP as E,G as k,aN as n,a,O as m,J as B}from"./@vue/runtime-core-C6bnekPw.js";import{Q as p}from"./@vue/shared-mAAVTE9n.js";import{y as F,n as b}from"./@vue/reactivity-DiY1c2vO.js";import{_ as M}from"./index-BOT-n-x_.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const V={class:"assign-log-panel"},K=T({__name:"AssignLogPanel",props:{diagnosisId:{}},setup(h,{expose:w}){const c=h,d=b(!1),_=b([]);function v(o){const e=o.related_po_creator_name;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(o.related_po_creator_id);return Number.isFinite(t)&&t>0?`ID:${t}`:"—"}function N(o){const e=o.related_po_create_time_text;if(e!=null&&String(e).trim()!=="")return String(e);const t=Number(o.related_po_create_time);if(!Number.isFinite(t)||t<=0)return"—";const i=new Date(t*1e3);if(Number.isNaN(i.getTime()))return"—";const r=s=>String(s).padStart(2,"0");return`${i.getFullYear()}-${r(i.getMonth()+1)}-${r(i.getDate())} ${r(i.getHours())}:${r(i.getMinutes())}:${r(i.getSeconds())}`}function u(o,e){const t=e==="from"?"from_assistant_name":"to_assistant_name",i=e==="from"?"from_assistant_id":"to_assistant_id",r=o[t];if(r!=null&&String(r).trim()!==""&&String(r)!=="—")return String(r);const s=Number(o[i]);return Number.isFinite(s)&&s>0?`ID:${s}`:"—"}const f=async()=>{if(c.diagnosisId){d.value=!0;try{const o=await L({id:c.diagnosisId}),e=Array.isArray(o)?o:[];_.value=e}catch(o){console.error(o),_.value=[]}finally{d.value=!1}}};return P(()=>c.diagnosisId,()=>{f()},{immediate:!0}),w({refresh:f}),(o,e)=>{const t=y,i=C,r=I,s=x,S=$;return g(),A("div",V,[E((g(),k(s,{data:_.value,border:"",stripe:"","empty-text":"暂无指派记录"},{default:n(()=>[a(t,{label:"操作时间",width:"175",prop:"create_time_text"}),a(t,{label:"原医助","min-width":"120"},{default:n(({row:l})=>[m(p(u(l,"from")),1)]),_:1}),a(t,{label:"新医助","min-width":"120"},{default:n(({row:l})=>[m(p(u(l,"to")),1)]),_:1}),a(t,{label:"快照·业务单创建人","min-width":"130","show-overflow-tooltip":""},{default:n(({row:l})=>[m(p(v(l)),1)]),_:1}),a(t,{label:"快照·业务单创建时间",width:"190"},{header:n(()=>[e[0]||(e[0]=B("span",null,"快照·业务单创建时间",-1)),a(r,{placement:"top",content:"related_po_create_time:触发本次操作的处方业务订单 create_time;与原文助一致时表示医助创建订单时间"},{default:n(()=>[a(i,{class:"assign-log-col-hint"},{default:n(()=>[a(F(D))]),_:1})]),_:1})]),default:n(({row:l})=>[m(p(N(l)),1)]),_:1}),a(t,{label:"操作人",width:"110",prop:"operator_name"}),a(t,{label:"操作账号",width:"120",prop:"operator_account","show-overflow-tooltip":""}),a(t,{label:"IP",width:"130",prop:"ip","show-overflow-tooltip":""})]),_:1},8,["data"])),[[S,d.value]])])}}}),yt=M(K,[["__scopeId","data-v-5465d5eb"]]);export{yt as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{i as L,L as T,N as V,T as D,M as z,R as M}from"./element-plus-FSf94q2-.js";import{a7 as O}from"./tcm-CZik0zOd.js";import{f as P,b as F,w as R,ak as a,I as n,a as i,aN as s,O as m,H as w,aP as j,G as g,F as A,J as H}from"./@vue/runtime-core-C6bnekPw.js";import{y as d,n as k}from"./@vue/reactivity-DiY1c2vO.js";import{Q as c}from"./@vue/shared-mAAVTE9n.js";import{_ as G}from"./index-CLwyu9dm.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const J={class:"case-record-list"},Q={key:0,class:"mb-3 flex justify-end"},Y={key:0},q={key:1,class:"text-gray-400"},K={class:"void-detail text-xs text-gray-500 mt-1"},U=P({__name:"CaseRecordList",props:{diagnosisId:{type:Number,default:0},readOnly:{type:Boolean,default:!1}},emits:["view","openPrescription"],setup(y,{expose:x,emit:C}){const _=y,h=C,l=k([]),p=k(!1),u=async()=>{if(_.diagnosisId){p.value=!0;try{const e=await O({diagnosis_id:_.diagnosisId});l.value=Array.isArray(e)?e:[]}catch(e){console.error("获取病历记录失败:",e),l.value=[]}finally{p.value=!1}}},S=e=>{if(!e)return"";const t=new Date(e*1e3);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`},$=e=>{h("view",e)},B=()=>{h("openPrescription")};return F(()=>{u()}),R(()=>_.diagnosisId,()=>{u()}),x({refresh:u}),(e,t)=>{const b=L,r=V,v=D,E=z,N=M,I=T;return a(),n("div",J,[y.readOnly?w("",!0):(a(),n("div",Q,[i(b,{type:"primary",size:"small",onClick:B},{default:s(()=>[...t[0]||(t[0]=[m("开方",-1)])]),_:1})])),j((a(),g(E,{data:d(l),border:""},{default:s(()=>[i(r,{prop:"prescription_date",label:"就诊日期",width:"120"}),i(r,{prop:"visit_no",label:"门诊号",width:"120"}),i(r,{prop:"clinical_diagnosis",label:"临床诊断","min-width":"160","show-overflow-tooltip":""}),i(r,{label:"处方摘要","min-width":"180"},{default:s(({row:o})=>[o.herbs&&o.herbs.length?(a(),n("span",Y,c(o.herbs.slice(0,3).map(f=>`${f.name}${f.dosage}克`).join("、"))+c(o.herbs.length>3?"...":""),1)):(a(),n("span",q,"—"))]),_:1}),i(r,{prop:"doctor_name",label:"医师",width:"90","show-overflow-tooltip":""}),i(r,{label:"状态",width:"140",align:"center"},{default:s(({row:o})=>[o.void_status===1?(a(),n(A,{key:0},[i(v,{type:"danger",size:"small"},{default:s(()=>[...t[1]||(t[1]=[m("已作废",-1)])]),_:1}),H("div",K,c(o.void_by_name||"—")+" "+c(S(o.void_time)),1)],64)):(a(),g(v,{key:1,type:"success",size:"small"},{default:s(()=>[...t[2]||(t[2]=[m("正常",-1)])]),_:1}))]),_:1}),i(r,{label:"操作",width:"120",fixed:"right"},{default:s(({row:o})=>[i(b,{link:"",type:"primary",size:"small",onClick:f=>$(o)},{default:s(()=>[...t[3]||(t[3]=[m(" 查看 ",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[I,d(p)]]),!d(p)&&d(l).length===0?(a(),g(N,{key:1,description:"暂无病历记录,开方后会自动显示",class:"mt-4"})):w("",!0)])}}}),zt=G(U,[["__scopeId","data-v-043d2738"]]);export{zt as default};
|
||||
import{i as L,L as T,N as V,T as D,M as z,R as M}from"./element-plus-DFTWCWyi.js";import{a8 as O}from"./tcm-B5X1iJji.js";import{f as P,b as F,w as R,ak as a,I as n,a as i,aN as s,O as m,H as w,aP as j,G as g,F as A,J as H}from"./@vue/runtime-core-C6bnekPw.js";import{y as d,n as k}from"./@vue/reactivity-DiY1c2vO.js";import{Q as c}from"./@vue/shared-mAAVTE9n.js";import{_ as G}from"./index-BOT-n-x_.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const J={class:"case-record-list"},Q={key:0,class:"mb-3 flex justify-end"},Y={key:0},q={key:1,class:"text-gray-400"},K={class:"void-detail text-xs text-gray-500 mt-1"},U=P({__name:"CaseRecordList",props:{diagnosisId:{type:Number,default:0},readOnly:{type:Boolean,default:!1}},emits:["view","openPrescription"],setup(y,{expose:x,emit:C}){const _=y,h=C,l=k([]),p=k(!1),u=async()=>{if(_.diagnosisId){p.value=!0;try{const e=await O({diagnosis_id:_.diagnosisId});l.value=Array.isArray(e)?e:[]}catch(e){console.error("获取病历记录失败:",e),l.value=[]}finally{p.value=!1}}},S=e=>{if(!e)return"";const t=new Date(e*1e3);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`},$=e=>{h("view",e)},B=()=>{h("openPrescription")};return F(()=>{u()}),R(()=>_.diagnosisId,()=>{u()}),x({refresh:u}),(e,t)=>{const b=L,r=V,v=D,E=z,N=M,I=T;return a(),n("div",J,[y.readOnly?w("",!0):(a(),n("div",Q,[i(b,{type:"primary",size:"small",onClick:B},{default:s(()=>[...t[0]||(t[0]=[m("开方",-1)])]),_:1})])),j((a(),g(E,{data:d(l),border:""},{default:s(()=>[i(r,{prop:"prescription_date",label:"就诊日期",width:"120"}),i(r,{prop:"visit_no",label:"门诊号",width:"120"}),i(r,{prop:"clinical_diagnosis",label:"临床诊断","min-width":"160","show-overflow-tooltip":""}),i(r,{label:"处方摘要","min-width":"180"},{default:s(({row:o})=>[o.herbs&&o.herbs.length?(a(),n("span",Y,c(o.herbs.slice(0,3).map(f=>`${f.name}${f.dosage}克`).join("、"))+c(o.herbs.length>3?"...":""),1)):(a(),n("span",q,"—"))]),_:1}),i(r,{prop:"doctor_name",label:"医师",width:"90","show-overflow-tooltip":""}),i(r,{label:"状态",width:"140",align:"center"},{default:s(({row:o})=>[o.void_status===1?(a(),n(A,{key:0},[i(v,{type:"danger",size:"small"},{default:s(()=>[...t[1]||(t[1]=[m("已作废",-1)])]),_:1}),H("div",K,c(o.void_by_name||"—")+" "+c(S(o.void_time)),1)],64)):(a(),g(v,{key:1,type:"success",size:"small"},{default:s(()=>[...t[2]||(t[2]=[m("正常",-1)])]),_:1}))]),_:1}),i(r,{label:"操作",width:"120",fixed:"right"},{default:s(({row:o})=>[i(b,{link:"",type:"primary",size:"small",onClick:f=>$(o)},{default:s(()=>[...t[3]||(t[3]=[m(" 查看 ",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[I,d(p)]]),!d(p)&&d(l).length===0?(a(),g(N,{key:1,description:"暂无病历记录,开方后会自动显示",class:"mt-4"})):w("",!0)])}}}),zt=G(U,[["__scopeId","data-v-043d2738"]]);export{zt as default};
|
||||
@@ -0,0 +1 @@
|
||||
.daily-matrix[data-v-3f89758f]{padding:16px}.daily-matrix__toolbar[data-v-3f89758f]{display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:12px}.daily-matrix__toolbar-left[data-v-3f89758f],.daily-matrix__toolbar-right[data-v-3f89758f]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.daily-matrix__table[data-v-3f89758f],.daily-matrix__table-wrap[data-v-3f89758f]{width:100%}.daily-matrix__chart[data-v-3f89758f]{margin-top:16px;padding:16px 18px;border:1px solid var(--el-border-color-lighter);border-radius:10px;background:linear-gradient(180deg,#fff,#f8fafc)}.daily-matrix__chart-canvas[data-v-3f89758f]{height:280px;width:100%}.daily-matrix__cell[data-v-3f89758f]{min-height:32px;display:inline-flex;align-items:center;justify-content:center;gap:2px;width:100%;color:var(--el-text-color-regular)}.daily-matrix__cell.is-clickable[data-v-3f89758f]{cursor:pointer}.daily-matrix__cell.is-empty[data-v-3f89758f]{color:var(--el-text-color-placeholder)}.daily-matrix__cell.is-high[data-v-3f89758f]{color:#dc2626;font-weight:700}.daily-matrix__cell-up[data-v-3f89758f]{color:#dc2626;font-size:13px}.daily-matrix__todo[data-v-3f89758f]{margin-top:16px}.daily-matrix__section-title[data-v-3f89758f]{font-size:14px;font-weight:600;margin-bottom:12px;color:var(--el-text-color-primary)}.daily-matrix__tracking-existing[data-v-3f89758f]{width:100%;max-height:180px;overflow:auto;padding:8px 10px;border:1px solid var(--el-border-color);border-radius:6px;background:var(--el-fill-color-light)}.daily-matrix__tracking-line[data-v-3f89758f]{font-size:12.5px;line-height:1.6;color:var(--el-text-color-regular);word-break:break-word}.daily-matrix__tracking-preview[data-v-3f89758f]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;width:100%;text-align:left}.daily-matrix__tracking-tooltip[data-v-3f89758f]{max-width:320px}.daily-matrix__tracking-tooltip-line[data-v-3f89758f]{font-size:12.5px;line-height:1.6;word-break:break-word}@media(max-width:768px){.daily-matrix[data-v-3f89758f],.daily-matrix__chart[data-v-3f89758f]{padding:12px}.daily-matrix__chart-canvas[data-v-3f89758f]{height:240px}}
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.daily-matrix[data-v-8849b23d]{padding:16px}.daily-matrix__toolbar[data-v-8849b23d]{display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:12px}.daily-matrix__toolbar-left[data-v-8849b23d],.daily-matrix__toolbar-right[data-v-8849b23d]{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.daily-matrix__table[data-v-8849b23d],.daily-matrix__table-wrap[data-v-8849b23d]{width:100%}.daily-matrix__chart[data-v-8849b23d]{margin-top:16px;padding:16px 18px;border:1px solid var(--el-border-color-lighter);border-radius:10px;background:linear-gradient(180deg,#fff,#f8fafc)}.daily-matrix__chart-canvas[data-v-8849b23d]{height:280px;width:100%}.daily-matrix__cell[data-v-8849b23d]{min-height:32px;display:inline-flex;align-items:center;justify-content:center;gap:2px;width:100%;color:var(--el-text-color-regular)}.daily-matrix__cell.is-clickable[data-v-8849b23d]{cursor:pointer}.daily-matrix__cell.is-empty[data-v-8849b23d]{color:var(--el-text-color-placeholder)}.daily-matrix__cell.is-high[data-v-8849b23d]{color:#dc2626;font-weight:700}.daily-matrix__cell-up[data-v-8849b23d]{color:#dc2626;font-size:13px}.daily-matrix__todo[data-v-8849b23d]{margin-top:16px}.daily-matrix__section-title[data-v-8849b23d]{font-size:14px;font-weight:600;margin-bottom:12px;color:var(--el-text-color-primary)}.daily-matrix__tracking-existing[data-v-8849b23d]{width:100%;max-height:180px;overflow:auto;padding:8px 10px;border:1px solid var(--el-border-color);border-radius:6px;background:var(--el-fill-color-light)}.daily-matrix__tracking-line[data-v-8849b23d]{font-size:12.5px;line-height:1.6;color:var(--el-text-color-regular);word-break:break-word}.daily-matrix__tracking-preview[data-v-8849b23d]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;width:100%;text-align:left}.daily-matrix__tracking-tooltip[data-v-8849b23d]{max-width:320px}.daily-matrix__tracking-tooltip-line[data-v-8849b23d]{font-size:12.5px;line-height:1.6;word-break:break-word}@media(max-width:768px){.daily-matrix[data-v-8849b23d],.daily-matrix__chart[data-v-8849b23d]{padding:12px}.daily-matrix__chart-canvas[data-v-8849b23d]{height:240px}}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{R as W,v as Z,d as q,a as K}from"./element-plus-FSf94q2-.js";import{_ as Y}from"./picker-S140y0Ck.js";import{e as ee,c as te,i as S,_ as ie}from"./index-CLwyu9dm.js";import{s as A}from"./@vue/runtime-dom-DDAG46FW.js";import{b as B,G as se}from"./@element-plus/icons-vue-BDOO1F0I.js";import{d as oe,a as R}from"./patient-DNjuO9kO.js";import{f as ne,w as re,ak as i,I as n,a as l,aN as c,J as a,H as d,F as v,ap as k,G as I}from"./@vue/runtime-core-C6bnekPw.js";import{n as w,y as C}from"./@vue/reactivity-DiY1c2vO.js";import{Q as z}from"./@vue/shared-mAAVTE9n.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./lodash-D3kF6u-c.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-CbVXHb-T.js";import"./index-Ddx1af9I.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./index-Dn5DJbNI.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const le={class:"note-timeline-wrap"},ae={key:0,class:"timeline-actions"},me={class:"upload-trigger"},de={class:"upload-trigger"},pe={key:1,class:"note-timeline"},ce={class:"timeline-date"},ue={class:"timeline-body"},ge={key:0,class:"timeline-content"},_e={key:1,class:"timeline-images"},fe={key:2,class:"timeline-images"},ve={key:0,class:"thumb-wrap"},he={key:1,class:"file-wrap"},ye=["href","title"],ke={class:"file-name"},T=8e3,Ie=ne({__name:"NoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(m,{emit:G}){const u=m,b=G,L=ee(),g=t=>L.getImageUrl(t),x=w([]),E=w([]),_=w(0),f=w(0),$=["jpg","jpeg","png","gif","bmp","webp","svg"],N=t=>{var s;const e=((s=t.split(".").pop())==null?void 0:s.toLowerCase().split("?")[0])||"";return $.includes(e)},M=t=>{var s;const e=t.split("/");return decodeURIComponent(((s=e[e.length-1])==null?void 0:s.split("?")[0])||"文件")},j=t=>t.filter(N).map(g),O=(t,e)=>{const s=t.filter(N),h=t[e];return s.indexOf(h)},X=t=>t?t.split(`
|
||||
import{R as W,v as Z,d as q,a as K}from"./element-plus-DFTWCWyi.js";import{_ as Y}from"./picker-teM-xyL3.js";import{e as ee,c as te,i as S,_ as ie}from"./index-BOT-n-x_.js";import{s as A}from"./@vue/runtime-dom-DDAG46FW.js";import{b as B,G as se}from"./@element-plus/icons-vue-HOEUG8sr.js";import{d as oe,a as R}from"./patient-BsVK09Ag.js";import{f as ne,w as re,ak as i,I as n,a as l,aN as c,J as a,H as d,F as v,ap as k,G as I}from"./@vue/runtime-core-C6bnekPw.js";import{n as w,y as C}from"./@vue/reactivity-DiY1c2vO.js";import{Q as z}from"./@vue/shared-mAAVTE9n.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./lodash-D3kF6u-c.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-D0I-ELPR.js";import"./index-o8OqlvxT.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./index-C60sYn0f.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const le={class:"note-timeline-wrap"},ae={key:0,class:"timeline-actions"},me={class:"upload-trigger"},de={class:"upload-trigger"},pe={key:1,class:"note-timeline"},ce={class:"timeline-date"},ue={class:"timeline-body"},ge={key:0,class:"timeline-content"},_e={key:1,class:"timeline-images"},fe={key:2,class:"timeline-images"},ve={key:0,class:"thumb-wrap"},he={key:1,class:"file-wrap"},ye=["href","title"],ke={class:"file-name"},T=8e3,Ie=ne({__name:"NoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(m,{emit:G}){const u=m,b=G,L=ee(),g=t=>L.getImageUrl(t),x=w([]),E=w([]),_=w(0),f=w(0),$=["jpg","jpeg","png","gif","bmp","webp","svg"],N=t=>{var s;const e=((s=t.split(".").pop())==null?void 0:s.toLowerCase().split("?")[0])||"";return $.includes(e)},M=t=>{var s;const e=t.split("/");return decodeURIComponent(((s=e[e.length-1])==null?void 0:s.split("?")[0])||"文件")},j=t=>t.filter(N).map(g),O=(t,e)=>{const s=t.filter(N),h=t[e];return s.indexOf(h)},X=t=>t?t.split(`
|
||||
`).filter(Boolean):[],H=t=>{if(!u.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=_.value){_.value=e.length;return}const s=e.slice(_.value);_.value=e.length,s.length>0&&R({diagnosis_id:u.diagnosisId,tongue_images:s}).then(()=>{S.msgSuccess("舌苔照片已添加"),b("refresh")})},J=t=>{if(!u.diagnosisId)return;const e=Array.isArray(t)?t:[t];if(e.length<=f.value){f.value=e.length;return}const s=e.slice(f.value);f.value=e.length,s.length>0&&R({diagnosis_id:u.diagnosisId,report_files:s}).then(()=>{S.msgSuccess("检查报告已添加"),b("refresh")})};re(()=>u.notes,()=>{x.value=[],E.value=[],_.value=0,f.value=0});const V=async(t,e,s)=>{try{await K.confirm("确认删除?","提示",{type:"warning"})}catch{return}await oe({note_id:t,image_type:e,image_path:s}),S.msgSuccess("已删除"),b("refresh")};return(t,e)=>{const s=te,h=Y,U=Z,y=q,Q=W;return i(),n("div",le,[!m.readonly&&m.diagnosisId?(i(),n("div",ae,[l(h,{modelValue:x.value,"onUpdate:modelValue":e[0]||(e[0]=o=>x.value=o),limit:99,type:"image","exclude-domain":!0,onChange:H},{upload:c(()=>[a("div",me,[l(s,{size:20,name:"el-icon-Plus"}),e[2]||(e[2]=a("span",null,"舌苔照片",-1))])]),_:1},8,["modelValue"]),l(h,{modelValue:E.value,"onUpdate:modelValue":e[1]||(e[1]=o=>E.value=o),limit:99,type:"file","exclude-domain":!0,onChange:J},{upload:c(()=>[a("div",de,[l(s,{size:20,name:"el-icon-Plus"}),e[3]||(e[3]=a("span",null,"检查报告",-1))])]),_:1},8,["modelValue"])])):d("",!0),m.notes.length?(i(),n("div",pe,[(i(!0),n(v,null,k(m.notes,o=>{var D,F;return i(),n("div",{key:o.id,class:"timeline-node"},[e[6]||(e[6]=a("div",{class:"timeline-dot"},null,-1)),a("div",ce,z(o.note_date),1),a("div",ue,[o.content?(i(),n("div",ge,[(i(!0),n(v,null,k(X(o.content),(r,p)=>(i(),n("div",{key:p,class:"content-line"},z(r),1))),128))])):d("",!0),(D=o.tongue_images)!=null&&D.length?(i(),n("div",_e,[e[4]||(e[4]=a("span",{class:"images-label"},"舌苔照片",-1)),(i(!0),n(v,null,k(o.tongue_images,(r,p)=>(i(),n("div",{key:p,class:"thumb-wrap"},[l(U,{src:g(r),"preview-src-list":o.tongue_images.map(g),"initial-index":p,"z-index":T,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),m.readonly?d("",!0):(i(),I(y,{key:0,class:"thumb-delete",onClick:A(P=>V(o.id,"tongue_images",r),["stop"])},{default:c(()=>[l(C(B))]),_:1},8,["onClick"]))]))),128))])):d("",!0),(F=o.report_files)!=null&&F.length?(i(),n("div",fe,[e[5]||(e[5]=a("span",{class:"images-label"},"检查报告",-1)),(i(!0),n(v,null,k(o.report_files,(r,p)=>(i(),n(v,{key:p},[N(r)?(i(),n("div",ve,[l(U,{src:g(r),"preview-src-list":j(o.report_files),"initial-index":O(o.report_files,p),"z-index":T,fit:"cover",class:"timeline-thumb","preview-teleported":""},null,8,["src","preview-src-list","initial-index"]),m.readonly?d("",!0):(i(),I(y,{key:0,class:"thumb-delete",onClick:A(P=>V(o.id,"report_files",r),["stop"])},{default:c(()=>[l(C(B))]),_:1},8,["onClick"]))])):(i(),n("div",he,[a("a",{href:g(r),target:"_blank",class:"file-link",title:M(r)},[l(y,{size:20},{default:c(()=>[l(C(se))]),_:1}),a("span",ke,z(M(r)),1)],8,ye),m.readonly?d("",!0):(i(),I(y,{key:0,class:"file-delete",onClick:A(P=>V(o.id,"report_files",r),["stop"])},{default:c(()=>[l(C(B))]),_:1},8,["onClick"]))]))],64))),128))])):d("",!0)])])}),128))])):d("",!0),!m.notes.length&&m.readonly?(i(),I(Q,{key:2,description:"暂无备注","image-size":48})):d("",!0)])}}}),It=ie(Ie,[["__scopeId","data-v-f77bb3f4"]]);export{It as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{m as x,f as y,a as B}from"./diag-display-DCz_VAqj.js";import{f as w,ak as I,I as P,J as a,H as N}from"./@vue/runtime-core-C6bnekPw.js";import{Q as i}from"./@vue/shared-mAAVTE9n.js";import{y as e}from"./@vue/reactivity-DiY1c2vO.js";import{_ as V}from"./index-CLwyu9dm.js";import"./lodash-D3kF6u-c.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const D={class:"card patient-card"},E={class:"patient-hero"},G={class:"patient-name"},H={class:"patient-meta"},J={key:0},Q=w({__name:"PatientInfoCard",props:{apt:{default:()=>({})},diag:{default:()=>({})}},setup(t){return(S,o)=>{var m,r,n,d,p,s,c,l,g,f,u,h,v,k,C;return I(),P("div",D,[o[0]||(o[0]=a("div",{class:"card-title"},"患者信息",-1)),a("div",E,[a("div",G,i(((m=t.apt)==null?void 0:m.patient_name)||"—"),1),a("div",H,[a("div",null,i(e(x)((r=t.apt)==null?void 0:r.patient_phone))+" · "+i(e(y)((n=t.diag)==null?void 0:n.gender))+" · "+i(((d=t.diag)==null?void 0:d.age)!=null?t.diag.age+"岁":"—"),1),a("div",null,i((p=t.diag)!=null&&p.height?t.diag.height+"cm":"—")+" / "+i((s=t.diag)!=null&&s.weight?t.diag.weight+"kg":"—")+" · "+i(((c=t.diag)==null?void 0:c.region)||"—"),1),a("div",null," 预约:"+i((l=t.apt)==null?void 0:l.appointment_date)+" "+i((g=t.apt)==null?void 0:g.appointment_time)+" · "+i(e(B)((f=t.apt)==null?void 0:f.period)),1),a("div",null,"医生:"+i(((u=t.apt)==null?void 0:u.doctor_name)||"—")+" 医助:"+i(((h=t.apt)==null?void 0:h.assistant_name)||"—"),1),a("div",null," 状态:"+i(((v=t.apt)==null?void 0:v.status_desc)||"—")+" · "+i((k=t.apt)!=null&&k.has_prescription?"已开方":"未开方"),1),(C=t.apt)!=null&&C.remark?(I(),P("div",J,"备注:"+i(t.apt.remark),1)):N("",!0)])])])}}}),Ct=V(Q,[["__scopeId","data-v-e3476da5"]]);export{Ct as default};
|
||||
import{m as x,f as y,a as B}from"./diag-display-DCz_VAqj.js";import{f as w,ak as I,I as P,J as a,H as N}from"./@vue/runtime-core-C6bnekPw.js";import{Q as i}from"./@vue/shared-mAAVTE9n.js";import{y as e}from"./@vue/reactivity-DiY1c2vO.js";import{_ as V}from"./index-BOT-n-x_.js";import"./lodash-D3kF6u-c.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const D={class:"card patient-card"},E={class:"patient-hero"},G={class:"patient-name"},H={class:"patient-meta"},J={key:0},Q=w({__name:"PatientInfoCard",props:{apt:{default:()=>({})},diag:{default:()=>({})}},setup(t){return(S,o)=>{var m,r,n,d,p,s,c,l,g,f,u,h,v,k,C;return I(),P("div",D,[o[0]||(o[0]=a("div",{class:"card-title"},"患者信息",-1)),a("div",E,[a("div",G,i(((m=t.apt)==null?void 0:m.patient_name)||"—"),1),a("div",H,[a("div",null,i(e(x)((r=t.apt)==null?void 0:r.patient_phone))+" · "+i(e(y)((n=t.diag)==null?void 0:n.gender))+" · "+i(((d=t.diag)==null?void 0:d.age)!=null?t.diag.age+"岁":"—"),1),a("div",null,i((p=t.diag)!=null&&p.height?t.diag.height+"cm":"—")+" / "+i((s=t.diag)!=null&&s.weight?t.diag.weight+"kg":"—")+" · "+i(((c=t.diag)==null?void 0:c.region)||"—"),1),a("div",null," 预约:"+i((l=t.apt)==null?void 0:l.appointment_date)+" "+i((g=t.apt)==null?void 0:g.appointment_time)+" · "+i(e(B)((f=t.apt)==null?void 0:f.period)),1),a("div",null,"医生:"+i(((u=t.apt)==null?void 0:u.doctor_name)||"—")+" 医助:"+i(((h=t.apt)==null?void 0:h.assistant_name)||"—"),1),a("div",null," 状态:"+i(((v=t.apt)==null?void 0:v.status_desc)||"—")+" · "+i((k=t.apt)!=null&&k.has_prescription?"已开方":"未开方"),1),(C=t.apt)!=null&&C.remark?(I(),P("div",J,"备注:"+i(t.apt.remark),1)):N("",!0)])])])}}}),Ct=V(Q,[["__scopeId","data-v-e3476da5"]]);export{Ct as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{_ as L}from"./element-plus-FSf94q2-.js";import B from"./RecordingVideoPlayer-CKvU5-RG.js";import{e as H,_ as I}from"./index-CLwyu9dm.js";import{f as w,ak as n,I as m,J as p,F as _,G as u,aN as v,O as k,H as y,ap as R,A as d}from"./@vue/runtime-core-C6bnekPw.js";import{Q as q}from"./@vue/shared-mAAVTE9n.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const C={key:0,class:"recording-list"},N={class:"recording-item"},P={key:1,class:"recording-alternates"},V={class:"recording-alternates__links"},A={key:1,class:"text-gray-400"},E=w({__name:"RecordingPlaybackBlock",props:{recordId:{},urls:{}},setup(c){const $=c,h=H(),l=d(()=>{const e=$.urls||[],t=new Set,r=[];for(const s of e){const o=String(s??"").trim();!o||t.has(o)||(t.add(o),r.push(o))}return r}),a=d(()=>{const e=l.value;if(!e.length)return null;const t=e.find(i=>/\.mp4(\?|#|$)/i.test(i));if(t)return t;const r=e.find(i=>/\.m3u8(\?|#|$)/i.test(i)&&/vod-qcloud\.com/i.test(i));if(r)return r;const s=e.find(i=>/\.m3u8(\?|#|$)/i.test(i)&&/\.cos\.[^/]+\.myqcloud\.com/i.test(i));if(s)return s;const o=e.find(i=>/\.m3u8(\?|#|$)/i.test(i));return o||e[0]}),f=d(()=>{const e=l.value,t=a.value;return t?e.filter(r=>r!==t):e.slice(1)});function b(e){if(!e||typeof e!="string")return!1;const t=e.trim();return/^https?:\/\//i.test(t)?/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t):/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t)||t.startsWith("/uploads/")}function g(e){return h.getImageUrl(String(e||"").trim())}function S(e,t){const r=e.trim();return/\.mp4(\?|#|$)/i.test(r)?`MP4 ${t+1}`:/vod-qcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`点播 ${t+1}`:/\.cos\.[^/]+\.myqcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`COS HLS ${t+1}`:/\.m3u8/i.test(r)?`HLS ${t+1}`:`链接 ${t+1}`}return(e,t)=>{const r=L;return l.value.length?(n(),m("div",C,[p("div",N,[a.value?(n(),m(_,{key:0},[b(a.value)?(n(),u(B,{key:`${c.recordId}-${a.value}`,src:a.value},null,8,["src"])):(n(),u(r,{key:1,href:g(a.value),target:"_blank",type:"primary"},{default:v(()=>[...t[0]||(t[0]=[k(" 打开回放 ",-1)])]),_:1},8,["href"]))],64)):y("",!0),f.value.length?(n(),m("div",P,[t[1]||(t[1]=p("span",{class:"recording-alternates__label"},"备用地址",-1)),p("div",V,[(n(!0),m(_,null,R(f.value,(s,o)=>(n(),u(r,{key:`${c.recordId}-alt-${o}-${s.slice(-32)}`,href:g(s),target:"_blank",type:"primary",class:"recording-alternates__link"},{default:v(()=>[k(q(S(s,o)),1)]),_:2},1032,["href"]))),128))])])):y("",!0)])])):(n(),m("span",A,"暂无"))}}}),ht=I(E,[["__scopeId","data-v-d67b2e91"]]);export{ht as default};
|
||||
import{_ as L}from"./element-plus-DFTWCWyi.js";import B from"./RecordingVideoPlayer-CnxtgiC2.js";import{e as H,_ as I}from"./index-BOT-n-x_.js";import{f as w,ak as n,I as m,J as p,F as _,G as u,aN as v,O as k,H as y,ap as R,A as d}from"./@vue/runtime-core-C6bnekPw.js";import{Q as q}from"./@vue/shared-mAAVTE9n.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const C={key:0,class:"recording-list"},N={class:"recording-item"},P={key:1,class:"recording-alternates"},V={class:"recording-alternates__links"},A={key:1,class:"text-gray-400"},E=w({__name:"RecordingPlaybackBlock",props:{recordId:{},urls:{}},setup(c){const $=c,h=H(),l=d(()=>{const e=$.urls||[],t=new Set,r=[];for(const s of e){const o=String(s??"").trim();!o||t.has(o)||(t.add(o),r.push(o))}return r}),a=d(()=>{const e=l.value;if(!e.length)return null;const t=e.find(i=>/\.mp4(\?|#|$)/i.test(i));if(t)return t;const r=e.find(i=>/\.m3u8(\?|#|$)/i.test(i)&&/vod-qcloud\.com/i.test(i));if(r)return r;const s=e.find(i=>/\.m3u8(\?|#|$)/i.test(i)&&/\.cos\.[^/]+\.myqcloud\.com/i.test(i));if(s)return s;const o=e.find(i=>/\.m3u8(\?|#|$)/i.test(i));return o||e[0]}),f=d(()=>{const e=l.value,t=a.value;return t?e.filter(r=>r!==t):e.slice(1)});function b(e){if(!e||typeof e!="string")return!1;const t=e.trim();return/^https?:\/\//i.test(t)?/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t):/\.(mp4|webm|ogg|mov|mkv|m4v|m3u8)(\?|#|$)/i.test(t)||t.startsWith("/uploads/")}function g(e){return h.getImageUrl(String(e||"").trim())}function S(e,t){const r=e.trim();return/\.mp4(\?|#|$)/i.test(r)?`MP4 ${t+1}`:/vod-qcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`点播 ${t+1}`:/\.cos\.[^/]+\.myqcloud\.com/i.test(r)&&/\.m3u8/i.test(r)?`COS HLS ${t+1}`:/\.m3u8/i.test(r)?`HLS ${t+1}`:`链接 ${t+1}`}return(e,t)=>{const r=L;return l.value.length?(n(),m("div",C,[p("div",N,[a.value?(n(),m(_,{key:0},[b(a.value)?(n(),u(B,{key:`${c.recordId}-${a.value}`,src:a.value},null,8,["src"])):(n(),u(r,{key:1,href:g(a.value),target:"_blank",type:"primary"},{default:v(()=>[...t[0]||(t[0]=[k(" 打开回放 ",-1)])]),_:1},8,["href"]))],64)):y("",!0),f.value.length?(n(),m("div",P,[t[1]||(t[1]=p("span",{class:"recording-alternates__label"},"备用地址",-1)),p("div",V,[(n(!0),m(_,null,R(f.value,(s,o)=>(n(),u(r,{key:`${c.recordId}-alt-${o}-${s.slice(-32)}`,href:g(s),target:"_blank",type:"primary",class:"recording-alternates__link"},{default:v(()=>[k(q(S(s,o)),1)]),_:2},1032,["href"]))),128))])])):y("",!0)])])):(n(),m("span",A,"暂无"))}}}),ht=I(E,[["__scopeId","data-v-d67b2e91"]]);export{ht as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,2 +1,2 @@
|
||||
import{C as I,i as V,R as w}from"./element-plus-FSf94q2-.js";import{Q as T}from"./tcm-CZik0zOd.js";import{i as C,_ as E}from"./index-CLwyu9dm.js";import{f as z,ak as t,I as e,a as p,J as l,aN as H,O as S,H as m,F as c,ap as u,G as F}from"./@vue/runtime-core-C6bnekPw.js";import{Q as f}from"./@vue/shared-mAAVTE9n.js";import{n as g}from"./@vue/reactivity-DiY1c2vO.js";/* empty css */import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const L={class:"tracking-timeline-wrap"},M={key:0,class:"timeline-input"},Q={class:"input-actions"},A={key:1,class:"tracking-timeline"},D={class:"timeline-date"},G={class:"timeline-body"},J={key:0,class:"timeline-content"},O=z({__name:"TrackingNoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(i,{emit:v}){const d=i,y=v,r=g(""),a=g(!1),_=o=>o?o.split(`
|
||||
`).filter(Boolean):[],k=async()=>{if(!d.diagnosisId)return;const o=r.value.trim();if(o){a.value=!0;try{await T({diagnosis_id:d.diagnosisId,tracking_content:o}),C.msgSuccess("已添加"),r.value="",y("refresh")}finally{a.value=!1}}};return(o,n)=>{const h=I,b=V,N=w;return t(),e("div",L,[!i.readonly&&i.diagnosisId?(t(),e("div",M,[p(h,{modelValue:r.value,"onUpdate:modelValue":n[0]||(n[0]=s=>r.value=s),type:"textarea",rows:2,placeholder:"输入跟踪备注,回车换行;保存后将以「[HH:MM] 内容」追加到当天记录",maxlength:"1000","show-word-limit":"",resize:"none",disabled:a.value},null,8,["modelValue","disabled"]),l("div",Q,[p(b,{type:"primary",size:"small",loading:a.value,disabled:!r.value.trim(),onClick:k},{default:H(()=>[...n[1]||(n[1]=[S(" 添加 ",-1)])]),_:1},8,["loading","disabled"])])])):m("",!0),i.notes.length?(t(),e("div",A,[(t(!0),e(c,null,u(i.notes,s=>(t(),e("div",{key:s.id,class:"timeline-node"},[n[2]||(n[2]=l("div",{class:"timeline-dot"},null,-1)),l("div",D,f(s.note_date),1),l("div",G,[s.content?(t(),e("div",J,[(t(!0),e(c,null,u(_(s.content),(x,B)=>(t(),e("div",{key:B,class:"content-line"},f(x),1))),128))])):m("",!0)])]))),128))])):m("",!0),!i.notes.length&&i.readonly?(t(),F(N,{key:2,description:"暂无跟踪备注","image-size":48})):m("",!0)])}}}),Tt=E(O,[["__scopeId","data-v-82b635bd"]]);export{Tt as default};
|
||||
import{C as I,i as V,R as w}from"./element-plus-DFTWCWyi.js";import{R as T}from"./tcm-B5X1iJji.js";import{i as C,_ as E}from"./index-BOT-n-x_.js";import{f as z,ak as t,I as e,a as p,J as l,aN as H,O as S,H as m,F as c,ap as u,G as F}from"./@vue/runtime-core-C6bnekPw.js";import{Q as f}from"./@vue/shared-mAAVTE9n.js";import{n as g}from"./@vue/reactivity-DiY1c2vO.js";/* empty css */import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";const L={class:"tracking-timeline-wrap"},M={key:0,class:"timeline-input"},R={class:"input-actions"},A={key:1,class:"tracking-timeline"},D={class:"timeline-date"},G={class:"timeline-body"},J={key:0,class:"timeline-content"},O=z({__name:"TrackingNoteTimeline",props:{notes:{},diagnosisId:{},readonly:{type:Boolean}},emits:["refresh"],setup(i,{emit:v}){const d=i,y=v,r=g(""),a=g(!1),_=o=>o?o.split(`
|
||||
`).filter(Boolean):[],k=async()=>{if(!d.diagnosisId)return;const o=r.value.trim();if(o){a.value=!0;try{await T({diagnosis_id:d.diagnosisId,tracking_content:o}),C.msgSuccess("已添加"),r.value="",y("refresh")}finally{a.value=!1}}};return(o,n)=>{const h=I,b=V,N=w;return t(),e("div",L,[!i.readonly&&i.diagnosisId?(t(),e("div",M,[p(h,{modelValue:r.value,"onUpdate:modelValue":n[0]||(n[0]=s=>r.value=s),type:"textarea",rows:2,placeholder:"输入跟踪备注,回车换行;保存后将以「[HH:MM] 内容」追加到当天记录",maxlength:"1000","show-word-limit":"",resize:"none",disabled:a.value},null,8,["modelValue","disabled"]),l("div",R,[p(b,{type:"primary",size:"small",loading:a.value,disabled:!r.value.trim(),onClick:k},{default:H(()=>[...n[1]||(n[1]=[S(" 添加 ",-1)])]),_:1},8,["loading","disabled"])])])):m("",!0),i.notes.length?(t(),e("div",A,[(t(!0),e(c,null,u(i.notes,s=>(t(),e("div",{key:s.id,class:"timeline-node"},[n[2]||(n[2]=l("div",{class:"timeline-dot"},null,-1)),l("div",D,f(s.note_date),1),l("div",G,[s.content?(t(),e("div",J,[(t(!0),e(c,null,u(_(s.content),(x,B)=>(t(),e("div",{key:B,class:"content-line"},f(x),1))),128))])):m("",!0)])]))),128))])):m("",!0),!i.notes.length&&i.readonly?(t(),F(N,{key:2,description:"暂无跟踪备注","image-size":48})):m("",!0)])}}}),Tt=E(O,[["__scopeId","data-v-82b635bd"]]);export{Tt as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-D5b6gD4R.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-CbVXHb-T.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";export{o as default};
|
||||
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-s0IS1uNa.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-D0I-ELPR.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{D as h,B as q,G as B,I,C as D}from"./element-plus-FSf94q2-.js";import{_ as F}from"./index-CbVXHb-T.js";import{i as b}from"./index-CLwyu9dm.js";import{f as G,w,ak as j,G as S,aN as r,J as U,a,O as u,A}from"./@vue/runtime-core-C6bnekPw.js";import{y as n,q as y,r as J}from"./@vue/reactivity-DiY1c2vO.js";import{Q as k}from"./@vue/shared-mAAVTE9n.js";const M={class:"pr-8"},L=G({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:V}){const s=y(),i=d,f=V,o=J({action:1,num:"",remark:""}),m=y(),c=A(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),R={num:[{required:!0,message:"请输入调整的金额"}]},g=e=>{if(e.includes("-"))return b.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=s.value)==null?void 0:e.validate()),f("confirm",o)},C=()=>{var e;f("update:show",!1),(e=s.value)==null||e.resetFields()};return w(()=>i.show,e=>{var t,l;e?(t=m.value)==null||t.open():(l=m.value)==null||l.close()}),w(c,e=>{e<0&&(b.msgError("调整后余额需大于0"),o.num="")}),(e,t)=>{const l=q,_=I,E=B,v=D,N=h;return j(),S(F,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:C},{default:r(()=>[U("div",M,[a(N,{ref_key:"formRef",ref:s,model:n(o),"label-width":"120px",rules:R},{default:r(()=>[a(l,{label:"当前余额"},{default:r(()=>[u("¥ "+k(d.value),1)]),_:1}),a(l,{label:"余额增减",required:"",prop:"action"},{default:r(()=>[a(E,{modelValue:n(o).action,"onUpdate:modelValue":t[0]||(t[0]=p=>n(o).action=p)},{default:r(()=>[a(_,{value:1},{default:r(()=>[...t[2]||(t[2]=[u("增加余额",-1)])]),_:1}),a(_,{value:2},{default:r(()=>[...t[3]||(t[3]=[u("扣减余额",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(l,{label:"调整余额",prop:"num"},{default:r(()=>[a(v,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:g},null,8,["model-value"])]),_:1}),a(l,{label:"调整后余额"},{default:r(()=>[u(" ¥ "+k(n(c)),1)]),_:1}),a(l,{label:"备注",prop:"remark"},{default:r(()=>[a(v,{modelValue:n(o).remark,"onUpdate:modelValue":t[1]||(t[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{L as _};
|
||||
import{D as h,B as q,G as B,I,C as D}from"./element-plus-DFTWCWyi.js";import{_ as F}from"./index-D0I-ELPR.js";import{i as b}from"./index-BOT-n-x_.js";import{f as G,w,ak as j,G as S,aN as r,J as U,a,O as u,A}from"./@vue/runtime-core-C6bnekPw.js";import{y as n,q as y,r as J}from"./@vue/reactivity-DiY1c2vO.js";import{Q as k}from"./@vue/shared-mAAVTE9n.js";const M={class:"pr-8"},L=G({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:V}){const s=y(),i=d,f=V,o=J({action:1,num:"",remark:""}),m=y(),c=A(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),R={num:[{required:!0,message:"请输入调整的金额"}]},g=e=>{if(e.includes("-"))return b.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=s.value)==null?void 0:e.validate()),f("confirm",o)},C=()=>{var e;f("update:show",!1),(e=s.value)==null||e.resetFields()};return w(()=>i.show,e=>{var t,l;e?(t=m.value)==null||t.open():(l=m.value)==null||l.close()}),w(c,e=>{e<0&&(b.msgError("调整后余额需大于0"),o.num="")}),(e,t)=>{const l=q,_=I,E=B,v=D,N=h;return j(),S(F,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:C},{default:r(()=>[U("div",M,[a(N,{ref_key:"formRef",ref:s,model:n(o),"label-width":"120px",rules:R},{default:r(()=>[a(l,{label:"当前余额"},{default:r(()=>[u("¥ "+k(d.value),1)]),_:1}),a(l,{label:"余额增减",required:"",prop:"action"},{default:r(()=>[a(E,{modelValue:n(o).action,"onUpdate:modelValue":t[0]||(t[0]=p=>n(o).action=p)},{default:r(()=>[a(_,{value:1},{default:r(()=>[...t[2]||(t[2]=[u("增加余额",-1)])]),_:1}),a(_,{value:2},{default:r(()=>[...t[3]||(t[3]=[u("扣减余额",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(l,{label:"调整余额",prop:"num"},{default:r(()=>[a(v,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:g},null,8,["model-value"])]),_:1}),a(l,{label:"调整后余额"},{default:r(()=>[u(" ¥ "+k(n(c)),1)]),_:1}),a(l,{label:"备注",prop:"remark"},{default:r(()=>[a(v,{modelValue:n(o).remark,"onUpdate:modelValue":t[1]||(t[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{L as _};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-CLFObqD9.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-KI5KMhoO.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{C as E,B,g as C,i as N}from"./element-plus-FSf94q2-.js";import{_ as $}from"./index-Dn5DJbNI.js";import{_ as z}from"./picker-BdCBwiHB.js";import{_ as A}from"./picker-S140y0Ck.js";import{c as D,i as r}from"./index-CLwyu9dm.js";import{D as I}from"./vuedraggable-5bKFmC7X.js";import{f as R,ak as p,I as F,J as l,a,aN as d,G,O as J,A as L}from"./@vue/runtime-core-C6bnekPw.js";import{y as c,a as O}from"./@vue/reactivity-DiY1c2vO.js";const P={class:"bg-fill-light flex items-center w-full p-4 mb-4"},S={class:"upload-btn w-[60px] h-[60px]"},T={class:"ml-3 flex-1"},j={class:"flex items-center"},q={class:"flex items-center mt-[18px]"},H={class:"flex-1 flex items-center"},K={class:"drag-move cursor-move ml-auto"},oe=R({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=L({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):r.msgError(`最多添加${t.max}个`)},g=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return r.msgError(`最少保留${t.min}个`);m.value.splice(s,1)};return(s,e)=>{const i=D,v=A,h=E,k=z,b=C,w=B,y=$,U=N;return p(),F("div",null,[l("div",null,[a(c(I),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>O(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:u})=>[(p(),G(y,{class:"w-[467px]",key:u,onClose:n=>g(u)},{default:d(()=>[l("div",P,[a(v,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",S,[a(i,{name:"el-icon-Plus",size:20})])]),_:1},8,["modelValue","onUpdate:modelValue"]),l("div",T,[l("div",j,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(h,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",q,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(k,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",H,[a(b,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",K,[a(i,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(U,{type:"primary",onClick:x},{default:d(()=>[...e[3]||(e[3]=[J("添加",-1)])]),_:1})])])}}});export{oe as _};
|
||||
import{C as E,B,g as C,i as N}from"./element-plus-DFTWCWyi.js";import{_ as $}from"./index-C60sYn0f.js";import{_ as z}from"./picker-CzLwL9Gk.js";import{_ as A}from"./picker-teM-xyL3.js";import{c as D,i as r}from"./index-BOT-n-x_.js";import{D as I}from"./vuedraggable-5bKFmC7X.js";import{f as R,ak as p,I as F,J as l,a,aN as d,G,O as J,A as L}from"./@vue/runtime-core-C6bnekPw.js";import{y as c,a as O}from"./@vue/reactivity-DiY1c2vO.js";const P={class:"bg-fill-light flex items-center w-full p-4 mb-4"},S={class:"upload-btn w-[60px] h-[60px]"},T={class:"ml-3 flex-1"},j={class:"flex items-center"},q={class:"flex items-center mt-[18px]"},H={class:"flex-1 flex items-center"},K={class:"drag-move cursor-move ml-auto"},oe=R({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=L({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):r.msgError(`最多添加${t.max}个`)},g=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return r.msgError(`最少保留${t.min}个`);m.value.splice(s,1)};return(s,e)=>{const i=D,v=A,h=E,k=z,b=C,w=B,y=$,U=N;return p(),F("div",null,[l("div",null,[a(c(I),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>O(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:u})=>[(p(),G(y,{class:"w-[467px]",key:u,onClose:n=>g(u)},{default:d(()=>[l("div",P,[a(v,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",S,[a(i,{name:"el-icon-Plus",size:20})])]),_:1},8,["modelValue","onUpdate:modelValue"]),l("div",T,[l("div",j,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(h,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",q,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(k,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",H,[a(b,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",K,[a(i,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(U,{type:"primary",onClick:x},{default:d(()=>[...e[3]||(e[3]=[J("添加",-1)])]),_:1})])])}}});export{oe as _};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{r as n}from"./index-CLwyu9dm.js";function e(t){return n.get({url:"/auth.admin/lists",params:t},{ignoreCancelToken:!0})}function i(t){return n.post({url:"/auth.admin/add",params:t})}function r(t){return n.post({url:"/auth.admin/edit",params:t})}function u(t){return n.post({url:"/auth.admin/delete",params:t})}function d(t){return n.get({url:"/auth.admin/detail",params:t})}export{e as a,r as b,u as c,i as d,d as e};
|
||||
import{r as n}from"./index-BOT-n-x_.js";function e(t){return n.get({url:"/auth.admin/lists",params:t},{ignoreCancelToken:!0})}function i(t){return n.post({url:"/auth.admin/add",params:t})}function r(t){return n.post({url:"/auth.admin/edit",params:t})}function u(t){return n.post({url:"/auth.admin/delete",params:t})}function d(t){return n.get({url:"/auth.admin/detail",params:t})}export{e as a,r as b,u as c,i as d,d as e};
|
||||
@@ -0,0 +1 @@
|
||||
.today-block-alert[data-v-dbfff1e8]{margin-bottom:16px}.appointment-form[data-v-dbfff1e8] .el-form-item__label{font-weight:500}.appointment-form .channel-source-select[data-v-dbfff1e8]{width:100%;max-width:360px}.doctor-list[data-v-dbfff1e8]{display:flex;flex-wrap:wrap;gap:12px}.doctor-list .doctor-radio[data-v-dbfff1e8]{margin-right:0}.doctor-list .doctor-radio[data-v-dbfff1e8] .el-radio__label{display:flex;align-items:center}.appointment-time-container[data-v-dbfff1e8]{width:100%}.date-selector[data-v-dbfff1e8]{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:20px}.date-selector .date-button[data-v-dbfff1e8]{min-width:130px;height:40px;font-size:14px;border-radius:8px;transition:all .2s}.date-selector .date-button[data-v-dbfff1e8]:hover{transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.time-slots-container[data-v-dbfff1e8]{background-color:#f8f9fa;border-radius:8px;padding:16px}.time-slots-header[data-v-dbfff1e8]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.time-slots-header .header-title[data-v-dbfff1e8]{font-size:15px;font-weight:600;color:#303133}.time-slots-grid[data-v-dbfff1e8]{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px;max-height:450px;overflow-y:auto;padding:2px}.time-slots-grid[data-v-dbfff1e8]::-webkit-scrollbar{width:6px}.time-slots-grid[data-v-dbfff1e8]::-webkit-scrollbar-thumb{background-color:#dcdfe6;border-radius:3px}.time-slots-grid[data-v-dbfff1e8]::-webkit-scrollbar-thumb:hover{background-color:#c0c4cc}.time-slots-grid .time-slot-item[data-v-dbfff1e8]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0 8px;border:2px solid #e4e7ed;border-radius:8px;cursor:pointer;transition:all .2s;background-color:#fff;min-height:70px}.time-slots-grid .time-slot-item .slot-time[data-v-dbfff1e8]{font-size:15px;font-weight:600;color:#303133;margin-bottom:6px}.time-slots-grid .time-slot-item .slot-status[data-v-dbfff1e8]{font-size:12px;color:#909399;padding:0 8px;border-radius:4px;background-color:#f4f4f5}.time-slots-grid .time-slot-item .slot-status.status-available[data-v-dbfff1e8]{color:#67c23a;background-color:#f0f9ff}.time-slots-grid .time-slot-item.available[data-v-dbfff1e8]{border-color:#e4e7ed}.time-slots-grid .time-slot-item.available[data-v-dbfff1e8]:hover{border-color:#409eff;background-color:#ecf5ff;transform:translateY(-2px);box-shadow:0 4px 12px #409eff26}.time-slots-grid .time-slot-item.unavailable[data-v-dbfff1e8]{background-color:#f5f7fa;border-color:#e4e7ed;cursor:not-allowed;opacity:.6}.time-slots-grid .time-slot-item.unavailable .slot-time[data-v-dbfff1e8]{color:#c0c4cc}.time-slots-grid .time-slot-item.unavailable .slot-status[data-v-dbfff1e8]{color:#c0c4cc;background-color:#f5f7fa}.time-slots-grid .time-slot-item.unavailable[data-v-dbfff1e8]:hover{transform:none;box-shadow:none}.time-slots-grid .time-slot-item.selected[data-v-dbfff1e8]{border-color:#409eff;background:linear-gradient(135deg,#409eff,#66b1ff);box-shadow:0 4px 12px #409eff4d}.time-slots-grid .time-slot-item.selected .slot-time[data-v-dbfff1e8]{color:#fff}.time-slots-grid .time-slot-item.selected .slot-status[data-v-dbfff1e8]{color:#fff;background-color:#fff3}[data-v-dbfff1e8] .el-radio-group{display:flex;flex-wrap:wrap;gap:12px}[data-v-dbfff1e8] .el-radio{margin-right:0}.drawer-footer[data-v-dbfff1e8]{display:flex;justify-content:flex-end;gap:12px;padding:12px 0}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.today-block-alert[data-v-0586cc07]{margin-bottom:16px}.appointment-form[data-v-0586cc07] .el-form-item__label{font-weight:500}.appointment-form .channel-source-select[data-v-0586cc07]{width:100%;max-width:360px}.doctor-list[data-v-0586cc07]{display:flex;flex-wrap:wrap;gap:12px}.doctor-list .doctor-radio[data-v-0586cc07]{margin-right:0}.doctor-list .doctor-radio[data-v-0586cc07] .el-radio__label{display:flex;align-items:center}.appointment-time-container[data-v-0586cc07]{width:100%}.date-selector[data-v-0586cc07]{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:20px}.date-selector .date-button[data-v-0586cc07]{min-width:130px;height:40px;font-size:14px;border-radius:8px;transition:all .2s}.date-selector .date-button[data-v-0586cc07]:hover{transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.time-slots-container[data-v-0586cc07]{background-color:#f8f9fa;border-radius:8px;padding:16px}.time-slots-header[data-v-0586cc07]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.time-slots-header .header-title[data-v-0586cc07]{font-size:15px;font-weight:600;color:#303133}.time-slots-grid[data-v-0586cc07]{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px;max-height:450px;overflow-y:auto;padding:2px}.time-slots-grid[data-v-0586cc07]::-webkit-scrollbar{width:6px}.time-slots-grid[data-v-0586cc07]::-webkit-scrollbar-thumb{background-color:#dcdfe6;border-radius:3px}.time-slots-grid[data-v-0586cc07]::-webkit-scrollbar-thumb:hover{background-color:#c0c4cc}.time-slots-grid .time-slot-item[data-v-0586cc07]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0 8px;border:2px solid #e4e7ed;border-radius:8px;cursor:pointer;transition:all .2s;background-color:#fff;min-height:70px}.time-slots-grid .time-slot-item .slot-time[data-v-0586cc07]{font-size:15px;font-weight:600;color:#303133;margin-bottom:6px}.time-slots-grid .time-slot-item .slot-status[data-v-0586cc07]{font-size:12px;color:#909399;padding:0 8px;border-radius:4px;background-color:#f4f4f5}.time-slots-grid .time-slot-item .slot-status.status-available[data-v-0586cc07]{color:#67c23a;background-color:#f0f9ff}.time-slots-grid .time-slot-item.available[data-v-0586cc07]{border-color:#e4e7ed}.time-slots-grid .time-slot-item.available[data-v-0586cc07]:hover{border-color:#409eff;background-color:#ecf5ff;transform:translateY(-2px);box-shadow:0 4px 12px #409eff26}.time-slots-grid .time-slot-item.unavailable[data-v-0586cc07]{background-color:#f5f7fa;border-color:#e4e7ed;cursor:not-allowed;opacity:.6}.time-slots-grid .time-slot-item.unavailable .slot-time[data-v-0586cc07]{color:#c0c4cc}.time-slots-grid .time-slot-item.unavailable .slot-status[data-v-0586cc07]{color:#c0c4cc;background-color:#f5f7fa}.time-slots-grid .time-slot-item.unavailable[data-v-0586cc07]:hover{transform:none;box-shadow:none}.time-slots-grid .time-slot-item.selected[data-v-0586cc07]{border-color:#409eff;background:linear-gradient(135deg,#409eff,#66b1ff);box-shadow:0 4px 12px #409eff4d}.time-slots-grid .time-slot-item.selected .slot-time[data-v-0586cc07]{color:#fff}.time-slots-grid .time-slot-item.selected .slot-status[data-v-0586cc07]{color:#fff;background-color:#fff3}[data-v-0586cc07] .el-radio-group{display:flex;flex-wrap:wrap;gap:12px}[data-v-0586cc07] .el-radio{margin-right:0}.drawer-footer[data-v-0586cc07]{display:flex;justify-content:flex-end;gap:12px;padding:12px 0}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{r as e}from"./index-CLwyu9dm.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{a,u as b,s as c,c as d,i as e,n as f,p as g,f as h,d as i,l as j,o as k,g as l,C as m};
|
||||
import{r as e}from"./index-BOT-n-x_.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{a,u as b,s as c,c as d,i as e,n as f,p as g,f as h,d as i,l as j,o as k,g as l,C as m};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-CKrj1IU6.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DxkrGyhp.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-fMHRVKyM.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DudcaLfz.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-lBj8VQox.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./index.vue_vue_type_script_setup_true_lang-CLqXsRdl.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BrfJN9e6.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./index.vue_vue_type_script_setup_true_lang-3vCdLVB9.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-LpW82yJ0.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CLFObqD9.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-8qSi4wRz.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-KI5KMhoO.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-D7CXSC4N.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./picker-S140y0Ck.js";import"./index-CbVXHb-T.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./index-Ddx1af9I.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./index-Dn5DJbNI.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DoC1u8Rb.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./picker-teM-xyL3.js";import"./index-D0I-ELPR.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./index-o8OqlvxT.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./index-C60sYn0f.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-ClHvuPzx.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index.vue_vue_type_script_setup_true_lang-CLqXsRdl.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./picker-S140y0Ck.js";import"./index-CbVXHb-T.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./index-Ddx1af9I.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./index-Dn5DJbNI.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-B-x_uNbm.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index.vue_vue_type_script_setup_true_lang-3vCdLVB9.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./picker-teM-xyL3.js";import"./index-D0I-ELPR.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./index-o8OqlvxT.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./index-C60sYn0f.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./usePaging-VsbTxSU0.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-CA8p5N1S.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CLFObqD9.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BsexX2bP.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-KI5KMhoO.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{m as b,l as c,D as V}from"./element-plus-FSf94q2-.js";import{_ as l}from"./menu-set.vue_vue_type_script_setup_true_lang-CF10ag2p.js";import{f as v,ak as x,I as k,J as E,a as o,aN as e,F as g,A as w}from"./@vue/runtime-core-C6bnekPw.js";import{y as r}from"./@vue/reactivity-DiY1c2vO.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";const Bt=v({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(n,{emit:s}){const u=n,d=s,m=w({get(){return u.modelValue},set(i){d("update:modelValue",i)}});return(i,t)=>{const a=c,f=b,_=V;return x(),k(g,null,[t[2]||(t[2]=E("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),o(_,{class:"mt-4","label-width":"70px"},{default:e(()=>[o(f,{"model-value":"nav"},{default:e(()=>[o(a,{label:"主导航设置",name:"nav"},{default:e(()=>[o(l,{modelValue:r(m).nav,"onUpdate:modelValue":t[0]||(t[0]=p=>r(m).nav=p)},null,8,["modelValue"])]),_:1}),o(a,{label:"菜单设置",name:"menu"},{default:e(()=>[o(l,{modelValue:r(m).menu,"onUpdate:modelValue":t[1]||(t[1]=p=>r(m).menu=p)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{Bt as default};
|
||||
import{m as b,l as c,D as V}from"./element-plus-DFTWCWyi.js";import{_ as l}from"./menu-set.vue_vue_type_script_setup_true_lang-BN_YxIow.js";import{f as v,ak as x,I as k,J as E,a as o,aN as e,F as g,A as w}from"./@vue/runtime-core-C6bnekPw.js";import{y as r}from"./@vue/reactivity-DiY1c2vO.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";const Bt=v({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(n,{emit:s}){const u=n,d=s,m=w({get(){return u.modelValue},set(i){d("update:modelValue",i)}});return(i,t)=>{const a=c,f=b,_=V;return x(),k(g,null,[t[2]||(t[2]=E("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),o(_,{class:"mt-4","label-width":"70px"},{default:e(()=>[o(f,{"model-value":"nav"},{default:e(()=>[o(a,{label:"主导航设置",name:"nav"},{default:e(()=>[o(l,{modelValue:r(m).nav,"onUpdate:modelValue":t[0]||(t[0]=p=>r(m).nav=p)},null,8,["modelValue"])]),_:1}),o(a,{label:"菜单设置",name:"menu"},{default:e(()=>[o(l,{modelValue:r(m).menu,"onUpdate:modelValue":t[1]||(t[1]=p=>r(m).menu=p)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{Bt as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-d1x2myow.js";import"./element-plus-FSf94q2-.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-BDOO1F0I.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-CMHaTV3p.js";import"./attr-BND3PnN6.js";import"./index-Dn5DJbNI.js";import"./index-CLwyu9dm.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-BdCBwiHB.js";import"./index-CbVXHb-T.js";import"./index.vue_vue_type_script_setup_true_lang-CcHKbLKc.js";import"./article-W2gheZBz.js";import"./usePaging-VsbTxSU0.js";import"./picker-S140y0Ck.js";import"./index-Ddx1af9I.js";import"./index-iCSIrF-J.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-C8XkNg4E.js";import"./index.vue_vue_type_script_setup_true_lang-zIjDr0d2.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./content.vue_vue_type_script_setup_true_lang-BCRs1VT4.js";import"./decoration-img-NvGaunpS.js";import"./attr.vue_vue_type_script_setup_true_lang-D7CXSC4N.js";import"./content-DENnuo1v.js";import"./attr.vue_vue_type_script_setup_true_lang-fMHRVKyM.js";import"./content.vue_vue_type_script_setup_true_lang-Djr2FkIp.js";import"./attr.vue_vue_type_script_setup_true_lang-LpW82yJ0.js";import"./add-nav.vue_vue_type_script_setup_true_lang-CLFObqD9.js";import"./content-evG7ktQh.js";import"./attr.vue_vue_type_script_setup_true_lang-CA8p5N1S.js";import"./content.vue_vue_type_script_setup_true_lang-DhgqQmH1.js";import"./attr.vue_vue_type_script_setup_true_lang-DnkDutJb.js";import"./content-Dz5lY5Gn.js";import"./decoration-5yiTAhzK.js";import"./attr.vue_vue_type_script_setup_true_lang-ClHvuPzx.js";import"./index.vue_vue_type_script_setup_true_lang-CLqXsRdl.js";import"./content-4OBnpB9V.js";import"./content.vue_vue_type_script_setup_true_lang-CsRqoYA8.js";import"./attr.vue_vue_type_script_setup_true_lang-ChKJn1wI.js";import"./content-8T6Q5geE.js";import"./attr.vue_vue_type_script_setup_true_lang-CKrj1IU6.js";import"./content.vue_vue_type_script_setup_true_lang-Cu72NSRR.js";import"./attr.vue_vue_type_script_setup_true_lang-9MDVGbdQ.js";import"./content-BVZ4KnZl.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-B6zFEILf.js";import"./element-plus-DFTWCWyi.js";import"./@vue/runtime-dom-DDAG46FW.js";import"./lodash-D3kF6u-c.js";import"./@vue/runtime-core-C6bnekPw.js";import"./@vue/reactivity-DiY1c2vO.js";import"./@vue/shared-mAAVTE9n.js";import"./@element-plus/icons-vue-HOEUG8sr.js";import"./lodash-es-C2A-Pj28.js";import"./@popperjs/core-C5az9QE8.js";import"./dayjs-DG77mNTn.js";import"./@ctrl/tinycolor-BuyEbX8F.js";import"./async-validator-CFA_igpM.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DxCd-0QL.js";import"./attr-DOCtv_oK.js";import"./index-C60sYn0f.js";import"./index-BOT-n-x_.js";import"./jspdf-BsthCvR5.js";import"./@babel/runtime-BanGtE2-.js";import"./fflate-_ayOYiT1.js";import"./vue-router-QlpZ4wdW.js";import"./pinia-B8cvXbiZ.js";import"./axios-C80V62Fs.js";import"./@vueuse/core-1S4fJlii.js";import"./@vueuse/shared-Cl3lVqjz.js";import"./css-color-function-DI01JAaZ.js";import"./balanced-match-BdS7OldZ.js";import"./color-B2zxaWkI.js";import"./clone-DBZ6_OiU.js";import"./color-convert-OrFsTA_V.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-DByT_rEQ.js";import"./clipboard-CHgxszqY.js";import"./echarts-CNLwa9wG.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DRNXw7y3.js";import"./highlight.js-Bxt7hFFy.js";import"./@highlightjs/vue-plugin-C4onkBvW.js";import"./picker-CzLwL9Gk.js";import"./index-D0I-ELPR.js";import"./index.vue_vue_type_script_setup_true_lang-CcvIuIWy.js";import"./article-J0ItGv5D.js";import"./usePaging-VsbTxSU0.js";import"./picker-teM-xyL3.js";import"./index-o8OqlvxT.js";import"./index-BH7GXDwY.js";import"./cos-js-sdk-v5-DSMN0LUT.js";import"./file-JgIj1qOk.js";import"./index.vue_vue_type_script_setup_true_lang-x0oL2ShL.js";import"./vuedraggable-5bKFmC7X.js";import"./vue-BVs68q8v.js";import"./@vue/compiler-dom-CqayqxS7.js";import"./@vue/compiler-core-CvbQOXIO.js";import"./sortablejs-dsEAUXlE.js";import"./content.vue_vue_type_script_setup_true_lang-BYuAxpjh.js";import"./decoration-img-W4DlFB3K.js";import"./attr.vue_vue_type_script_setup_true_lang-DoC1u8Rb.js";import"./content-DtT0Bcta.js";import"./attr.vue_vue_type_script_setup_true_lang-DxkrGyhp.js";import"./content.vue_vue_type_script_setup_true_lang-BHtPY9sB.js";import"./attr.vue_vue_type_script_setup_true_lang-8qSi4wRz.js";import"./add-nav.vue_vue_type_script_setup_true_lang-KI5KMhoO.js";import"./content-mzjzc6Fn.js";import"./attr.vue_vue_type_script_setup_true_lang-BsexX2bP.js";import"./content.vue_vue_type_script_setup_true_lang-Dtvfz6J7.js";import"./attr.vue_vue_type_script_setup_true_lang-DnkDutJb.js";import"./content-jwBf3bWV.js";import"./decoration-BM96VJBA.js";import"./attr.vue_vue_type_script_setup_true_lang-B-x_uNbm.js";import"./index.vue_vue_type_script_setup_true_lang-3vCdLVB9.js";import"./content-BDSsBC_D.js";import"./content.vue_vue_type_script_setup_true_lang-CWeiGm42.js";import"./attr.vue_vue_type_script_setup_true_lang-ChKJn1wI.js";import"./content-CGEcYUdt.js";import"./attr.vue_vue_type_script_setup_true_lang-DudcaLfz.js";import"./content.vue_vue_type_script_setup_true_lang-Jl3c2iof.js";import"./attr.vue_vue_type_script_setup_true_lang-9MDVGbdQ.js";import"./content-YvI--ggT.js";export{o as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{J as y,s as g}from"./element-plus-FSf94q2-.js";import{e as b}from"./index-CMHaTV3p.js";import{f as x,ak as o,I as _,a as r,aN as c,J as h,G as i,K as w,at as k}from"./@vue/runtime-core-C6bnekPw.js";import{Q as v}from"./@vue/shared-mAAVTE9n.js";import{y as C}from"./@vue/reactivity-DiY1c2vO.js";const B={class:"pages-setting"},E={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},V=x({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:m}){const d=m,p=a=>{d("update:content",a)};return(a,N)=>{const f=y,u=g;return o(),_("div",B,[r(f,{shadow:"never",class:"!border-none flex"},{default:c(()=>{var t;return[h("div",E,v((t=e.widget)==null?void 0:t.title),1)]}),_:1}),r(u,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:c(()=>{var t,n,s,l;return[(o(),i(w,null,[(o(),i(k((n=C(b)[(t=e.widget)==null?void 0:t.name])==null?void 0:n.attr),{content:(s=e.widget)==null?void 0:s.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{V as _};
|
||||
import{J as y,s as g}from"./element-plus-DFTWCWyi.js";import{e as b}from"./index-DxCd-0QL.js";import{f as x,ak as o,I as _,a as r,aN as c,J as h,G as i,K as w,at as k}from"./@vue/runtime-core-C6bnekPw.js";import{Q as v}from"./@vue/shared-mAAVTE9n.js";import{y as C}from"./@vue/reactivity-DiY1c2vO.js";const B={class:"pages-setting"},E={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},V=x({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:m}){const d=m,p=a=>{d("update:content",a)};return(a,N)=>{const f=y,u=g;return o(),_("div",B,[r(f,{shadow:"never",class:"!border-none flex"},{default:c(()=>{var t;return[h("div",E,v((t=e.widget)==null?void 0:t.title),1)]}),_:1}),r(u,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:c(()=>{var t,n,s,l;return[(o(),i(w,null,[(o(),i(k((n=C(b)[(t=e.widget)==null?void 0:t.name])==null?void 0:n.attr),{content:(s=e.widget)==null?void 0:s.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{V as _};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{J as b,B as y,C as E,G as w,I as B,D as C}from"./element-plus-FSf94q2-.js";import{_ as I}from"./add-nav.vue_vue_type_script_setup_true_lang-CLFObqD9.js";import{f as N,ak as k,I as O,a as t,aN as o,J as a,O as u,A as U}from"./@vue/runtime-core-C6bnekPw.js";import{y as s}from"./@vue/reactivity-DiY1c2vO.js";const g={class:"flex-1"},J=N({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(p,{emit:i}){const f=i,_=p,l=U({get:()=>_.content,set:m=>{f("update:content",m)}});return(m,e)=>{const x=E,c=y,d=b,r=B,v=w,V=C;return k(),O("div",null,[t(V,{"label-width":"70px"},{default:o(()=>[t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[t(c,{label:"标题"},{default:o(()=>[t(x,{class:"w-[396px]",modelValue:s(l).title,"onUpdate:modelValue":e[0]||(e[0]=n=>s(l).title=n)},null,8,["modelValue"])]),_:1})]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[5]||(e[5]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),t(v,{modelValue:s(l).style,"onUpdate:modelValue":e[1]||(e[1]=n=>s(l).style=n)},{default:o(()=>[t(r,{value:1},{default:o(()=>[...e[3]||(e[3]=[u("横排",-1)])]),_:1}),t(r,{value:2},{default:o(()=>[...e[4]||(e[4]=[u("竖排",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",g,[t(I,{modelValue:s(l).data,"onUpdate:modelValue":e[2]||(e[2]=n=>s(l).data=n)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{J as _};
|
||||
import{J as b,B as y,C as E,G as w,I as B,D as C}from"./element-plus-DFTWCWyi.js";import{_ as I}from"./add-nav.vue_vue_type_script_setup_true_lang-KI5KMhoO.js";import{f as N,ak as k,I as O,a as t,aN as o,J as a,O as u,A as U}from"./@vue/runtime-core-C6bnekPw.js";import{y as s}from"./@vue/reactivity-DiY1c2vO.js";const g={class:"flex-1"},J=N({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(p,{emit:i}){const f=i,_=p,l=U({get:()=>_.content,set:m=>{f("update:content",m)}});return(m,e)=>{const x=E,c=y,d=b,r=B,v=w,V=C;return k(),O("div",null,[t(V,{"label-width":"70px"},{default:o(()=>[t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[t(c,{label:"标题"},{default:o(()=>[t(x,{class:"w-[396px]",modelValue:s(l).title,"onUpdate:modelValue":e[0]||(e[0]=n=>s(l).title=n)},null,8,["modelValue"])]),_:1})]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[5]||(e[5]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),t(v,{modelValue:s(l).style,"onUpdate:modelValue":e[1]||(e[1]=n=>s(l).style=n)},{default:o(()=>[t(r,{value:1},{default:o(()=>[...e[3]||(e[3]=[u("横排",-1)])]),_:1}),t(r,{value:2},{default:o(()=>[...e[4]||(e[4]=[u("竖排",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",g,[t(I,{modelValue:s(l).data,"onUpdate:modelValue":e[2]||(e[2]=n=>s(l).data=n)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{J as _};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{J as E,B as C,G as F,I as N,C as B,D as z}from"./element-plus-FSf94q2-.js";import{_ as G}from"./index.vue_vue_type_script_setup_true_lang-CLqXsRdl.js";import{_ as I}from"./picker-S140y0Ck.js";import{f as O,ak as r,G as s,aN as t,a as l,O as p,H as i,J as y,A as j}from"./@vue/runtime-core-C6bnekPw.js";import{y as a}from"./@vue/reactivity-DiY1c2vO.js";const T=O({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(m,{emit:g}){const b=g,x=m,o=j({get:()=>x.content,set:_=>{b("update:content",_)}});return(_,e)=>{const u=N,f=F,d=C,k=B,V=I,v=G,U=E,w=z;return r(),s(w,{ref:"form","label-width":"80px",size:"large"},{default:t(()=>[l(U,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l(d,{label:"页面标题"},{default:t(()=>[l(f,{modelValue:a(o).title_type,"onUpdate:modelValue":e[0]||(e[0]=n=>a(o).title_type=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[7]||(e[7]=[p("文字",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[8]||(e[8]=[p("图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),m.content.title_type==1?(r(),s(d,{key:0},{default:t(()=>[l(k,{modelValue:a(o).title,"onUpdate:modelValue":e[1]||(e[1]=n=>a(o).title=n),maxlength:"8","show-word-limit":"",class:"w-[300px]",placeholder:"请输入页面标题"},null,8,["modelValue"])]),_:1})):i("",!0),m.content.title_type==2?(r(),s(d,{key:1},{default:t(()=>[l(V,{modelValue:a(o).title_img,"onUpdate:modelValue":e[2]||(e[2]=n=>a(o).title_img=n),limit:1,size:"100px"},null,8,["modelValue"]),e[9]||(e[9]=y("div",{class:"form-tips"},"建议图片尺寸:300px*40px",-1))]),_:1})):i("",!0),m.content.title_type==1?(r(),s(d,{key:2,label:"文字颜色"},{default:t(()=>[l(f,{modelValue:a(o).text_color,"onUpdate:modelValue":e[3]||(e[3]=n=>a(o).text_color=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[10]||(e[10]=[p("白色",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[11]||(e[11]=[p("黑色",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})):i("",!0),l(d,{label:"页面背景"},{default:t(()=>[l(f,{modelValue:a(o).bg_type,"onUpdate:modelValue":e[4]||(e[4]=n=>a(o).bg_type=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[12]||(e[12]=[p("背景颜色",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[13]||(e[13]=[p("背景图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),m.content.bg_type==1?(r(),s(d,{key:3},{default:t(()=>[l(v,{modelValue:a(o).bg_color,"onUpdate:modelValue":e[5]||(e[5]=n=>a(o).bg_color=n),"reset-color":"#F5F5F5"},null,8,["modelValue"])]),_:1})):i("",!0),m.content.bg_type==2?(r(),s(d,{key:4},{default:t(()=>[l(V,{modelValue:a(o).bg_image,"onUpdate:modelValue":e[6]||(e[6]=n=>a(o).bg_image=n),limit:1,size:"100px"},null,8,["modelValue"]),e[14]||(e[14]=y("div",{class:"form-tips"},"建议图片尺寸:750px*高度不限",-1))]),_:1})):i("",!0)]),_:1})]),_:1},512)}}});export{T as _};
|
||||
import{J as E,B as C,G as F,I as N,C as B,D as z}from"./element-plus-DFTWCWyi.js";import{_ as G}from"./index.vue_vue_type_script_setup_true_lang-3vCdLVB9.js";import{_ as I}from"./picker-teM-xyL3.js";import{f as O,ak as r,G as s,aN as t,a as l,O as p,H as i,J as y,A as j}from"./@vue/runtime-core-C6bnekPw.js";import{y as a}from"./@vue/reactivity-DiY1c2vO.js";const T=O({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(m,{emit:g}){const b=g,x=m,o=j({get:()=>x.content,set:_=>{b("update:content",_)}});return(_,e)=>{const u=N,f=F,d=C,k=B,V=I,v=G,U=E,w=z;return r(),s(w,{ref:"form","label-width":"80px",size:"large"},{default:t(()=>[l(U,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l(d,{label:"页面标题"},{default:t(()=>[l(f,{modelValue:a(o).title_type,"onUpdate:modelValue":e[0]||(e[0]=n=>a(o).title_type=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[7]||(e[7]=[p("文字",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[8]||(e[8]=[p("图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),m.content.title_type==1?(r(),s(d,{key:0},{default:t(()=>[l(k,{modelValue:a(o).title,"onUpdate:modelValue":e[1]||(e[1]=n=>a(o).title=n),maxlength:"8","show-word-limit":"",class:"w-[300px]",placeholder:"请输入页面标题"},null,8,["modelValue"])]),_:1})):i("",!0),m.content.title_type==2?(r(),s(d,{key:1},{default:t(()=>[l(V,{modelValue:a(o).title_img,"onUpdate:modelValue":e[2]||(e[2]=n=>a(o).title_img=n),limit:1,size:"100px"},null,8,["modelValue"]),e[9]||(e[9]=y("div",{class:"form-tips"},"建议图片尺寸:300px*40px",-1))]),_:1})):i("",!0),m.content.title_type==1?(r(),s(d,{key:2,label:"文字颜色"},{default:t(()=>[l(f,{modelValue:a(o).text_color,"onUpdate:modelValue":e[3]||(e[3]=n=>a(o).text_color=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[10]||(e[10]=[p("白色",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[11]||(e[11]=[p("黑色",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1})):i("",!0),l(d,{label:"页面背景"},{default:t(()=>[l(f,{modelValue:a(o).bg_type,"onUpdate:modelValue":e[4]||(e[4]=n=>a(o).bg_type=n)},{default:t(()=>[l(u,{value:"1"},{default:t(()=>[...e[12]||(e[12]=[p("背景颜色",-1)])]),_:1}),l(u,{value:"2"},{default:t(()=>[...e[13]||(e[13]=[p("背景图片",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),m.content.bg_type==1?(r(),s(d,{key:3},{default:t(()=>[l(v,{modelValue:a(o).bg_color,"onUpdate:modelValue":e[5]||(e[5]=n=>a(o).bg_color=n),"reset-color":"#F5F5F5"},null,8,["modelValue"])]),_:1})):i("",!0),m.content.bg_type==2?(r(),s(d,{key:4},{default:t(()=>[l(V,{modelValue:a(o).bg_image,"onUpdate:modelValue":e[6]||(e[6]=n=>a(o).bg_image=n),limit:1,size:"100px"},null,8,["modelValue"]),e[14]||(e[14]=y("div",{class:"form-tips"},"建议图片尺寸:750px*高度不限",-1))]),_:1})):i("",!0)]),_:1})]),_:1},512)}}});export{T as _};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user