更新
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export interface PrescriptionAiVisibilityConfig {
|
||||
role_ids: number[]
|
||||
admin_ids: number[]
|
||||
}
|
||||
|
||||
export interface PrescriptionAiVisibilityOptions {
|
||||
roles: { id: number; name: string }[]
|
||||
admins: { id: number; name: string; account: string }[]
|
||||
}
|
||||
|
||||
export interface PrescriptionAiCapabilities {
|
||||
agreement_visible: boolean
|
||||
can_manage_visibility: boolean
|
||||
}
|
||||
|
||||
// A failed or malformed read must never become an empty policy that can be saved.
|
||||
function readVisibilityConfig(value: unknown): PrescriptionAiVisibilityConfig {
|
||||
const config = value as Partial<PrescriptionAiVisibilityConfig> | null
|
||||
const validIds = (ids: unknown): ids is number[] =>
|
||||
Array.isArray(ids) && ids.every((id) => Number.isSafeInteger(id) && id > 0)
|
||||
if (!config || !validIds(config.role_ids) || !validIds(config.admin_ids)) {
|
||||
throw new Error('可见范围配置响应格式不正确')
|
||||
}
|
||||
return { role_ids: [...config.role_ids], admin_ids: [...config.admin_ids] }
|
||||
}
|
||||
|
||||
export async function getPrescriptionAiVisibilityConfig() {
|
||||
const data = await request.get({ url: '/tcm.prescriptionAi/getVisibilityConfig' })
|
||||
return readVisibilityConfig(data)
|
||||
}
|
||||
|
||||
export async function setPrescriptionAiVisibilityConfig(params: PrescriptionAiVisibilityConfig) {
|
||||
const data = await request.post({ url: '/tcm.prescriptionAi/setVisibilityConfig', params })
|
||||
return readVisibilityConfig(data)
|
||||
}
|
||||
|
||||
export function getPrescriptionAiVisibilityOptions() {
|
||||
return request.get({
|
||||
url: '/tcm.prescriptionAi/visibilityOptions'
|
||||
}) as Promise<PrescriptionAiVisibilityOptions>
|
||||
}
|
||||
|
||||
export function getPrescriptionAiCapabilities() {
|
||||
return request.get({
|
||||
url: '/tcm.prescriptionAi/capabilities'
|
||||
}) as Promise<PrescriptionAiCapabilities>
|
||||
}
|
||||
@@ -490,8 +490,10 @@ export function prescriptionOrderPatchPrescriptionUsage(params: {
|
||||
times_per_day: number
|
||||
usage_days: number
|
||||
medication_days: number
|
||||
dosage_bag_count?: number
|
||||
aux_times_per_day?: number
|
||||
aux_usage_days?: number
|
||||
aux_dosage_bag_count?: number
|
||||
}) {
|
||||
return request.post({ url: '/tcm.prescriptionOrder/patchPrescriptionUsage', params })
|
||||
}
|
||||
|
||||
@@ -312,6 +312,17 @@
|
||||
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}袋
|
||||
<el-button
|
||||
v-if="!readonly && detailData.prescription_id && !String(detailData.prescription_detail_error || '').trim()"
|
||||
v-perms="['tcm.prescriptionOrder/patchPrescriptionUsage']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
class="ml-2"
|
||||
@click="openPatchUsageDialog"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
|
||||
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
|
||||
@@ -325,6 +336,17 @@
|
||||
{{ detailAuxUsage.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailAuxUsage.dosage_bag_count) > 0 ? Number(detailAuxUsage.dosage_bag_count) : 1 }}袋
|
||||
<el-button
|
||||
v-if="!readonly && detailData.prescription_id && !String(detailData.prescription_detail_error || '').trim()"
|
||||
v-perms="['tcm.prescriptionOrder/patchPrescriptionUsage']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
class="ml-2"
|
||||
@click="openPatchUsageDialog"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片'" class="ml-2 text-gray-500">
|
||||
({{ detailAuxUsage.need_decoction ? '代煎' : '不代煎' }})
|
||||
@@ -943,6 +965,15 @@
|
||||
label-width="108px"
|
||||
>
|
||||
<div v-if="detailHasAuxHerbs" class="text-xs font-medium text-gray-500 mb-3">主方</div>
|
||||
<el-form-item
|
||||
v-if="detailPrescription?.prescription_type === '浓缩水丸'"
|
||||
label="每次袋数"
|
||||
prop="dosage_bag_count"
|
||||
>
|
||||
<el-select v-model="patchUsageForm.dosage_bag_count" class="w-full">
|
||||
<el-option v-for="count in 5" :key="count" :label="`${count}袋`" :value="count" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="每天次数" prop="times_per_day">
|
||||
<el-input-number
|
||||
v-model="patchUsageForm.times_per_day"
|
||||
@@ -968,6 +999,15 @@
|
||||
</el-form-item>
|
||||
<template v-if="detailHasAuxHerbs">
|
||||
<div class="text-xs font-medium text-gray-500 mb-3 mt-2 pt-2 border-t border-gray-100">辅方</div>
|
||||
<el-form-item
|
||||
v-if="detailPrescription?.prescription_type === '浓缩水丸'"
|
||||
label="每次袋数"
|
||||
prop="aux_dosage_bag_count"
|
||||
>
|
||||
<el-select v-model="patchUsageForm.aux_dosage_bag_count" class="w-full">
|
||||
<el-option v-for="count in 5" :key="count" :label="`${count}袋`" :value="count" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="每天次数" prop="aux_times_per_day">
|
||||
<el-input-number
|
||||
v-model="patchUsageForm.aux_times_per_day"
|
||||
@@ -1432,10 +1472,12 @@ const patchUsageVisible = ref(false)
|
||||
const patchUsageSaving = ref(false)
|
||||
const patchUsageFormRef = ref<FormInstance>()
|
||||
const patchUsageForm = reactive({
|
||||
dosage_bag_count: 1 as number | undefined,
|
||||
times_per_day: 3 as number | undefined,
|
||||
usage_days: 7 as number | undefined,
|
||||
aux_times_per_day: 3 as number | undefined,
|
||||
aux_usage_days: 7 as number | undefined,
|
||||
aux_dosage_bag_count: 1 as number | undefined,
|
||||
medication_days: undefined as number | undefined
|
||||
})
|
||||
const patchUsageRules = computed<FormRules>(() => {
|
||||
@@ -1444,9 +1486,15 @@ const patchUsageRules = computed<FormRules>(() => {
|
||||
usage_days: [{ required: true, message: '请填写主方开立天数', trigger: 'change' }],
|
||||
medication_days: [{ required: true, message: '请填写订单服用天数', trigger: 'change' }]
|
||||
}
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
rules.dosage_bag_count = [{ required: true, message: '请选择主方每次袋数', trigger: 'change' }]
|
||||
}
|
||||
if (detailHasAuxHerbs.value) {
|
||||
rules.aux_times_per_day = [{ required: true, message: '请填写辅方每天次数', trigger: 'change' }]
|
||||
rules.aux_usage_days = [{ required: true, message: '请填写辅方开立天数', trigger: 'change' }]
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
rules.aux_dosage_bag_count = [{ required: true, message: '请选择辅方每次袋数', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
return rules
|
||||
})
|
||||
@@ -1459,6 +1507,7 @@ function openPatchUsageDialog() {
|
||||
return
|
||||
}
|
||||
const aux = detailAuxUsage.value
|
||||
patchUsageForm.dosage_bag_count = Number(rx.dosage_bag_count) > 0 ? Number(rx.dosage_bag_count) : 1
|
||||
patchUsageForm.times_per_day =
|
||||
Number(rx.times_per_day) > 0 ? Number(rx.times_per_day) : 3
|
||||
patchUsageForm.usage_days =
|
||||
@@ -1468,16 +1517,20 @@ function openPatchUsageDialog() {
|
||||
patchUsageForm.aux_usage_days =
|
||||
aux && Number(aux.usage_days) > 0 ? Number(aux.usage_days) : 7
|
||||
const md = Number(ord.medication_days)
|
||||
patchUsageForm.aux_dosage_bag_count =
|
||||
aux && Number(aux.dosage_bag_count) > 0 ? Number(aux.dosage_bag_count) : 1
|
||||
patchUsageForm.medication_days = md > 0 ? md : Number(rx.usage_days) > 0 ? Number(rx.usage_days) : 7
|
||||
patchUsageVisible.value = true
|
||||
nextTick(() => patchUsageFormRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
function resetPatchUsageForm() {
|
||||
patchUsageForm.dosage_bag_count = 1
|
||||
patchUsageForm.times_per_day = 3
|
||||
patchUsageForm.usage_days = 7
|
||||
patchUsageForm.aux_times_per_day = 3
|
||||
patchUsageForm.aux_usage_days = 7
|
||||
patchUsageForm.aux_dosage_bag_count = 1
|
||||
patchUsageForm.medication_days = undefined
|
||||
}
|
||||
|
||||
@@ -1498,17 +1551,25 @@ async function submitPatchUsage() {
|
||||
times_per_day: number
|
||||
usage_days: number
|
||||
medication_days: number
|
||||
dosage_bag_count?: number
|
||||
aux_times_per_day?: number
|
||||
aux_usage_days?: number
|
||||
aux_dosage_bag_count?: number
|
||||
} = {
|
||||
id: ordId,
|
||||
times_per_day: Number(patchUsageForm.times_per_day),
|
||||
usage_days: Number(patchUsageForm.usage_days),
|
||||
medication_days: Number(patchUsageForm.medication_days)
|
||||
}
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
payload.dosage_bag_count = Number(patchUsageForm.dosage_bag_count)
|
||||
}
|
||||
if (detailHasAuxHerbs.value) {
|
||||
payload.aux_times_per_day = Number(patchUsageForm.aux_times_per_day)
|
||||
payload.aux_usage_days = Number(patchUsageForm.aux_usage_days)
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
payload.aux_dosage_bag_count = Number(patchUsageForm.aux_dosage_bag_count)
|
||||
}
|
||||
}
|
||||
await prescriptionOrderPatchPrescriptionUsage(payload)
|
||||
feedback.msgSuccess('保存成功')
|
||||
@@ -1763,7 +1824,7 @@ async function refresh() {
|
||||
detailLogisticsExpress.value = String(d.express_company || 'auto') || 'auto'
|
||||
void fetchLogisticsTrace()
|
||||
}
|
||||
if (props.loadRelatedData) void fetchLogs(id)
|
||||
if (props.loadRelatedData) await fetchLogs(id)
|
||||
const diagId = d.diagnosis_id
|
||||
if (props.loadRelatedData && diagId) {
|
||||
void loadDetailUnlinkedPayOrders(diagId, id, d.pay_order_ids || [])
|
||||
|
||||
@@ -883,6 +883,17 @@
|
||||
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}袋
|
||||
<el-button
|
||||
v-if="detailData.prescription_id && !String(detailData.prescription_detail_error || '').trim()"
|
||||
v-perms="['tcm.prescriptionOrder/patchPrescriptionUsage']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
class="ml-2"
|
||||
@click="openPatchUsageDialog"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
|
||||
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
|
||||
@@ -896,6 +907,17 @@
|
||||
{{ detailAuxUsage.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailAuxUsage.dosage_bag_count) > 0 ? Number(detailAuxUsage.dosage_bag_count) : 1 }}袋
|
||||
<el-button
|
||||
v-if="detailData.prescription_id && !String(detailData.prescription_detail_error || '').trim()"
|
||||
v-perms="['tcm.prescriptionOrder/patchPrescriptionUsage']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
class="ml-2"
|
||||
@click="openPatchUsageDialog"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片'" class="ml-2 text-gray-500">
|
||||
({{ detailAuxUsage.need_decoction ? '代煎' : '不代煎' }})
|
||||
@@ -2261,6 +2283,15 @@
|
||||
label-width="96px"
|
||||
>
|
||||
<div v-if="detailHasAuxHerbs" class="text-xs font-medium text-gray-500 mb-3">主方</div>
|
||||
<el-form-item
|
||||
v-if="detailPrescription?.prescription_type === '浓缩水丸'"
|
||||
label="每次袋数"
|
||||
prop="dosage_bag_count"
|
||||
>
|
||||
<el-select v-model="patchUsageForm.dosage_bag_count" class="w-full">
|
||||
<el-option v-for="count in 5" :key="count" :label="`${count}袋`" :value="count" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="每天次数" prop="times_per_day">
|
||||
<el-input-number
|
||||
v-model="patchUsageForm.times_per_day"
|
||||
@@ -2286,6 +2317,15 @@
|
||||
</el-form-item>
|
||||
<template v-if="detailHasAuxHerbs">
|
||||
<div class="text-xs font-medium text-gray-500 mb-3 mt-2 pt-2 border-t border-gray-100">辅方</div>
|
||||
<el-form-item
|
||||
v-if="detailPrescription?.prescription_type === '浓缩水丸'"
|
||||
label="每次袋数"
|
||||
prop="aux_dosage_bag_count"
|
||||
>
|
||||
<el-select v-model="patchUsageForm.aux_dosage_bag_count" class="w-full">
|
||||
<el-option v-for="count in 5" :key="count" :label="`${count}袋`" :value="count" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="每天次数" prop="aux_times_per_day">
|
||||
<el-input-number
|
||||
v-model="patchUsageForm.aux_times_per_day"
|
||||
@@ -3954,10 +3994,12 @@ const patchUsageVisible = ref(false)
|
||||
const patchUsageSaving = ref(false)
|
||||
const patchUsageFormRef = ref<FormInstance>()
|
||||
const patchUsageForm = reactive({
|
||||
dosage_bag_count: 1 as number | undefined,
|
||||
times_per_day: 3 as number | undefined,
|
||||
usage_days: 7 as number | undefined,
|
||||
aux_times_per_day: 3 as number | undefined,
|
||||
aux_usage_days: 7 as number | undefined,
|
||||
aux_dosage_bag_count: 1 as number | undefined,
|
||||
medication_days: undefined as number | undefined
|
||||
})
|
||||
const patchUsageRules = computed<FormRules>(() => {
|
||||
@@ -3966,9 +4008,15 @@ const patchUsageRules = computed<FormRules>(() => {
|
||||
usage_days: [{ required: true, message: '请填写主方开立天数', trigger: 'change' }],
|
||||
medication_days: [{ required: true, message: '请填写订单服用天数', trigger: 'change' }]
|
||||
}
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
rules.dosage_bag_count = [{ required: true, message: '请选择主方每次袋数', trigger: 'change' }]
|
||||
}
|
||||
if (detailHasAuxHerbs.value) {
|
||||
rules.aux_times_per_day = [{ required: true, message: '请填写辅方每天次数', trigger: 'change' }]
|
||||
rules.aux_usage_days = [{ required: true, message: '请填写辅方开立天数', trigger: 'change' }]
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
rules.aux_dosage_bag_count = [{ required: true, message: '请选择辅方每次袋数', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
return rules
|
||||
})
|
||||
@@ -3981,6 +4029,7 @@ function openPatchUsageDialog() {
|
||||
return
|
||||
}
|
||||
const aux = detailAuxUsage.value
|
||||
patchUsageForm.dosage_bag_count = Number(rx.dosage_bag_count) > 0 ? Number(rx.dosage_bag_count) : 1
|
||||
patchUsageForm.times_per_day =
|
||||
Number(rx.times_per_day) > 0 ? Number(rx.times_per_day) : 3
|
||||
patchUsageForm.usage_days =
|
||||
@@ -3990,16 +4039,20 @@ function openPatchUsageDialog() {
|
||||
patchUsageForm.aux_usage_days =
|
||||
aux && Number(aux.usage_days) > 0 ? Number(aux.usage_days) : 7
|
||||
const md = Number(ord.medication_days)
|
||||
patchUsageForm.aux_dosage_bag_count =
|
||||
aux && Number(aux.dosage_bag_count) > 0 ? Number(aux.dosage_bag_count) : 1
|
||||
patchUsageForm.medication_days = md > 0 ? md : Number(rx.usage_days) > 0 ? Number(rx.usage_days) : 7
|
||||
patchUsageVisible.value = true
|
||||
nextTick(() => patchUsageFormRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
function resetPatchUsageForm() {
|
||||
patchUsageForm.dosage_bag_count = 1
|
||||
patchUsageForm.times_per_day = 3
|
||||
patchUsageForm.usage_days = 7
|
||||
patchUsageForm.aux_times_per_day = 3
|
||||
patchUsageForm.aux_usage_days = 7
|
||||
patchUsageForm.aux_dosage_bag_count = 1
|
||||
patchUsageForm.medication_days = undefined
|
||||
}
|
||||
|
||||
@@ -4020,17 +4073,25 @@ async function submitPatchUsage() {
|
||||
times_per_day: number
|
||||
usage_days: number
|
||||
medication_days: number
|
||||
dosage_bag_count?: number
|
||||
aux_times_per_day?: number
|
||||
aux_usage_days?: number
|
||||
aux_dosage_bag_count?: number
|
||||
} = {
|
||||
id: ordId,
|
||||
times_per_day: Number(patchUsageForm.times_per_day),
|
||||
usage_days: Number(patchUsageForm.usage_days),
|
||||
medication_days: Number(patchUsageForm.medication_days)
|
||||
}
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
payload.dosage_bag_count = Number(patchUsageForm.dosage_bag_count)
|
||||
}
|
||||
if (detailHasAuxHerbs.value) {
|
||||
payload.aux_times_per_day = Number(patchUsageForm.aux_times_per_day)
|
||||
payload.aux_usage_days = Number(patchUsageForm.aux_usage_days)
|
||||
if (detailPrescription.value?.prescription_type === '浓缩水丸') {
|
||||
payload.aux_dosage_bag_count = Number(patchUsageForm.aux_dosage_bag_count)
|
||||
}
|
||||
}
|
||||
await prescriptionOrderPatchPrescriptionUsage(payload)
|
||||
feedback.msgSuccess('保存成功')
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<div class="prescription-ai-visibility">
|
||||
<el-card v-loading="loading" shadow="never" class="!border-none">
|
||||
<div class="text-xl font-medium mb-[20px]">处方 AI 一致度可见范围</div>
|
||||
<el-alert type="info" :closable="false" show-icon class="mb-5">
|
||||
<template #title>指定用户组或用户可查看一致度</template>
|
||||
<div class="leading-6">
|
||||
用户命中任一指定系统角色,或被单独指定,即可查看桌面端“与 AI
|
||||
一致度”、逐味对照和一致度统计。AI
|
||||
报告继续按原有权限开放,已有报告不受影响,患者与处方的数据范围保持原有规则。
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<el-alert
|
||||
v-if="loadError"
|
||||
:title="loadError"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-5"
|
||||
/>
|
||||
<div v-if="loading" role="status" class="mb-5">正在读取当前可见范围…</div>
|
||||
|
||||
<el-form
|
||||
v-if="hasLoaded"
|
||||
class="ls-form"
|
||||
:model="formData"
|
||||
:disabled="!ready || loading || saving"
|
||||
label-width="140px"
|
||||
>
|
||||
<el-form-item label="指定用户组" prop="role_ids">
|
||||
<div class="w-full max-w-[640px]">
|
||||
<el-select
|
||||
v-model="formData.role_ids"
|
||||
class="w-full"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择系统角色,可搜索角色名称"
|
||||
>
|
||||
<el-option
|
||||
v-for="role in roleOptions"
|
||||
:key="role.id"
|
||||
:label="role.name"
|
||||
:value="role.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="form-tips">账号拥有任一指定角色即可命中。</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="指定用户" prop="admin_ids">
|
||||
<div class="w-full max-w-[640px]">
|
||||
<el-select
|
||||
v-model="formData.admin_ids"
|
||||
class="w-full"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择用户,可搜索姓名或账号"
|
||||
>
|
||||
<el-option
|
||||
v-for="admin in adminOptions"
|
||||
:key="admin.id"
|
||||
:label="admin.label"
|
||||
:value="admin.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="form-tips">
|
||||
单独指定的用户无需同时命中用户组。当前不在可选列表中的已配置 ID
|
||||
会保留,移除后保存才会取消该项授权。
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效规则">
|
||||
<div>
|
||||
<div class="leading-6">指定用户组与指定用户按“或”生效。</div>
|
||||
<div class="form-tips">
|
||||
两项均为空时,所有账号(包括超级管理员)均隐藏一致度、对照与统计。
|
||||
拥有本页管理权限不自动获得一致度查看权,超级管理员也需命中上述名单。
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-alert
|
||||
v-if="ready && !formData.role_ids.length && !formData.admin_ids.length"
|
||||
title="当前选择为空:保存后所有账号将隐藏一致度、对照与统计,AI 报告仍保留。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-5"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="saveError"
|
||||
:title="saveError"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-5"
|
||||
/>
|
||||
<el-button :disabled="loading || saving" @click="loadConfig">
|
||||
{{ loadError ? '重试加载' : '重新加载' }}
|
||||
</el-button>
|
||||
</el-card>
|
||||
|
||||
<footer-btns v-if="canManage">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
:disabled="!ready || loading || saving"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ saving ? '正在保存' : '保存' }}
|
||||
</el-button>
|
||||
</footer-btns>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="prescriptionAiVisibility">
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
|
||||
import type {
|
||||
PrescriptionAiVisibilityConfig,
|
||||
PrescriptionAiVisibilityOptions
|
||||
} from '@/api/setting/prescription_ai_visibility'
|
||||
import {
|
||||
getPrescriptionAiCapabilities,
|
||||
getPrescriptionAiVisibilityConfig,
|
||||
getPrescriptionAiVisibilityOptions,
|
||||
setPrescriptionAiVisibilityConfig
|
||||
} from '@/api/setting/prescription_ai_visibility'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const ready = ref(false)
|
||||
const hasLoaded = ref(false)
|
||||
const canManage = ref(false)
|
||||
const loadError = ref('')
|
||||
const saveError = ref('')
|
||||
const formData = reactive<PrescriptionAiVisibilityConfig>({ role_ids: [], admin_ids: [] })
|
||||
const optionsData = reactive<PrescriptionAiVisibilityOptions>({ roles: [], admins: [] })
|
||||
|
||||
const roleOptions = computed(() => {
|
||||
const options = new Map(optionsData.roles.map((role) => [role.id, role]))
|
||||
for (const id of formData.role_ids) {
|
||||
if (!options.has(id)) {
|
||||
options.set(id, { id, name: `已配置角色 #${id}(当前不在可选列表)` })
|
||||
}
|
||||
}
|
||||
return [...options.values()]
|
||||
})
|
||||
|
||||
const adminOptions = computed(() => {
|
||||
const options = new Map(
|
||||
optionsData.admins.map((admin) => [
|
||||
admin.id,
|
||||
{ id: admin.id, label: `${admin.name || `用户 #${admin.id}`}(${admin.account})` }
|
||||
])
|
||||
)
|
||||
for (const id of formData.admin_ids) {
|
||||
if (!options.has(id)) {
|
||||
options.set(id, { id, label: `已配置用户 #${id}(当前不在可选列表)` })
|
||||
}
|
||||
}
|
||||
return [...options.values()]
|
||||
})
|
||||
|
||||
const assignConfig = (config: PrescriptionAiVisibilityConfig) => {
|
||||
formData.role_ids = [...config.role_ids]
|
||||
formData.admin_ids = [...config.admin_ids]
|
||||
}
|
||||
|
||||
const loadConfig = async () => {
|
||||
if (loading.value || saving.value) return
|
||||
loading.value = true
|
||||
ready.value = false
|
||||
canManage.value = false
|
||||
loadError.value = ''
|
||||
saveError.value = ''
|
||||
try {
|
||||
const capabilities = await getPrescriptionAiCapabilities()
|
||||
if (capabilities.can_manage_visibility !== true) {
|
||||
loadError.value = '当前账号无权管理处方 AI 一致度可见范围。'
|
||||
return
|
||||
}
|
||||
canManage.value = true
|
||||
const [config, options] = await Promise.all([
|
||||
getPrescriptionAiVisibilityConfig(),
|
||||
getPrescriptionAiVisibilityOptions()
|
||||
])
|
||||
if (!Array.isArray(options?.roles) || !Array.isArray(options?.admins)) {
|
||||
throw new Error('可选用户组或用户响应格式不正确')
|
||||
}
|
||||
// Apply both responses together; an incomplete load cannot overwrite the current policy.
|
||||
assignConfig(config)
|
||||
optionsData.roles = options.roles
|
||||
optionsData.admins = options.admins
|
||||
hasLoaded.value = true
|
||||
ready.value = true
|
||||
} catch {
|
||||
loadError.value = '可见范围加载失败,尚未读取完整配置。请重试加载后再保存。'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!ready.value || !canManage.value || loading.value || saving.value) return
|
||||
saving.value = true
|
||||
saveError.value = ''
|
||||
try {
|
||||
const config = await setPrescriptionAiVisibilityConfig({
|
||||
role_ids: [...formData.role_ids],
|
||||
admin_ids: [...formData.admin_ids]
|
||||
})
|
||||
assignConfig(config)
|
||||
feedback.msgSuccess('可见范围已保存')
|
||||
} catch {
|
||||
saveError.value = '保存结果未确认,当前选择已保留。请重试保存,或重新加载以确认服务端策略。'
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
void loadConfig()
|
||||
</script>
|
||||
@@ -0,0 +1,253 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const test = require('node:test')
|
||||
const ts = require('typescript')
|
||||
const vue = require('vue')
|
||||
const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const apiPath = path.join(root, 'src/api/setting/prescription_ai_visibility.ts')
|
||||
const pagePath = path.join(root, 'src/views/setting/prescription_ai_visibility/index.vue')
|
||||
const { descriptor, errors } = parse(fs.readFileSync(pagePath, 'utf8'), { filename: pagePath })
|
||||
assert.deepEqual(errors, [])
|
||||
const script = compileScript(descriptor, { id: 'prescription-ai-visibility' })
|
||||
|
||||
function moduleFrom(source, dependencies) {
|
||||
const compiled = ts.transpileModule(source, {
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
esModuleInterop: true
|
||||
}
|
||||
}).outputText
|
||||
const module = { exports: {} }
|
||||
new Function('require', 'module', 'exports', compiled)(dependencies, module, module.exports)
|
||||
return module.exports
|
||||
}
|
||||
|
||||
const makeApi = (request) =>
|
||||
moduleFrom(fs.readFileSync(apiPath, 'utf8'), (name) => {
|
||||
assert.equal(name, '@/utils/request')
|
||||
return request
|
||||
})
|
||||
|
||||
const tick = () => new Promise((resolve) => setImmediate(resolve))
|
||||
const deferred = () => {
|
||||
let resolve
|
||||
let reject
|
||||
const promise = new Promise((yes, no) => {
|
||||
resolve = yes
|
||||
reject = no
|
||||
})
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
|
||||
function makePage(overrides = {}) {
|
||||
const saved = []
|
||||
const messages = []
|
||||
const calls = []
|
||||
const api = {
|
||||
getPrescriptionAiCapabilities: async () => {
|
||||
calls.push('capabilities')
|
||||
return { agreement_visible: false, can_manage_visibility: true }
|
||||
},
|
||||
getPrescriptionAiVisibilityConfig: async () => {
|
||||
calls.push('config')
|
||||
return { role_ids: [2, 90], admin_ids: [3, 91] }
|
||||
},
|
||||
getPrescriptionAiVisibilityOptions: async () => {
|
||||
calls.push('options')
|
||||
return {
|
||||
roles: [{ id: 2, name: '医生' }],
|
||||
admins: [{ id: 3, name: '张医生', account: 'zhang.doctor' }]
|
||||
}
|
||||
},
|
||||
setPrescriptionAiVisibilityConfig: async (params) => {
|
||||
saved.push(params)
|
||||
return params
|
||||
},
|
||||
...overrides
|
||||
}
|
||||
const component = moduleFrom(script.content, (name) => {
|
||||
if (name === 'vue') return vue
|
||||
if (name === '@/api/setting/prescription_ai_visibility') return api
|
||||
if (name === '@/utils/feedback') {
|
||||
return { msgSuccess: (message) => messages.push(message) }
|
||||
}
|
||||
throw new Error(`Unexpected import: ${name}`)
|
||||
}).default
|
||||
const page = component.setup({}, { expose() {} })
|
||||
return { page, api, saved, messages, calls }
|
||||
}
|
||||
|
||||
test('API uses the dedicated routes and rejects malformed policy responses', async () => {
|
||||
const calls = []
|
||||
let response = { role_ids: [2], admin_ids: [3] }
|
||||
const api = makeApi({
|
||||
get: async (params) => {
|
||||
calls.push(['get', params])
|
||||
return response
|
||||
},
|
||||
post: async (params) => {
|
||||
calls.push(['post', params])
|
||||
return response
|
||||
}
|
||||
})
|
||||
const data = await api.getPrescriptionAiVisibilityConfig()
|
||||
data.role_ids.push(5)
|
||||
assert.deepEqual(response.role_ids, [2])
|
||||
await api.setPrescriptionAiVisibilityConfig({ role_ids: [], admin_ids: [] })
|
||||
await api.getPrescriptionAiVisibilityOptions()
|
||||
await api.getPrescriptionAiCapabilities()
|
||||
assert.deepEqual(calls, [
|
||||
['get', { url: '/tcm.prescriptionAi/getVisibilityConfig' }],
|
||||
[
|
||||
'post',
|
||||
{
|
||||
url: '/tcm.prescriptionAi/setVisibilityConfig',
|
||||
params: { role_ids: [], admin_ids: [] }
|
||||
}
|
||||
],
|
||||
['get', { url: '/tcm.prescriptionAi/visibilityOptions' }],
|
||||
['get', { url: '/tcm.prescriptionAi/capabilities' }]
|
||||
])
|
||||
for (response of [
|
||||
null,
|
||||
{},
|
||||
{ role_ids: [], admin_ids: ['3'] },
|
||||
{ role_ids: [-1], admin_ids: [] }
|
||||
]) {
|
||||
await assert.rejects(api.getPrescriptionAiVisibilityConfig(), /响应格式不正确/)
|
||||
await assert.rejects(
|
||||
api.setPrescriptionAiVisibilityConfig({ role_ids: [], admin_ids: [] }),
|
||||
/响应格式不正确/
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test('initial loading is pending and cannot submit before the real policy is read', async () => {
|
||||
const pending = deferred()
|
||||
const { page, saved } = makePage({ getPrescriptionAiVisibilityConfig: () => pending.promise })
|
||||
assert.equal(page.loading.value, true)
|
||||
assert.equal(page.ready.value, false)
|
||||
assert.equal(page.hasLoaded.value, false)
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [])
|
||||
pending.reject(new Error('offline'))
|
||||
await tick()
|
||||
assert.equal(page.loading.value, false)
|
||||
assert.equal(page.ready.value, false)
|
||||
assert.match(page.loadError.value, /加载失败/)
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [])
|
||||
})
|
||||
|
||||
test('a failed reload cannot apply a partial policy or overwrite saved selections', async () => {
|
||||
const { page, api, saved } = makePage()
|
||||
await tick()
|
||||
api.getPrescriptionAiVisibilityConfig = async () => ({ role_ids: [], admin_ids: [] })
|
||||
api.getPrescriptionAiVisibilityOptions = async () => {
|
||||
throw new Error('options failed')
|
||||
}
|
||||
await page.loadConfig()
|
||||
assert.deepEqual(page.formData.role_ids, [2, 90])
|
||||
assert.deepEqual(page.formData.admin_ids, [3, 91])
|
||||
assert.equal(page.hasLoaded.value, true)
|
||||
assert.equal(page.ready.value, false)
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [])
|
||||
api.getPrescriptionAiVisibilityOptions = async () => ({ roles: [], admins: [] })
|
||||
await page.loadConfig()
|
||||
assert.equal(page.ready.value, true)
|
||||
assert.equal(page.loadError.value, '')
|
||||
assert.deepEqual(page.formData.role_ids, [])
|
||||
})
|
||||
|
||||
test('missing selected IDs stay visible and user labels support both name and account search', async () => {
|
||||
const { page, saved } = makePage()
|
||||
await tick()
|
||||
assert.equal(page.canManage.value, true)
|
||||
assert.equal(page.ready.value, true)
|
||||
assert.deepEqual(
|
||||
page.roleOptions.value.map((row) => row.id),
|
||||
[2, 90]
|
||||
)
|
||||
assert.match(page.roleOptions.value[1].name, /#90.*当前不在可选列表/)
|
||||
assert.deepEqual(
|
||||
page.adminOptions.value.map((row) => row.id),
|
||||
[3, 91]
|
||||
)
|
||||
assert.match(page.adminOptions.value[0].label, /张医生.*zhang\.doctor/)
|
||||
assert.match(page.adminOptions.value[1].label, /#91.*当前不在可选列表/)
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [{ role_ids: [2, 90], admin_ids: [3, 91] }])
|
||||
})
|
||||
|
||||
test('explicitly clearing both selectors saves the empty policy and applies normalized response', async () => {
|
||||
const { page, saved, messages, api } = makePage()
|
||||
await tick()
|
||||
page.formData.role_ids = []
|
||||
page.formData.admin_ids = []
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [{ role_ids: [], admin_ids: [] }])
|
||||
assert.equal(messages.length, 1)
|
||||
page.formData.role_ids = [90, 2, 90]
|
||||
api.setPrescriptionAiVisibilityConfig = async () => ({ role_ids: [2, 90], admin_ids: [] })
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(page.formData.role_ids, [2, 90])
|
||||
assert.equal(page.saving.value, false)
|
||||
})
|
||||
|
||||
test('pending save blocks duplicate writes and reload; failed write keeps the user selection', async () => {
|
||||
const pending = deferred()
|
||||
const saved = []
|
||||
const { page, calls } = makePage({
|
||||
setPrescriptionAiVisibilityConfig: (params) => {
|
||||
saved.push(params)
|
||||
return pending.promise
|
||||
}
|
||||
})
|
||||
await tick()
|
||||
page.formData.role_ids = [90]
|
||||
const first = page.handleSubmit()
|
||||
assert.equal(page.saving.value, true)
|
||||
await page.handleSubmit()
|
||||
await page.loadConfig()
|
||||
assert.equal(saved.length, 1)
|
||||
assert.equal(calls.filter((name) => name === 'capabilities').length, 1)
|
||||
pending.reject(new Error('offline'))
|
||||
await first
|
||||
assert.equal(page.saving.value, false)
|
||||
assert.equal(page.ready.value, true)
|
||||
assert.deepEqual(page.formData.role_ids, [90])
|
||||
assert.deepEqual(page.formData.admin_ids, [3, 91])
|
||||
assert.match(page.saveError.value, /当前选择已保留/)
|
||||
})
|
||||
|
||||
test('management capability is independent from viewing and denies policy reads when absent', async () => {
|
||||
const { page, calls, saved } = makePage({
|
||||
getPrescriptionAiCapabilities: async () => ({
|
||||
agreement_visible: true,
|
||||
can_manage_visibility: false
|
||||
})
|
||||
})
|
||||
await tick()
|
||||
assert.deepEqual(calls, [])
|
||||
assert.equal(page.canManage.value, false)
|
||||
assert.equal(page.hasLoaded.value, false)
|
||||
assert.equal(page.ready.value, false)
|
||||
assert.match(page.loadError.value, /无权管理/)
|
||||
await page.handleSubmit()
|
||||
assert.deepEqual(saved, [])
|
||||
})
|
||||
|
||||
test('actual settings template compiles with its script bindings', () => {
|
||||
const compiled = compileTemplate({
|
||||
source: descriptor.template.content,
|
||||
filename: pagePath,
|
||||
id: 'prescription-ai-visibility',
|
||||
compilerOptions: { bindingMetadata: script.bindings }
|
||||
})
|
||||
assert.deepEqual(compiled.errors, [])
|
||||
})
|
||||
@@ -0,0 +1,167 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const test = require('node:test')
|
||||
const ts = require('typescript')
|
||||
const vue = require('vue')
|
||||
const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc')
|
||||
|
||||
const selected = [
|
||||
'patchUsageVisible', 'patchUsageSaving', 'patchUsageFormRef', 'patchUsageForm',
|
||||
'patchUsageRules', 'openPatchUsageDialog', 'resetPatchUsageForm', 'submitPatchUsage'
|
||||
]
|
||||
|
||||
for (const page of ['components/PrescriptionOrderDetailDrawer.vue', 'order_list_h5.vue']) {
|
||||
const filename = path.join(__dirname, '../src/views/consumer/prescription', page)
|
||||
const { descriptor, errors } = parse(fs.readFileSync(filename, 'utf8'), { filename })
|
||||
assert.deepEqual(errors, [])
|
||||
const script = compileScript(descriptor, { id: page })
|
||||
const template = compileTemplate({
|
||||
source: descriptor.template.content, filename, id: page,
|
||||
compilerOptions: { bindingMetadata: script.bindings }
|
||||
})
|
||||
assert.deepEqual(template.errors, [])
|
||||
|
||||
// Run the real handlers; replace the network and the rest of the order page.
|
||||
const ast = ts.createSourceFile(filename + '.ts', descriptor.scriptSetup.content, ts.ScriptTarget.Latest, true)
|
||||
const declarations = new Map()
|
||||
for (const node of ast.statements) {
|
||||
if (ts.isFunctionDeclaration(node) && node.name) declarations.set(node.name.text, node.getText(ast))
|
||||
if (ts.isVariableStatement(node)) {
|
||||
for (const declaration of node.declarationList.declarations) {
|
||||
declarations.set(declaration.name.getText(ast), `const ${declaration.getText(ast)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
const executed = page.startsWith('components/') ? [...selected, 'refresh'] : selected
|
||||
for (const name of executed) assert.ok(declarations.has(name), `${page}: missing ${name}`)
|
||||
const compiled = ts.transpileModule(executed.map(name => declarations.get(name)).join('\n'), {
|
||||
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
|
||||
}).outputText
|
||||
|
||||
function instance({ hasAux = true, prescriptionType = '浓缩水丸', bagCount = 2, mainBagCount = 2, status = 2, fail = false } = {}) {
|
||||
const calls = []
|
||||
const events = []
|
||||
const aux = { times_per_day: 3, usage_days: 10, dosage_bag_count: bagCount }
|
||||
const detailPrescription = vue.ref({ prescription_type: prescriptionType, times_per_day: 3, usage_days: 10, dosage_bag_count: mainBagCount })
|
||||
const detailAuxUsage = vue.ref(hasAux ? aux : null)
|
||||
const detailData = vue.ref({
|
||||
id: 42, prescription_id: 17, medication_days: 10, fulfillment_status: status,
|
||||
gancao_reciperl_order_no: 'TEST-SUBMITTED', ej_pharmacy_order_no: 'TEST-EJ-SUBMITTED'
|
||||
})
|
||||
const refresh = async () => {
|
||||
if (calls[0]?.dosage_bag_count) detailPrescription.value.dosage_bag_count = calls[0].dosage_bag_count
|
||||
if (calls[0]?.aux_dosage_bag_count) detailAuxUsage.value.dosage_bag_count = calls[0].aux_dosage_bag_count
|
||||
events.push('refresh')
|
||||
return { ...detailData.value }
|
||||
}
|
||||
const globals = {
|
||||
ref: vue.ref, reactive: vue.reactive, computed: vue.computed, nextTick: vue.nextTick,
|
||||
detailPrescription, detailAuxUsage, detailData, detailHasAuxHerbs: vue.ref(hasAux),
|
||||
feedback: { msgWarning() {}, msgSuccess() {} },
|
||||
prescriptionOrderPatchPrescriptionUsage: async payload => {
|
||||
calls.push(payload)
|
||||
if (fail) throw new Error('Save failed')
|
||||
},
|
||||
requestDetail: refresh, refreshCurrentPrescriptionOrderDetail: refresh,
|
||||
props: { loadRelatedData: true },
|
||||
emit: event => events.push(event),
|
||||
fetchLogs: async () => {
|
||||
events.push('logs:start')
|
||||
await Promise.resolve()
|
||||
events.push('logs:done')
|
||||
},
|
||||
getLists: () => events.push('lists')
|
||||
}
|
||||
const state = new Function(...Object.keys(globals), `${compiled}\nreturn { ${selected.join(', ')} }`)(...Object.values(globals))
|
||||
state.patchUsageFormRef.value = { validate: async () => {}, clearValidate() {} }
|
||||
return { state, calls, events, detailAuxUsage, detailPrescription }
|
||||
}
|
||||
|
||||
test(`${page}: edits auxiliary bags and refreshes the persisted value without changing usage`, async () => {
|
||||
const { state, calls, events, detailAuxUsage } = instance()
|
||||
state.openPatchUsageDialog()
|
||||
assert.equal(state.patchUsageForm.aux_dosage_bag_count, 2)
|
||||
state.patchUsageForm.aux_dosage_bag_count = 4
|
||||
await state.submitPatchUsage()
|
||||
assert.deepEqual(calls, [{
|
||||
id: 42, times_per_day: 3, usage_days: 10, medication_days: 10,
|
||||
dosage_bag_count: 2,
|
||||
aux_times_per_day: 3, aux_usage_days: 10, aux_dosage_bag_count: 4
|
||||
}])
|
||||
assert.equal(state.patchUsageVisible.value, false)
|
||||
assert.equal(detailAuxUsage.value.dosage_bag_count, 4)
|
||||
assert.ok(events.includes('refresh'))
|
||||
assert.ok(events.indexOf('logs:done') > events.indexOf('refresh'))
|
||||
const completedEvent = page.startsWith('components/') ? 'detail-changed' : 'lists'
|
||||
assert.ok(events.indexOf(completedEvent) > events.indexOf('logs:done'))
|
||||
state.openPatchUsageDialog()
|
||||
assert.equal(state.patchUsageForm.aux_dosage_bag_count, 4)
|
||||
})
|
||||
|
||||
test(`${page}: missing historical bag count defaults to one and resets on close`, () => {
|
||||
const { state } = instance({ bagCount: null, mainBagCount: null })
|
||||
state.openPatchUsageDialog()
|
||||
assert.equal(state.patchUsageForm.aux_dosage_bag_count, 1)
|
||||
assert.equal(state.patchUsageForm.dosage_bag_count, 1)
|
||||
state.patchUsageForm.dosage_bag_count = 4
|
||||
state.patchUsageForm.aux_dosage_bag_count = 5
|
||||
state.resetPatchUsageForm()
|
||||
assert.equal(state.patchUsageForm.aux_dosage_bag_count, 1)
|
||||
assert.equal(state.patchUsageForm.dosage_bag_count, 1)
|
||||
})
|
||||
|
||||
test(`${page}: only water pills with an auxiliary formula submit or require auxiliary bags`, async () => {
|
||||
for (const options of [{ hasAux: false }, { prescriptionType: '饮片' }, { prescriptionType: '颗粒' }]) {
|
||||
const { state, calls } = instance(options)
|
||||
state.openPatchUsageDialog()
|
||||
state.patchUsageForm.aux_dosage_bag_count = 5
|
||||
assert.equal(state.patchUsageRules.value.aux_dosage_bag_count, undefined)
|
||||
await state.submitPatchUsage()
|
||||
assert.ok(!Object.hasOwn(calls[0], 'aux_dosage_bag_count'))
|
||||
assert.equal(Object.hasOwn(calls[0], 'dosage_bag_count'), !options.prescriptionType)
|
||||
}
|
||||
const { state } = instance()
|
||||
assert.equal(state.patchUsageRules.value.aux_dosage_bag_count[0].required, true)
|
||||
assert.equal(state.patchUsageRules.value.dosage_bag_count[0].required, true)
|
||||
})
|
||||
|
||||
test(`${page}: main and auxiliary bags can be edited independently in every submitted order status`, async () => {
|
||||
for (let status = 1; status <= 12; status++) {
|
||||
const { state, calls, detailPrescription, detailAuxUsage } = instance({ status })
|
||||
state.openPatchUsageDialog()
|
||||
assert.equal(state.patchUsageForm.dosage_bag_count, 2)
|
||||
state.patchUsageForm.dosage_bag_count = 5
|
||||
state.patchUsageForm.aux_dosage_bag_count = 1
|
||||
await state.submitPatchUsage()
|
||||
assert.equal(calls.length, 1, `status ${status}`)
|
||||
assert.equal(calls[0].dosage_bag_count, 5)
|
||||
assert.equal(calls[0].aux_dosage_bag_count, 1)
|
||||
assert.equal(detailPrescription.value.dosage_bag_count, 5)
|
||||
assert.equal(detailAuxUsage.value.dosage_bag_count, 1)
|
||||
}
|
||||
const { state, calls } = instance({ hasAux: false })
|
||||
state.openPatchUsageDialog()
|
||||
state.patchUsageForm.dosage_bag_count = 4
|
||||
await state.submitPatchUsage()
|
||||
assert.equal(calls[0].dosage_bag_count, 4)
|
||||
assert.ok(!Object.hasOwn(calls[0], 'aux_dosage_bag_count'))
|
||||
})
|
||||
|
||||
test(`${page}: validation blocks requests and a rejected save retains the selected bags`, async () => {
|
||||
const { state, calls, events } = instance({ fail: true })
|
||||
state.openPatchUsageDialog()
|
||||
state.patchUsageForm.aux_dosage_bag_count = 3
|
||||
state.patchUsageForm.dosage_bag_count = 4
|
||||
state.patchUsageFormRef.value.validate = async () => { throw new Error('Required') }
|
||||
await state.submitPatchUsage()
|
||||
assert.equal(calls.length, 0)
|
||||
state.patchUsageFormRef.value.validate = async () => {}
|
||||
await state.submitPatchUsage()
|
||||
assert.equal(state.patchUsageForm.aux_dosage_bag_count, 3)
|
||||
assert.equal(state.patchUsageForm.dosage_bag_count, 4)
|
||||
assert.equal(state.patchUsageVisible.value, true)
|
||||
assert.equal(state.patchUsageSaving.value, false)
|
||||
assert.deepEqual(events, [])
|
||||
})
|
||||
}
|
||||
@@ -46,6 +46,8 @@ export default defineConfig(({ mode }) => {
|
||||
|
||||
return {
|
||||
base: '/admin/',
|
||||
// 临时 UI 预览可能共用 node_modules;主开发服务独立缓存,避免预构建互相覆盖。
|
||||
cacheDir: path.resolve(__dirname, 'node_modules/.vite-admin'),
|
||||
server: {
|
||||
port: 5555,
|
||||
host: '0.0.0.0',
|
||||
|
||||
Reference in New Issue
Block a user