Compare commits
10
Commits
3c7b88bb15
..
mcp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49fc974421 | ||
|
|
88ce52620a | ||
|
|
0a17f071fc | ||
|
|
8e2b4fc763 | ||
|
|
b7e91f5fe3 | ||
|
|
68fc86a167 | ||
|
|
bd22e5f476 | ||
|
|
dbf474ddd7 | ||
|
|
bbe3e870a1 | ||
|
|
caab2cabe3 |
@@ -0,0 +1,24 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/** AI 助手(MCP)后台接口:挂在 /mcp/admin 下,沿用后台登录令牌 */
|
||||||
|
const opts = { urlPrefix: 'mcp' }
|
||||||
|
|
||||||
|
/** AI 授权列表(有 ai.grant/lists 看全部,否则只看自己的) */
|
||||||
|
export function aiGrantLists(params: any) {
|
||||||
|
return request.get({ url: '/admin/grants', params }, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 撤销 AI 授权 */
|
||||||
|
export function aiGrantRevoke(params: { id: number }) {
|
||||||
|
return request.post({ url: '/admin/revoke', params }, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** AI 访问日志 */
|
||||||
|
export function aiAccessLogLists(params: any) {
|
||||||
|
return request.get({ url: '/admin/logs', params }, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** AI 数据目录与覆盖情况 */
|
||||||
|
export function aiCatalogLists(params: any) {
|
||||||
|
return request.get({ url: '/admin/catalog', params }, opts)
|
||||||
|
}
|
||||||
@@ -467,7 +467,7 @@ export function prescriptionOrderEditTime(params: { id: number; create_time: str
|
|||||||
return request.post({ url: '/tcm.prescriptionOrder/editTime', params })
|
return request.post({ url: '/tcm.prescriptionOrder/editTime', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仅修改业务订单的承运商与快递单号;所有履约状态均可使用 */
|
/** 仅修改业务订单的承运商与快递单号;处方和支付单均审核通过后,所有履约状态均可使用 */
|
||||||
export function prescriptionOrderDdcode(params: {
|
export function prescriptionOrderDdcode(params: {
|
||||||
id: number
|
id: number
|
||||||
express_company: string
|
express_company: string
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ai-access-log-page">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item label="时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="timeRange"
|
||||||
|
type="datetimerange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
clearable
|
||||||
|
@change="resetPage"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结果">
|
||||||
|
<el-select v-model="queryParams.status" clearable placeholder="全部" class="w-[120px]" @change="resetPage">
|
||||||
|
<el-option v-for="(label, value) in STATUS" :key="value" :label="label" :value="value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据资源">
|
||||||
|
<el-input v-model="queryParams.resource" placeholder="如 tcm.diagnosis" clearable class="w-[180px]" @keyup.enter="resetPage" @clear="resetPage" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="记录ID">
|
||||||
|
<el-input v-model="queryParams.record_id" placeholder="如诊单ID" clearable class="w-[130px]" @keyup.enter="resetPage" @clear="resetPage" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="任务号">
|
||||||
|
<el-input v-model="queryParams.client_task_id" placeholder="行知任务号" clearable class="w-[200px]" @keyup.enter="resetPage" @clear="resetPage" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="page-hint">
|
||||||
|
记录每一次 AI 查询:谁、通过哪个客户端任务、查了哪个数据资源、返回了哪些记录。按“记录ID”可以查某个诊单被哪些账号通过 AI 看过。
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="!border-none mt-3" shadow="never">
|
||||||
|
<el-table :data="pager.lists" v-loading="pager.loading" size="default" stripe>
|
||||||
|
<el-table-column label="时间" width="165" prop="create_time_text" />
|
||||||
|
<el-table-column label="账号" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.admin_name || (row.admin_id ? '#' + row.admin_id : '—') }}</div>
|
||||||
|
<div class="cell-sub">{{ row.ip }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="工具" width="170" prop="tool" />
|
||||||
|
<el-table-column label="数据资源" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.resource_name || row.resource || '—' }}</div>
|
||||||
|
<div class="cell-sub">{{ row.resource }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="结果" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.status === 'ok' ? 'success' : 'warning'" size="small">{{ STATUS[row.status] || row.status }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="条数" width="70" align="right" prop="result_rows" />
|
||||||
|
<el-table-column label="记录ID / 原因" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip v-if="row.record_ids" :content="row.record_ids" placement="top">
|
||||||
|
<span class="ellipsis">{{ row.record_ids }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
<span v-else class="cell-sub">{{ row.message }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="参数" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip v-if="row.arguments" :content="row.arguments" placement="top">
|
||||||
|
<span class="ellipsis">{{ row.arguments }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="耗时" width="80" align="right">
|
||||||
|
<template #default="{ row }">{{ row.duration_ms }}ms</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="任务号" min-width="160" prop="client_task_id" />
|
||||||
|
</el-table>
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<pagination v-model="pager" @change="getLists" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="aiMcpAccessLog">
|
||||||
|
import { aiAccessLogLists } from '@/api/ai_mcp'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
const STATUS: Record<string, string> = { ok: '成功', denied: '拒绝', invalid: '参数错误', limited: '超限', error: '失败' }
|
||||||
|
const timeRange = ref<[string, string] | null>(null)
|
||||||
|
const queryParams = reactive({ start_time: '', end_time: '', status: '', resource: '', record_id: '', client_task_id: '' })
|
||||||
|
|
||||||
|
watch(timeRange, (val) => {
|
||||||
|
queryParams.start_time = val?.[0] ?? ''
|
||||||
|
queryParams.end_time = val?.[1] ?? ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
|
fetchFun: aiAccessLogLists,
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
timeRange.value = null
|
||||||
|
resetParams()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => getLists())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-hint {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.cell-sub {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.ellipsis {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ai-catalog-page">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<div class="summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<div class="summary-value success">{{ counts.open ?? 0 }}</div>
|
||||||
|
<div class="summary-label">已开放</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<div class="summary-value warning">{{ counts.pending ?? 0 }}</div>
|
||||||
|
<div class="summary-label">待整改</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<div class="summary-value">{{ counts.excluded ?? 0 }}</div>
|
||||||
|
<div class="summary-label">不开放</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form :inline="true" class="mt-3">
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryParams.status" clearable placeholder="全部" class="w-[120px]" @change="resetPage">
|
||||||
|
<el-option label="已开放" value="open" />
|
||||||
|
<el-option label="待整改" value="pending" />
|
||||||
|
<el-option label="不开放" value="excluded" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="业务分组">
|
||||||
|
<el-select v-model="queryParams.domain" clearable filterable placeholder="全部" class="w-[180px]" @change="resetPage">
|
||||||
|
<el-option v-for="d in domains" :key="d" :label="d" :value="d" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryParams.keyword" placeholder="名称或资源标识" clearable class="w-[200px]" @keyup.enter="resetPage" @clear="resetPage" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||||
|
<el-button @click="resetParams">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="page-hint">
|
||||||
|
后台每个只读接口都是一个“数据资源”。已开放的资源由 AI 以调用账号自己的权限和数据范围执行原有接口;
|
||||||
|
待整改的资源写明了原因(如缺少逐条权限校验、会调用外部接口、权限点未登记),整改或审核通过后开放。
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="!border-none mt-3" shadow="never">
|
||||||
|
<el-table :data="pager.lists" v-loading="pager.loading" size="default" stripe>
|
||||||
|
<el-table-column label="资源" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.name }}</div>
|
||||||
|
<div class="cell-sub">{{ row.resource }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="业务分组" width="150" prop="domain" />
|
||||||
|
<el-table-column label="类型" width="80">
|
||||||
|
<template #default="{ row }">{{ KIND[row.kind] || row.kind }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="STATUS[row.status]?.type" size="small">{{ STATUS[row.status]?.label || row.status }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="原因" min-width="260" prop="reason" />
|
||||||
|
<el-table-column label="已审核" width="80" align="center">
|
||||||
|
<template #default="{ row }">{{ row.reviewed ? '是' : '自动' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<pagination v-model="pager" @change="getLists" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="aiMcpCatalog">
|
||||||
|
import { aiCatalogLists } from '@/api/ai_mcp'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import { computed, onMounted, reactive } from 'vue'
|
||||||
|
|
||||||
|
const KIND: Record<string, string> = { list: '列表', detail: '详情', report: '统计', other: '其他', write: '写操作' }
|
||||||
|
const STATUS: Record<string, { label: string; type: 'success' | 'warning' | 'info' }> = {
|
||||||
|
open: { label: '已开放', type: 'success' },
|
||||||
|
pending: { label: '待整改', type: 'warning' },
|
||||||
|
excluded: { label: '不开放', type: 'info' }
|
||||||
|
}
|
||||||
|
const queryParams = reactive({ status: '', domain: '', keyword: '' })
|
||||||
|
|
||||||
|
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
|
fetchFun: aiCatalogLists,
|
||||||
|
params: queryParams,
|
||||||
|
size: 50
|
||||||
|
})
|
||||||
|
|
||||||
|
const counts = computed<Record<string, number>>(() => pager.extend?.counts || {})
|
||||||
|
const domains = computed<string[]>(() => pager.extend?.domains || [])
|
||||||
|
|
||||||
|
onMounted(() => getLists())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.summary {
|
||||||
|
display: flex;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
.summary-value {
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.summary-value.success {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
.summary-value.warning {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
.summary-label,
|
||||||
|
.page-hint,
|
||||||
|
.cell-sub {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.page-hint {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ai-grant-page">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-alert
|
||||||
|
v-if="pager.extend && pager.extend.enabled === false"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
class="mb-3"
|
||||||
|
title="AI 助手接口未启用:服务器 .env 的 [AI_MCP] ENABLED 为 false,客户端暂时无法绑定和查询。"
|
||||||
|
/>
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryParams.status" clearable placeholder="全部" class="w-[120px]" @change="resetPage">
|
||||||
|
<el-option label="有效" value="1" />
|
||||||
|
<el-option label="已撤销" value="2" />
|
||||||
|
<el-option label="已过期" value="3" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.keyword"
|
||||||
|
placeholder="姓名 / 账号 / 备注"
|
||||||
|
clearable
|
||||||
|
class="w-[220px]"
|
||||||
|
@keyup.enter="resetPage"
|
||||||
|
@clear="resetPage"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||||
|
<el-button @click="resetParams">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="page-hint">
|
||||||
|
员工在行知等 AI 助手里用甄养堂账号密码绑定后,会在这里生成一条只读授权。AI 只能按该账号自己的权限和数据范围查询;
|
||||||
|
撤销后立即失效。账号改密、停用、失去“允许 AI 助手查询”权限时,授权也会自动失效。
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="!border-none mt-3" shadow="never">
|
||||||
|
<el-table :data="pager.lists" v-loading="pager.loading" size="default" stripe>
|
||||||
|
<el-table-column label="账号" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.admin_name || '—' }}</div>
|
||||||
|
<div class="cell-sub">{{ row.admin_account }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="客户端" min-width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.client }}<span v-if="row.client_instance"> · {{ row.client_instance }}</span></div>
|
||||||
|
<div class="cell-sub">{{ row.label }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="令牌" width="130" prop="token_prefix" />
|
||||||
|
<el-table-column label="状态" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.status_text === '有效' ? 'success' : 'info'" size="small">{{ row.status_text }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最近使用" min-width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.last_used_time_text || '—' }}</div>
|
||||||
|
<div class="cell-sub">{{ row.last_used_ip }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="到期时间" width="150" prop="expire_time_text" />
|
||||||
|
<el-table-column label="签发时间" width="150" prop="create_time_text" />
|
||||||
|
<el-table-column label="撤销" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="row.revoke_time_text">
|
||||||
|
<div>{{ row.revoke_time_text }}</div>
|
||||||
|
<div class="cell-sub">{{ reasonText(row.revoke_reason) }}</div>
|
||||||
|
</template>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="90" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button v-if="row.can_revoke" type="danger" link @click="handleRevoke(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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="aiMcpGrant">
|
||||||
|
import { aiGrantLists, aiGrantRevoke } from '@/api/ai_mcp'
|
||||||
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
import { onMounted, reactive } from 'vue'
|
||||||
|
|
||||||
|
const queryParams = reactive({ status: '', keyword: '' })
|
||||||
|
|
||||||
|
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
|
fetchFun: aiGrantLists,
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
|
||||||
|
const REASONS: Record<string, string> = {
|
||||||
|
client_revoke: '客户端解绑',
|
||||||
|
admin_revoke: '后台撤销',
|
||||||
|
rebind: '重新绑定',
|
||||||
|
expired: '到期/闲置失效',
|
||||||
|
password_changed: '账号改密',
|
||||||
|
admin_disabled: '账号停用',
|
||||||
|
admin_deleted: '账号删除'
|
||||||
|
}
|
||||||
|
const reasonText = (reason: string) => REASONS[reason] || reason || ''
|
||||||
|
|
||||||
|
const handleRevoke = async (row: any) => {
|
||||||
|
await feedback.confirm(`确认撤销 ${row.admin_name || row.admin_account} 的这条 AI 授权?撤销后该客户端需要重新绑定。`)
|
||||||
|
await aiGrantRevoke({ id: row.id })
|
||||||
|
feedback.msgSuccess('已撤销')
|
||||||
|
getLists()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => getLists())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-hint {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.cell-sub {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -871,8 +871,17 @@
|
|||||||
<el-option label="京东快递" value="jd" />
|
<el-option label="京东快递" value="jd" />
|
||||||
<el-option label="极兔速递" value="jt" />
|
<el-option label="极兔速递" value="jt" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-model="editForm.tracking_number" maxlength="80" placeholder="快递单号" class="flex-1 min-w-0" />
|
<el-input
|
||||||
</div>
|
v-model="editForm.tracking_number"
|
||||||
|
disabled
|
||||||
|
maxlength="80"
|
||||||
|
placeholder="快递单号"
|
||||||
|
class="flex-1 min-w-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-gray-400 mt-1">
|
||||||
|
编辑订单后需重新审核;双审通过后,请通过列表的单号操作填写或修改
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -1067,8 +1076,17 @@
|
|||||||
<el-option label="京东快递" value="jd" />
|
<el-option label="京东快递" value="jd" />
|
||||||
<el-option label="极兔速递" value="jt" />
|
<el-option label="极兔速递" value="jt" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-model="editForm.tracking_number" maxlength="80" placeholder="快递单号" class="flex-1 min-w-0" />
|
<el-input
|
||||||
</div>
|
v-model="editForm.tracking_number"
|
||||||
|
disabled
|
||||||
|
maxlength="80"
|
||||||
|
placeholder="快递单号"
|
||||||
|
class="flex-1 min-w-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-gray-400 mt-1">
|
||||||
|
编辑订单后需重新审核;双审通过后,请通过列表的单号操作填写或修改
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@@ -2975,9 +2993,13 @@ function canWithdrawRow(row: { fulfillment_status?: number }) {
|
|||||||
return Number(row.fulfillment_status) === 1
|
return Number(row.fulfillment_status) === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function canShipRow(row: { fulfillment_status?: number }) {
|
function canShipRow(row: {
|
||||||
// 履约中(2) 可发货
|
fulfillment_status?: number
|
||||||
return Number(row.fulfillment_status) === 2
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
// 履约中(2) 且处方、支付单均审核通过才可发货
|
||||||
|
return Number(row.fulfillment_status) === 2 && isDualAuditPassed(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShipMode = 'gancao' | 'direct'
|
type ShipMode = 'gancao' | 'direct'
|
||||||
@@ -3073,8 +3095,11 @@ function canRefundRow(row: { fulfillment_status?: number; payment_slip_audit_sta
|
|||||||
return (fs === 5 || fs === 6 || fs === 3 || fs === 9) && Number(row.payment_slip_audit_status) === 1
|
return (fs === 5 || fs === 6 || fs === 3 || fs === 9) && Number(row.payment_slip_audit_status) === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function canQuickTrackRow(_row: { fulfillment_status?: number }) {
|
function canQuickTrackRow(row: {
|
||||||
return true
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
return isDualAuditPassed(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function canUploadPharmacyRow(row: {
|
function canUploadPharmacyRow(row: {
|
||||||
@@ -3270,7 +3295,7 @@ const editSaving = ref(false)
|
|||||||
/** 编辑订单分步:0 收货 / 1 服务与支付单 / 2 金额与确认(与处方列表「创建业务订单」一致) */
|
/** 编辑订单分步:0 收货 / 1 服务与支付单 / 2 金额与确认(与处方列表「创建业务订单」一致) */
|
||||||
const editOrderStep = ref(0)
|
const editOrderStep = ref(0)
|
||||||
/** 顶部「关联处方」卡片数据(来自业务订单详情中的 prescription) */
|
/** 顶部「关联处方」卡片数据(来自业务订单详情中的 prescription) */
|
||||||
const editOrderPrescription = ref<Record<string, any> | null>(null)
|
const editOrderPrescription = ref<Record<string, any> | null>(null)
|
||||||
/** 甘草 SCM 已提交:弹窗仅展示并提交快递单号与承运商 */
|
/** 甘草 SCM 已提交:弹窗仅展示并提交快递单号与承运商 */
|
||||||
const editGancaoLogisticsOnlyMode = ref(false)
|
const editGancaoLogisticsOnlyMode = ref(false)
|
||||||
/** 用于提示文案展示甘草处方单号 */
|
/** 用于提示文案展示甘草处方单号 */
|
||||||
@@ -3455,7 +3480,7 @@ const editRules = computed<FormRules>(() => {
|
|||||||
return rules
|
return rules
|
||||||
})
|
})
|
||||||
|
|
||||||
function resetEditOrderDialog() {
|
function resetEditOrderDialog() {
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editGancaoLogisticsOnlyMode.value = false
|
editGancaoLogisticsOnlyMode.value = false
|
||||||
@@ -3507,8 +3532,8 @@ async function openEdit(row: {
|
|||||||
}
|
}
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editVisible.value = true
|
editVisible.value = true
|
||||||
editDialogLoading.value = true
|
editDialogLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res: any = await prescriptionOrderDetail({ id: row.id })
|
const res: any = await prescriptionOrderDetail({ id: row.id })
|
||||||
const d = res?.data ?? res
|
const d = res?.data ?? res
|
||||||
@@ -3531,7 +3556,7 @@ async function openEdit(row: {
|
|||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
editForm.id = d.id
|
editForm.id = d.id
|
||||||
editForm.prescription_id = Number(d.prescription_id) || 0
|
editForm.prescription_id = Number(d.prescription_id) || 0
|
||||||
editForm.diagnosis_id = Number(d.diagnosis_id) || 0
|
editForm.diagnosis_id = Number(d.diagnosis_id) || 0
|
||||||
editForm.assistant_id = Number(d.assistant_id) || 0
|
editForm.assistant_id = Number(d.assistant_id) || 0
|
||||||
@@ -3627,7 +3652,6 @@ async function submitEdit() {
|
|||||||
service_channel: editForm.service_channel || '',
|
service_channel: editForm.service_channel || '',
|
||||||
service_package: Array.isArray(editForm.service_package) ? editForm.service_package.join(',') : '',
|
service_package: Array.isArray(editForm.service_package) ? editForm.service_package.join(',') : '',
|
||||||
express_company: editForm.express_company || 'auto',
|
express_company: editForm.express_company || 'auto',
|
||||||
tracking_number: editForm.tracking_number || '',
|
|
||||||
fee_type: editForm.fee_type,
|
fee_type: editForm.fee_type,
|
||||||
amount: editForm.amount,
|
amount: editForm.amount,
|
||||||
remark_extra: editForm.remark_extra || '',
|
remark_extra: editForm.remark_extra || '',
|
||||||
@@ -3915,7 +3939,17 @@ const quickTrackForm = reactive({
|
|||||||
tracking_number: ''
|
tracking_number: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
function openQuickTrack(row: { id: number; express_company?: unknown; tracking_number?: unknown }) {
|
function openQuickTrack(row: {
|
||||||
|
id: number
|
||||||
|
express_company?: unknown
|
||||||
|
tracking_number?: unknown
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
if (!canQuickTrackRow(row)) {
|
||||||
|
feedback.msgWarning('处方审核和支付单审核均通过后,才可填写或修改快递单号')
|
||||||
|
return
|
||||||
|
}
|
||||||
quickTrackRowId.value = row.id
|
quickTrackRowId.value = row.id
|
||||||
quickTrackForm.express_company = String(row.express_company || 'auto') || 'auto'
|
quickTrackForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||||
quickTrackForm.tracking_number = String(row.tracking_number || '')
|
quickTrackForm.tracking_number = String(row.tracking_number || '')
|
||||||
@@ -3963,7 +3997,19 @@ function resolveShipModeForRow(row: { id: number; ship_mode?: unknown }) {
|
|||||||
return normalizeShipMode(row.ship_mode)
|
return normalizeShipMode(row.ship_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
function openShip(row: {
|
||||||
|
id: number
|
||||||
|
express_company?: unknown
|
||||||
|
tracking_number?: unknown
|
||||||
|
ship_mode?: unknown
|
||||||
|
fulfillment_status?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
if (!canShipRow(row)) {
|
||||||
|
feedback.msgWarning('仅处方审核和支付单审核均通过的履约中订单可填写单号并发货')
|
||||||
|
return
|
||||||
|
}
|
||||||
shipRowId.value = Number(row.id)
|
shipRowId.value = Number(row.id)
|
||||||
shipForm.ship_mode = resolveShipModeForRow(row)
|
shipForm.ship_mode = resolveShipModeForRow(row)
|
||||||
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||||
|
|||||||
@@ -1624,8 +1624,17 @@
|
|||||||
<el-option label="京东快递" value="jd" />
|
<el-option label="京东快递" value="jd" />
|
||||||
<el-option label="极兔速递" value="jt" />
|
<el-option label="极兔速递" value="jt" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-model="editForm.tracking_number" maxlength="80" placeholder="快递单号" class="flex-1 min-w-0" />
|
<el-input
|
||||||
</div>
|
v-model="editForm.tracking_number"
|
||||||
|
disabled
|
||||||
|
maxlength="80"
|
||||||
|
placeholder="快递单号"
|
||||||
|
class="flex-1 min-w-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-gray-400 mt-1">
|
||||||
|
编辑订单后需重新审核;双审通过后,请通过列表的单号操作填写或修改
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@@ -3422,9 +3431,13 @@ function canWithdrawRow(row: { fulfillment_status?: number }) {
|
|||||||
return Number(row.fulfillment_status) === 1
|
return Number(row.fulfillment_status) === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function canShipRow(row: { fulfillment_status?: number }) {
|
function canShipRow(row: {
|
||||||
// 履约中(2) 可发货
|
fulfillment_status?: number
|
||||||
return Number(row.fulfillment_status) === 2
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
// 履约中(2) 且处方、支付单均审核通过才可发货
|
||||||
|
return Number(row.fulfillment_status) === 2 && isDualAuditPassed(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function canAddPayOrderRow(row: { fulfillment_status?: number }) {
|
function canAddPayOrderRow(row: { fulfillment_status?: number }) {
|
||||||
@@ -3444,8 +3457,11 @@ function canRefundRow(row: { fulfillment_status?: number; payment_slip_audit_sta
|
|||||||
return (fs === 5 || fs === 6 || fs === 3 || fs === 9) && Number(row.payment_slip_audit_status) === 1
|
return (fs === 5 || fs === 6 || fs === 3 || fs === 9) && Number(row.payment_slip_audit_status) === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function canQuickTrackRow(_row: { fulfillment_status?: number }) {
|
function canQuickTrackRow(row: {
|
||||||
return true
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
return isDualAuditPassed(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function canUploadPharmacyRow(row: {
|
function canUploadPharmacyRow(row: {
|
||||||
@@ -4181,7 +4197,7 @@ const editSaving = ref(false)
|
|||||||
/** 编辑订单分步:0 收货 / 1 服务与支付单 / 2 金额与确认(与处方列表「创建业务订单」一致) */
|
/** 编辑订单分步:0 收货 / 1 服务与支付单 / 2 金额与确认(与处方列表「创建业务订单」一致) */
|
||||||
const editOrderStep = ref(0)
|
const editOrderStep = ref(0)
|
||||||
/** 顶部「关联处方」卡片数据(来自业务订单详情中的 prescription) */
|
/** 顶部「关联处方」卡片数据(来自业务订单详情中的 prescription) */
|
||||||
const editOrderPrescription = ref<Record<string, any> | null>(null)
|
const editOrderPrescription = ref<Record<string, any> | null>(null)
|
||||||
|
|
||||||
const editOrderStepLead = computed(() => {
|
const editOrderStepLead = computed(() => {
|
||||||
const texts = [
|
const texts = [
|
||||||
@@ -4381,7 +4397,7 @@ const editRules = computed<FormRules>(() => {
|
|||||||
return rules
|
return rules
|
||||||
})
|
})
|
||||||
|
|
||||||
function resetEditOrderDialog() {
|
function resetEditOrderDialog() {
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editFormRef.value?.clearValidate()
|
editFormRef.value?.clearValidate()
|
||||||
@@ -4431,8 +4447,8 @@ async function openEdit(row: {
|
|||||||
}
|
}
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editVisible.value = true
|
editVisible.value = true
|
||||||
editDialogLoading.value = true
|
editDialogLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res: any = await prescriptionOrderDetail({ id: row.id })
|
const res: any = await prescriptionOrderDetail({ id: row.id })
|
||||||
const d = res?.data ?? res
|
const d = res?.data ?? res
|
||||||
@@ -4455,7 +4471,7 @@ async function openEdit(row: {
|
|||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
editForm.id = d.id
|
editForm.id = d.id
|
||||||
editForm.prescription_id = Number(d.prescription_id) || 0
|
editForm.prescription_id = Number(d.prescription_id) || 0
|
||||||
editForm.diagnosis_id = Number(d.diagnosis_id) || 0
|
editForm.diagnosis_id = Number(d.diagnosis_id) || 0
|
||||||
editForm.assistant_id = Number(d.assistant_id) || 0
|
editForm.assistant_id = Number(d.assistant_id) || 0
|
||||||
@@ -4540,7 +4556,6 @@ async function submitEdit() {
|
|||||||
service_channel: editForm.service_channel || '',
|
service_channel: editForm.service_channel || '',
|
||||||
service_package: Array.isArray(editForm.service_package) ? editForm.service_package.join(',') : '',
|
service_package: Array.isArray(editForm.service_package) ? editForm.service_package.join(',') : '',
|
||||||
express_company: editForm.express_company || 'auto',
|
express_company: editForm.express_company || 'auto',
|
||||||
tracking_number: editForm.tracking_number || '',
|
|
||||||
fee_type: editForm.fee_type,
|
fee_type: editForm.fee_type,
|
||||||
amount: editForm.amount,
|
amount: editForm.amount,
|
||||||
remark_extra: editForm.remark_extra || '',
|
remark_extra: editForm.remark_extra || '',
|
||||||
@@ -4788,7 +4803,17 @@ const quickTrackForm = reactive({
|
|||||||
tracking_number: ''
|
tracking_number: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
function openQuickTrack(row: { id: number; express_company?: unknown; tracking_number?: unknown }) {
|
function openQuickTrack(row: {
|
||||||
|
id: number
|
||||||
|
express_company?: unknown
|
||||||
|
tracking_number?: unknown
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
if (!canQuickTrackRow(row)) {
|
||||||
|
feedback.msgWarning('处方审核和支付单审核均通过后,才可填写或修改快递单号')
|
||||||
|
return
|
||||||
|
}
|
||||||
quickTrackRowId.value = row.id
|
quickTrackRowId.value = row.id
|
||||||
quickTrackForm.express_company = String(row.express_company || 'auto') || 'auto'
|
quickTrackForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||||
quickTrackForm.tracking_number = String(row.tracking_number || '')
|
quickTrackForm.tracking_number = String(row.tracking_number || '')
|
||||||
@@ -4840,7 +4865,19 @@ const shipDialogModeDisplay = computed(() =>
|
|||||||
shipForm.ship_mode === 'direct' ? '洛阳药房直发' : '甘草药房直发'
|
shipForm.ship_mode === 'direct' ? '洛阳药房直发' : '甘草药房直发'
|
||||||
)
|
)
|
||||||
|
|
||||||
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
function openShip(row: {
|
||||||
|
id: number
|
||||||
|
express_company?: unknown
|
||||||
|
tracking_number?: unknown
|
||||||
|
ship_mode?: unknown
|
||||||
|
fulfillment_status?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
if (!canShipRow(row)) {
|
||||||
|
feedback.msgWarning('仅处方审核和支付单审核均通过的履约中订单可填写单号并发货')
|
||||||
|
return
|
||||||
|
}
|
||||||
shipRowId.value = row.id
|
shipRowId.value = row.id
|
||||||
shipForm.ship_mode = String(row.ship_mode || 'gancao') || 'gancao'
|
shipForm.ship_mode = String(row.ship_mode || 'gancao') || 'gancao'
|
||||||
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
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 handlers = [
|
||||||
|
'isDualAuditPassed', 'canQuickTrackRow', 'canShipRow', 'openQuickTrack',
|
||||||
|
'submitQuickTrack', 'openShip', 'openEdit', 'submitEdit', 'resetEditOrderDialog'
|
||||||
|
]
|
||||||
|
const stateNames = [
|
||||||
|
'quickTrackVisible', 'quickTrackSaving', 'quickTrackRowId', 'quickTrackForm',
|
||||||
|
'shipVisible', 'shipRowId', 'shipForm', 'editVisible', 'editDialogLoading',
|
||||||
|
'editSaving', 'editOrderStep', 'editOrderPrescription',
|
||||||
|
'editFormRef', 'editForm', 'editDepositMin'
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const page of ['order_list.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 ast = ts.createSourceFile(filename + '.ts', descriptor.scriptSetup.content, ts.ScriptTarget.Latest, true)
|
||||||
|
// Execute the actual order handlers with only network and unrelated UI dependencies stubbed.
|
||||||
|
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 selected = [...handlers, ...stateNames]
|
||||||
|
for (const name of selected) assert.ok(declarations.has(name), `${page}: missing ${name}`)
|
||||||
|
const compiled = ts.transpileModule(selected.map(name => declarations.get(name)).join('\n'), {
|
||||||
|
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
|
||||||
|
}).outputText
|
||||||
|
|
||||||
|
function instance(detail = {}, saveTracking = async () => {}) {
|
||||||
|
const warnings = []
|
||||||
|
const errors = []
|
||||||
|
const edits = []
|
||||||
|
const tracks = []
|
||||||
|
const globals = {
|
||||||
|
ref: vue.ref, reactive: vue.reactive, nextTick: vue.nextTick,
|
||||||
|
feedback: { msgWarning: message => warnings.push(message), msgError: message => errors.push(message), msgSuccess() {} },
|
||||||
|
prescriptionOrderDetail: async () => ({ data: detail }),
|
||||||
|
prescriptionOrderEdit: async payload => edits.push(payload),
|
||||||
|
prescriptionOrderDdcode: async payload => { tracks.push(payload); await saveTracking(payload) },
|
||||||
|
canEditRow: () => true,
|
||||||
|
parseServicePackageValues: () => [],
|
||||||
|
loadEditPaidOrders: async () => {},
|
||||||
|
resolveShipModeForRow: row => row.ship_mode || 'gancao',
|
||||||
|
editGancaoLogisticsOnlyMode: vue.ref(false),
|
||||||
|
editGancaoDisplayNo: vue.ref(''),
|
||||||
|
detailDrawerRef: vue.ref(null),
|
||||||
|
detailVisible: vue.ref(false),
|
||||||
|
getLists() {}
|
||||||
|
}
|
||||||
|
const state = new Function(...Object.keys(globals), `${compiled}\nreturn { ${selected.join(', ')} }`)(...Object.values(globals))
|
||||||
|
state.editFormRef.value = { validate: async () => {}, clearValidate() {} }
|
||||||
|
return { state, warnings, errors, edits, tracks }
|
||||||
|
}
|
||||||
|
|
||||||
|
test(`${page}: only two approved audits unlock tracking, including legacy string statuses`, () => {
|
||||||
|
const { state } = instance()
|
||||||
|
for (const rx of [undefined, null, 0, 1, 2, '0', '1', '2']) {
|
||||||
|
for (const pay of [undefined, null, 0, 1, 2, '0', '1', '2']) {
|
||||||
|
const row = { prescription_audit_status: rx, payment_slip_audit_status: pay, fulfillment_status: 2 }
|
||||||
|
const allowed = [1, '1'].includes(rx) && [1, '1'].includes(pay)
|
||||||
|
assert.equal(state.canQuickTrackRow(row), allowed, `tracking: ${rx}/${pay}`)
|
||||||
|
assert.equal(state.canShipRow(row), allowed, `shipping: ${rx}/${pay}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const status of [1, 3, 4, 5, 6, 9]) {
|
||||||
|
assert.equal(state.canShipRow({ prescription_audit_status: 1, payment_slip_audit_status: 1, fulfillment_status: status }), false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: direct handler calls cannot open tracking or shipping before both approvals`, () => {
|
||||||
|
for (const [rx, pay] of [[0, 0], [1, 0], [0, 1], [2, 1], [1, 2]]) {
|
||||||
|
const { state, warnings } = instance()
|
||||||
|
const row = { id: 42, prescription_audit_status: rx, payment_slip_audit_status: pay, fulfillment_status: 2 }
|
||||||
|
state.openQuickTrack(row)
|
||||||
|
state.openShip(row)
|
||||||
|
assert.equal(state.quickTrackVisible.value, false)
|
||||||
|
assert.equal(state.shipVisible.value, false)
|
||||||
|
assert.equal(state.quickTrackRowId.value, 0)
|
||||||
|
assert.equal(state.shipRowId.value, 0)
|
||||||
|
assert.equal(warnings.length, 2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: approved orders can fill or replace the number and preserve the carrier`, async () => {
|
||||||
|
for (const oldNumber of ['', 'SF-OLD']) {
|
||||||
|
const { state, tracks } = instance()
|
||||||
|
const row = { id: 42, prescription_audit_status: 1, payment_slip_audit_status: 1, fulfillment_status: 2, express_company: 'sf', tracking_number: oldNumber }
|
||||||
|
state.openQuickTrack(row)
|
||||||
|
assert.equal(state.quickTrackVisible.value, true)
|
||||||
|
assert.equal(state.quickTrackForm.tracking_number, oldNumber)
|
||||||
|
state.quickTrackForm.tracking_number = ' SF-NEW '
|
||||||
|
await state.submitQuickTrack()
|
||||||
|
assert.deepEqual(tracks, [{ id: 42, express_company: 'sf', tracking_number: 'SF-NEW' }])
|
||||||
|
assert.equal(state.quickTrackVisible.value, false)
|
||||||
|
state.openShip(row)
|
||||||
|
assert.equal(state.shipVisible.value, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: a server rejection after audit revocation retains the dialog and entered number`, async () => {
|
||||||
|
const { state } = instance({}, async () => { throw new Error('audit revoked') })
|
||||||
|
state.openQuickTrack({ id: 42, prescription_audit_status: 1, payment_slip_audit_status: 1 })
|
||||||
|
state.quickTrackForm.tracking_number = 'SF-NEW'
|
||||||
|
await state.submitQuickTrack()
|
||||||
|
assert.equal(state.quickTrackVisible.value, true)
|
||||||
|
assert.equal(state.quickTrackSaving.value, false)
|
||||||
|
assert.equal(state.quickTrackForm.tracking_number, 'SF-NEW')
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: editing preserves the existing number while saving other fields before approval`, async () => {
|
||||||
|
const { state, edits, errors } = instance({ id: 42, prescription_audit_status: 1, payment_slip_audit_status: 0, tracking_number: 'SF-EXISTING' })
|
||||||
|
await state.openEdit({ id: 42, prescription_audit_status: 1, payment_slip_audit_status: 1 })
|
||||||
|
assert.deepEqual(errors, [])
|
||||||
|
assert.equal(state.editForm.tracking_number, 'SF-EXISTING')
|
||||||
|
state.editForm.tracking_number = 'FORGED'
|
||||||
|
state.editForm.recipient_name = '修改后的收货人'
|
||||||
|
state.editOrderStep.value = 2
|
||||||
|
await state.submitEdit()
|
||||||
|
assert.equal(edits.length, 1)
|
||||||
|
assert.equal(edits[0].recipient_name, '修改后的收货人')
|
||||||
|
assert.equal(Object.hasOwn(edits[0], 'tracking_number'), false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: ordinary editing cannot change tracking while resetting an approved payment audit`, async () => {
|
||||||
|
const { state, edits, errors } = instance({ id: 42, prescription_audit_status: '1', payment_slip_audit_status: '1' })
|
||||||
|
await state.openEdit({ id: 42 })
|
||||||
|
assert.deepEqual(errors, [])
|
||||||
|
state.editForm.tracking_number = 'SF-NEW'
|
||||||
|
state.editOrderStep.value = 2
|
||||||
|
await state.submitEdit()
|
||||||
|
assert.equal(edits.length, 1)
|
||||||
|
assert.equal(Object.hasOwn(edits[0], 'tracking_number'), false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${page}: template compiles with tracking controls bound to audit restrictions`, () => {
|
||||||
|
const template = compileTemplate({ source: descriptor.template.content, filename, id: page, compilerOptions: { bindingMetadata: script.bindings } })
|
||||||
|
assert.deepEqual(template.errors, [])
|
||||||
|
const inputs = [...descriptor.template.content.matchAll(/<el-input\b[^>]*v-model="editForm\.tracking_number"[^>]*>/g)]
|
||||||
|
assert.equal(inputs.length, page === 'order_list.vue' ? 2 : 1)
|
||||||
|
for (const [input] of inputs) assert.match(input, /\sdisabled(?:\s|\/?>)/)
|
||||||
|
assert.match(descriptor.template.content, /v-if="canQuickTrackRow\(row\)"/)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# 已开处方 → AI 界面改造
|
||||||
|
|
||||||
|
## 功能分析
|
||||||
|
|
||||||
|
这是基于已保存资料快照的双模型分析与医生复核工作台。主流程是确认患者和批次、查看模型完成情况、对照原方差异、核查资料缺口、记录所选模型的复核意见。
|
||||||
|
|
||||||
|
保留六个入口:对比总览、完整报告、候选与逐味、资料与缺口、处理进度、历史与趋势。历史批次切换、刷新、重新分析、单模型重试、独立复核草稿及保存继续使用现有服务和权限逻辑。医生原方及支持报告仍可访问。
|
||||||
|
|
||||||
|
## 设计决定
|
||||||
|
|
||||||
|
- 主程序 `shell.py` 将 prescriptions 等业务页列为科技蓝页面。因此继续复用 `reception_style.TECH_BLUE`:主色 #1769E8,背景 #F3F7FD,白色面板,分割线 #DBE5F2。没有采用主程序其他页面的靛蓝令牌,也没有修改全局主题。
|
||||||
|
- 深蓝渐变横幅改为白色标题工具栏。批次状态放在顶行,患者及原方信息单独成行,当前导航用浅蓝背景与蓝色底线标识。
|
||||||
|
- 缩小一致度圆环和数字,保留共同药味、候选药味与药味重合的计算依据;修复指标区样式误把分隔线应用到每个子标签的问题。
|
||||||
|
- 总览由三栏改成剂量差异主区与固定复核侧栏。三方交集和附件覆盖改为可展开的摘要,减少初始屏幕空白和对主图的挤压。
|
||||||
|
- 复核模型和复核状态并排显示,意见输入和保存按钮放在一起。顶部保存动作显示当前模型名,切换时同步更新。
|
||||||
|
- 短窗口收起次要信息,保留剂量主图和复核输入;展开图表时剂量滚动区域可让出高度。列表内容保持最小高度,避免刷新后条目重叠。
|
||||||
|
- 保留未知值与零值区分、不可比/历史状态处理、模型各自失败重试以及现有一致度说明。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- 处方 AI 五组回归测试覆盖数据处理、权限、异步读取、逐味比较、页面、模型复核与布局。
|
||||||
|
- 新增八种窗口/展开状态组合的控件边界检查,以及展开/收起不改变比较数据和复核清单的检查。
|
||||||
|
- 离线模拟数据预览覆盖 1440×940、1280×860、1024×700、940×640,各内容页面、展开图表、运行中、失败、历史及统计窗口。
|
||||||
|
- 预览输出:`app/artifacts/issued_prescription_redesign/`。模拟数据仅供 UI 验证,不对应真实患者。
|
||||||
|
|
||||||
|
本次改动在现有未提交工作区基础上完成,仅调整桌面呈现与相关回归检查;没有重新打包或发布桌面安装程序。
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
"""Render the prescription comparison window with synthetic, offline data."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from copy import deepcopy
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs import issued_prescription_ai as ai
|
||||||
|
from doctor_workstation.ui.theme import apply_theme
|
||||||
|
|
||||||
|
|
||||||
|
def example_batch() -> dict[str, Any]:
|
||||||
|
"""Three prescriptions that actually differ, so every state of the page has something to draw."""
|
||||||
|
|
||||||
|
doctor = {"生地黄": 16, "天花粉": 15, "干石斛": 20, "醋五味子": 6,
|
||||||
|
"生麦冬": 12, "茯苓": 10, "红参片": 6, "生牡丹皮": 10}
|
||||||
|
candidates = {
|
||||||
|
"qwen": {"生地黄": 15, "干石斛": 12, "醋五味子": 6, "生麦冬": 12, "茯苓": 15, "麸炒白术": 12, "丹参": 15},
|
||||||
|
"openai": {"生地黄": 12, "醋五味子": 6, "生麦冬": 10, "茯苓": 12, "生白芍": 5, "炒酸枣仁": 6},
|
||||||
|
}
|
||||||
|
reports = {
|
||||||
|
"qwen": {"summary": "界面演示数据:对照药味组成、剂量及用法,辅助医生逐项复核。",
|
||||||
|
"diagnosis": "消渴病,气阴两虚兼血热。兼证需结合舌脉资料核实。",
|
||||||
|
"analysis": "阅读药方对比页,查看同名药材的剂量差异、仅医生方药味与仅候选方药味。",
|
||||||
|
"risk_assessment": [{"level": "high", "label": "血压数据缺失,影响补气药安全性评估"},
|
||||||
|
{"level": "medium", "label": "肝肾功能具体指标未提供"}],
|
||||||
|
"missing_information": ["甲状腺功能及眼底检查报告缺失", "舌象与脉诊仅有附件,无文本记录"]},
|
||||||
|
"openai": {"summary": "界面演示数据:两份候选方独立生成,引用编号可回查原始资料。",
|
||||||
|
"diagnosis": "已记录气阴两虚证;辨证依据需补充。",
|
||||||
|
"analysis": "候选方以益气养阴为主,安神药味为本模型新增,需要医师确认。",
|
||||||
|
"risk_assessment": [{"level": "high", "label": "缺少当前用药记录,无法排除配伍风险"}],
|
||||||
|
"missing_information": ["舌象与脉诊仅有附件,无文本记录", "近期体重变化及 BMI 数据缺失"]},
|
||||||
|
}
|
||||||
|
models = {}
|
||||||
|
for key, doses in candidates.items():
|
||||||
|
herbs, rows = [], []
|
||||||
|
for name in sorted(set(doctor) | set(doses), key=lambda item: (item not in doses, item)):
|
||||||
|
common = {"name": name, "unit": "g", "dose_basis": "per_dose", "formula_type": "主方"}
|
||||||
|
left, right = doctor.get(name), doses.get(name)
|
||||||
|
if right is not None:
|
||||||
|
herbs.append({**common, "dosage": right})
|
||||||
|
rows.append({
|
||||||
|
**common, "key": name,
|
||||||
|
"doctor": {**common, "dosage": left} if left is not None else None,
|
||||||
|
"candidate": {**common, "dosage": right, "source_rows": [len(herbs) - 1]} if right is not None else None,
|
||||||
|
"doctor_dosage": left, "candidate_dosage": right,
|
||||||
|
"match_type": "candidate_only" if left is None else "doctor_only" if right is None else "matched",
|
||||||
|
"contribution": min(left, right) / max(left, right) if left and right else None,
|
||||||
|
})
|
||||||
|
matched = sum(row["match_type"] == "matched" for row in rows)
|
||||||
|
denominator = len(doctor) + len(herbs)
|
||||||
|
models[key] = {
|
||||||
|
"status": "success", "report_id": 10 if key == "qwen" else 11,
|
||||||
|
"candidate": {"status": "available_for_review", "prescription_name": "候选药方 · 界面示例",
|
||||||
|
"herbs": herbs, "dose_basis": "per_dose", "prescription_type": "浓缩水丸",
|
||||||
|
"usage_instruction": "服法由医生复核后确认。", "usage_days": 7, "times_per_day": 2,
|
||||||
|
"rationale": "这是用于检查界面排版的模拟药方,不对应真实患者。",
|
||||||
|
"risk_warnings": ["药味与剂量差异需要逐项复核。"]},
|
||||||
|
"comparison": {"status": "comparable",
|
||||||
|
"score": 200 * sum(row["contribution"] or 0 for row in rows) / denominator,
|
||||||
|
"herb_score": 200 * matched / denominator, "matched_count": matched,
|
||||||
|
"doctor_count": len(doctor), "candidate_count": len(herbs), "rows": rows,
|
||||||
|
"reason": "药味与剂量可比;服法与疗程需单独复核。", "usage_differences": []},
|
||||||
|
"report": reports[key],
|
||||||
|
"coverage": {"status": "incomplete", "complete": False,
|
||||||
|
"files": [{"file_id": f"a{index:04d}", "type": "image" if index % 3 else "document",
|
||||||
|
"status": "processed", "transmitted": True, "version_verified": True}
|
||||||
|
for index in range(20 if key == "qwen" else 17)]
|
||||||
|
+ [{"file_id": f"z{index:04d}", "type": "document", "status": "restricted",
|
||||||
|
"transmitted": False, "version_verified": False,
|
||||||
|
"reason": "FILE_UNAVAILABLE_OR_UNSUPPORTED"} for index in range(2)]},
|
||||||
|
"progress": {"stage_label": "处理完成", "elapsed_seconds": 135 if key == "qwen" else 591,
|
||||||
|
"attempt": 1},
|
||||||
|
"usage": {"total_calls": 3, "calls": [
|
||||||
|
{"stage": "text:0", "ok": True, "latency_ms": 3120, "file_count": 0,
|
||||||
|
"usage": {"completion_tokens": 980}, "error_code": ""},
|
||||||
|
{"stage": "files:0", "ok": True, "latency_ms": 22400,
|
||||||
|
"file_count": 20 if key == "qwen" else 17,
|
||||||
|
"usage": {"completion_tokens": 2140}, "error_code": ""},
|
||||||
|
{"stage": "final", "ok": True, "latency_ms": 18800, "file_count": 0,
|
||||||
|
"usage": {"completion_tokens": 5617}, "error_code": ""}]},
|
||||||
|
"review": {"status": "viewed", "comment": ""},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.1.0",
|
||||||
|
"prompt_version": "manual-prescription-required-candidate-v4",
|
||||||
|
}
|
||||||
|
return {"id": 40, "prescription_id": 7556, "patient_id": 1391, "diagnosis_id": 1391,
|
||||||
|
"prescription_revision": 1, "status": "success", "validity": "current",
|
||||||
|
"comparison_type": "non_independent", "models": models, "coverage_status": "partial",
|
||||||
|
"created_at": "2026-09-10 15:29:00", "cutoff_at": "2026-09-10 15:29:00",
|
||||||
|
"source_summary": {"diagnoses_count": 2, "attachment_count": 22, "video_calls_count": 4,
|
||||||
|
"chat_messages_count": 137, "source_record_count": 31},
|
||||||
|
"doctor_snapshot": {"patient": {"name": "张卫君", "gender": 2, "gender_label": "女", "age": 58},
|
||||||
|
"diagnosis": {"clinical_diagnosis": "2型糖尿病 消渴病 · 气阴两虚兼血热",
|
||||||
|
"chief_complaint": "咳嗽反复1月余"},
|
||||||
|
"prescription": {"herbs": [{"name": name, "dosage": dose, "unit": "g",
|
||||||
|
"dose_basis": "per_dose", "formula_type": "主方"}
|
||||||
|
for name, dose in doctor.items()],
|
||||||
|
"prescription_type": "浓缩水丸", "dose_count": 1,
|
||||||
|
"usage_instruction": "每日1剂,水煎分服。"}},
|
||||||
|
"missing": [{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True},
|
||||||
|
{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True},
|
||||||
|
{"code": "ARCHIVE_SYNC_WATERMARK_UNAVAILABLE", "critical": False}]}
|
||||||
|
|
||||||
|
|
||||||
|
def example_statistics() -> dict[str, Any]:
|
||||||
|
"""Doctor-level shape the statistics window renders; synthetic, but structurally complete."""
|
||||||
|
|
||||||
|
def doctor(identifier: int, name: str, totals: tuple[int, int, int],
|
||||||
|
qwen: tuple[int, float | None], openai: tuple[int, float | None],
|
||||||
|
review: tuple[int, int]) -> dict[str, Any]:
|
||||||
|
total, patients, paired = totals
|
||||||
|
models = {}
|
||||||
|
for key, (eligible, mean) in (("qwen", qwen), ("openai", openai)):
|
||||||
|
share = (0.34, 0.38, 0.18, 0.07, 0.03)
|
||||||
|
models[key] = {"eligible_count": eligible, "mean": mean,
|
||||||
|
"median": None if mean is None else round(mean - 1.4, 1),
|
||||||
|
"excluded_reasons": {"SOURCE_HISTORY_VERSIONS_UNAVAILABLE": max(0, total - eligible - 2),
|
||||||
|
"incomplete_coverage": min(2, max(0, total - eligible))},
|
||||||
|
"distribution": {name: round(eligible * fraction)
|
||||||
|
for name, fraction in zip(
|
||||||
|
("[0,20)", "[20,40)", "[40,60)", "[60,80)", "[80,100]"),
|
||||||
|
share, strict=True)}}
|
||||||
|
evaluated, qualified = review
|
||||||
|
return {"doctor_id": identifier, "doctor_name": name, "total_count": total,
|
||||||
|
"patient_count": patients, "paired_count": paired, "models": models,
|
||||||
|
"review": {"evaluated_count": evaluated, "qualified_count": qualified,
|
||||||
|
"qualified_rate": None if not evaluated else round(100 * qualified / evaluated, 1)}}
|
||||||
|
|
||||||
|
doctors = [doctor(26, "何医生", (18, 14, 9), (12, 21.4), (9, 18.9), (6, 4)),
|
||||||
|
doctor(31, "李医生", (11, 9, 4), (7, 26.8), (5, 24.1), (3, 1)),
|
||||||
|
doctor(44, "王医生", (6, 5, 1), (2, 15.2), (0, None), (0, 0))]
|
||||||
|
return {"total_count": sum(item["total_count"] for item in doctors),
|
||||||
|
"patient_count": sum(item["patient_count"] for item in doctors), "doctors": doctors}
|
||||||
|
|
||||||
|
|
||||||
|
class PreviewRepository:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.batch = example_batch()
|
||||||
|
|
||||||
|
def list_prescription_ai_reports(self, **_params: Any) -> dict[str, Any]:
|
||||||
|
return {"enabled": True, "lists": deepcopy(self.history()), "count": len(self.history())}
|
||||||
|
|
||||||
|
def history(self) -> list[dict[str, Any]]:
|
||||||
|
"""The current batch plus the earlier ones it supersedes, newest first."""
|
||||||
|
|
||||||
|
older = [
|
||||||
|
{"id": 39, "created_at": "2026-09-10 15:18:00", "status": "success", "validity": "superseded",
|
||||||
|
"comparison_type": "non_independent",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "comparable", "score": 48.9},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1", "prompt_version": "v3"},
|
||||||
|
"openai": {"comparison": {"status": "comparable", "score": 41.7},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1", "prompt_version": "v3"}}},
|
||||||
|
{"id": 38, "created_at": "2026-09-10 14:05:00", "status": "success", "validity": "superseded",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "comparable", "score": 33.4},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1", "prompt_version": "v3"},
|
||||||
|
"openai": {"comparison": {"status": "comparable", "score": 31.1},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1", "prompt_version": "v3"}}},
|
||||||
|
{"id": 37, "created_at": "2026-09-10 13:25:00", "status": "failed", "validity": "superseded",
|
||||||
|
"models": {"qwen": {"error_message": "模型返回未通过校验",
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1"},
|
||||||
|
"openai": {"comparison": {"status": "not_comparable"}}}},
|
||||||
|
{"id": 36, "created_at": "2026-09-10 12:34:00", "status": "success", "validity": "superseded",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "not_comparable"},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.0"},
|
||||||
|
"openai": {"comparison": {"status": "not_comparable"}}}},
|
||||||
|
]
|
||||||
|
return [self.batch, *older]
|
||||||
|
|
||||||
|
def get_prescription_ai_report(self, _batch_id: int) -> dict[str, Any]:
|
||||||
|
return deepcopy(self.batch)
|
||||||
|
|
||||||
|
def prescription_ai_statistics(self, *_args: Any, **_params: Any) -> dict[str, Any]:
|
||||||
|
return deepcopy(example_statistics())
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
application = QApplication.instance() or QApplication([])
|
||||||
|
apply_theme(application)
|
||||||
|
output = Path(__file__).resolve().parents[1] / "artifacts" / "issued_prescription_redesign"
|
||||||
|
output.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
def immediate(function: Any, **callbacks: Any) -> None:
|
||||||
|
result = function()
|
||||||
|
if callbacks.get("on_success"):
|
||||||
|
callbacks["on_success"](result)
|
||||||
|
if callbacks.get("on_finished"):
|
||||||
|
callbacks["on_finished"]()
|
||||||
|
|
||||||
|
ai.run_async = immediate
|
||||||
|
repository = PreviewRepository()
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=7556,
|
||||||
|
current_user={"name": "张医生"})
|
||||||
|
dialog.show()
|
||||||
|
names = {dialog.tabs.tabText(index): index for index in range(dialog.tabs.count())}
|
||||||
|
for filename, width, height, tab in (
|
||||||
|
("prescription-1440.png", 1440, 940, "对比总览"),
|
||||||
|
("prescription-1280.png", 1280, 860, "对比总览"),
|
||||||
|
("prescription-1024.png", 1024, 700, "对比总览"),
|
||||||
|
("prescription-940.png", 940, 640, "对比总览"),
|
||||||
|
("original-1280.png", 1280, 860, "原方记录"),
|
||||||
|
("analysis-1280.png", 1280, 860, "完整报告"),
|
||||||
|
("candidate-1280.png", 1280, 860, "方义与用法"),
|
||||||
|
("per-herb-1440.png", 1440, 940, "候选与逐味"),
|
||||||
|
("sources-1440.png", 1440, 940, "资料与缺口"),
|
||||||
|
("history-1440.png", 1440, 940, "历史与趋势"),
|
||||||
|
("pipeline-1440.png", 1440, 940, "处理进度"),
|
||||||
|
):
|
||||||
|
dialog.resize(width, height)
|
||||||
|
dialog.tabs.setCurrentIndex(names[tab])
|
||||||
|
for _ in range(4):
|
||||||
|
application.processEvents()
|
||||||
|
assert (dialog.width(), dialog.height()) == (width, height), (filename, dialog.size())
|
||||||
|
assert dialog.grab().save(str(output / filename))
|
||||||
|
print(filename, "window", width, height, "workspace", dialog.tabs.width(), dialog.tabs.height())
|
||||||
|
dialog.tabs.setCurrentIndex(names["完整报告"])
|
||||||
|
dialog._set_report_mode("differences")
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "report-differences-1440.png"))
|
||||||
|
dialog._set_report_mode("both")
|
||||||
|
dialog.tabs.setCurrentIndex(names["对比总览"])
|
||||||
|
dialog.resize(1280, 860)
|
||||||
|
for _ in range(4):
|
||||||
|
application.processEvents()
|
||||||
|
dialog.review_model.setCurrentIndex(1)
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "openai-1280.png"))
|
||||||
|
dialog.review_model.setCurrentIndex(0)
|
||||||
|
repository.batch["status"] = "running"
|
||||||
|
repository.batch["models"]["openai"].update(status="running", candidate=None, comparison=None, report=None)
|
||||||
|
dialog.refresh()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "partial-1280.png"))
|
||||||
|
repository.batch.update(validity="superseded", status="success")
|
||||||
|
dialog.refresh()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "historical-1280.png"))
|
||||||
|
repository.batch.update(validity="current", status="running")
|
||||||
|
repository.batch["models"]["qwen"].update(status="running", candidate=None, comparison=None, report=None)
|
||||||
|
dialog.refresh()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "pending-1280.png"))
|
||||||
|
# A failed model must state the reason and offer its own retry on the card itself.
|
||||||
|
repository.batch.update(validity="current", status="partial")
|
||||||
|
repository.batch["models"]["qwen"].update(
|
||||||
|
status="failed", error_code="upstream_timeout", error_message="上游模型超时,未返回结果",
|
||||||
|
candidate=None, comparison=None, report=None, retry_count=1, max_retry=3)
|
||||||
|
repository.batch["models"]["openai"] = deepcopy(example_batch()["models"]["openai"])
|
||||||
|
dialog.refresh()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.grab().save(str(output / "failed-1280.png"))
|
||||||
|
# The light theme is the same window with the other palette; capture it once.
|
||||||
|
dialog.resize(1440, 940)
|
||||||
|
dialog._switch_theme()
|
||||||
|
for _ in range(12):
|
||||||
|
application.processEvents()
|
||||||
|
dialog.repaint()
|
||||||
|
assert dialog.grab().save(str(output / "light-1440.png"))
|
||||||
|
dialog._switch_theme()
|
||||||
|
for _ in range(4):
|
||||||
|
application.processEvents()
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
statistics = ai.PrescriptionAiStatisticsDialog(repository, ["*"])
|
||||||
|
statistics.resize(1240, 880)
|
||||||
|
statistics.show()
|
||||||
|
for _ in range(4):
|
||||||
|
application.processEvents()
|
||||||
|
assert statistics.grab().save(str(output / "statistics-1240.png"))
|
||||||
|
statistics.close()
|
||||||
|
print("statistics-1240.png", statistics.panel.doctors.rowCount(), "doctors")
|
||||||
|
print(output)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
__all__ = ["DEBUG_MODE", "ONLINE_API_BASE_URL", "__version__"]
|
__all__ = ["DEBUG_MODE", "ONLINE_API_BASE_URL", "__version__"]
|
||||||
|
|
||||||
# Single source of truth for runtime, package, installer, and executable versions.
|
# Single source of truth for runtime, package, installer, and executable versions.
|
||||||
__version__ = "1.4.2"
|
__version__ = "1.4.5"
|
||||||
|
|
||||||
# 调试模式开启时,登录页显示“演示模式”和“服务器设置”。
|
# 调试模式开启时,登录页显示“演示模式”和“服务器设置”。
|
||||||
# 正式发布请保持 False;此时程序只使用下面配置的线上域名。
|
# 正式发布请保持 False;此时程序只使用下面配置的线上域名。
|
||||||
DEBUG_MODE = True
|
DEBUG_MODE = False
|
||||||
ONLINE_API_BASE_URL = "https://admin.zhenyangtang.com.cn"
|
ONLINE_API_BASE_URL = "https://admin.zhenyangtang.com.cn"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,375 @@
|
|||||||
|
"""Painted charts for the prescription analysis window, in the workstation's tech blue.
|
||||||
|
|
||||||
|
Every widget draws only what the saved report contains: a value that is missing stays visibly
|
||||||
|
absent instead of being drawn as zero, and no chart implies a medical judgement by colour.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtCore import QPointF, QRectF, QSize, Qt
|
||||||
|
from PySide6.QtGui import QColor, QFont, QPainter, QPaintEvent
|
||||||
|
from PySide6.QtWidgets import QSizePolicy, QWidget
|
||||||
|
|
||||||
|
from .issued_prescription_ai_theme import CONSOLE as TECH_BLUE
|
||||||
|
|
||||||
|
# Colour names resolve through the active palette at paint time, so a theme switch needs no
|
||||||
|
# rebuild here: the next repaint already draws in the new colours.
|
||||||
|
|
||||||
|
|
||||||
|
class _Palette:
|
||||||
|
"""Attribute access into the live palette: ``COLOUR.qwen`` is always the current hue."""
|
||||||
|
|
||||||
|
__slots__ = ("_keys",)
|
||||||
|
|
||||||
|
def __init__(self, **keys: str) -> None:
|
||||||
|
object.__setattr__(self, "_keys", dict(keys))
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> str:
|
||||||
|
return TECH_BLUE[self._keys[name]]
|
||||||
|
|
||||||
|
|
||||||
|
COLOUR = _Palette(doctor="muted", qwen="qwen", openai="openai", track="raised",
|
||||||
|
limited="amber", ink="heading", muted="muted")
|
||||||
|
|
||||||
|
|
||||||
|
def _mapping(value: Any) -> dict[str, Any]:
|
||||||
|
return dict(value) if isinstance(value, Mapping) else {}
|
||||||
|
|
||||||
|
|
||||||
|
def _count(value: Any) -> int:
|
||||||
|
if value is None or isinstance(value, bool):
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
number = int(float(value))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return 0
|
||||||
|
return max(0, number)
|
||||||
|
|
||||||
|
|
||||||
|
class DonutGauge(QWidget):
|
||||||
|
"""A ring reading one percentage. An unavailable value leaves the track empty, never zero."""
|
||||||
|
|
||||||
|
def __init__(self, accent: str, parent: QWidget | None = None, *, diameter: int = 88,
|
||||||
|
thickness: int = 11, track: str = COLOUR.track) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._accent, self._track = accent, track
|
||||||
|
self._thickness = thickness
|
||||||
|
self._value: float | None = None
|
||||||
|
self.setFixedSize(diameter, diameter)
|
||||||
|
self.setAccessibleName("一致度环形图")
|
||||||
|
self.set_value(None)
|
||||||
|
|
||||||
|
def set_value(self, value: Any) -> None:
|
||||||
|
parsed = None
|
||||||
|
try:
|
||||||
|
parsed = None if value is None or isinstance(value, bool) else float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
parsed = None
|
||||||
|
self._value = None if parsed is None or parsed < 0 or parsed > 100 else parsed
|
||||||
|
self.setAccessibleDescription("暂无可比结果" if self._value is None else f"{self._value:.1f}%")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def value(self) -> float | None:
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
inset = self._thickness / 2 + 1
|
||||||
|
box = QRectF(inset, inset, self.width() - 2 * inset, self.height() - 2 * inset)
|
||||||
|
pen = painter.pen()
|
||||||
|
pen.setWidthF(self._thickness)
|
||||||
|
pen.setCapStyle(Qt.PenCapStyle.FlatCap)
|
||||||
|
pen.setColor(QColor(self._track))
|
||||||
|
painter.setPen(pen)
|
||||||
|
painter.setBrush(Qt.BrushStyle.NoBrush)
|
||||||
|
painter.drawEllipse(box)
|
||||||
|
if self._value:
|
||||||
|
pen.setColor(QColor(self._accent))
|
||||||
|
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
|
||||||
|
painter.setPen(pen)
|
||||||
|
# Qt angles are sixteenths of a degree; start at twelve o'clock and run clockwise.
|
||||||
|
painter.drawArc(box, 90 * 16, -int(360 * 16 * self._value / 100))
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class VennChart(QWidget):
|
||||||
|
"""Doctor / model-A / model-B herb sets with their real intersection counts."""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._sets: dict[str, int] = {}
|
||||||
|
self._labels = ("医生原方", "千问", "OpenAI")
|
||||||
|
self.setMinimumHeight(96)
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
|
def set_counts(self, counts: Mapping[str, int], labels: tuple[str, str, str] | None = None) -> None:
|
||||||
|
"""Regions: doctor_only, qwen_only, openai_only, doctor_qwen, doctor_openai, qwen_openai, all."""
|
||||||
|
|
||||||
|
self._sets = {key: _count(value) for key, value in _mapping(counts).items()}
|
||||||
|
if labels:
|
||||||
|
self._labels = labels
|
||||||
|
total = sum(self._sets.values())
|
||||||
|
self.setAccessibleDescription(
|
||||||
|
"三方用药交集:" + " · ".join(f"{key} {value}" for key, value in self._sets.items()) if total
|
||||||
|
else "尚无可比较的候选药方")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def has_data(self) -> bool:
|
||||||
|
return any(self._sets.values())
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
if not self.has_data():
|
||||||
|
return
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
# The three set names sit above and below the circles, so their bands are reserved first.
|
||||||
|
top_band, bottom_band = 18.0, 18.0
|
||||||
|
available = max(40.0, self.height() - top_band - bottom_band)
|
||||||
|
side = min(self.width() * 0.62, available / 1.34)
|
||||||
|
radius = side / 2
|
||||||
|
offset = radius * 0.52
|
||||||
|
centre_x = self.width() / 2
|
||||||
|
centre_y = top_band + available / 2 - offset * 0.1
|
||||||
|
circles = (
|
||||||
|
(centre_x - offset, centre_y - offset * 0.55, COLOUR.doctor),
|
||||||
|
(centre_x + offset, centre_y - offset * 0.55, COLOUR.qwen),
|
||||||
|
(centre_x, centre_y + offset * 0.75, COLOUR.openai),
|
||||||
|
)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
for x, y, colour in circles:
|
||||||
|
fill = QColor(colour)
|
||||||
|
fill.setAlpha(52)
|
||||||
|
painter.setBrush(fill)
|
||||||
|
painter.drawEllipse(QPointF(x, y), radius, radius)
|
||||||
|
|
||||||
|
font = QFont(self.font())
|
||||||
|
font.setPixelSize(13)
|
||||||
|
font.setBold(True)
|
||||||
|
painter.setFont(font)
|
||||||
|
regions = (
|
||||||
|
("doctor_only", centre_x - offset * 1.5, centre_y - offset * 0.75, COLOUR.doctor),
|
||||||
|
("qwen_only", centre_x + offset * 1.5, centre_y - offset * 0.75, COLOUR.qwen),
|
||||||
|
("openai_only", centre_x, centre_y + offset * 1.45, COLOUR.openai),
|
||||||
|
("doctor_qwen", centre_x, centre_y - offset * 0.95, COLOUR.ink),
|
||||||
|
("doctor_openai", centre_x - offset * 0.85, centre_y + offset * 0.55, COLOUR.ink),
|
||||||
|
("qwen_openai", centre_x + offset * 0.85, centre_y + offset * 0.55, COLOUR.ink),
|
||||||
|
("all", centre_x, centre_y + offset * 0.1, COLOUR.ink),
|
||||||
|
)
|
||||||
|
for key, x, y, colour in regions:
|
||||||
|
value = self._sets.get(key, 0)
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
painter.setPen(QColor(colour))
|
||||||
|
painter.drawText(QRectF(x - 22, y - 10, 44, 20), Qt.AlignmentFlag.AlignCenter, str(value))
|
||||||
|
|
||||||
|
font.setPixelSize(11)
|
||||||
|
font.setBold(False)
|
||||||
|
painter.setFont(font)
|
||||||
|
painter.setPen(QColor(COLOUR.muted))
|
||||||
|
top = centre_y - offset * 0.55 - radius - 17
|
||||||
|
painter.setPen(QColor(COLOUR.doctor))
|
||||||
|
painter.drawText(QRectF(0, top, centre_x - 6, 16),
|
||||||
|
Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, self._labels[0])
|
||||||
|
painter.setPen(QColor(COLOUR.qwen))
|
||||||
|
painter.drawText(QRectF(centre_x + 6, top, centre_x - 6, 16),
|
||||||
|
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, self._labels[1])
|
||||||
|
painter.setPen(QColor(COLOUR.openai))
|
||||||
|
painter.drawText(QRectF(0, centre_y + offset * 0.75 + radius + 1, self.width(), 16),
|
||||||
|
Qt.AlignmentFlag.AlignCenter, self._labels[2])
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
def minimumSizeHint(self) -> QSize:
|
||||||
|
return QSize(200, 96)
|
||||||
|
|
||||||
|
|
||||||
|
class DivergingDoses(QWidget):
|
||||||
|
"""Per-herb dose difference against the doctor's prescription, one row per herb."""
|
||||||
|
|
||||||
|
ROW = 30
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._rows: list[dict[str, Any]] = []
|
||||||
|
self._span = 1.0
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
|
|
||||||
|
def set_rows(self, rows: list[Mapping[str, Any]]) -> None:
|
||||||
|
"""Each row: name, doctor, qwen, openai (floats or None), unit."""
|
||||||
|
|
||||||
|
self._rows = []
|
||||||
|
for row in rows:
|
||||||
|
value = _mapping(row)
|
||||||
|
entry = {"name": str(value.get("name") or ""), "unit": str(value.get("unit") or "")}
|
||||||
|
for key in ("doctor", "qwen", "openai"):
|
||||||
|
raw = value.get(key)
|
||||||
|
entry[key] = None if raw is None or isinstance(raw, bool) else float(raw)
|
||||||
|
self._rows.append(entry)
|
||||||
|
deltas = [abs((row[key] or 0) - (row["doctor"] or 0))
|
||||||
|
for row in self._rows for key in ("qwen", "openai")
|
||||||
|
if row[key] is not None and row["doctor"] is not None]
|
||||||
|
self._span = max(1.0, max(deltas, default=1.0))
|
||||||
|
self.setAccessibleDescription("剂量差异:" + " · ".join(
|
||||||
|
f"{row['name']} 千问 {self._delta_text(row, 'qwen')} OpenAI {self._delta_text(row, 'openai')}"
|
||||||
|
for row in self._rows) if self._rows else "暂无可比药味")
|
||||||
|
self.setMinimumHeight(self.ROW * max(1, len(self._rows)) + 18)
|
||||||
|
self.updateGeometry()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def _delta_text(self, row: Mapping[str, Any], key: str) -> str:
|
||||||
|
if row.get(key) is None or row.get("doctor") is None:
|
||||||
|
return "—"
|
||||||
|
delta = row[key] - row["doctor"]
|
||||||
|
return "一致" if abs(delta) < 1e-9 else f"{delta:+g}{row.get('unit') or ''}"
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
if not self._rows:
|
||||||
|
return
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
label_width, value_width = 96, 96
|
||||||
|
left = label_width + 10
|
||||||
|
right = self.width() - value_width - 10
|
||||||
|
middle = (left + right) / 2
|
||||||
|
scale = max(1.0, (right - left) / 2) / self._span
|
||||||
|
font = QFont(self.font())
|
||||||
|
font.setPixelSize(12)
|
||||||
|
painter.setFont(font)
|
||||||
|
for index, row in enumerate(self._rows):
|
||||||
|
top = index * self.ROW + 4
|
||||||
|
painter.setPen(QColor(COLOUR.ink))
|
||||||
|
painter.drawText(QRectF(0, top, label_width, self.ROW - 8),
|
||||||
|
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, row["name"])
|
||||||
|
painter.setPen(QColor(COLOUR.track))
|
||||||
|
painter.drawLine(QPointF(middle, top), QPointF(middle, top + self.ROW - 10))
|
||||||
|
for offset, key, colour in ((0, "qwen", COLOUR.qwen), (10, "openai", COLOUR.openai)):
|
||||||
|
if row[key] is None or row["doctor"] is None:
|
||||||
|
continue
|
||||||
|
delta = (row[key] - row["doctor"]) * scale
|
||||||
|
bar = QRectF(min(middle, middle + delta), top + offset, max(abs(delta), 2.0), 8)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawRoundedRect(bar, 3, 3)
|
||||||
|
painter.setPen(QColor(COLOUR.muted))
|
||||||
|
painter.drawText(QRectF(right + 8, top, value_width - 8, self.ROW - 8),
|
||||||
|
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
|
||||||
|
f"{self._delta_text(row, 'qwen')} / {self._delta_text(row, 'openai')}")
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class WaffleCoverage(QWidget):
|
||||||
|
"""One square per attachment: read, limited, or not delivered."""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None, *, columns: int = 11) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._columns = max(4, columns)
|
||||||
|
self._read = self._limited = self._total = 0
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
|
|
||||||
|
def set_counts(self, read: int, limited: int, total: int | None = None) -> None:
|
||||||
|
self._read, self._limited = _count(read), _count(limited)
|
||||||
|
self._total = max(_count(total), self._read + self._limited)
|
||||||
|
self.setAccessibleDescription(
|
||||||
|
f"附件 {self._total} 个:已读 {self._read},受限或不支持 {self._limited}" if self._total else "本次没有附件")
|
||||||
|
rows = max(1, -(-self._total // self._columns)) if self._total else 0
|
||||||
|
self.setMinimumHeight(rows * 16 + max(0, rows - 1) * 4)
|
||||||
|
self.updateGeometry()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def has_data(self) -> bool:
|
||||||
|
return self._total > 0
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
if not self._total:
|
||||||
|
return
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
gap = 4
|
||||||
|
size = max(8.0, min(16.0, (self.width() - gap * (self._columns - 1)) / self._columns))
|
||||||
|
for index in range(self._total):
|
||||||
|
column, row = index % self._columns, index // self._columns
|
||||||
|
colour = COLOUR.qwen if index < self._read else (COLOUR.limited if index < self._read + self._limited else COLOUR.track)
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawRoundedRect(QRectF(column * (size + gap), row * (size + gap), size, size), 4, 4)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class TrendBars(QWidget):
|
||||||
|
"""Grouped bars per batch: one column per model, oldest batch first."""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._points: list[dict[str, Any]] = []
|
||||||
|
self.setMinimumHeight(170)
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
|
|
||||||
|
def set_points(self, points: list[Mapping[str, Any]]) -> None:
|
||||||
|
"""Each point: label plus qwen/openai scores; None keeps the slot visibly empty."""
|
||||||
|
|
||||||
|
self._points = []
|
||||||
|
for point in points:
|
||||||
|
value = _mapping(point)
|
||||||
|
entry = {"label": str(value.get("label") or "")}
|
||||||
|
for key in ("qwen", "openai"):
|
||||||
|
raw = value.get(key)
|
||||||
|
entry[key] = None if raw is None or isinstance(raw, bool) else float(raw)
|
||||||
|
self._points.append(entry)
|
||||||
|
described = [f"{entry['label']} 千问 {self._text(entry['qwen'])} OpenAI {self._text(entry['openai'])}"
|
||||||
|
for entry in self._points]
|
||||||
|
self.setAccessibleDescription("一致度趋势:" + (" · ".join(described) or "暂无批次"))
|
||||||
|
self.setToolTip("\n".join(described) or "暂无批次")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _text(value: float | None) -> str:
|
||||||
|
return "—" if value is None else f"{value:.1f}%"
|
||||||
|
|
||||||
|
def has_data(self) -> bool:
|
||||||
|
return any(point.get(key) is not None for point in self._points for key in ("qwen", "openai"))
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
if not self._points:
|
||||||
|
return
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
# The top band is left free so each bar can print its own figure above it.
|
||||||
|
left, right, top, bottom = 44.0, self.width() - 10.0, 24.0, self.height() - 24.0
|
||||||
|
values = [point[key] for point in self._points for key in ("qwen", "openai") if point[key] is not None]
|
||||||
|
ceiling = max(10.0, max(values, default=10.0))
|
||||||
|
font = QFont(self.font())
|
||||||
|
font.setPixelSize(10)
|
||||||
|
painter.setFont(font)
|
||||||
|
for fraction in (0.0, 0.5, 1.0):
|
||||||
|
y = bottom - (bottom - top) * fraction
|
||||||
|
painter.setPen(QColor(COLOUR.track))
|
||||||
|
painter.drawLine(QPointF(left, y), QPointF(right, y))
|
||||||
|
painter.setPen(QColor(COLOUR.muted))
|
||||||
|
painter.drawText(QRectF(0, y - 8, left - 6, 16),
|
||||||
|
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
|
||||||
|
f"{ceiling * fraction:.0f}%")
|
||||||
|
slot = (right - left) / max(1, len(self._points))
|
||||||
|
width = min(26.0, slot / 3.6)
|
||||||
|
# The two bars of a batch sit side by side, far enough apart for each figure to fit above.
|
||||||
|
gap = width / 2 + 4
|
||||||
|
for index, point in enumerate(self._points):
|
||||||
|
centre = left + slot * (index + 0.5)
|
||||||
|
for offset, key, colour in ((-gap, "qwen", COLOUR.qwen), (gap, "openai", COLOUR.openai)):
|
||||||
|
value = point[key]
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
height = (bottom - top) * min(1.0, value / ceiling)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawRoundedRect(QRectF(centre + offset - width / 2, bottom - height, width, height), 3, 3)
|
||||||
|
# The design prints each figure above its bar, in that model's own colour.
|
||||||
|
painter.setPen(QColor(colour))
|
||||||
|
painter.drawText(QRectF(centre + offset - 21, bottom - height - 17, 42, 14),
|
||||||
|
Qt.AlignmentFlag.AlignCenter, f"{value:.1f}%")
|
||||||
|
painter.setPen(QColor(COLOUR.muted))
|
||||||
|
painter.drawText(QRectF(centre - slot / 2, bottom + 4, slot, 16),
|
||||||
|
Qt.AlignmentFlag.AlignCenter, point["label"])
|
||||||
|
painter.end()
|
||||||
@@ -0,0 +1,644 @@
|
|||||||
|
"""Native, read-only prescription comparison using saved report snapshots only."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from copy import deepcopy
|
||||||
|
from dataclasses import dataclass, replace
|
||||||
|
from decimal import Decimal, InvalidOperation
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtCore import QRectF, QSize, Qt
|
||||||
|
from PySide6.QtGui import QColor, QFont, QPainter, QPaintEvent
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QAbstractItemView,
|
||||||
|
QButtonGroup,
|
||||||
|
QFrame,
|
||||||
|
QHBoxLayout,
|
||||||
|
QHeaderView,
|
||||||
|
QLabel,
|
||||||
|
QLineEdit,
|
||||||
|
QPushButton,
|
||||||
|
QScrollArea,
|
||||||
|
QSizePolicy,
|
||||||
|
QTableWidget,
|
||||||
|
QTableWidgetItem,
|
||||||
|
QVBoxLayout,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .issued_prescription_ai_labels import EXTRA_FIELD_LABELS, SYSTEM_LABELS, system_text
|
||||||
|
from .issued_prescription_ai_progress import ACTIVE_STATES, SUCCESS_STATES
|
||||||
|
|
||||||
|
MODEL_NAMES = {"qwen": "千问", "openai": "OpenAI"}
|
||||||
|
MODEL_COLORS = {"qwen": "#3676C8", "openai": "#268578"}
|
||||||
|
DOCTOR_COLOR = "#526479"
|
||||||
|
INK, MUTED, LINE = "#23384C", "#758395", "#E6EDF3"
|
||||||
|
_UNITS = {
|
||||||
|
"g": "克", "克": "克", "mg": "毫克", "毫克": "毫克", "kg": "千克", "千克": "千克",
|
||||||
|
"ml": "毫升", "毫升": "毫升", "l": "升", "升": "升", "iu": "国际单位", "国际单位": "国际单位",
|
||||||
|
**{unit: unit for unit in ("片", "丸", "粒", "袋", "包", "支", "滴", "枚", "个", "条", "付", "帖", "两", "钱")},
|
||||||
|
}
|
||||||
|
_BASES = {"per_dose": "每剂", "每剂": "每剂", "per_day": "每日", "每日": "每日", "每天": "每日"}
|
||||||
|
_FORMULAS = {"main": "主方", "1": "主方", "主方": "主方", "aux": "辅方", "auxiliary": "辅方", "2": "辅方", "辅方": "辅方"}
|
||||||
|
_STALE = {
|
||||||
|
"stale": "处方已变更", "superseded": "处方已变更", "prescription_changed": "处方已变更",
|
||||||
|
"source_updated": "资料已更新", "invalid": "报告已失效", "voided": "处方已作废",
|
||||||
|
"deleted": "处方已删除", "revoked": "资料权限已变更",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _mapping(value: Any) -> dict[str, Any]:
|
||||||
|
return dict(value) if isinstance(value, Mapping) else {}
|
||||||
|
|
||||||
|
|
||||||
|
def _text(value: Any) -> str:
|
||||||
|
"""Never stringify a structured payload, which could expose internal identifiers."""
|
||||||
|
return str(value).strip() if isinstance(value, (str, int, float, Decimal)) and not isinstance(value, bool) else ""
|
||||||
|
|
||||||
|
|
||||||
|
def _reason(value: Any) -> str:
|
||||||
|
if isinstance(value, Mapping):
|
||||||
|
return _reason(value.get("reason") or value.get("message") or value.get("code"))
|
||||||
|
if isinstance(value, list):
|
||||||
|
return ";".join(filter(None, (_reason(item) for item in value)))
|
||||||
|
return system_text(value, SYSTEM_LABELS, EXTRA_FIELD_LABELS, strict=True) if _text(value) else ""
|
||||||
|
|
||||||
|
|
||||||
|
def _number(value: Any) -> Decimal | None:
|
||||||
|
if not _text(value):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
number = Decimal(str(value))
|
||||||
|
except (InvalidOperation, ValueError):
|
||||||
|
return None
|
||||||
|
return number if number.is_finite() and number >= 0 else None
|
||||||
|
|
||||||
|
|
||||||
|
def _formula(value: Any) -> str:
|
||||||
|
return _FORMULAS.get(_text(value), "主辅方未注明")
|
||||||
|
|
||||||
|
|
||||||
|
def _metadata(value: Any) -> str:
|
||||||
|
return system_text(value, SYSTEM_LABELS, EXTRA_FIELD_LABELS, strict=True) if _text(value) else ""
|
||||||
|
|
||||||
|
|
||||||
|
def _join_instructions(values: list[str]) -> str:
|
||||||
|
parts = [part.strip() for value in values for part in value.split(";")]
|
||||||
|
return ";".join(dict.fromkeys(part for part in parts if part and part.lower() not in {"无", "明确无", "none"}))
|
||||||
|
|
||||||
|
|
||||||
|
def _instructions(snapshot: dict[str, Any]) -> str:
|
||||||
|
"""Read both original herb fields and the service's normalized usage snapshot."""
|
||||||
|
fields = ("instructions", "decoction_instruction", "special_usage", "usage_instruction", "usage_time", "usage_way")
|
||||||
|
usage = _mapping(snapshot.get("usage"))
|
||||||
|
values = [_text(owner.get(field)) for owner in (snapshot, usage) for field in fields]
|
||||||
|
if not usage:
|
||||||
|
values.append(_text(snapshot.get("usage")))
|
||||||
|
return _join_instructions(values)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Dose:
|
||||||
|
raw: Any
|
||||||
|
unit: str
|
||||||
|
basis: str
|
||||||
|
formula: str
|
||||||
|
processing: str
|
||||||
|
route: str
|
||||||
|
group: str
|
||||||
|
instructions: str = ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def number(self) -> Decimal | None:
|
||||||
|
return _number(self.raw)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scale(self) -> tuple[str, str] | None:
|
||||||
|
unit, basis = _UNITS.get(self.unit.lower()), _BASES.get(self.basis)
|
||||||
|
return (unit, basis) if unit and basis else None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def label(self) -> str:
|
||||||
|
amount = _text(self.raw)
|
||||||
|
if not amount:
|
||||||
|
return "—"
|
||||||
|
unit = _UNITS.get(self.unit.lower()) or _metadata(self.unit) or "单位未注明"
|
||||||
|
basis = _BASES.get(self.basis) or "基准未确认"
|
||||||
|
return f"{amount} {unit} / {basis}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identity(self) -> tuple[str, str, str, str]:
|
||||||
|
# Unknown enum values must remain distinct even when their display label is generic.
|
||||||
|
return (_FORMULAS.get(self.formula, self.formula), SYSTEM_LABELS.get(self.processing, self.processing), SYSTEM_LABELS.get(self.route, self.route), self.group)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Row:
|
||||||
|
name: str
|
||||||
|
context: str
|
||||||
|
doctor: _Dose | None
|
||||||
|
candidate: _Dose | None
|
||||||
|
match_type: str
|
||||||
|
origin: str = "comparison"
|
||||||
|
source_details: str = ""
|
||||||
|
source_names: tuple[str, ...] = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scale(self) -> tuple[str, str] | None:
|
||||||
|
if self.origin != "comparison":
|
||||||
|
return None
|
||||||
|
doses = [dose for dose in (self.doctor, self.candidate) if dose is not None]
|
||||||
|
if not doses or any(dose.scale is None for dose in doses):
|
||||||
|
return None
|
||||||
|
if len({dose.scale for dose in doses}) != 1 or len({dose.identity for dose in doses}) != 1:
|
||||||
|
return None
|
||||||
|
return doses[0].scale
|
||||||
|
|
||||||
|
@property
|
||||||
|
def incompatibility(self) -> str:
|
||||||
|
if self.origin == "uncompared":
|
||||||
|
return "未纳入对比,仅保留候选原方;医生剂量未知"
|
||||||
|
if self.origin == "original":
|
||||||
|
return "候选原方附列;对应关系未保存,不推断同药"
|
||||||
|
if self.doctor is not None and self.candidate is not None:
|
||||||
|
if self.doctor.identity != self.candidate.identity:
|
||||||
|
return "主辅方、炮制或给药分组不同,不作条形比较"
|
||||||
|
if self.doctor.scale and self.candidate.scale and self.doctor.scale != self.candidate.scale:
|
||||||
|
return "单位或剂量基准不同,不作条形比较"
|
||||||
|
return "单位或每剂/每日基准未明确,不作条形比较"
|
||||||
|
|
||||||
|
def usage_description(self, model_name: str) -> str:
|
||||||
|
return ";".join(filter(None, ("医生:" + self.doctor.instructions if self.doctor and self.doctor.instructions else "", model_name + ":" + self.candidate.instructions if self.candidate and self.candidate.instructions else "")))
|
||||||
|
|
||||||
|
def description(self, model_name: str) -> str:
|
||||||
|
dosage = f"{self.name},{self.context};医生:{self.doctor.label if self.doctor else '—'};{model_name}:{self.candidate.label if self.candidate else '—'}"
|
||||||
|
return ";".join(filter(None, (dosage, self.usage_description(model_name), self.source_details)))
|
||||||
|
|
||||||
|
|
||||||
|
def _dose(row: dict[str, Any], side: str) -> _Dose | None:
|
||||||
|
# An explicit null snapshot takes precedence over contradictory legacy flat fields.
|
||||||
|
if side in row and row[side] is None:
|
||||||
|
return None
|
||||||
|
nested = _mapping(row.get(side))
|
||||||
|
keys = ("doctor_dosage", "doctor_dose") if side == "doctor" else ("candidate_dosage", "candidate_dose", "ai_dose")
|
||||||
|
raw = nested.get("dosage") if "dosage" in nested else next((row[key] for key in keys if key in row), None)
|
||||||
|
if not nested and raw is None:
|
||||||
|
return None
|
||||||
|
values = {key: _text(nested[key] if key in nested else row.get(key)) for key in ("unit", "dose_basis", "formula_type", "processing", "administration_route", "group")}
|
||||||
|
instructions = _instructions(nested) or _instructions(row)
|
||||||
|
return _Dose(raw, values["unit"], values["dose_basis"], values["formula_type"], values["processing"], values["administration_route"], values["group"], instructions)
|
||||||
|
|
||||||
|
|
||||||
|
def _row(value: dict[str, Any]) -> _Row:
|
||||||
|
doctor, candidate = _dose(value, "doctor"), _dose(value, "candidate")
|
||||||
|
details = []
|
||||||
|
for dose in (doctor, candidate):
|
||||||
|
if dose is not None:
|
||||||
|
detail = " · ".join(filter(None, (_formula(dose.formula), _metadata(dose.processing), _metadata(dose.route), _metadata(dose.group))))
|
||||||
|
if detail not in details:
|
||||||
|
details.append(detail)
|
||||||
|
context = " / ".join(details) or _formula(value.get("formula_type"))
|
||||||
|
if len(details) > 1:
|
||||||
|
context = "医生与模型:" + context
|
||||||
|
return _Row(_text(value.get("name") or value.get("canonical_name") or value.get("herb_name")) or "药名未保存", context, doctor, candidate, _text(value.get("match_type")))
|
||||||
|
|
||||||
|
|
||||||
|
def _original_row(herb: Any, *, origin: str) -> _Row:
|
||||||
|
saved = dict(herb) if isinstance(herb, Mapping) else {"name": _text(herb) or "药材记录需核对"}
|
||||||
|
row = _row({"name": saved.get("name"), "doctor": None, "candidate": saved, "match_type": "candidate_only"})
|
||||||
|
note = "未纳入对比" if origin == "uncompared" else "候选原方附列 · 对应关系未保存"
|
||||||
|
return replace(row, origin=origin, context=note + " · " + row.context)
|
||||||
|
|
||||||
|
|
||||||
|
def _saved_rows(candidate: dict[str, Any], comparison: dict[str, Any]) -> list[_Row]:
|
||||||
|
saved = comparison.get("rows")
|
||||||
|
comparison_rows = [dict(value) for value in saved if isinstance(value, Mapping)] if isinstance(saved, list) else []
|
||||||
|
herbs = candidate.get("herbs")
|
||||||
|
herbs = herbs if isinstance(herbs, list) else []
|
||||||
|
result: list[_Row] = []
|
||||||
|
covered: set[int] = set()
|
||||||
|
missing_correspondence = False
|
||||||
|
for value in comparison_rows:
|
||||||
|
row = _row(value)
|
||||||
|
if row.candidate is not None and herbs:
|
||||||
|
indices = _mapping(value.get("candidate")).get("source_rows")
|
||||||
|
# The service uses zero-based array_values indices; never infer correspondence
|
||||||
|
# from names, doses, order, or the number of normalized rows.
|
||||||
|
valid_trace = isinstance(indices, list) and bool(indices) and all(isinstance(index, int) and not isinstance(index, bool) and 0 <= index < len(herbs) for index in indices)
|
||||||
|
if valid_trace:
|
||||||
|
indices = list(dict.fromkeys(indices))
|
||||||
|
covered.update(indices)
|
||||||
|
originals = [_original_row(herbs[index], origin="original") for index in indices]
|
||||||
|
details = ";".join(f"第 {index + 1} 项 {original.name} {original.candidate.label if original.candidate else '—'}" + ("," + original.candidate.instructions if original.candidate and original.candidate.instructions else "") for index, original in zip(indices, originals, strict=True))
|
||||||
|
instructions = _join_instructions([row.candidate.instructions, *[original.candidate.instructions for original in originals if original.candidate]])
|
||||||
|
row = replace(row, candidate=replace(row.candidate, instructions=instructions), source_details="候选原方记录:" + details, source_names=tuple(original.name for original in originals))
|
||||||
|
else:
|
||||||
|
missing_correspondence = True
|
||||||
|
result.append(row)
|
||||||
|
# Normalization can omit unknown names, processing conflicts, or incompatible duplicate
|
||||||
|
# entries. Keep every unaccounted original, but do not claim its doctor counterpart.
|
||||||
|
origin = "original" if missing_correspondence else "uncompared"
|
||||||
|
result.extend(_original_row(herb, origin=origin) for index, herb in enumerate(herbs) if index not in covered)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class _DoseChart(QWidget):
|
||||||
|
"""A scrollable painted chart; the adjacent table provides native accessibility."""
|
||||||
|
|
||||||
|
ROW_HEIGHT = 80
|
||||||
|
GROUP_HEIGHT = 38
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("PrescriptionDoseChart")
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
|
||||||
|
self.setAccessibleName("药方逐味剂量对比图")
|
||||||
|
self.rows: list[_Row] = []
|
||||||
|
self.groups: list[tuple[tuple[str, str] | None, list[_Row], Decimal]] = []
|
||||||
|
self.model_key = "qwen"
|
||||||
|
self.message = "暂无药方数据"
|
||||||
|
self.bars_enabled = False
|
||||||
|
|
||||||
|
def set_rows(self, rows: list[_Row], model_key: str, *, bars_enabled: bool, message: str) -> None:
|
||||||
|
self.rows, self.model_key, self.bars_enabled, self.message = rows, model_key, bars_enabled, message
|
||||||
|
grouped: dict[tuple[str, str] | None, list[_Row]] = {}
|
||||||
|
for row in rows:
|
||||||
|
grouped.setdefault(row.scale if bars_enabled else None, []).append(row)
|
||||||
|
self.groups = []
|
||||||
|
for scale, members in grouped.items():
|
||||||
|
numbers = [dose.number for row in members for dose in (row.doctor, row.candidate) if dose is not None and dose.number is not None]
|
||||||
|
self.groups.append((scale, members, max(numbers, default=Decimal(0))))
|
||||||
|
color_name = "蓝色" if model_key == "qwen" else "青绿色"
|
||||||
|
descriptions = [message, f"医生为深灰蓝;{MODEL_NAMES[model_key]}为{color_name}。各单位与基准组独立标尺,组间长度不可比较。缺失值为—,不按零计算。"]
|
||||||
|
for row in rows:
|
||||||
|
descriptions.append(row.description(MODEL_NAMES[model_key]))
|
||||||
|
if bars_enabled and row.scale is None:
|
||||||
|
descriptions.append(row.incompatibility)
|
||||||
|
if any(dose is not None and _text(dose.raw) and dose.number is None for dose in (row.doctor, row.candidate)):
|
||||||
|
descriptions.append("非数值、负数及非有限剂量保留原值,未绘制条形。")
|
||||||
|
self.setAccessibleDescription("\n".join(descriptions))
|
||||||
|
height = 16 + sum(self.GROUP_HEIGHT + len(members) * self.ROW_HEIGHT for _, members, _ in self.groups)
|
||||||
|
self.setMinimumHeight(max(120, height))
|
||||||
|
self.updateGeometry()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def sizeHint(self) -> QSize:
|
||||||
|
return QSize(440, self.minimumHeight())
|
||||||
|
|
||||||
|
def minimumSizeHint(self) -> QSize:
|
||||||
|
return QSize(0, 0)
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||||
|
painter.fillRect(self.rect(), QColor("#FFFFFF"))
|
||||||
|
painter.setFont(self.font())
|
||||||
|
if not self.rows:
|
||||||
|
painter.setPen(QColor(MUTED))
|
||||||
|
painter.drawText(self.rect().adjusted(24, 12, -24, -12), Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap, self.message)
|
||||||
|
return
|
||||||
|
width, y = max(0, self.width() - 32), 8
|
||||||
|
normal = QFont(self.font())
|
||||||
|
bold = QFont(normal)
|
||||||
|
bold.setBold(True)
|
||||||
|
for scale, members, maximum in self.groups:
|
||||||
|
painter.fillRect(QRectF(16, y, width, self.GROUP_HEIGHT - 8), QColor("#F3F6F9"))
|
||||||
|
painter.setFont(bold)
|
||||||
|
painter.setPen(QColor(INK))
|
||||||
|
heading = f"{scale[0]} · {scale[1]} 独立标尺 0–{maximum}" if scale else "原始剂量 · 不作条形比较"
|
||||||
|
painter.drawText(QRectF(24, y, max(0, width - 16), self.GROUP_HEIGHT - 8), Qt.AlignmentFlag.AlignVCenter, painter.fontMetrics().elidedText(heading, Qt.TextElideMode.ElideRight, max(0, width - 16)))
|
||||||
|
y += self.GROUP_HEIGHT
|
||||||
|
for row in members:
|
||||||
|
if y + self.ROW_HEIGHT >= event.rect().top() and y <= event.rect().bottom():
|
||||||
|
painter.setFont(bold)
|
||||||
|
painter.setPen(QColor(INK))
|
||||||
|
title = f"{row.name} · {row.context}"
|
||||||
|
painter.drawText(QRectF(16, y, width, 21), Qt.AlignmentFlag.AlignVCenter, painter.fontMetrics().elidedText(title, Qt.TextElideMode.ElideRight, width))
|
||||||
|
painter.setFont(normal)
|
||||||
|
if scale is None:
|
||||||
|
painter.setPen(QColor(MUTED))
|
||||||
|
detail = ";".join(("医生 " + (row.doctor.label if row.doctor else "—"), MODEL_NAMES[self.model_key] + " " + (row.candidate.label if row.candidate else "—")))
|
||||||
|
painter.drawText(QRectF(16, y + 24, width, 22), Qt.AlignmentFlag.AlignVCenter, painter.fontMetrics().elidedText(detail, Qt.TextElideMode.ElideRight, width))
|
||||||
|
note = row.incompatibility if self.bars_enabled else "按已保存原值列示,详见左侧药材表"
|
||||||
|
painter.drawText(QRectF(16, y + 49, width, 22), Qt.AlignmentFlag.AlignVCenter, painter.fontMetrics().elidedText(note, Qt.TextElideMode.ElideRight, width))
|
||||||
|
else:
|
||||||
|
for index, (dose, color, name) in enumerate(((row.doctor, DOCTOR_COLOR, "医生"), (row.candidate, MODEL_COLORS[self.model_key], MODEL_NAMES[self.model_key]))):
|
||||||
|
bar_y = y + 24 + index * 20
|
||||||
|
painter.setPen(QColor(color))
|
||||||
|
painter.drawText(QRectF(16, bar_y - 5, 54, 22), Qt.AlignmentFlag.AlignVCenter, name)
|
||||||
|
value = dose.label if dose else "—"
|
||||||
|
label_width = min(max(118, painter.fontMetrics().horizontalAdvance(value) + 8), max(118, width // 2))
|
||||||
|
bar_x, bar_width = 76, max(8, width - 66 - label_width - 12)
|
||||||
|
if dose is not None and dose.number is not None:
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor("#F0F3F7"))
|
||||||
|
painter.drawRoundedRect(QRectF(bar_x, bar_y, bar_width, 9), 3, 3)
|
||||||
|
fraction = float(dose.number / maximum) if maximum else 0.0
|
||||||
|
if fraction > 0:
|
||||||
|
painter.setBrush(QColor(color))
|
||||||
|
painter.drawRoundedRect(QRectF(bar_x, bar_y, bar_width * fraction, 9), 3, 3)
|
||||||
|
painter.setPen(QColor(INK))
|
||||||
|
painter.drawText(QRectF(bar_x + bar_width + 10, bar_y - 5, label_width, 22), Qt.AlignmentFlag.AlignVCenter, painter.fontMetrics().elidedText(value, Qt.TextElideMode.ElideRight, int(label_width)))
|
||||||
|
painter.setPen(QColor(LINE))
|
||||||
|
painter.drawLine(16, y + self.ROW_HEIGHT - 6, self.width() - 16, y + self.ROW_HEIGHT - 6)
|
||||||
|
y += self.ROW_HEIGHT
|
||||||
|
|
||||||
|
|
||||||
|
class PrescriptionComparisonPanel(QWidget):
|
||||||
|
"""Switch between saved candidate prescriptions without issuing any requests."""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("PrescriptionComparisonPanel")
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||||
|
self._batch: dict[str, Any] = {}
|
||||||
|
self._rows: list[_Row] = []
|
||||||
|
self._model_key = "qwen"
|
||||||
|
self._bars_enabled = False
|
||||||
|
self._chart_message = "暂无药方数据"
|
||||||
|
self._candidate_count: int | None = None
|
||||||
|
self._updating_rows = False
|
||||||
|
self.setStyleSheet(f"""
|
||||||
|
QWidget#PrescriptionComparisonPanel {{ background: transparent; }}
|
||||||
|
QFrame#PrescriptionPane {{ background: white; border: 1px solid {LINE}; border-radius: 9px; }}
|
||||||
|
QLabel {{ color: {INK}; background: transparent; }}
|
||||||
|
QLabel#PrescriptionTitle {{ font-size: 20px; font-weight: 700; }}
|
||||||
|
QLabel#PrescriptionCaption {{ color: {MUTED}; font-size: 12px; }}
|
||||||
|
QLabel#PrescriptionStatus {{ color: #5F7287; font-size: 12px; }}
|
||||||
|
QPushButton#PrescriptionModel {{ border: 1px solid #DAE4EE; background: white; color: #607388;
|
||||||
|
border-radius: 6px; padding: 6px 19px; font-weight: 600; }}
|
||||||
|
QPushButton#PrescriptionModel[model="qwen"]:checked {{ background: #EDF4FE; color: #3676C8; border-color: #AAC7EB; }}
|
||||||
|
QPushButton#PrescriptionModel[model="openai"]:checked {{ background: #EDF7F4; color: #268578; border-color: #A5D2C6; }}
|
||||||
|
QLineEdit#PrescriptionSearch {{ background: white; border: 1px solid #DAE4EE; border-radius: 6px; padding: 6px 10px; }}
|
||||||
|
QTableWidget#PrescriptionHerbs {{ background: white; border: 0; color: {INK}; gridline-color: {LINE}; }}
|
||||||
|
QTableWidget#PrescriptionHerbs::item {{ padding: 4px 7px; border-bottom: 1px solid #EDF1F5; }}
|
||||||
|
QTableWidget#PrescriptionHerbs::item:selected {{ background: #EDF4FC; color: {INK}; }}
|
||||||
|
QHeaderView::section {{ background: #F4F7FA; color: #687C91; border: 0; padding: 7px; font-weight: 600; }}
|
||||||
|
QScrollArea {{ background: white; border: 0; }}
|
||||||
|
""")
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(10, 8, 10, 8)
|
||||||
|
layout.setSpacing(10)
|
||||||
|
toolbar = QHBoxLayout()
|
||||||
|
toolbar.setSpacing(7)
|
||||||
|
self.model_buttons: dict[str, QPushButton] = {}
|
||||||
|
self.model_group = QButtonGroup(self)
|
||||||
|
for model_key, name in MODEL_NAMES.items():
|
||||||
|
button = QPushButton(name, self)
|
||||||
|
button.setObjectName("PrescriptionModel")
|
||||||
|
button.setProperty("model", model_key)
|
||||||
|
button.setCheckable(True)
|
||||||
|
button.setChecked(model_key == self._model_key)
|
||||||
|
button.setAccessibleName(f"查看{name}候选方与医生方对比")
|
||||||
|
button.clicked.connect(lambda _checked=False, key=model_key: self._select_model(key))
|
||||||
|
self.model_group.addButton(button)
|
||||||
|
self.model_buttons[model_key] = button
|
||||||
|
toolbar.addWidget(button)
|
||||||
|
toolbar.addStretch(1)
|
||||||
|
self.search = QLineEdit(self)
|
||||||
|
self.search.setObjectName("PrescriptionSearch")
|
||||||
|
self.search.setPlaceholderText("搜索药名")
|
||||||
|
self.search.setAccessibleName("搜索药名,同时筛选药材表和图表")
|
||||||
|
self.search.setClearButtonEnabled(True)
|
||||||
|
self.search.setMaximumWidth(240)
|
||||||
|
self.search.textChanged.connect(self._filter_rows)
|
||||||
|
toolbar.addWidget(self.search)
|
||||||
|
layout.addLayout(toolbar)
|
||||||
|
panes = QHBoxLayout()
|
||||||
|
panes.setSpacing(12)
|
||||||
|
left = QFrame(self)
|
||||||
|
left.setObjectName("PrescriptionPane")
|
||||||
|
left.setMinimumWidth(0)
|
||||||
|
left.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding)
|
||||||
|
left_layout = QVBoxLayout(left)
|
||||||
|
left_layout.setContentsMargins(15, 13, 15, 10)
|
||||||
|
left_layout.setSpacing(6)
|
||||||
|
self.caption_label = self._label("千问 · 候选药方", left, "PrescriptionCaption")
|
||||||
|
self.name_label = self._label("暂无候选药方", left, "PrescriptionTitle")
|
||||||
|
self.name_label.setWordWrap(True)
|
||||||
|
self.name_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
|
||||||
|
self.usage_label = self._label("", left, "PrescriptionCaption")
|
||||||
|
self.usage_label.setWordWrap(True)
|
||||||
|
left_layout.addWidget(self.caption_label)
|
||||||
|
left_layout.addWidget(self.name_label)
|
||||||
|
left_layout.addWidget(self.usage_label)
|
||||||
|
self.herb_table = QTableWidget(0, 3, left)
|
||||||
|
self.herb_table.setObjectName("PrescriptionHerbs")
|
||||||
|
self.herb_table.setAccessibleName("候选药方药材与医生剂量对照表")
|
||||||
|
self.herb_table.setHorizontalHeaderLabels(["药材 / 主辅方", "医生剂量", "千问剂量"])
|
||||||
|
self.herb_table.verticalHeader().hide()
|
||||||
|
self.herb_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||||||
|
self.herb_table.horizontalHeader().setMinimumSectionSize(36)
|
||||||
|
self.herb_table.setShowGrid(False)
|
||||||
|
self.herb_table.setWordWrap(True)
|
||||||
|
self.herb_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||||
|
self.herb_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||||
|
self.herb_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
|
self.herb_table.itemSelectionChanged.connect(self._focus_chart_row)
|
||||||
|
self.herb_table.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
|
self.herb_table.setMinimumSize(0, 0)
|
||||||
|
self.herb_table.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding)
|
||||||
|
left_layout.addWidget(self.herb_table, 1)
|
||||||
|
self.empty_label = self._label("选择报告后显示已保存的候选药方", left, "PrescriptionCaption")
|
||||||
|
self.empty_label.setWordWrap(True)
|
||||||
|
left_layout.addWidget(self.empty_label)
|
||||||
|
self.count_label = self._label("", left, "PrescriptionCaption")
|
||||||
|
self.count_label.setWordWrap(True)
|
||||||
|
left_layout.addWidget(self.count_label)
|
||||||
|
panes.addWidget(left, 5)
|
||||||
|
right = QFrame(self)
|
||||||
|
right.setObjectName("PrescriptionPane")
|
||||||
|
right.setMinimumWidth(0)
|
||||||
|
right.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding)
|
||||||
|
right_layout = QVBoxLayout(right)
|
||||||
|
right_layout.setContentsMargins(15, 13, 15, 10)
|
||||||
|
right_layout.setSpacing(6)
|
||||||
|
title = self._label("逐味剂量对比", right)
|
||||||
|
title.setStyleSheet("font-size: 15px; font-weight: 700;")
|
||||||
|
legend_row = QHBoxLayout()
|
||||||
|
legend_row.addWidget(title)
|
||||||
|
legend_row.addStretch()
|
||||||
|
self.legend = self._label("● 医生 ● 千问", right, "PrescriptionCaption")
|
||||||
|
self.legend.setTextFormat(Qt.TextFormat.RichText)
|
||||||
|
legend_row.addWidget(self.legend)
|
||||||
|
right_layout.addLayout(legend_row)
|
||||||
|
self.status_label = self._label("", right, "PrescriptionStatus")
|
||||||
|
self.status_label.setWordWrap(True)
|
||||||
|
self.status_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
|
||||||
|
right_layout.addWidget(self.status_label)
|
||||||
|
self.chart_scroll = QScrollArea(right)
|
||||||
|
self.chart_scroll.setWidgetResizable(True)
|
||||||
|
self.chart_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||||
|
self.chart_scroll.setMinimumSize(0, 0)
|
||||||
|
self.chart_scroll.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding)
|
||||||
|
self.chart_scroll.setAccessibleName("可滚动查看全部药味的剂量图")
|
||||||
|
self.chart = _DoseChart(self.chart_scroll)
|
||||||
|
self.chart_scroll.setWidget(self.chart)
|
||||||
|
right_layout.addWidget(self.chart_scroll, 1)
|
||||||
|
footnote = self._label("同组同标尺,组间勿比较;剂量差异不代表医疗优劣。", right, "PrescriptionCaption")
|
||||||
|
footnote.setWordWrap(True)
|
||||||
|
right_layout.addWidget(footnote)
|
||||||
|
panes.addWidget(right, 5)
|
||||||
|
layout.addLayout(panes, 1)
|
||||||
|
self._render()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _label(text: str, parent: QWidget, name: str = "") -> QLabel:
|
||||||
|
label = QLabel(text, parent)
|
||||||
|
label.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
label.setObjectName(name)
|
||||||
|
return label
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected_model(self) -> str:
|
||||||
|
return self._model_key
|
||||||
|
|
||||||
|
def minimumSizeHint(self) -> QSize:
|
||||||
|
return QSize(0, 0)
|
||||||
|
|
||||||
|
def set_batch(self, batch: dict) -> None:
|
||||||
|
data = _mapping(batch)
|
||||||
|
if data == self._batch:
|
||||||
|
return
|
||||||
|
self._batch = deepcopy(data)
|
||||||
|
self._render()
|
||||||
|
|
||||||
|
def _select_model(self, model_key: str) -> None:
|
||||||
|
if model_key == self._model_key:
|
||||||
|
return
|
||||||
|
self._model_key = model_key
|
||||||
|
self._render()
|
||||||
|
|
||||||
|
def _state(self, model: dict[str, Any], candidate: dict[str, Any], comparison: dict[str, Any]) -> tuple[bool, str]:
|
||||||
|
if not self._batch:
|
||||||
|
return False, "选择一份报告后,查看已保存的候选药方与剂量对比。"
|
||||||
|
validity = _text(self._batch.get("validity"))
|
||||||
|
if validity and validity not in {"current", "valid"}:
|
||||||
|
return False, (_STALE.get(validity) or "报告有效性未确认") + ";仅查看历史原值,暂停条形比较。"
|
||||||
|
status = _text(model.get("status"))
|
||||||
|
candidate_status = _text(candidate.get("status"))
|
||||||
|
if status in ACTIVE_STATES or candidate_status in ACTIVE_STATES:
|
||||||
|
return False, f"{MODEL_NAMES[self._model_key]}正在生成候选方,完成后显示剂量对比。"
|
||||||
|
if status in _STALE:
|
||||||
|
return False, _STALE[status] + ";仅查看历史原值。"
|
||||||
|
if status in {"failed", "cancelled", "canceled", "blocked"}:
|
||||||
|
return False, "候选方分析未完成;已保存内容仅供查看。"
|
||||||
|
if candidate_status in {"insufficient_data", "withheld_for_risk", "no_medication", "no_medication_recommended"}:
|
||||||
|
headline = {"insufficient_data": "资料不足,暂未提供候选药方", "withheld_for_risk": "因风险暂缓候选用药", "no_medication": "建议暂不使用药物", "no_medication_recommended": "建议暂不使用药物"}[candidate_status]
|
||||||
|
return False, headline + (";" + _reason(candidate.get("reason")) if candidate.get("reason") else "。")
|
||||||
|
if status and status not in SUCCESS_STATES | {"partial"}:
|
||||||
|
return False, "模型状态未确认;仅列示已保存原值。"
|
||||||
|
if candidate_status and candidate_status not in {"available_for_review", "success", "succeeded", "completed"}:
|
||||||
|
return False, "候选方状态未确认;仅列示已保存原值。"
|
||||||
|
if comparison.get("status") == "not_comparable":
|
||||||
|
return False, "本报告不可比:" + (_reason(comparison.get("reason") or comparison.get("reason_code")) or "未通过单位、剂量或资料完整性核验。")
|
||||||
|
saved_rows = comparison.get("rows")
|
||||||
|
if not isinstance(saved_rows, list) or not any(isinstance(row, Mapping) for row in saved_rows):
|
||||||
|
return False, "尚无已保存的逐味对比;医生剂量以—表示,不推算历史处方。"
|
||||||
|
if comparison.get("status") != "comparable":
|
||||||
|
return False, "可比状态未确认;仅列示已保存原值。"
|
||||||
|
prefix = "报告有效性未注明;" if not validity else ""
|
||||||
|
return True, prefix + "按明确单位与每剂/每日基准分组;缺失剂量不按零计算。"
|
||||||
|
|
||||||
|
def _render(self) -> None:
|
||||||
|
model = _mapping(_mapping(self._batch.get("models")).get(self._model_key))
|
||||||
|
candidate, comparison = _mapping(model.get("candidate")), _mapping(model.get("comparison"))
|
||||||
|
self._rows = _saved_rows(candidate, comparison)
|
||||||
|
self._bars_enabled, self._chart_message = self._state(model, candidate, comparison)
|
||||||
|
name = MODEL_NAMES[self._model_key]
|
||||||
|
self.caption_label.setText(f"{name} · 候选药方")
|
||||||
|
candidate_herbs = candidate.get("herbs")
|
||||||
|
self._candidate_count = len(candidate_herbs) if isinstance(candidate_herbs, list) else None
|
||||||
|
has_herbs = isinstance(candidate_herbs, list) and bool(candidate_herbs)
|
||||||
|
if any(row.origin == "original" for row in self._rows):
|
||||||
|
self._chart_message += " 候选原方另行附列;历史对应关系未保存,不推断同药。"
|
||||||
|
elif comparison.get("rows") and any(row.origin == "uncompared" for row in self._rows):
|
||||||
|
self._chart_message += " 未纳入对比的候选药材已补列原值。"
|
||||||
|
empty_title = "候选方生成中" if model.get("status") in ACTIVE_STATES else "暂无候选药方"
|
||||||
|
self.name_label.setText(_text(candidate.get("prescription_name")) or ("已保存候选方" if has_herbs else empty_title))
|
||||||
|
self.name_label.setToolTip(self.name_label.text())
|
||||||
|
usage = []
|
||||||
|
if _text(candidate.get("usage_instruction")):
|
||||||
|
usage.append(_text(candidate["usage_instruction"]))
|
||||||
|
if _number(candidate.get("times_per_day")) is not None:
|
||||||
|
usage.append(f"每日 {candidate['times_per_day']} 次")
|
||||||
|
if _number(candidate.get("usage_days")) is not None:
|
||||||
|
usage.append(f"共 {candidate['usage_days']} 天")
|
||||||
|
usage_text = " · ".join(usage)
|
||||||
|
self.usage_label.setText(usage_text[:100] + ("…" if len(usage_text) > 100 else ""))
|
||||||
|
self.usage_label.setToolTip(usage_text)
|
||||||
|
self.usage_label.setVisible(bool(usage_text))
|
||||||
|
self.status_label.setText(self._chart_message)
|
||||||
|
self.legend.setText(f'<span style="color:{DOCTOR_COLOR}">● 医生</span> <span style="color:{MODEL_COLORS[self._model_key]}">● {name}</span>')
|
||||||
|
self.herb_table.setHorizontalHeaderLabels(["药材 / 主辅方", "医生剂量", f"{name}剂量"])
|
||||||
|
self._filter_rows()
|
||||||
|
|
||||||
|
def _filter_rows(self) -> None:
|
||||||
|
self._updating_rows = True
|
||||||
|
query = self.search.text().strip().casefold()
|
||||||
|
rows = [row for row in self._rows if any(query in name.casefold() for name in (row.name, *row.source_names))]
|
||||||
|
table_scroll = self.herb_table.verticalScrollBar().value()
|
||||||
|
chart_scroll = self.chart_scroll.verticalScrollBar().value()
|
||||||
|
selected_row = self.herb_table.currentRow()
|
||||||
|
selected_name = self.herb_table.item(selected_row, 0).text() if selected_row >= 0 and self.herb_table.item(selected_row, 0) else ""
|
||||||
|
self.herb_table.setRowCount(len(rows))
|
||||||
|
for index, row in enumerate(rows):
|
||||||
|
name_text = row.name + "\n" + row.context
|
||||||
|
instructions = row.usage_description(MODEL_NAMES[self._model_key])
|
||||||
|
if instructions:
|
||||||
|
name_text += "\n" + instructions
|
||||||
|
values = (name_text, row.doctor.label if row.doctor else "—", row.candidate.label if row.candidate else "—")
|
||||||
|
for column, value in enumerate(values):
|
||||||
|
item = QTableWidgetItem(value)
|
||||||
|
item.setToolTip(row.description(MODEL_NAMES[self._model_key]))
|
||||||
|
item.setData(Qt.ItemDataRole.AccessibleTextRole, value)
|
||||||
|
if column == 0:
|
||||||
|
item.setData(Qt.ItemDataRole.UserRole, row)
|
||||||
|
item.setData(Qt.ItemDataRole.AccessibleDescriptionRole, row.description(MODEL_NAMES[self._model_key]))
|
||||||
|
item.setTextAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
if column == 1:
|
||||||
|
item.setForeground(QColor(DOCTOR_COLOR))
|
||||||
|
if column == 2:
|
||||||
|
item.setForeground(QColor(MODEL_COLORS[self._model_key]))
|
||||||
|
self.herb_table.setItem(index, column, item)
|
||||||
|
self.herb_table.setRowHeight(index, max(48, self.herb_table.fontMetrics().height() * (3 if instructions else 2) + 12))
|
||||||
|
if name_text == selected_name:
|
||||||
|
self.herb_table.selectRow(index)
|
||||||
|
self.herb_table.verticalScrollBar().setValue(table_scroll)
|
||||||
|
self.herb_table.setVisible(bool(rows))
|
||||||
|
self.empty_label.setText("没有匹配的药名,请调整搜索。" if self._rows and not rows else self._chart_message)
|
||||||
|
self.empty_label.setVisible(not rows)
|
||||||
|
comparison_count = sum(row.origin == "comparison" for row in self._rows)
|
||||||
|
uncompared_count = sum(row.origin == "uncompared" for row in self._rows)
|
||||||
|
original_count = sum(row.origin == "original" for row in self._rows)
|
||||||
|
counts = [f"候选原方 {self._candidate_count} 项" if self._candidate_count is not None else "候选原方未保存", f"对比 {comparison_count} 项"]
|
||||||
|
if uncompared_count:
|
||||||
|
counts.append(f"未纳入 {uncompared_count} 项")
|
||||||
|
if original_count:
|
||||||
|
counts.append(f"原方附列 {original_count} 项")
|
||||||
|
if query:
|
||||||
|
counts.append(f"搜索显示 {len(rows)} 项")
|
||||||
|
self.count_label.setText(" · ".join(counts))
|
||||||
|
self.count_label.setToolTip("原方项数来自候选药材完整清单;对比项数来自已保存的标准化记录。附列药材不推断与对比记录的对应关系;— 表示该侧未保存剂量。")
|
||||||
|
self.count_label.setVisible(bool(self._rows))
|
||||||
|
empty_message = "没有匹配的药名" if self._rows and not rows else self._chart_message
|
||||||
|
self.chart.set_rows(rows, self._model_key, bars_enabled=self._bars_enabled, message=empty_message)
|
||||||
|
self.chart_scroll.verticalScrollBar().setValue(chart_scroll)
|
||||||
|
self._updating_rows = False
|
||||||
|
|
||||||
|
def _focus_chart_row(self) -> None:
|
||||||
|
if self._updating_rows:
|
||||||
|
return
|
||||||
|
item = self.herb_table.item(self.herb_table.currentRow(), 0)
|
||||||
|
if item is None:
|
||||||
|
return
|
||||||
|
target = item.data(Qt.ItemDataRole.UserRole)
|
||||||
|
y = 8
|
||||||
|
for _scale, members, _maximum in self.chart.groups:
|
||||||
|
y += self.chart.GROUP_HEIGHT
|
||||||
|
for row in members:
|
||||||
|
if row is target:
|
||||||
|
self.chart_scroll.verticalScrollBar().setValue(y)
|
||||||
|
return
|
||||||
|
y += self.chart.ROW_HEIGHT
|
||||||
@@ -0,0 +1,558 @@
|
|||||||
|
"""The console chrome: the agreement comparison bar and the numbered step rail.
|
||||||
|
|
||||||
|
Both widgets read only what the saved batch carries. A model without a comparable candidate keeps
|
||||||
|
an empty track instead of a zero-length bar, and the rail's counters stay blank until the batch
|
||||||
|
actually reports them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtCore import QPointF, QRectF, QSize, Qt, Signal
|
||||||
|
from PySide6.QtGui import (
|
||||||
|
QBrush,
|
||||||
|
QColor,
|
||||||
|
QFont,
|
||||||
|
QFontMetricsF,
|
||||||
|
QLinearGradient,
|
||||||
|
QPainter,
|
||||||
|
QPainterPath,
|
||||||
|
QPaintEvent,
|
||||||
|
QPalette,
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QFrame,
|
||||||
|
QHBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QProgressBar,
|
||||||
|
QPushButton,
|
||||||
|
QSizePolicy,
|
||||||
|
QVBoxLayout,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .issued_prescription_ai_theme import CONSOLE, MODEL_HUE, MODEL_TEXT, num_font
|
||||||
|
|
||||||
|
MODEL_NAMES = {"qwen": "千问", "openai": "OpenAI"}
|
||||||
|
|
||||||
|
|
||||||
|
def _number(value: Any) -> float | None:
|
||||||
|
if value is None or isinstance(value, bool):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
parsed = float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def _mapping(value: Any) -> dict[str, Any]:
|
||||||
|
return dict(value) if isinstance(value, Mapping) else {}
|
||||||
|
|
||||||
|
|
||||||
|
def _score(model: Mapping[str, Any]) -> float | None:
|
||||||
|
comparison = _mapping(_mapping(model).get("comparison"))
|
||||||
|
status = comparison.get("status") or _mapping(model).get("comparison_status")
|
||||||
|
if status != "comparable":
|
||||||
|
return None
|
||||||
|
value = _number(comparison.get("score", _mapping(model).get("score")))
|
||||||
|
return None if value is None or value < 0 or value > 100 else value
|
||||||
|
|
||||||
|
|
||||||
|
class ScaleTrack(QWidget):
|
||||||
|
"""A 0–100 track: the model's own fill, and a grey mark where the other model stands."""
|
||||||
|
|
||||||
|
# The design's scale: a 12px band for the rival's label, a 13px rail, then the axis row.
|
||||||
|
LABEL_BAND = 13.0
|
||||||
|
RAIL_TOP = 16.0
|
||||||
|
RAIL_HEIGHT = 13.0
|
||||||
|
AXIS_TOP = 31.0
|
||||||
|
|
||||||
|
def __init__(self, model_key: str, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.model_key = model_key
|
||||||
|
self._value: float | None = None
|
||||||
|
self._other: float | None = None
|
||||||
|
self._other_label = ""
|
||||||
|
self.setFixedHeight(44)
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
|
||||||
|
def set_values(self, value: float | None, other: float | None, other_label: str) -> None:
|
||||||
|
self._value, self._other, self._other_label = value, other, other_label
|
||||||
|
self.setAccessibleDescription("暂无可比结果" if value is None else f"{value:.1f}%")
|
||||||
|
self.setToolTip("" if other is None else f"{other_label} 在 {other:.1f}%")
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
top, height = self.RAIL_TOP, self.RAIL_HEIGHT
|
||||||
|
radius = height / 2
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor(CONSOLE["raised"]))
|
||||||
|
painter.drawRoundedRect(QRectF(0, top, self.width(), height), radius, radius)
|
||||||
|
if self._value:
|
||||||
|
filled = self.width() * self._value / 100
|
||||||
|
hue = QColor(MODEL_HUE[self.model_key])
|
||||||
|
faded = QColor(hue)
|
||||||
|
faded.setAlphaF(0.45)
|
||||||
|
gradient = QLinearGradient(QPointF(0, top), QPointF(filled, top))
|
||||||
|
gradient.setColorAt(0.0, faded)
|
||||||
|
gradient.setColorAt(1.0, hue)
|
||||||
|
painter.setBrush(QBrush(gradient))
|
||||||
|
painter.drawRoundedRect(QRectF(0, top, filled, height), radius, radius)
|
||||||
|
# Quarter dividers sit on the rail itself, as the design draws them.
|
||||||
|
painter.setPen(QColor(CONSOLE["grid_line"]))
|
||||||
|
for fraction in (0.25, 0.5, 0.75):
|
||||||
|
x = self.width() * fraction
|
||||||
|
painter.drawLine(QPointF(x, top), QPointF(x, top + height))
|
||||||
|
axis = QFont(self.font())
|
||||||
|
axis.setPixelSize(9)
|
||||||
|
painter.setFont(axis)
|
||||||
|
painter.setPen(QColor(CONSOLE["faint"]))
|
||||||
|
for fraction in (0.0, 0.25, 0.5, 0.75, 1.0):
|
||||||
|
label = f"{int(fraction * 100)}%"
|
||||||
|
width = 44.0
|
||||||
|
left = self.width() * fraction - width / 2
|
||||||
|
align = Qt.AlignmentFlag.AlignCenter
|
||||||
|
if fraction == 0.0:
|
||||||
|
left, align = 0.0, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
|
||||||
|
elif fraction == 1.0:
|
||||||
|
left, align = self.width() - width, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
|
||||||
|
painter.drawText(QRectF(left, self.AXIS_TOP, width, 12), align, label)
|
||||||
|
if self._other is not None:
|
||||||
|
x = self.width() * self._other / 100
|
||||||
|
marker = QColor(CONSOLE["muted"])
|
||||||
|
marker.setAlphaF(0.8)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(marker)
|
||||||
|
painter.drawRoundedRect(QRectF(x - 1, top - 4, 2, height + 8), 1, 1)
|
||||||
|
painter.setPen(QColor(CONSOLE["faint"]))
|
||||||
|
painter.drawText(QRectF(max(0.0, min(x - 60, self.width() - 120)), 0, 120, self.LABEL_BAND),
|
||||||
|
Qt.AlignmentFlag.AlignCenter, f"{self._other_label} 在此")
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class ScoreLabel(QLabel):
|
||||||
|
"""The big figure with its unit set small and raised, the way the design prints it.
|
||||||
|
|
||||||
|
``text()`` still returns the whole string, so callers and tests read one plain value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
SIZE = 27
|
||||||
|
UNIT_SIZE = 13
|
||||||
|
|
||||||
|
def _parts(self) -> tuple[str, str]:
|
||||||
|
text = self.text()
|
||||||
|
for unit in ("%", "pt"):
|
||||||
|
if text.endswith(unit) and len(text) > len(unit):
|
||||||
|
return text[: -len(unit)], unit
|
||||||
|
return text, ""
|
||||||
|
|
||||||
|
def sizeHint(self) -> QSize: # noqa: N802 - Qt virtual
|
||||||
|
figure, unit = self._parts()
|
||||||
|
width = QFontMetricsF(num_font(self.SIZE, weight=QFont.Weight.DemiBold)).horizontalAdvance(figure)
|
||||||
|
if unit:
|
||||||
|
width += 2 + QFontMetricsF(num_font(self.UNIT_SIZE)).horizontalAdvance(unit)
|
||||||
|
return QSize(int(width) + 1, int(self.SIZE * 1.1) + 1)
|
||||||
|
|
||||||
|
def minimumSizeHint(self) -> QSize: # noqa: N802 - Qt virtual
|
||||||
|
return self.sizeHint()
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
figure, unit = self._parts()
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
figure_font = num_font(self.SIZE, weight=QFont.Weight.DemiBold)
|
||||||
|
painter.setFont(figure_font)
|
||||||
|
painter.setPen(self.palette().color(QPalette.ColorRole.WindowText))
|
||||||
|
metrics = QFontMetricsF(figure_font)
|
||||||
|
baseline = (self.height() + metrics.capHeight()) / 2
|
||||||
|
painter.drawText(QPointF(0, baseline), figure)
|
||||||
|
if unit:
|
||||||
|
unit_font = num_font(self.UNIT_SIZE)
|
||||||
|
painter.setFont(unit_font)
|
||||||
|
painter.setPen(QColor(CONSOLE["muted"]))
|
||||||
|
painter.drawText(QPointF(metrics.horizontalAdvance(figure) + 2,
|
||||||
|
baseline - metrics.capHeight() + QFontMetricsF(unit_font).capHeight()),
|
||||||
|
unit)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class AgreementBar(QFrame):
|
||||||
|
"""Both models' agreement on one scale, with the gap between them stated in points."""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("AiAgreement")
|
||||||
|
layout = QHBoxLayout(self)
|
||||||
|
layout.setContentsMargins(22, 16, 22, 16)
|
||||||
|
layout.setSpacing(24)
|
||||||
|
self.blocks: dict[str, dict[str, Any]] = {}
|
||||||
|
for index, key in enumerate(("qwen", "openai")):
|
||||||
|
if index:
|
||||||
|
divider = QFrame(self)
|
||||||
|
divider.setObjectName("AiFactDivider")
|
||||||
|
divider.setFixedWidth(1)
|
||||||
|
layout.addWidget(divider)
|
||||||
|
layout.addWidget(self._delta_block())
|
||||||
|
divider = QFrame(self)
|
||||||
|
divider.setObjectName("AiFactDivider")
|
||||||
|
divider.setFixedWidth(1)
|
||||||
|
layout.addWidget(divider)
|
||||||
|
# Both heads line up at the top, so a model that failed does not slide its column down.
|
||||||
|
layout.addWidget(self._model_block(key), 1, Qt.AlignmentFlag.AlignTop)
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
"""The design runs a blue-to-violet bar down the card's left edge, inside its rounding."""
|
||||||
|
|
||||||
|
super().paintEvent(event)
|
||||||
|
card = QPainterPath()
|
||||||
|
card.addRoundedRect(QRectF(1, 1, self.width() - 2, self.height() - 2), 9, 9)
|
||||||
|
strip = QPainterPath()
|
||||||
|
strip.addRect(QRectF(0, 0, 4, self.height()))
|
||||||
|
gradient = QLinearGradient(QPointF(0, 0), QPointF(0, self.height()))
|
||||||
|
gradient.setColorAt(0.0, QColor(CONSOLE["accent"]))
|
||||||
|
gradient.setColorAt(1.0, QColor(CONSOLE["openai"]))
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.fillPath(card.intersected(strip), QBrush(gradient))
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
def _model_block(self, key: str) -> QWidget:
|
||||||
|
holder = QWidget(self)
|
||||||
|
column = QVBoxLayout(holder)
|
||||||
|
column.setContentsMargins(0, 0, 0, 0)
|
||||||
|
column.setSpacing(9)
|
||||||
|
head = QHBoxLayout()
|
||||||
|
head.setSpacing(9)
|
||||||
|
dot = QLabel(holder)
|
||||||
|
dot.setFixedSize(9, 9)
|
||||||
|
dot.setStyleSheet(f"background: {MODEL_HUE[key]}; border-radius: 3px;")
|
||||||
|
head.addWidget(dot)
|
||||||
|
name = QLabel(MODEL_NAMES[key], holder)
|
||||||
|
name.setStyleSheet(f"color: {CONSOLE['heading']}; font-size: 13.8px; font-weight: 600;")
|
||||||
|
head.addWidget(name)
|
||||||
|
status = QLabel("尚无报告", holder)
|
||||||
|
status.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
head.addWidget(status)
|
||||||
|
coverage = QLabel("", holder)
|
||||||
|
coverage.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
coverage.setStyleSheet(
|
||||||
|
f"background: {CONSOLE['amber_dim']}; color: {CONSOLE['amber_text']};"
|
||||||
|
f" border: 1px solid {CONSOLE['amber']}; border-radius: 9px; padding: 1px 8px; font-size: 9.9px;")
|
||||||
|
head.addWidget(coverage)
|
||||||
|
elapsed = QLabel("", holder)
|
||||||
|
elapsed.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
elapsed.setStyleSheet(
|
||||||
|
f"background: transparent; color: {CONSOLE['faint']};"
|
||||||
|
f" border: 1px solid {CONSOLE['line']}; border-radius: 9px; padding: 1px 8px; font-size: 9.9px;")
|
||||||
|
head.addWidget(elapsed)
|
||||||
|
head.addStretch(1)
|
||||||
|
column.addLayout(head)
|
||||||
|
score = ScoreLabel("—", holder)
|
||||||
|
score.setObjectName(f"AiAgreementScore{key.capitalize()}")
|
||||||
|
score.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
column.addWidget(score)
|
||||||
|
caption = QLabel("药味与剂量一致率", holder)
|
||||||
|
caption.setStyleSheet(f"color: {CONSOLE['faint']}; font-size: 10.5px;")
|
||||||
|
column.addWidget(caption)
|
||||||
|
track = ScaleTrack(key, holder)
|
||||||
|
column.addWidget(track)
|
||||||
|
|
||||||
|
# A failed model states why it stopped and offers its retry on its own block, which is
|
||||||
|
# the only place in the console that belongs to that model alone.
|
||||||
|
failure = QWidget(holder)
|
||||||
|
failure_row = QHBoxLayout(failure)
|
||||||
|
failure_row.setContentsMargins(0, 2, 0, 0)
|
||||||
|
failure_row.setSpacing(8)
|
||||||
|
failure_text = QLabel("", failure)
|
||||||
|
failure_text.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
failure_text.setWordWrap(True)
|
||||||
|
failure_text.setStyleSheet(f"color: {CONSOLE['rose_text']}; font-size: 12px;")
|
||||||
|
failure_row.addWidget(failure_text, 1)
|
||||||
|
retry_slot = QHBoxLayout()
|
||||||
|
retry_slot.setContentsMargins(0, 0, 0, 0)
|
||||||
|
retry_slot.setSpacing(6)
|
||||||
|
failure_row.addLayout(retry_slot)
|
||||||
|
failure.setVisible(False)
|
||||||
|
column.addWidget(failure)
|
||||||
|
|
||||||
|
stage = QLabel("等待处理进度", holder)
|
||||||
|
stage.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
stage.setWordWrap(True)
|
||||||
|
stage.setStyleSheet(f"color: {MODEL_TEXT[key]}; font-size: 12px;")
|
||||||
|
stage.hide()
|
||||||
|
# The plain bar stays as a value carrier for callers and accessibility tools; the painted
|
||||||
|
# track above is the visual, so it is never added to the layout.
|
||||||
|
carrier = QProgressBar(holder)
|
||||||
|
carrier.setRange(0, 1000)
|
||||||
|
carrier.setTextVisible(False)
|
||||||
|
carrier.setFixedHeight(4)
|
||||||
|
carrier.setAccessibleName(f"{MODEL_NAMES[key]}与医生方的药味及剂量一致度")
|
||||||
|
carrier.setVisible(False)
|
||||||
|
self.blocks[key] = {"score": score, "coverage": coverage, "elapsed": elapsed, "track": track,
|
||||||
|
"status": status, "failure": failure, "failure_text": failure_text,
|
||||||
|
"retry_slot": retry_slot, "stage": stage, "carrier": carrier}
|
||||||
|
return holder
|
||||||
|
|
||||||
|
def views(self, key: str) -> dict[str, Any]:
|
||||||
|
"""Widget map kept stable for the dialog and its regression tests."""
|
||||||
|
|
||||||
|
block = self.blocks[key]
|
||||||
|
return {"card": self, "model_label": block["score"], "status_chip": block["status"],
|
||||||
|
"coverage_chip": block["coverage"], "score": block["score"], "elapsed": block["elapsed"],
|
||||||
|
"agreement_bar": block["carrier"], "gauge": block["track"], "stage": block["stage"],
|
||||||
|
"failure": block["failure"], "failure_text": block["failure_text"]}
|
||||||
|
|
||||||
|
def attach_action(self, key: str, button: QWidget) -> None:
|
||||||
|
"""Host a dialog-owned action (retry) inside that model's failure row."""
|
||||||
|
|
||||||
|
self.blocks[key]["retry_slot"].addWidget(button)
|
||||||
|
|
||||||
|
def set_failure(self, key: str, message: str, retryable: bool) -> None:
|
||||||
|
block = self.blocks[key]
|
||||||
|
block["failure_text"].setText(message)
|
||||||
|
block["failure"].setVisible(bool(message))
|
||||||
|
slot = block["retry_slot"]
|
||||||
|
for index in range(slot.count()):
|
||||||
|
widget = slot.itemAt(index).widget()
|
||||||
|
if widget is not None:
|
||||||
|
widget.setVisible(retryable)
|
||||||
|
|
||||||
|
def _delta_block(self) -> QWidget:
|
||||||
|
holder = QWidget(self)
|
||||||
|
holder.setFixedWidth(180)
|
||||||
|
column = QVBoxLayout(holder)
|
||||||
|
column.setContentsMargins(0, 6, 0, 0)
|
||||||
|
column.setSpacing(2)
|
||||||
|
column.addStretch(1)
|
||||||
|
self.delta = QLabel("—", holder)
|
||||||
|
self.delta.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
self.delta.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.delta.setFont(num_font(20, weight=QFont.Weight.DemiBold))
|
||||||
|
self.delta.setStyleSheet(f"color: {CONSOLE['accent_text']}; font-size: 19.5px; font-weight: 600;")
|
||||||
|
column.addWidget(self.delta)
|
||||||
|
self.delta_note = QLabel("等待两个模型", holder)
|
||||||
|
self.delta_note.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.delta_note.setStyleSheet(f"color: {CONSOLE['faint']}; font-size: 10.2px;")
|
||||||
|
column.addWidget(self.delta_note)
|
||||||
|
self.overlap = QLabel("", holder)
|
||||||
|
self.overlap.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.overlap.setStyleSheet(f"color: {CONSOLE['faint']}; font-size: 10.2px;")
|
||||||
|
column.addWidget(self.overlap)
|
||||||
|
column.addStretch(1)
|
||||||
|
return holder
|
||||||
|
|
||||||
|
def apply(self, batch: Mapping[str, Any] | None, *, coverage: Mapping[str, str] | None = None,
|
||||||
|
elapsed: Mapping[str, str] | None = None) -> None:
|
||||||
|
data = _mapping(batch)
|
||||||
|
models = _mapping(data.get("models"))
|
||||||
|
scores = {key: _score(_mapping(models.get(key))) for key in ("qwen", "openai")}
|
||||||
|
for key in ("qwen", "openai"):
|
||||||
|
block = self.blocks[key]
|
||||||
|
value = scores[key]
|
||||||
|
block["score"].setText("—" if value is None else f"{value:.1f}%")
|
||||||
|
other = scores["openai" if key == "qwen" else "qwen"]
|
||||||
|
block["track"].set_values(value, other, MODEL_NAMES["openai" if key == "qwen" else "qwen"])
|
||||||
|
text = _mapping(coverage).get(key, "")
|
||||||
|
block["coverage"].setText(text)
|
||||||
|
block["coverage"].setVisible(bool(text))
|
||||||
|
spent = _mapping(elapsed).get(key, "")
|
||||||
|
block["elapsed"].setText(spent)
|
||||||
|
block["elapsed"].setVisible(bool(spent))
|
||||||
|
if scores["qwen"] is None or scores["openai"] is None:
|
||||||
|
self.delta.setText("—")
|
||||||
|
self.delta_note.setText("两个模型都可比后才给差值")
|
||||||
|
self.overlap.setText("")
|
||||||
|
return
|
||||||
|
gap = scores["qwen"] - scores["openai"]
|
||||||
|
leader = MODEL_NAMES["qwen"] if gap >= 0 else MODEL_NAMES["openai"]
|
||||||
|
self.delta.setText(f"{'+' if gap >= 0 else '−'}{abs(gap):.1f}pt")
|
||||||
|
self.delta_note.setText(f"{leader}领先" if gap else "两模型持平")
|
||||||
|
overlaps = []
|
||||||
|
for key in ("qwen", "openai"):
|
||||||
|
herb = _number(_mapping(_mapping(models.get(key)).get("comparison")).get("herb_score"))
|
||||||
|
overlaps.append("—" if herb is None else f"{herb:.1f}%")
|
||||||
|
self.overlap.setText("药味重合 " + " / ".join(overlaps))
|
||||||
|
|
||||||
|
|
||||||
|
class _StepButton(QPushButton):
|
||||||
|
"""A step row; the design marks the selected one with a rounded bar down its left edge."""
|
||||||
|
|
||||||
|
BAR_INSET = 9
|
||||||
|
BAR_WIDTH = 3
|
||||||
|
|
||||||
|
def paintEvent(self, event: Any) -> None: # noqa: N802 - Qt virtual
|
||||||
|
super().paintEvent(event)
|
||||||
|
if not self.isChecked():
|
||||||
|
return
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor(CONSOLE["accent"]))
|
||||||
|
painter.drawRoundedRect(
|
||||||
|
QRectF(0, self.BAR_INSET, self.BAR_WIDTH, self.height() - self.BAR_INSET * 2), 1.5, 1.5)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class StepRail(QFrame):
|
||||||
|
"""The six destinations as numbered steps, with what this batch needs looked at below them."""
|
||||||
|
|
||||||
|
selected = Signal(str)
|
||||||
|
save_requested = Signal()
|
||||||
|
|
||||||
|
FOCUS_ROWS = (("differences", "剂量差异", "项", "amber_text"),
|
||||||
|
("critical", "关键缺口", "项", "rose_text"),
|
||||||
|
("restricted", "附件受限", "个", "text"),
|
||||||
|
("consensus", "三方共识", "味", "accent_text"))
|
||||||
|
|
||||||
|
def __init__(self, steps: tuple[tuple[str, str], ...], parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("AiRail")
|
||||||
|
self.setFixedWidth(232)
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
layout.setSpacing(12)
|
||||||
|
|
||||||
|
nav = QFrame(self)
|
||||||
|
nav.setObjectName("AiPanel")
|
||||||
|
nav_layout = QVBoxLayout(nav)
|
||||||
|
nav_layout.setContentsMargins(6, 6, 6, 6)
|
||||||
|
nav_layout.setSpacing(0)
|
||||||
|
self._tones: dict[str, str] = {}
|
||||||
|
self.buttons: dict[str, QPushButton] = {}
|
||||||
|
self.badges: dict[str, QLabel] = {}
|
||||||
|
self.numbers: dict[str, QLabel] = {}
|
||||||
|
self.names: dict[str, QLabel] = {}
|
||||||
|
for index, (key, text) in enumerate(steps, start=1):
|
||||||
|
button = _StepButton(nav)
|
||||||
|
button.setObjectName("AiStep")
|
||||||
|
button.setCheckable(True)
|
||||||
|
button.setChecked(index == 1)
|
||||||
|
button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
|
button.setFixedHeight(42)
|
||||||
|
button.clicked.connect(lambda _checked=False, target=key: self.selected.emit(target))
|
||||||
|
row = QHBoxLayout(button)
|
||||||
|
row.setContentsMargins(11, 0, 11, 0)
|
||||||
|
row.setSpacing(10)
|
||||||
|
number = QLabel(f"{index:02d}", button)
|
||||||
|
number.setObjectName("AiStepNumber")
|
||||||
|
number.setFixedSize(22, 22)
|
||||||
|
number.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
number.setFont(num_font(10, weight=QFont.Weight.Bold))
|
||||||
|
row.addWidget(number)
|
||||||
|
name = QLabel(text, button)
|
||||||
|
name.setObjectName("AiStepName")
|
||||||
|
row.addWidget(name, 1)
|
||||||
|
badge = QLabel("", button)
|
||||||
|
badge.setObjectName("AiStepBadge")
|
||||||
|
badge.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
badge.setMinimumWidth(20)
|
||||||
|
badge.setFixedHeight(17)
|
||||||
|
badge.setFont(num_font(10))
|
||||||
|
row.addWidget(badge, 0, Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
self.buttons[key] = button
|
||||||
|
self.badges[key] = badge
|
||||||
|
self.numbers[key] = number
|
||||||
|
self.names[key] = name
|
||||||
|
nav_layout.addWidget(button)
|
||||||
|
layout.addWidget(nav)
|
||||||
|
self.set_current(steps[0][0] if steps else "")
|
||||||
|
|
||||||
|
focus = QFrame(self)
|
||||||
|
focus.setObjectName("AiPanel")
|
||||||
|
focus_layout = QVBoxLayout(focus)
|
||||||
|
focus_layout.setContentsMargins(12, 13, 12, 14)
|
||||||
|
focus_layout.setSpacing(7)
|
||||||
|
caption = QLabel("本次关注", focus)
|
||||||
|
caption.setStyleSheet(f"color: {CONSOLE['faint']}; font-size: 10.2px; letter-spacing: 1.4px;"
|
||||||
|
" padding-left: 4px;")
|
||||||
|
focus_layout.addWidget(caption)
|
||||||
|
self.focus_values: dict[str, QLabel] = {}
|
||||||
|
for key, text, unit, tone in self.FOCUS_ROWS:
|
||||||
|
row = QFrame(focus)
|
||||||
|
row.setObjectName("AiFocusRow")
|
||||||
|
row_layout = QHBoxLayout(row)
|
||||||
|
row_layout.setContentsMargins(10, 8, 10, 8)
|
||||||
|
row_layout.setSpacing(8)
|
||||||
|
name = QLabel(text, row)
|
||||||
|
name.setStyleSheet(f"color: {CONSOLE['muted']}; font-size: 11.25px;")
|
||||||
|
row_layout.addWidget(name)
|
||||||
|
row_layout.addStretch(1)
|
||||||
|
value = QLabel(f"— {unit}", row)
|
||||||
|
value.setTextFormat(Qt.TextFormat.PlainText)
|
||||||
|
value.setFont(num_font(14))
|
||||||
|
value.setStyleSheet(f"color: {CONSOLE[tone]};")
|
||||||
|
row_layout.addWidget(value)
|
||||||
|
self.focus_values[key] = value
|
||||||
|
focus_layout.addWidget(row)
|
||||||
|
self.save_button = QPushButton("保存本次复核", focus)
|
||||||
|
self.save_button.setObjectName("AiPrimaryAction")
|
||||||
|
self.save_button.setFixedHeight(34)
|
||||||
|
self.save_button.clicked.connect(self.save_requested.emit)
|
||||||
|
focus_layout.addWidget(self.save_button)
|
||||||
|
layout.addWidget(focus)
|
||||||
|
layout.addStretch(1)
|
||||||
|
|
||||||
|
BADGE_TONES = {"hot": ("rose_dim", "rose_text"), "warn": ("amber_dim", "amber_text")}
|
||||||
|
|
||||||
|
def set_current(self, key: str) -> None:
|
||||||
|
"""The selected step is a tinted row with a blue index chip, not a solid blue button."""
|
||||||
|
|
||||||
|
for target, button in self.buttons.items():
|
||||||
|
selected = target == key
|
||||||
|
button.setChecked(selected)
|
||||||
|
self.numbers[target].setStyleSheet(
|
||||||
|
f"background: {CONSOLE['accent'] if selected else CONSOLE['raised']};"
|
||||||
|
f" color: {'#F2F7FF' if selected else CONSOLE['faint']}; border-radius: 6px;")
|
||||||
|
self.names[target].setStyleSheet(
|
||||||
|
f"color: {CONSOLE['heading'] if selected else CONSOLE['muted']}; font-size: 12.6px;"
|
||||||
|
+ (" font-weight: 600;" if selected else ""))
|
||||||
|
self._paint_badge(target)
|
||||||
|
|
||||||
|
def _paint_badge(self, key: str) -> None:
|
||||||
|
background, colour = self.BADGE_TONES.get(self._tones.get(key, ""), ("raised", "faint"))
|
||||||
|
self.badges[key].setStyleSheet(
|
||||||
|
f"background: {CONSOLE[background]}; color: {CONSOLE[colour]};"
|
||||||
|
" border-radius: 8px; padding: 0 6px;")
|
||||||
|
|
||||||
|
def set_badges(self, counts: Mapping[str, Any], tones: Mapping[str, str] | None = None) -> None:
|
||||||
|
"""Counts, and the severity that decides whether one reads as rose, amber or quiet."""
|
||||||
|
|
||||||
|
self._tones = dict(tones or {})
|
||||||
|
for key, badge in self.badges.items():
|
||||||
|
value = counts.get(key)
|
||||||
|
badge.setText("" if value in (None, "") else str(value))
|
||||||
|
badge.setVisible(bool(badge.text()))
|
||||||
|
self._paint_badge(key)
|
||||||
|
|
||||||
|
def set_focus(self, counts: Mapping[str, Any]) -> None:
|
||||||
|
for key, _text, unit, _tone in self.FOCUS_ROWS:
|
||||||
|
value = counts.get(key)
|
||||||
|
self.focus_values[key].setText(f"{'—' if value is None else value} {unit}")
|
||||||
|
|
||||||
|
|
||||||
|
def rail_qss() -> str:
|
||||||
|
"""The rail and agreement styles for the palette that is active right now."""
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
QFrame#AiRail {{ background: transparent; border: 0; }}
|
||||||
|
QFrame#AiFocusRow {{ background: {CONSOLE['surface_2']}; border: 0; border-radius: 7px; }}
|
||||||
|
QPushButton#AiStep {{ background: transparent; border: 1px solid transparent; border-radius: 7px;
|
||||||
|
text-align: left; }}
|
||||||
|
QPushButton#AiStep:hover {{ background: {CONSOLE['surface_2']}; }}
|
||||||
|
QPushButton#AiStep:checked {{ background: {CONSOLE['selection']};
|
||||||
|
border-color: {CONSOLE['selection_line']}; }}
|
||||||
|
QLabel#AiStepNumber {{ color: {CONSOLE['faint']}; }}
|
||||||
|
QLabel#AiStepName {{ color: {CONSOLE['muted']}; font-size: 12.6px; }}
|
||||||
|
QLabel#AiStepBadge {{ color: {CONSOLE['faint']}; background: {CONSOLE['raised']};
|
||||||
|
border-radius: 8px; padding: 0 6px; }}
|
||||||
|
QFrame#AiAgreement {{ background: {CONSOLE['surface']}; border: 1px solid {CONSOLE['line_soft']};
|
||||||
|
border-radius: 10px; }}
|
||||||
|
QLabel#AiAgreementScoreQwen {{ color: {MODEL_TEXT['qwen']}; font-size: 27px; font-weight: 600; }}
|
||||||
|
QLabel#AiAgreementScoreOpenai {{ color: {MODEL_TEXT['openai']}; font-size: 27px; font-weight: 600; }}
|
||||||
|
"""
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
"""Painted glyphs for the prescription analysis window.
|
||||||
|
|
||||||
|
The window ships no image assets, so every icon in the design is drawn here with QPainter at the
|
||||||
|
size it is used. Each glyph is line art on a transparent background; the colour is supplied by the
|
||||||
|
caller, which keeps a glyph usable on a card, inside a tinted tile, or on the navigation bar.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PySide6.QtCore import QPointF, QRectF, QSize, Qt
|
||||||
|
from PySide6.QtGui import (
|
||||||
|
QBrush,
|
||||||
|
QColor,
|
||||||
|
QIcon,
|
||||||
|
QLinearGradient,
|
||||||
|
QPainter,
|
||||||
|
QPaintEvent,
|
||||||
|
QPen,
|
||||||
|
QPixmap,
|
||||||
|
QPolygonF,
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import QSizePolicy, QWidget
|
||||||
|
|
||||||
|
from .issued_prescription_ai_theme import CONSOLE as TECH_BLUE
|
||||||
|
|
||||||
|
|
||||||
|
def _pen(painter: QPainter, colour: str, width: float) -> QPen:
|
||||||
|
pen = QPen(QColor(colour))
|
||||||
|
pen.setWidthF(width)
|
||||||
|
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
|
||||||
|
pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
|
||||||
|
painter.setPen(pen)
|
||||||
|
painter.setBrush(Qt.BrushStyle.NoBrush)
|
||||||
|
return pen
|
||||||
|
|
||||||
|
|
||||||
|
def paint_glyph(painter: QPainter, kind: str, box: QRectF, colour: str) -> None:
|
||||||
|
"""Draw one glyph inside ``box``. Unknown names draw nothing rather than a placeholder."""
|
||||||
|
|
||||||
|
painter.save()
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
x, y, w, h = box.x(), box.y(), box.width(), box.height()
|
||||||
|
stroke = max(1.2, min(w, h) / 11)
|
||||||
|
if kind == "bars":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
for index, height in enumerate((0.45, 0.75, 0.6)):
|
||||||
|
left = x + w * (0.22 + index * 0.28)
|
||||||
|
painter.drawLine(QPointF(left, y + h * 0.82), QPointF(left, y + h * (0.82 - height)))
|
||||||
|
elif kind == "doc":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawRoundedRect(QRectF(x + w * 0.22, y + h * 0.12, w * 0.56, h * 0.76), w * 0.08, w * 0.08)
|
||||||
|
for index in range(2):
|
||||||
|
top = y + h * (0.38 + index * 0.2)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.34, top), QPointF(x + w * 0.66, top))
|
||||||
|
elif kind == "box":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
top, bottom, middle = y + h * 0.2, y + h * 0.8, y + h * 0.5
|
||||||
|
left, right = x + w * 0.16, x + w * 0.84
|
||||||
|
painter.drawPolygon(QPolygonF([QPointF(x + w * 0.5, top), QPointF(right, middle * 0.75 + top * 0.25),
|
||||||
|
QPointF(right, bottom - h * 0.12), QPointF(x + w * 0.5, bottom),
|
||||||
|
QPointF(left, bottom - h * 0.12), QPointF(left, middle * 0.75 + top * 0.25)]))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.5, y + h * 0.5), QPointF(x + w * 0.5, bottom))
|
||||||
|
elif kind == "image":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawRoundedRect(QRectF(x + w * 0.16, y + h * 0.22, w * 0.68, h * 0.56), w * 0.08, w * 0.08)
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.24, y + h * 0.7), QPointF(x + w * 0.42, y + h * 0.48),
|
||||||
|
QPointF(x + w * 0.58, y + h * 0.66), QPointF(x + w * 0.68, y + h * 0.56)]))
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawEllipse(QPointF(x + w * 0.64, y + h * 0.36), stroke * 0.9, stroke * 0.9)
|
||||||
|
elif kind == "clock":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawEllipse(QRectF(x + w * 0.16, y + h * 0.16, w * 0.68, h * 0.68))
|
||||||
|
centre = QPointF(x + w * 0.5, y + h * 0.5)
|
||||||
|
painter.drawLine(centre, QPointF(x + w * 0.5, y + h * 0.3))
|
||||||
|
painter.drawLine(centre, QPointF(x + w * 0.66, y + h * 0.58))
|
||||||
|
elif kind == "trend":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.18, y + h * 0.68), QPointF(x + w * 0.4, y + h * 0.46),
|
||||||
|
QPointF(x + w * 0.56, y + h * 0.58), QPointF(x + w * 0.82, y + h * 0.28)]))
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.62, y + h * 0.28), QPointF(x + w * 0.82, y + h * 0.28),
|
||||||
|
QPointF(x + w * 0.82, y + h * 0.48)]))
|
||||||
|
elif kind == "bell":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawPolyline(QPolygonF([
|
||||||
|
QPointF(x + w * 0.24, y + h * 0.68), QPointF(x + w * 0.3, y + h * 0.58),
|
||||||
|
QPointF(x + w * 0.3, y + h * 0.42), QPointF(x + w * 0.5, y + h * 0.2),
|
||||||
|
QPointF(x + w * 0.7, y + h * 0.42), QPointF(x + w * 0.7, y + h * 0.58),
|
||||||
|
QPointF(x + w * 0.76, y + h * 0.68), QPointF(x + w * 0.24, y + h * 0.68)]))
|
||||||
|
painter.drawArc(QRectF(x + w * 0.4, y + h * 0.66, w * 0.2, h * 0.18), 0, -180 * 16)
|
||||||
|
elif kind == "clipboard":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawRoundedRect(QRectF(x + w * 0.22, y + h * 0.2, w * 0.56, h * 0.66), w * 0.08, w * 0.08)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.36, y + h * 0.48), QPointF(x + w * 0.64, y + h * 0.48))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.36, y + h * 0.64), QPointF(x + w * 0.56, y + h * 0.64))
|
||||||
|
elif kind == "pencil":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.24, y + h * 0.76), QPointF(x + w * 0.28, y + h * 0.6),
|
||||||
|
QPointF(x + w * 0.64, y + h * 0.24), QPointF(x + w * 0.78, y + h * 0.38),
|
||||||
|
QPointF(x + w * 0.42, y + h * 0.74), QPointF(x + w * 0.24, y + h * 0.76)]))
|
||||||
|
elif kind == "bulb":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawArc(QRectF(x + w * 0.28, y + h * 0.18, w * 0.44, h * 0.46), 0, 180 * 16)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.28, y + h * 0.41), QPointF(x + w * 0.38, y + h * 0.62))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.72, y + h * 0.41), QPointF(x + w * 0.62, y + h * 0.62))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.38, y + h * 0.66), QPointF(x + w * 0.62, y + h * 0.66))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.42, y + h * 0.78), QPointF(x + w * 0.58, y + h * 0.78))
|
||||||
|
elif kind == "flask":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.38, y + h * 0.2), QPointF(x + w * 0.62, y + h * 0.2))
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.44, y + h * 0.2), QPointF(x + w * 0.44, y + h * 0.44),
|
||||||
|
QPointF(x + w * 0.24, y + h * 0.78), QPointF(x + w * 0.76, y + h * 0.78),
|
||||||
|
QPointF(x + w * 0.56, y + h * 0.44), QPointF(x + w * 0.56, y + h * 0.2)]))
|
||||||
|
elif kind == "alert":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawEllipse(QRectF(x + w * 0.16, y + h * 0.16, w * 0.68, h * 0.68))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.5, y + h * 0.33), QPointF(x + w * 0.5, y + h * 0.56))
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawEllipse(QPointF(x + w * 0.5, y + h * 0.68), stroke * 0.7, stroke * 0.7)
|
||||||
|
elif kind == "paperclip":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawRoundedRect(QRectF(x + w * 0.18, y + h * 0.22, w * 0.64, h * 0.5), w * 0.1, w * 0.1)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.3, y + h * 0.72), QPointF(x + w * 0.3, y + h * 0.84))
|
||||||
|
elif kind == "info":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawEllipse(QRectF(x + w * 0.16, y + h * 0.16, w * 0.68, h * 0.68))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.5, y + h * 0.46), QPointF(x + w * 0.5, y + h * 0.68))
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawEllipse(QPointF(x + w * 0.5, y + h * 0.34), stroke * 0.7, stroke * 0.7)
|
||||||
|
elif kind == "refresh":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawArc(QRectF(x + w * 0.2, y + h * 0.2, w * 0.6, h * 0.6), 40 * 16, 280 * 16)
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawPolygon(QPolygonF([QPointF(x + w * 0.72, y + h * 0.12), QPointF(x + w * 0.86, y + h * 0.34),
|
||||||
|
QPointF(x + w * 0.6, y + h * 0.32)]))
|
||||||
|
elif kind == "save":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawRoundedRect(QRectF(x + w * 0.2, y + h * 0.2, w * 0.6, h * 0.6), w * 0.08, w * 0.08)
|
||||||
|
painter.drawLine(QPointF(x + w * 0.36, y + h * 0.2), QPointF(x + w * 0.36, y + h * 0.42))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.36, y + h * 0.42), QPointF(x + w * 0.64, y + h * 0.42))
|
||||||
|
painter.drawLine(QPointF(x + w * 0.64, y + h * 0.42), QPointF(x + w * 0.64, y + h * 0.2))
|
||||||
|
painter.drawRect(QRectF(x + w * 0.36, y + h * 0.56, w * 0.28, h * 0.24))
|
||||||
|
elif kind == "chevron":
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.4, y + h * 0.28), QPointF(x + w * 0.62, y + h * 0.5),
|
||||||
|
QPointF(x + w * 0.4, y + h * 0.72)]))
|
||||||
|
elif kind == "shield":
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QColor(colour))
|
||||||
|
painter.drawPolygon(QPolygonF([QPointF(x + w * 0.5, y + h * 0.16), QPointF(x + w * 0.82, y + h * 0.3),
|
||||||
|
QPointF(x + w * 0.82, y + h * 0.56), QPointF(x + w * 0.5, y + h * 0.84),
|
||||||
|
QPointF(x + w * 0.18, y + h * 0.56), QPointF(x + w * 0.18, y + h * 0.3)]))
|
||||||
|
elif kind == "check":
|
||||||
|
_pen(painter, colour, stroke * 1.2)
|
||||||
|
painter.drawPolyline(QPolygonF([QPointF(x + w * 0.32, y + h * 0.52), QPointF(x + w * 0.45, y + h * 0.65),
|
||||||
|
QPointF(x + w * 0.7, y + h * 0.37)]))
|
||||||
|
elif kind == "spark":
|
||||||
|
# The 千问 mark: three crossing strokes forming a six-pointed star.
|
||||||
|
_pen(painter, colour, stroke * 1.1)
|
||||||
|
centre = QPointF(x + w * 0.5, y + h * 0.5)
|
||||||
|
radius = min(w, h) * 0.3
|
||||||
|
for angle in (90, 30, -30):
|
||||||
|
from math import cos, radians, sin
|
||||||
|
dx, dy = cos(radians(angle)) * radius, -sin(radians(angle)) * radius
|
||||||
|
painter.drawLine(QPointF(centre.x() - dx, centre.y() - dy), QPointF(centre.x() + dx, centre.y() + dy))
|
||||||
|
elif kind == "knot":
|
||||||
|
# The OpenAI mark, reduced to the interlocking hexagon it is built from.
|
||||||
|
_pen(painter, colour, stroke)
|
||||||
|
from math import cos, radians, sin
|
||||||
|
centre = QPointF(x + w * 0.5, y + h * 0.5)
|
||||||
|
radius = min(w, h) * 0.3
|
||||||
|
points = [QPointF(centre.x() + cos(radians(angle)) * radius, centre.y() + sin(radians(angle)) * radius)
|
||||||
|
for angle in range(0, 360, 60)]
|
||||||
|
painter.drawPolygon(QPolygonF(points))
|
||||||
|
painter.drawLine(points[0], points[3])
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
|
||||||
|
class Glyph(QWidget):
|
||||||
|
"""A single painted icon at a fixed size."""
|
||||||
|
|
||||||
|
def __init__(self, kind: str, colour: str, size: int = 18, parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.kind, self.colour = kind, colour
|
||||||
|
self.setFixedSize(size, size)
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||||
|
|
||||||
|
def set_colour(self, colour: str) -> None:
|
||||||
|
if colour != self.colour:
|
||||||
|
self.colour = colour
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
painter = QPainter(self)
|
||||||
|
paint_glyph(painter, self.kind, QRectF(0, 0, self.width(), self.height()), self.colour)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
|
||||||
|
class LogoTile(QWidget):
|
||||||
|
"""A rounded tile with a glyph on it: the window mark and the two model marks."""
|
||||||
|
|
||||||
|
def __init__(self, kind: str, *, start: str, end: str, glyph: str = "#FFFFFF",
|
||||||
|
size: int = 34, radius: float = 10.0, circle: bool = False,
|
||||||
|
parent: QWidget | None = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.kind, self.start, self.end, self.glyph_colour = kind, start, end, glyph
|
||||||
|
self.radius, self.circle = radius, circle
|
||||||
|
self.setFixedSize(size, size)
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None: # noqa: N802 - Qt virtual
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
||||||
|
box = QRectF(0, 0, self.width(), self.height())
|
||||||
|
gradient = QLinearGradient(box.topLeft(), box.bottomRight())
|
||||||
|
gradient.setColorAt(0.0, QColor(self.start))
|
||||||
|
gradient.setColorAt(1.0, QColor(self.end))
|
||||||
|
painter.setPen(Qt.PenStyle.NoPen)
|
||||||
|
painter.setBrush(QBrush(gradient))
|
||||||
|
if self.circle:
|
||||||
|
painter.drawEllipse(box)
|
||||||
|
else:
|
||||||
|
painter.drawRoundedRect(box, self.radius, self.radius)
|
||||||
|
paint_glyph(painter, self.kind, box, self.glyph_colour)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
def minimumSizeHint(self) -> QSize:
|
||||||
|
return QSize(self.width(), self.height())
|
||||||
|
|
||||||
|
|
||||||
|
def window_mark(parent: QWidget | None = None, size: int = 38) -> LogoTile:
|
||||||
|
tile = LogoTile("check", start=TECH_BLUE["accent"], end=TECH_BLUE["accent_pressed"],
|
||||||
|
size=size, radius=11, parent=parent)
|
||||||
|
tile.setAccessibleName("诊断与药方对照")
|
||||||
|
return tile
|
||||||
|
|
||||||
|
|
||||||
|
def model_mark(model_key: str, parent: QWidget | None = None, size: int = 30) -> LogoTile:
|
||||||
|
if model_key == "openai":
|
||||||
|
return LogoTile("knot", start=TECH_BLUE["openai_dim"], end=TECH_BLUE["surface_2"],
|
||||||
|
glyph=TECH_BLUE["openai_text"], size=size, circle=True, parent=parent)
|
||||||
|
return LogoTile("spark", start=TECH_BLUE["qwen_dim"], end=TECH_BLUE["surface_2"],
|
||||||
|
glyph=TECH_BLUE["qwen_text"], size=size, radius=9, parent=parent)
|
||||||
|
|
||||||
|
|
||||||
|
def glyph_icon(kind: str, colour: str, size: int = 16) -> QIcon:
|
||||||
|
"""The same line art as a QIcon, for buttons that place their own icon and label."""
|
||||||
|
|
||||||
|
pixmap = QPixmap(size, size)
|
||||||
|
pixmap.fill(Qt.GlobalColor.transparent)
|
||||||
|
painter = QPainter(pixmap)
|
||||||
|
paint_glyph(painter, kind, QRectF(0, 0, size, size), colour)
|
||||||
|
painter.end()
|
||||||
|
return QIcon(pixmap)
|
||||||
@@ -283,3 +283,61 @@ def value_text(value: Any, field: str, labels: Mapping[str, str], fields: Mappin
|
|||||||
if field and field not in fields:
|
if field and field not in fields:
|
||||||
return system_text(value, labels, fields)
|
return system_text(value, labels, fields)
|
||||||
return plain_text(value)
|
return plain_text(value)
|
||||||
|
|
||||||
|
|
||||||
|
STATE_LABELS = {
|
||||||
|
"blank": "尚未开方", "not_generated": "尚无分析记录", "unavailable": "暂无可比结果",
|
||||||
|
"not_applicable": "尚未开方", "not_started": "尚未分析", "pending": "待分析",
|
||||||
|
"preparing": "准备资料", "waiting_sources": "等待转写/资料", "retry_wait": "等待重试", "blocked": "需完善资料关联",
|
||||||
|
"queued": "待分析", "waiting": "等待资料", "waiting_transcript": "等待转写",
|
||||||
|
"waiting_transcription": "等待转写", "running": "分析中", "processing": "分析中",
|
||||||
|
"retrying": "重试中", "succeeded": "已完成", "completed": "已完成", "success": "已完成",
|
||||||
|
"partial": "部分完成", "failed": "需重试", "cancelled": "已取消", "canceled": "已取消",
|
||||||
|
"stale": "处方已变更", "superseded": "处方已变更", "invalid": "已失效",
|
||||||
|
"prescription_changed": "处方已变更", "source_updated": "资料已更新", "voided": "处方已作废", "deleted": "处方已删除", "revoked": "权限已撤销",
|
||||||
|
"current": "当前版本", "valid": "当前有效", "complete": "资料清单完整",
|
||||||
|
"incomplete": "资料不全", "missing": "资料缺失", "unknown": "未确认",
|
||||||
|
"needs_patient_link": "需完善患者关联", "patient_unlinked": "需完善患者关联",
|
||||||
|
"independent_baseline": "独立基线", "baseline": "独立基线",
|
||||||
|
"latest_context": "最新资料对照", "supplemental": "最新资料对照",
|
||||||
|
"assisted_revision": "AI 辅助后修订", "ai_assisted": "AI 辅助后修订",
|
||||||
|
"non_independent": "非独立对照", "auxiliary": "辅助复核",
|
||||||
|
"comparable": "可比", "not_comparable": "不可比",
|
||||||
|
"available_for_review": "供医生复核", "insufficient_data": "资料不足,暂不提供候选用药",
|
||||||
|
"withheld_for_risk": "因风险暂缓候选用药", "viewed": "已查看", "needs_information": "需补充资料",
|
||||||
|
"not_adopted": "不采纳", "reviewed": "已复核",
|
||||||
|
"per_dose": "每剂", "per_day": "每日", "matched": "共同药味", "doctor_only": "仅医生方", "candidate_only": "仅模型方",
|
||||||
|
"insufficient_sample": "样本不足", "descriptive_only": "仅作描述性统计",
|
||||||
|
}
|
||||||
|
FIELD_LABELS = {
|
||||||
|
"summary": "概要", "timeline": "病程", "analysis": "综合分析", "tcm_analysis": "中医辨证",
|
||||||
|
"diagnosis": "辨证分析", "treatment_advice": "治疗与随访建议", "risk_assessment": "需复核风险",
|
||||||
|
"evidence_references": "证据来源编号", "missing_information": "待补充资料", "level": "风险等级", "label": "说明",
|
||||||
|
"risk_warnings": "需复核风险", "risks": "风险", "follow_up": "随访建议", "evidence": "依据",
|
||||||
|
"sources": "来源", "manifest": "来源清单", "missing": "资料缺口", "status": "状态",
|
||||||
|
"reason": "原因", "name": "药名", "herb_name": "规范药名", "canonical_name": "规范药名",
|
||||||
|
"processing": "炮制", "dosage": "剂量", "dose": "剂量", "unit": "单位", "dose_basis": "剂量基准",
|
||||||
|
"formula_type": "主辅方", "doctor_dosage": "医生剂量", "candidate_dosage": "模型剂量",
|
||||||
|
"doctor_dose": "医生剂量", "candidate_dose": "模型剂量", "ai_dose": "模型剂量",
|
||||||
|
"contribution": "匹配贡献", "ratio": "匹配贡献", "match_ratio": "匹配贡献", "match": "匹配情况",
|
||||||
|
"prescription_name": "候选方名称", "prescription_type": "剂型", "herbs": "药味",
|
||||||
|
"dose_count": "剂数", "usage_days": "疗程(天)", "times_per_day": "每日服次",
|
||||||
|
"usage_instruction": "服法", "usage_time": "服药时间", "usage_way": "给药途径",
|
||||||
|
"rationale": "方义与依据", "usage_differences": "用法、疗程与风险差异", "normalization": "规范化记录",
|
||||||
|
"algorithm_version": "算法版本", "dictionary_version": "药材字典版本", "model_version": "模型版本",
|
||||||
|
"prompt_version": "提示词版本", "doctor_count": "医生药项数", "candidate_count": "候选药项数",
|
||||||
|
"matched_count": "共同药项数", "coverage": "模型资料覆盖", "source_summary": "来源汇总",
|
||||||
|
"cutoff_at": "资料截止时间", "generated_at": "报告生成时间", "comment": "复核意见",
|
||||||
|
"match_type": "增减药项", "administration_route": "给药途径", "group": "用药组",
|
||||||
|
"delivered": "已送达", "unreadable": "不可读", "unsupported": "不支持", "parsed": "已解析",
|
||||||
|
"diagnosis_count": "病历数", "prescription_count": "历史处方数", "chat_count": "聊天记录数",
|
||||||
|
"daily_record_count": "日常记录数", "transcript_count": "转写数", "attachment_count": "附件数",
|
||||||
|
"files": "附件处理清单", "source_ids": "已读取来源编号", "source_id": "来源编号", "file_id": "附件编号",
|
||||||
|
"complete": "资料清单完整", "source_complete": "文字来源齐全", "transmitted": "附件已送达", "critical": "关键资料缺口",
|
||||||
|
"baseline_eligible": "独立基线统计资格", "baseline_exclusion_reasons": "基线排除原因", "instructions": "特殊煎服说明",
|
||||||
|
"versions": "版本信息", "strata": "按版本分层", "count": "样本数", "mean": "均值", "median": "中位数",
|
||||||
|
"distribution": "一致度分布", "sample_status": "样本说明",
|
||||||
|
}
|
||||||
|
STATE_LABELS.update(SYSTEM_LABELS)
|
||||||
|
FIELD_LABELS.update(EXTRA_FIELD_LABELS)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,210 @@
|
|||||||
|
"""The analysis console's palette, and the switch between its dark and light themes.
|
||||||
|
|
||||||
|
The prescription analysis window has its own theme, separate from the workstation's light chrome:
|
||||||
|
tech blue for the interface itself, and two model hues (cyan for 千问, violet for OpenAI) that stay
|
||||||
|
apart from the interface colour and from each other. Severity keeps amber and rose, so a reading
|
||||||
|
never depends on hue alone.
|
||||||
|
|
||||||
|
``CONSOLE`` is the active palette and is mutated in place by :func:`use_theme`, so every module
|
||||||
|
that imported it sees the new values. Anything derived from it — a stylesheet string, a colour
|
||||||
|
constant — must be rebuilt in a callback registered through :func:`on_theme_changed`.
|
||||||
|
|
||||||
|
Key names mirror ``reception_style.TECH_BLUE`` so a widget can take either palette.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import sys
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtGui import QFont
|
||||||
|
|
||||||
|
DARK: dict[str, str] = {
|
||||||
|
# ground and cards
|
||||||
|
"canvas": "#080D18",
|
||||||
|
"canvas_soft": "#0C1322",
|
||||||
|
"surface": "#0F1726",
|
||||||
|
"surface_2": "#131D2E",
|
||||||
|
"raised": "#1A2637",
|
||||||
|
"line": "#22304A",
|
||||||
|
"line_soft": "#18233A",
|
||||||
|
# type
|
||||||
|
"heading": "#E8EFFB",
|
||||||
|
"text": "#B6C5DA",
|
||||||
|
"muted": "#8195AF",
|
||||||
|
"faint": "#6A7F99",
|
||||||
|
"selected_text": "#E8EFFB",
|
||||||
|
# interface colour
|
||||||
|
"accent": "#2E7BF6",
|
||||||
|
"accent_text": "#6BA5FF",
|
||||||
|
"accent_pressed": "#1A5FD0",
|
||||||
|
"selection": "#142645",
|
||||||
|
"selection_line": "#1B3E77",
|
||||||
|
# model hues
|
||||||
|
"qwen": "#17BFDD",
|
||||||
|
"qwen_text": "#55D8EE",
|
||||||
|
"qwen_dim": "#10303C",
|
||||||
|
"openai": "#9B7BFF",
|
||||||
|
"openai_text": "#B69FFF",
|
||||||
|
"openai_dim": "#241F48",
|
||||||
|
# severity
|
||||||
|
"amber": "#F5B942",
|
||||||
|
"amber_text": "#F8C661",
|
||||||
|
"amber_dim": "#2C2415",
|
||||||
|
"rose": "#F4697A",
|
||||||
|
"rose_text": "#F88694",
|
||||||
|
"rose_dim": "#2C1620",
|
||||||
|
"ok": "#2E7BF6",
|
||||||
|
"zebra": "#0E1626",
|
||||||
|
"grid_line": "#191F2C",
|
||||||
|
}
|
||||||
|
|
||||||
|
LIGHT: dict[str, str] = {
|
||||||
|
"canvas": "#F1F5FC",
|
||||||
|
"canvas_soft": "#E7EEFA",
|
||||||
|
"surface": "#FFFFFF",
|
||||||
|
"surface_2": "#F5F8FE",
|
||||||
|
"raised": "#E8EFFA",
|
||||||
|
"line": "#D5E1F2",
|
||||||
|
"line_soft": "#E3EBF8",
|
||||||
|
"heading": "#0A182E",
|
||||||
|
"text": "#2A3C56",
|
||||||
|
"muted": "#54697F",
|
||||||
|
"faint": "#7A8DA3",
|
||||||
|
"selected_text": "#0A182E",
|
||||||
|
"accent": "#1A5FD0",
|
||||||
|
"accent_text": "#124DAE",
|
||||||
|
"accent_pressed": "#0E3F90",
|
||||||
|
"selection": "#E8EFFA",
|
||||||
|
"selection_line": "#AEC7EE",
|
||||||
|
"qwen": "#0C89AC",
|
||||||
|
"qwen_text": "#086C88",
|
||||||
|
"qwen_dim": "#E2F5FA",
|
||||||
|
"openai": "#6B4AE0",
|
||||||
|
"openai_text": "#5537C6",
|
||||||
|
"openai_dim": "#EDE8FE",
|
||||||
|
"amber": "#B4791A",
|
||||||
|
"amber_text": "#8F5F0B",
|
||||||
|
"amber_dim": "#FCF2DF",
|
||||||
|
"rose": "#D0435A",
|
||||||
|
"rose_text": "#AE3149",
|
||||||
|
"rose_dim": "#FCE9EC",
|
||||||
|
"ok": "#1A5FD0",
|
||||||
|
"zebra": "#FAFBFE",
|
||||||
|
"grid_line": "#DDE4EE",
|
||||||
|
}
|
||||||
|
|
||||||
|
CONSOLE: dict[str, str] = dict(DARK)
|
||||||
|
_THEME = "dark"
|
||||||
|
_LISTENERS: list[Callable[[], None]] = []
|
||||||
|
|
||||||
|
|
||||||
|
def current_theme() -> str:
|
||||||
|
return _THEME
|
||||||
|
|
||||||
|
|
||||||
|
def on_theme_changed(callback: Callable[[], None]) -> Callable[[], None]:
|
||||||
|
"""Register a rebuild for anything derived from the palette; returns the callback."""
|
||||||
|
|
||||||
|
_LISTENERS.append(callback)
|
||||||
|
return callback
|
||||||
|
|
||||||
|
|
||||||
|
def use_theme(name: str) -> str:
|
||||||
|
"""Switch the active palette in place and let every derived value rebuild itself."""
|
||||||
|
|
||||||
|
global _THEME
|
||||||
|
palette = LIGHT if name == "light" else DARK
|
||||||
|
_THEME = "light" if name == "light" else "dark"
|
||||||
|
CONSOLE.clear()
|
||||||
|
CONSOLE.update(palette)
|
||||||
|
_rebuild()
|
||||||
|
return _THEME
|
||||||
|
|
||||||
|
|
||||||
|
def toggle_theme() -> str:
|
||||||
|
return use_theme("light" if _THEME == "dark" else "dark")
|
||||||
|
|
||||||
|
|
||||||
|
def _rebuild() -> None:
|
||||||
|
for callback in list(_LISTENERS):
|
||||||
|
callback()
|
||||||
|
|
||||||
|
|
||||||
|
MODEL_HUE = {"qwen": CONSOLE["qwen"], "openai": CONSOLE["openai"]}
|
||||||
|
MODEL_TEXT = {"qwen": CONSOLE["qwen_text"], "openai": CONSOLE["openai_text"]}
|
||||||
|
MODEL_DIM = {"qwen": CONSOLE["qwen_dim"], "openai": CONSOLE["openai_dim"]}
|
||||||
|
|
||||||
|
|
||||||
|
@on_theme_changed
|
||||||
|
def _rebuild_model_hues() -> None:
|
||||||
|
MODEL_HUE.update({"qwen": CONSOLE["qwen"], "openai": CONSOLE["openai"]})
|
||||||
|
MODEL_TEXT.update({"qwen": CONSOLE["qwen_text"], "openai": CONSOLE["openai_text"]})
|
||||||
|
MODEL_DIM.update({"qwen": CONSOLE["qwen_dim"], "openai": CONSOLE["openai_dim"]})
|
||||||
|
|
||||||
|
|
||||||
|
RADIUS = 10
|
||||||
|
RADIUS_SM = 7
|
||||||
|
|
||||||
|
# The design sets numbers in a condensed face so columns of figures line up; the fallbacks keep
|
||||||
|
# the same tabular behaviour when Bahnschrift is missing.
|
||||||
|
NUM_FAMILIES = ("Bahnschrift", "Segoe UI", "Microsoft YaHei UI", "Microsoft YaHei")
|
||||||
|
|
||||||
|
|
||||||
|
def num_font(size: int, *, weight: QFont.Weight | None = None) -> QFont:
|
||||||
|
"""A tabular face for figures, at the pixel size the design states."""
|
||||||
|
|
||||||
|
font = QFont()
|
||||||
|
font.setFamilies(list(NUM_FAMILIES))
|
||||||
|
font.setPixelSize(size)
|
||||||
|
if weight is not None:
|
||||||
|
font.setWeight(weight)
|
||||||
|
font.setStyleStrategy(QFont.StyleStrategy.PreferAntialias)
|
||||||
|
return font
|
||||||
|
|
||||||
|
|
||||||
|
# Windows draws the title bar itself, so the console's dark ground stops at the frame unless the
|
||||||
|
# window asks DWM for a matching caption. Windows 11 (build 22000+) honours these attributes;
|
||||||
|
# anywhere else the call fails and the native bar is left as it is.
|
||||||
|
_DWMWA_USE_IMMERSIVE_DARK_MODE = 20
|
||||||
|
_DWMWA_BORDER_COLOR = 34
|
||||||
|
_DWMWA_CAPTION_COLOR = 35
|
||||||
|
_DWMWA_TEXT_COLOR = 36
|
||||||
|
|
||||||
|
|
||||||
|
def _colorref(value: str) -> int:
|
||||||
|
"""A ``#RRGGBB`` string as the ``0x00BBGGRR`` integer DWM expects."""
|
||||||
|
|
||||||
|
colour = value.lstrip("#")
|
||||||
|
red, green, blue = (int(colour[index:index + 2], 16) for index in (0, 2, 4))
|
||||||
|
return (blue << 16) | (green << 8) | red
|
||||||
|
|
||||||
|
|
||||||
|
def apply_window_chrome(widget: Any) -> bool:
|
||||||
|
"""Paint the native title bar in the palette that is active right now.
|
||||||
|
|
||||||
|
Returns whether DWM accepted the change, so a caller can tell "not Windows 11" from "done".
|
||||||
|
"""
|
||||||
|
|
||||||
|
if sys.platform != "win32":
|
||||||
|
return False
|
||||||
|
handle = int(widget.winId())
|
||||||
|
if not handle:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
dwm = ctypes.windll.dwmapi
|
||||||
|
except (AttributeError, OSError): # pragma: no cover - not Windows
|
||||||
|
return False
|
||||||
|
dark = ctypes.c_int(1 if _THEME == "dark" else 0)
|
||||||
|
caption = ctypes.c_uint(_colorref(CONSOLE["canvas"]))
|
||||||
|
text = ctypes.c_uint(_colorref(CONSOLE["heading"]))
|
||||||
|
border = ctypes.c_uint(_colorref(CONSOLE["line"]))
|
||||||
|
applied = False
|
||||||
|
for attribute, value in ((_DWMWA_USE_IMMERSIVE_DARK_MODE, dark), (_DWMWA_CAPTION_COLOR, caption),
|
||||||
|
(_DWMWA_TEXT_COLOR, text), (_DWMWA_BORDER_COLOR, border)):
|
||||||
|
result = dwm.DwmSetWindowAttribute(ctypes.c_void_p(handle), ctypes.c_int(attribute),
|
||||||
|
ctypes.byref(value), ctypes.sizeof(value))
|
||||||
|
applied = applied or result == 0
|
||||||
|
return applied
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -445,7 +445,22 @@ def _formula(value: Any) -> str:
|
|||||||
return "辅方" if text in {"2", "aux", "auxiliary", "辅方"} else "主方"
|
return "辅方" if text in {"2", "aux", "auxiliary", "辅方"} else "主方"
|
||||||
|
|
||||||
|
|
||||||
def _order_warnings(row: Any) -> list[str]:
|
def _is_blank_prescription(row: Any) -> bool:
|
||||||
|
if row is None or _truthy(get_value(row, "is_system_auto")):
|
||||||
|
return True
|
||||||
|
raw = getattr(row, "raw", None)
|
||||||
|
source = raw if isinstance(raw, Mapping) and raw else row
|
||||||
|
missing = object()
|
||||||
|
herbs = get_value(source, "herbs", missing)
|
||||||
|
# Compact historical rows may omit herbs; omission is not an empty prescription.
|
||||||
|
if herbs is missing:
|
||||||
|
return False
|
||||||
|
return not isinstance(herbs, (list, tuple)) or not any(
|
||||||
|
str(get_value(herb, "name", "") or "").strip() for herb in herbs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _order_warnings(row: Any) -> list[str]:
|
||||||
"""Match the PC list's linked-order checks, including blank herb rows."""
|
"""Match the PC list's linked-order checks, including blank herb rows."""
|
||||||
|
|
||||||
raw = getattr(row, "raw", None)
|
raw = getattr(row, "raw", None)
|
||||||
@@ -1183,7 +1198,7 @@ class PrescriptionsPage(QWidget):
|
|||||||
self.view_button = self._action_button("查看", "cf.prescription/read", self._view_selected)
|
self.view_button = self._action_button("查看", "cf.prescription/read", self._view_selected)
|
||||||
toolbar.addWidget(self.view_button)
|
toolbar.addWidget(self.view_button)
|
||||||
self.ai_report_button = self._action_button("AI 报告", "tcm.prescriptionAi/reports", self._open_ai_report)
|
self.ai_report_button = self._action_button("AI 报告", "tcm.prescriptionAi/reports", self._open_ai_report)
|
||||||
self.ai_report_button.setVisible(can_open_issued_ai(self.permissions) and callable(getattr(self.repository, "list_prescription_ai_reports", None)))
|
self.ai_report_button.hide()
|
||||||
toolbar.addWidget(self.ai_report_button)
|
toolbar.addWidget(self.ai_report_button)
|
||||||
self.patch_button = self._action_button(
|
self.patch_button = self._action_button(
|
||||||
"修改患者", "tcm.prescription/patchPatient", self._patch_selected
|
"修改患者", "tcm.prescription/patchPatient", self._patch_selected
|
||||||
@@ -1565,7 +1580,7 @@ class PrescriptionsPage(QWidget):
|
|||||||
actions_host,
|
actions_host,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if self._ai_enabled and can_open_issued_ai(self.permissions):
|
if self._ai_enabled and can_open_issued_ai(self.permissions) and not _is_blank_prescription(row):
|
||||||
actions.addWidget(
|
actions.addWidget(
|
||||||
_row_action_button(
|
_row_action_button(
|
||||||
"eye", "AI 报告",
|
"eye", "AI 报告",
|
||||||
@@ -1618,6 +1633,8 @@ class PrescriptionsPage(QWidget):
|
|||||||
if item is None or top + self.table.rowHeight(index) <= 0 or top >= viewport.height():
|
if item is None or top + self.table.rowHeight(index) <= 0 or top >= viewport.height():
|
||||||
continue
|
continue
|
||||||
row = item.data(Qt.ItemDataRole.UserRole)
|
row = item.data(Qt.ItemDataRole.UserRole)
|
||||||
|
if _is_blank_prescription(row):
|
||||||
|
continue
|
||||||
value = _int(first_value(row, "id", "prescription_id"), 0)
|
value = _int(first_value(row, "id", "prescription_id"), 0)
|
||||||
if value > 0:
|
if value > 0:
|
||||||
ids.append(value)
|
ids.append(value)
|
||||||
@@ -1628,6 +1645,15 @@ class PrescriptionsPage(QWidget):
|
|||||||
self._ai_scroll_timer.start()
|
self._ai_scroll_timer.start()
|
||||||
|
|
||||||
def _load_ai_statuses(self) -> None:
|
def _load_ai_statuses(self) -> None:
|
||||||
|
only_blank = self.table.rowCount() > 0 and all(
|
||||||
|
_is_blank_prescription(self.table.item(index, 0).data(Qt.ItemDataRole.UserRole))
|
||||||
|
for index in range(self.table.rowCount())
|
||||||
|
)
|
||||||
|
self.ai_status_notice.setVisible(not only_blank)
|
||||||
|
if only_blank:
|
||||||
|
self._ai_timer.stop()
|
||||||
|
self._set_ai_columns(False)
|
||||||
|
return
|
||||||
method = getattr(self.repository, "list_prescription_ai_statuses", None)
|
method = getattr(self.repository, "list_prescription_ai_statuses", None)
|
||||||
if not has_permission(self.permissions, "tcm.prescriptionAi/statuses", default=False):
|
if not has_permission(self.permissions, "tcm.prescriptionAi/statuses", default=False):
|
||||||
self.ai_status_notice.setText("AI 分析:当前账号没有查看分析状态的权限,请联系管理员授权。")
|
self.ai_status_notice.setText("AI 分析:当前账号没有查看分析状态的权限,请联系管理员授权。")
|
||||||
@@ -1711,15 +1737,17 @@ class PrescriptionsPage(QWidget):
|
|||||||
row = self.table.item(index, 0).data(Qt.ItemDataRole.UserRole)
|
row = self.table.item(index, 0).data(Qt.ItemDataRole.UserRole)
|
||||||
value = _int(first_value(row, "id", "prescription_id"), 0)
|
value = _int(first_value(row, "id", "prescription_id"), 0)
|
||||||
batch = self._ai_statuses.get(value)
|
batch = self._ai_statuses.get(value)
|
||||||
if batch is None:
|
blank = _is_blank_prescription(row)
|
||||||
|
if batch is None and not blank:
|
||||||
continue
|
continue
|
||||||
state = state_text(batch) if batch else ("尚未开方" if _truthy(get_value(row, "is_system_auto")) else "尚无分析记录")
|
state = "" if blank else (state_text(batch) if batch else "尚无分析记录")
|
||||||
for column, text in ((11, state), (12, agreement_text(batch))):
|
agreement = "" if blank else agreement_text(batch)
|
||||||
|
for column, text in ((11, state), (12, agreement)):
|
||||||
item = self.table.item(index, column)
|
item = self.table.item(index, column)
|
||||||
if item.text() != text:
|
if item.text() != text:
|
||||||
item.setText(text)
|
item.setText(text)
|
||||||
changed = True
|
changed = True
|
||||||
item.setToolTip(status_tooltip(batch))
|
item.setToolTip("" if blank else status_tooltip(batch))
|
||||||
item.setData(Qt.ItemDataRole.AccessibleTextRole, text)
|
item.setData(Qt.ItemDataRole.AccessibleTextRole, text)
|
||||||
if changed:
|
if changed:
|
||||||
self.table.resizeRowsToContents()
|
self.table.resizeRowsToContents()
|
||||||
@@ -1736,6 +1764,8 @@ class PrescriptionsPage(QWidget):
|
|||||||
|
|
||||||
def _open_ai_report(self) -> None:
|
def _open_ai_report(self) -> None:
|
||||||
row = self._selected()
|
row = self._selected()
|
||||||
|
if _is_blank_prescription(row):
|
||||||
|
return
|
||||||
value = _int(first_value(row, "id", "prescription_id"), 0)
|
value = _int(first_value(row, "id", "prescription_id"), 0)
|
||||||
if value > 0:
|
if value > 0:
|
||||||
present_issued_prescription_ai(self.repository, self.permissions, self, prescription_id=value)
|
present_issued_prescription_ai(self.repository, self.permissions, self, prescription_id=value)
|
||||||
@@ -1748,6 +1778,8 @@ class PrescriptionsPage(QWidget):
|
|||||||
return
|
return
|
||||||
self.table.selectRow(item.row())
|
self.table.selectRow(item.row())
|
||||||
row = self._selected()
|
row = self._selected()
|
||||||
|
if _is_blank_prescription(row):
|
||||||
|
return
|
||||||
menu = QMenu(self.table)
|
menu = QMenu(self.table)
|
||||||
action = menu.addAction("AI 报告 / 逐味对照")
|
action = menu.addAction("AI 报告 / 逐味对照")
|
||||||
action.triggered.connect(lambda: self._run_row_action(row, self._open_ai_report))
|
action.triggered.connect(lambda: self._run_row_action(row, self._open_ai_report))
|
||||||
@@ -1795,7 +1827,13 @@ class PrescriptionsPage(QWidget):
|
|||||||
row = self.table.current_data()
|
row = self.table.current_data()
|
||||||
active = not self._mutation_pending
|
active = not self._mutation_pending
|
||||||
self.view_button.setEnabled(active and row is not None)
|
self.view_button.setEnabled(active and row is not None)
|
||||||
self.ai_report_button.setEnabled(row is not None)
|
ai_available = (
|
||||||
|
not _is_blank_prescription(row)
|
||||||
|
and can_open_issued_ai(self.permissions)
|
||||||
|
and callable(getattr(self.repository, "list_prescription_ai_reports", None))
|
||||||
|
)
|
||||||
|
self.ai_report_button.setVisible(ai_available)
|
||||||
|
self.ai_report_button.setEnabled(ai_available)
|
||||||
self.patch_button.setEnabled(active and can_patch_patient(row))
|
self.patch_button.setEnabled(active and can_patch_patient(row))
|
||||||
self.create_order_button.setEnabled(active and can_create_order(row))
|
self.create_order_button.setEnabled(active and can_create_order(row))
|
||||||
self.edit_button.setEnabled(active and can_edit_or_delete(row))
|
self.edit_button.setEnabled(active and can_edit_or_delete(row))
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ from uuid import UUID
|
|||||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import QEvent, QObject, QRect, Qt
|
||||||
from PySide6.QtWidgets import QApplication, QPushButton
|
from PySide6.QtWidgets import QApplication, QPushButton, QWidget
|
||||||
|
|
||||||
from doctor_workstation.services import DemoDoctorRepository
|
from doctor_workstation.services import DemoDoctorRepository
|
||||||
from doctor_workstation.services.repository import RemoteDoctorRepository
|
from doctor_workstation.services.repository import RemoteDoctorRepository
|
||||||
@@ -138,6 +138,215 @@ def test_shared_report_reads_only_and_renders_each_model_independently(applicati
|
|||||||
assert repository.calls == calls
|
assert repository.calls == calls
|
||||||
|
|
||||||
|
|
||||||
|
def test_report_open_and_poll_never_show_auxiliary_windows(application: QApplication, immediate: None) -> None:
|
||||||
|
shown_windows = []
|
||||||
|
|
||||||
|
class WindowObserver(QObject):
|
||||||
|
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
|
||||||
|
if event.type() == QEvent.Type.Show and isinstance(watched, QWidget) and watched.isWindow():
|
||||||
|
shown_windows.append((watched.metaObject().className(), watched.windowTitle()))
|
||||||
|
return False
|
||||||
|
|
||||||
|
observer = WindowObserver()
|
||||||
|
application.installEventFilter(observer)
|
||||||
|
dialog = None
|
||||||
|
try:
|
||||||
|
repository = Repository()
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
initial_windows = list(shown_windows)
|
||||||
|
shown_windows.clear()
|
||||||
|
dialog.tabs.setCurrentIndex(3)
|
||||||
|
dialog.model_views["qwen"]["comment"].setPlainText("正在填写的复核意见")
|
||||||
|
for _ in range(3):
|
||||||
|
dialog._timer.timeout.emit()
|
||||||
|
dialog._progress_timer.timeout.emit()
|
||||||
|
application.processEvents()
|
||||||
|
assert len([call for call in repository.calls if call[0] == "detail"]) == 4
|
||||||
|
assert shown_windows == [], "Polling must not show even transient top-level widgets"
|
||||||
|
assert initial_windows == [("IssuedPrescriptionAiDialog", dialog.windowTitle())]
|
||||||
|
assert dialog.tabs.currentIndex() == 3
|
||||||
|
assert dialog.model_views["qwen"]["comment"].toPlainText() == "正在填写的复核意见"
|
||||||
|
assert dialog.chip_row.count() == 2
|
||||||
|
assert all(dialog.chip_row.itemAt(index).widget().isVisible() for index in range(2))
|
||||||
|
assert dialog._timer.isActive() and dialog._progress_timer.isActive()
|
||||||
|
finally:
|
||||||
|
application.removeEventFilter(observer)
|
||||||
|
if dialog is not None:
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_navigation_names_every_destination_once(application: QApplication, immediate: None) -> None:
|
||||||
|
"""A duplicated tab name makes the navigation unreadable; the live progress lives in the one tab."""
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(Repository(), ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
names = [dialog.tabs.tabText(index) for index in range(dialog.tabs.count())]
|
||||||
|
assert len(names) == len(set(names)), names
|
||||||
|
assert names.count("处理进度") == 1
|
||||||
|
# Those pages are hosted in a scroll area, so the tab holds the host, not the page itself.
|
||||||
|
for key in ("per_herb", "gaps", "history"):
|
||||||
|
assert dialog.tabs.indexOf(dialog.tab_pages[key]) >= 0
|
||||||
|
bar = dialog.model_views["qwen"]["progress_bar"]
|
||||||
|
assert dialog.pipeline_page.isAncestorOf(bar)
|
||||||
|
assert dialog.pipeline_page.stage_labels["qwen"].isHidden()
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("width", "height"), [(1280, 860), (1024, 700), (940, 640)])
|
||||||
|
def test_prescription_workspace_is_the_primary_view(application: QApplication, immediate: None, width: int, height: int) -> None:
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(Repository(), ["*"], prescription_id=801)
|
||||||
|
dialog.resize(width, height)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert (dialog.width(), dialog.height()) == (width, height)
|
||||||
|
assert dialog.tabs.currentWidget() is dialog.comparison_panel
|
||||||
|
assert dialog.tabs.tabText(0) == "对比总览"
|
||||||
|
assert dialog.comparison_panel.isVisible()
|
||||||
|
assert dialog.tabs.height() > height * 0.28
|
||||||
|
assert dialog.model_views["qwen"]["comment"].isVisible()
|
||||||
|
# The chart strip is on the first screen but must never outgrow its budget, and a model
|
||||||
|
# without a comparable score shows no filled bar.
|
||||||
|
assert dialog.model_views["qwen"]["agreement_bar"].value() == 0
|
||||||
|
assert dialog.model_views["openai"]["agreement_bar"].isHidden()
|
||||||
|
assert dialog.model_views["qwen"]["gauge"].accessibleDescription() == "0.0%"
|
||||||
|
assert dialog.model_views["openai"]["gauge"].accessibleDescription() == "暂无可比结果"
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("width", "height"), [(1440, 940), (1280, 860), (1024, 700), (940, 640)])
|
||||||
|
def test_review_controls_remain_inside_the_sidebar(application: QApplication, immediate: None,
|
||||||
|
width: int, height: int) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
model = repository.batches[0]["models"]["qwen"]
|
||||||
|
herb = model["candidate"]["herbs"][0]
|
||||||
|
model["comparison"]["rows"][0].update(key="黄芪", doctor={**herb, "dosage": 16}, candidate=herb)
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.resize(width, height)
|
||||||
|
dialog.show()
|
||||||
|
panel = dialog.comparison_panel
|
||||||
|
for _ in range(4):
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.nav_buttons["overview"].isChecked()
|
||||||
|
for key in ai.MODELS:
|
||||||
|
dialog.review_model.setCurrentIndex(0 if key == "qwen" else 1)
|
||||||
|
application.processEvents()
|
||||||
|
views = dialog.model_views[key]
|
||||||
|
for control in (dialog.review_model, views["review_state"], views["comment"], views["save"]):
|
||||||
|
assert control.isVisible()
|
||||||
|
bounds = QRect(control.mapTo(panel.checklist, control.rect().topLeft()), control.size())
|
||||||
|
assert panel.checklist.rect().contains(bounds), (key, control.accessibleName(), bounds)
|
||||||
|
assert ai.MODELS[key] in dialog.save_review_button.toolTip()
|
||||||
|
assert panel.checklist.isVisible()
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_hero_band_carries_the_frozen_identity_and_yields_on_short_windows(application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0]["doctor_snapshot"] = {
|
||||||
|
"patient": {"name": "测试患者", "gender_label": "女", "age": 58},
|
||||||
|
"diagnosis": {"clinical_diagnosis": "消渴病 气阴两虚"},
|
||||||
|
"prescription": {"prescription_type": "浓缩水丸", "dose_count": 1, "dose_unit": "剂"},
|
||||||
|
}
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.resize(1280, 860)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert "测试患者 · 女 · 58 岁" in dialog.identity.text()
|
||||||
|
assert "消渴病 气阴两虚" in dialog.identity.text()
|
||||||
|
assert "浓缩水丸 · 1 剂" in dialog.identity.text()
|
||||||
|
assert dialog.fact_values["basis"].text() == "同单位 · 每剂"
|
||||||
|
assert dialog.fact_values["prescription"].text() == "801 / 501"
|
||||||
|
dialog.resize(1024, 700)
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.fact_values["batch"].text() == "#40"
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_hero_band_shows_dashes_when_the_snapshot_is_not_deployed(application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0].pop("doctor_snapshot", None)
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.resize(1280, 860)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.identity.text() == "尚无冻结快照"
|
||||||
|
assert dialog.fact_values["prescription"].text().startswith("801")
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_failed_model_states_the_reason_and_offers_its_own_retry(application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0]["models"]["openai"].update(status="failed", error_code="INVALID_REPORT_OUTPUT",
|
||||||
|
error_message="INVALID_REPORT_OUTPUT")
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.model_views["openai"]["failure"].isVisible()
|
||||||
|
text = dialog.model_views["openai"]["failure_text"].text()
|
||||||
|
assert "OpenAI 失败" in text and "格式校验" in text and "原资料快照" in text
|
||||||
|
assert "INVALID_REPORT_OUTPUT" not in text
|
||||||
|
assert dialog.model_views["openai"]["retry"].isEnabled()
|
||||||
|
assert dialog.model_views["openai"]["retry"].isVisible()
|
||||||
|
# A model that is still running never shows a failure row.
|
||||||
|
dialog.model_views["openai"]["retry"].click()
|
||||||
|
application.processEvents()
|
||||||
|
assert ("retry", (40, "openai")) in repository.calls
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_exhausted_manual_retries_point_at_regeneration_instead_of_retry(application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0]["models"]["openai"].update(status="failed", error_code="UPSTREAM_TIMEOUT",
|
||||||
|
error_message="UPSTREAM_TIMEOUT", manual_retries=2)
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
text = dialog.model_views["openai"]["failure_text"].text()
|
||||||
|
assert "手动重试次数已用完" in text and "重新分析" in text
|
||||||
|
assert not dialog.model_views["openai"]["retry"].isVisible()
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_inline_review_keeps_model_drafts_and_saves_selected_model(application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0]["models"]["openai"].update(report_id=92, report={"summary": "已保存报告"})
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
dialog.model_views["qwen"]["comment"].setPlainText("千问复核草稿")
|
||||||
|
dialog.review_model.setCurrentIndex(1)
|
||||||
|
dialog.model_views["openai"]["comment"].setPlainText("OpenAI复核草稿")
|
||||||
|
dialog.model_views["openai"]["review_state"].setCurrentIndex(3)
|
||||||
|
dialog._poll()
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.review_model.currentData() == "openai"
|
||||||
|
assert dialog.model_views["qwen"]["comment"].toPlainText() == "千问复核草稿"
|
||||||
|
assert dialog.model_views["openai"]["comment"].toPlainText() == "OpenAI复核草稿"
|
||||||
|
dialog.save_review_button.click()
|
||||||
|
assert ("review", (40, "openai", "reviewed", "OpenAI复核草稿")) in repository.calls
|
||||||
|
assert not any(call[0] == "review" and call[1][1] == "qwen" for call in repository.calls)
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_workspace_links_reach_existing_supporting_reports(application: QApplication, immediate: None) -> None:
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(Repository(), ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
# Each link opens the composed page that answers it, not the raw saved text.
|
||||||
|
for field, key in (("report", "report"), ("candidate", "candidate"), ("comparison", "per_herb"),
|
||||||
|
("sources", "gaps"), ("progress", "pipeline"), ("doctor", "original")):
|
||||||
|
dialog.comparison_panel.open_report.emit(field)
|
||||||
|
assert dialog.tabs.currentWidget() is dialog.tab_pages[key], field
|
||||||
|
dialog.nav_buttons["overview"].click()
|
||||||
|
assert dialog.tabs.currentWidget() is dialog.comparison_panel
|
||||||
|
assert dialog.nav_buttons["overview"].isChecked()
|
||||||
|
assert not dialog.nav_buttons["gaps"].isChecked()
|
||||||
|
dialog.nav_buttons["per_herb"].click()
|
||||||
|
assert dialog.tabs.currentWidget() is dialog.tab_pages["per_herb"]
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
def test_retry_and_review_target_only_selected_model(application: QApplication, immediate: None) -> None:
|
def test_retry_and_review_target_only_selected_model(application: QApplication, immediate: None) -> None:
|
||||||
repository = Repository()
|
repository = Repository()
|
||||||
repository.batches[0]["status"] = "partial"
|
repository.batches[0]["status"] = "partial"
|
||||||
@@ -250,6 +459,69 @@ def test_list_batches_visible_ids_and_stops_on_hide(application: QApplication, i
|
|||||||
assert repository.calls == calls
|
assert repository.calls == calls
|
||||||
|
|
||||||
|
|
||||||
|
def test_blank_list_row_has_no_ai_display_or_entry_points(application: QApplication, immediate: None, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
opened = []
|
||||||
|
monkeypatch.setattr(page_module, "present_issued_prescription_ai", lambda *args, **kwargs: opened.append(kwargs))
|
||||||
|
page = page_module.PrescriptionsPage(repository, ["*"])
|
||||||
|
page.resize(1366, 800)
|
||||||
|
page.show()
|
||||||
|
application.processEvents()
|
||||||
|
# Demo row 801 contains herbs; row 802 is an empty manual prescription.
|
||||||
|
assert all(call[1] == [801] for call in repository.calls if call[0] == "statuses")
|
||||||
|
assert not page.table.isColumnHidden(11) and not page.table.isColumnHidden(12)
|
||||||
|
page._ai_statuses[802] = batch(prescription_id=802)
|
||||||
|
page._render_ai_statuses()
|
||||||
|
for column in (11, 12):
|
||||||
|
item = page.table.item(1, column)
|
||||||
|
assert item.text() == item.toolTip() == item.data(Qt.ItemDataRole.AccessibleTextRole) == ""
|
||||||
|
assert not any(button.accessibleName() == "AI 报告" for button in page.table.cellWidget(1, 2).findChildren(QPushButton))
|
||||||
|
page.table.selectRow(1)
|
||||||
|
assert page.ai_report_button.isHidden() and not page.ai_report_button.isEnabled()
|
||||||
|
page._open_ai_report()
|
||||||
|
page.table.itemClicked.emit(page.table.item(1, 11))
|
||||||
|
page.table.itemClicked.emit(page.table.item(1, 12))
|
||||||
|
page._open_ai_context_menu(page.table.visualItemRect(page.table.item(1, 1)).center())
|
||||||
|
assert not hasattr(page, "_ai_context_menu")
|
||||||
|
assert opened == []
|
||||||
|
page.table.selectRow(0)
|
||||||
|
assert page.ai_report_button.isVisible() and page.ai_report_button.isEnabled()
|
||||||
|
page.ai_report_button.click()
|
||||||
|
assert opened == [{"prescription_id": 801}]
|
||||||
|
page.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("blank_fields", [
|
||||||
|
{"is_system_auto": "1", "herbs": []},
|
||||||
|
{"is_system_auto": True, "herbs": [{"name": "黄芪"}]},
|
||||||
|
{"is_system_auto": 0, "herbs": [{}, {"name": " "}]},
|
||||||
|
{"is_system_auto": 0, "herbs": None},
|
||||||
|
])
|
||||||
|
def test_all_blank_list_hides_ai_until_prescription_is_saved(application: QApplication, immediate: None, monkeypatch: pytest.MonkeyPatch, blank_fields: dict[str, Any]) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
row = {"id": 803, "sn": "RX-BLANK", "patient_name": "测试患者", **blank_fields}
|
||||||
|
monkeypatch.setattr(repository, "list_prescriptions", lambda **_filters: {"lists": [row], "count": 1})
|
||||||
|
page = page_module.PrescriptionsPage(repository, ["*"])
|
||||||
|
page.resize(1366, 800)
|
||||||
|
page.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert page.table.isColumnHidden(11) and page.table.isColumnHidden(12)
|
||||||
|
assert page.ai_report_button.isHidden() and page.ai_status_notice.isHidden()
|
||||||
|
assert not page._ai_timer.isActive()
|
||||||
|
assert not any(call[0] == "statuses" for call in repository.calls)
|
||||||
|
assert not any(button.accessibleName() == "AI 报告" for button in page.table.cellWidget(0, 2).findChildren(QPushButton))
|
||||||
|
|
||||||
|
row.update(is_system_auto=0, herbs=[{"name": "黄芪", "dosage": 12}])
|
||||||
|
page.refresh()
|
||||||
|
application.processEvents()
|
||||||
|
assert ("statuses", [803]) in repository.calls
|
||||||
|
assert not page.table.isColumnHidden(11) and not page.table.isColumnHidden(12)
|
||||||
|
assert page.ai_report_button.isVisible() and page.ai_status_notice.isVisible()
|
||||||
|
assert "千问 0%" in page.table.item(0, 12).text()
|
||||||
|
assert any(button.accessibleName() == "AI 报告" for button in page.table.cellWidget(0, 2).findChildren(QPushButton))
|
||||||
|
page.close()
|
||||||
|
|
||||||
|
|
||||||
def test_list_disabled_explains_availability_and_keeps_history(application: QApplication, immediate: None) -> None:
|
def test_list_disabled_explains_availability_and_keeps_history(application: QApplication, immediate: None) -> None:
|
||||||
repository = Repository()
|
repository = Repository()
|
||||||
repository.enabled = False
|
repository.enabled = False
|
||||||
@@ -447,8 +719,9 @@ def test_context_menu_targets_clicked_prescription(application: QApplication, im
|
|||||||
page.resize(1366, 800)
|
page.resize(1366, 800)
|
||||||
page.show()
|
page.show()
|
||||||
application.processEvents()
|
application.processEvents()
|
||||||
item = page.table.item(1, 1)
|
page.table.selectRow(1)
|
||||||
expected_id = page.table.item(1, 0).data(Qt.ItemDataRole.UserRole).id
|
item = page.table.item(0, 1)
|
||||||
|
expected_id = page.table.item(0, 0).data(Qt.ItemDataRole.UserRole).id
|
||||||
page._open_ai_context_menu(page.table.visualItemRect(item).center())
|
page._open_ai_context_menu(page.table.visualItemRect(item).center())
|
||||||
page._ai_context_menu.actions()[0].trigger()
|
page._ai_context_menu.actions()[0].trigger()
|
||||||
assert opened == [{"prescription_id": expected_id}]
|
assert opened == [{"prescription_id": expected_id}]
|
||||||
@@ -617,6 +890,25 @@ def test_metadata_and_prose_remain_html_escaped(application: QApplication) -> No
|
|||||||
browser.close()
|
browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_candidate_reading_view_keeps_full_medicine_instructions(application: QApplication) -> None:
|
||||||
|
candidate = {
|
||||||
|
"prescription_name": "测试候选方", "dose_basis": "per_dose", "unit": "g",
|
||||||
|
"herbs": [{"name": "测试药材", "dosage": 12, "formula_type": "主方",
|
||||||
|
"instructions": "先煎,具体时长由医生复核", "processing": "生品",
|
||||||
|
"evidence_references": ["diagnoses:501"]}],
|
||||||
|
"rationale": "保留完整方义", "risk_warnings": ["复核提示 <script>bad()</script>"],
|
||||||
|
}
|
||||||
|
html = ai._candidate_html(candidate)
|
||||||
|
assert html.index("测试药材") < html.index("保留完整方义")
|
||||||
|
assert "<script>" not in html
|
||||||
|
browser = ai._browser()
|
||||||
|
browser.setHtml(html)
|
||||||
|
text = browser.toPlainText()
|
||||||
|
for expected in ("测试药材", "12", "克", "每剂", "主方", "先煎", "生品", "诊单(编号:501)", "保留完整方义"):
|
||||||
|
assert expected in text
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
|
||||||
def chinese_history_batch() -> dict[str, Any]:
|
def chinese_history_batch() -> dict[str, Any]:
|
||||||
"""Synthetic fixture; no patient or network data is used in tests or visual QA."""
|
"""Synthetic fixture; no patient or network data is used in tests or visual QA."""
|
||||||
value = batch(39, status="success")
|
value = batch(39, status="success")
|
||||||
@@ -907,6 +1199,7 @@ def test_progress_updates_preserve_report_document_scroll_and_selection(applicat
|
|||||||
repository.batches = [value]
|
repository.batches = [value]
|
||||||
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
dialog.tabs.setCurrentWidget(dialog.report_pages["report"])
|
||||||
application.processEvents()
|
application.processEvents()
|
||||||
report = dialog.model_views["qwen"]["report"]
|
report = dialog.model_views["qwen"]["report"]
|
||||||
report.verticalScrollBar().setValue(300)
|
report.verticalScrollBar().setValue(300)
|
||||||
@@ -1035,3 +1328,93 @@ def test_retry_wait_freezes_attempt_duration_while_countdown_advances_and_poll_r
|
|||||||
def test_unknown_or_invalid_attempt_count_is_not_invented(attempt: Any) -> None:
|
def test_unknown_or_invalid_attempt_count_is_not_invented(attempt: Any) -> None:
|
||||||
view = ai.progress_view({"status": "retry_wait", "progress": {"stage": "retry_wait", "attempt": attempt}})
|
view = ai.progress_view({"status": "retry_wait", "progress": {"stage": "retry_wait", "attempt": attempt}})
|
||||||
assert "次尝试" not in view.detail
|
assert "次尝试" not in view.detail
|
||||||
|
|
||||||
|
|
||||||
|
def test_theme_switch_keeps_the_page_the_drafts_and_every_step(application: QApplication,
|
||||||
|
immediate: None) -> None:
|
||||||
|
"""The palette swaps in place; nothing the doctor typed or opened is lost."""
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs import issued_prescription_ai_theme as theme
|
||||||
|
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(Repository(), ["*"], prescription_id=801)
|
||||||
|
dialog.resize(1280, 860)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
dialog._goto("gaps")
|
||||||
|
dialog.model_views["qwen"]["comment"].setPlainText("切换前写的复核意见")
|
||||||
|
dark = theme.CONSOLE["canvas"]
|
||||||
|
try:
|
||||||
|
dialog._switch_theme()
|
||||||
|
application.processEvents()
|
||||||
|
assert theme.CONSOLE["canvas"] != dark
|
||||||
|
assert theme.current_theme() == "light"
|
||||||
|
assert dialog.model_views["qwen"]["comment"].toPlainText() == "切换前写的复核意见"
|
||||||
|
assert dialog.tabs.currentWidget() is dialog.tab_pages["gaps"]
|
||||||
|
assert len(dialog.rail.buttons) == len(ai.NAV_PRIMARY)
|
||||||
|
assert all(button.isVisible() for button in dialog.rail.buttons.values())
|
||||||
|
finally:
|
||||||
|
dialog._switch_theme()
|
||||||
|
application.processEvents()
|
||||||
|
assert theme.CONSOLE["canvas"] == dark
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_step_stays_readable_in_both_palettes(application: QApplication, immediate: None) -> None:
|
||||||
|
"""A selected step paints its own labels: a descendant :checked rule would not reach them."""
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs import issued_prescription_ai_theme as theme
|
||||||
|
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(Repository(), ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
application.processEvents()
|
||||||
|
rail = dialog.rail
|
||||||
|
assert theme.CONSOLE["heading"] in rail.names["overview"].styleSheet()
|
||||||
|
assert theme.CONSOLE["muted"] in rail.names["report"].styleSheet()
|
||||||
|
rail.set_current("report")
|
||||||
|
assert theme.CONSOLE["heading"] in rail.names["report"].styleSheet()
|
||||||
|
assert theme.CONSOLE["muted"] in rail.names["overview"].styleSheet()
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_native_title_bar_takes_the_console_palette() -> None:
|
||||||
|
"""The frame Windows draws follows the theme, so the dark body does not stop at the caption."""
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs import issued_prescription_ai_theme as theme
|
||||||
|
|
||||||
|
# DWM wants 0x00BBGGRR, not the #RRGGBB the palette is written in.
|
||||||
|
assert theme._colorref("#080D18") == 0x180D08
|
||||||
|
assert theme._colorref("#FFFFFF") == 0xFFFFFF
|
||||||
|
|
||||||
|
class Handleless:
|
||||||
|
def winId(self) -> int:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
assert theme.apply_window_chrome(Handleless()) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_report_rail_lists_the_saved_sections_and_filters_to_differences(
|
||||||
|
application: QApplication, immediate: None) -> None:
|
||||||
|
repository = Repository()
|
||||||
|
repository.batches[0]["models"]["qwen"]["report"] = {"summary": "两侧一致的概要", "analysis": "千问的分析"}
|
||||||
|
repository.batches[0]["models"]["openai"]["report"] = {"summary": "两侧一致的概要", "analysis": "OpenAI 的分析"}
|
||||||
|
dialog = ai.IssuedPrescriptionAiDialog(repository, ["*"], prescription_id=801)
|
||||||
|
dialog.show()
|
||||||
|
dialog._goto("report")
|
||||||
|
application.processEvents()
|
||||||
|
titles = [dialog.report_toc.itemAt(index).widget().text()
|
||||||
|
for index in range(dialog.report_toc.count())]
|
||||||
|
assert "概要" in titles and "综合分析" in titles
|
||||||
|
both = dialog.model_views["qwen"]["report"].toPlainText()
|
||||||
|
assert "两侧一致的概要" in both and "千问的分析" in both
|
||||||
|
dialog._set_report_mode("differences")
|
||||||
|
application.processEvents()
|
||||||
|
filtered = dialog.model_views["qwen"]["report"].toPlainText()
|
||||||
|
assert "千问的分析" in filtered
|
||||||
|
assert "两侧一致的概要" not in filtered # 两侧完全相同的段落不算差异
|
||||||
|
dialog._set_report_mode("qwen")
|
||||||
|
application.processEvents()
|
||||||
|
assert not dialog.report_columns["openai"].isVisible()
|
||||||
|
dialog._set_report_mode("both")
|
||||||
|
application.processEvents()
|
||||||
|
assert dialog.report_columns["openai"].isVisible()
|
||||||
|
dialog.close()
|
||||||
|
|||||||
@@ -0,0 +1,399 @@
|
|||||||
|
"""Offline rendering and data-integrity checks for the prescription workspace."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
from copy import deepcopy
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from PySide6.QtCore import QEvent, QObject, Qt
|
||||||
|
from PySide6.QtTest import QTest
|
||||||
|
from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs.issued_prescription_ai_comparison import (
|
||||||
|
PrescriptionComparisonPanel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def saved_row(name: str = "黄芪", *, doctor: Any = 30, candidate: Any = 15, unit: str = "g", basis: str = "per_dose", formula: str = "主方", processing: str = "生品") -> dict[str, Any]:
|
||||||
|
identity = {"name": name, "processing": processing, "formula_type": formula, "administration_route": "口服", "group": "", "unit": unit, "dose_basis": basis}
|
||||||
|
return {**identity, "key": "a1b2c3d4" * 8, "doctor": {**identity, "dosage": doctor}, "candidate": {**identity, "dosage": candidate}, "doctor_dosage": doctor, "candidate_dosage": candidate, "match_type": "matched"}
|
||||||
|
|
||||||
|
|
||||||
|
def saved_batch(rows: list[dict[str, Any]] | None = None) -> dict[str, Any]:
|
||||||
|
rows = rows if rows is not None else [saved_row(), saved_row("白术", doctor=9, candidate=12)]
|
||||||
|
herbs = []
|
||||||
|
for row in rows:
|
||||||
|
if row.get("candidate"):
|
||||||
|
original = deepcopy(row["candidate"])
|
||||||
|
original.pop("source_rows", None)
|
||||||
|
row["candidate"].setdefault("source_rows", [len(herbs)])
|
||||||
|
herbs.append(original)
|
||||||
|
candidate = {"status": "available_for_review", "prescription_name": "补中益气汤加减", "herbs": herbs, "usage_instruction": "水煎温服", "times_per_day": 2, "usage_days": 7}
|
||||||
|
qwen = {"status": "succeeded", "candidate": candidate, "comparison": {"status": "comparable", "score": 68.5, "herb_score": 100, "rows": rows}}
|
||||||
|
openai = deepcopy(qwen)
|
||||||
|
openai["candidate"]["prescription_name"] = "益气健脾方"
|
||||||
|
return {"id": 44, "validity": "current", "status": "completed", "models": {"qwen": qwen, "openai": openai}}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def application() -> QApplication:
|
||||||
|
return QApplication.instance() or QApplication([])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def no_network(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
def denied(*_args: Any, **_kwargs: Any) -> None:
|
||||||
|
pytest.fail("The comparison panel must never contact external systems")
|
||||||
|
monkeypatch.setattr(socket.socket, "connect", denied)
|
||||||
|
monkeypatch.setattr(socket.socket, "connect_ex", denied)
|
||||||
|
monkeypatch.setattr(socket, "create_connection", denied)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def panel(application: QApplication):
|
||||||
|
host = QWidget()
|
||||||
|
host.resize(1120, 400)
|
||||||
|
layout = QVBoxLayout(host)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
widget = PrescriptionComparisonPanel(host)
|
||||||
|
layout.addWidget(widget)
|
||||||
|
host.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
host.close()
|
||||||
|
host.deleteLater()
|
||||||
|
application.processEvents()
|
||||||
|
|
||||||
|
|
||||||
|
def test_saved_snapshot_is_readable_and_model_switch_is_explicit(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
before = deepcopy(data)
|
||||||
|
panel.set_batch(data)
|
||||||
|
application.processEvents()
|
||||||
|
assert data == before
|
||||||
|
assert panel.selected_model == "qwen"
|
||||||
|
assert panel.model_buttons["qwen"].isChecked()
|
||||||
|
assert panel.name_label.text() == "补中益气汤加减"
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "30 克 / 每剂"
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||||
|
assert "主方" in panel.herb_table.item(0, 0).text()
|
||||||
|
assert "a1b2c3d4" not in panel.chart.accessibleDescription()
|
||||||
|
assert "药味剂量一致度" not in panel.chart.accessibleDescription()
|
||||||
|
assert "水煎温服" in panel.usage_label.text()
|
||||||
|
assert panel.chart.bars_enabled
|
||||||
|
assert panel.chart.groups[0][0] == ("克", "每剂")
|
||||||
|
panel.model_buttons["openai"].click()
|
||||||
|
assert panel.selected_model == "openai"
|
||||||
|
assert panel.name_label.text() == "益气健脾方"
|
||||||
|
assert panel.herb_table.horizontalHeaderItem(2).text() == "OpenAI剂量"
|
||||||
|
assert panel.chart.model_key == "openai"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("candidate_key", ["candidate_dose", "candidate_dosage", "ai_dose"])
|
||||||
|
def test_legacy_flat_doses_remain_supported(panel: PrescriptionComparisonPanel, candidate_key: str) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["models"]["qwen"]["comparison"]["rows"] = [{"name": "黄芪", "doctor_dose": 0, candidate_key: "12.50", "unit": "g", "dose_basis": "每剂", "formula_type": "主方"}]
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "0 克 / 每剂"
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "12.50 克 / 每剂"
|
||||||
|
assert panel.chart.rows[0].doctor.number == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_absent_historical_doctor_is_never_replaced_with_current_prescription(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["models"]["qwen"]["comparison"]["rows"] = []
|
||||||
|
data["prescription"] = {"herbs": [{"name": "黄芪", "dosage": 99, "unit": "g", "dose_basis": "per_dose"}]}
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "—"
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||||
|
assert not panel.chart.bars_enabled
|
||||||
|
assert "历史处方" in panel.status_label.text()
|
||||||
|
assert "99" not in panel.chart.accessibleDescription()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("rows", [None, {}, "unavailable"])
|
||||||
|
def test_malformed_comparison_keeps_candidate_list_without_claiming_a_snapshot(panel: PrescriptionComparisonPanel, rows: Any) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["models"]["qwen"]["comparison"]["rows"] = rows
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "—"
|
||||||
|
assert not panel.chart.bars_enabled
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_missing_snapshot_and_nested_unknown_unit_override_flat_values(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
row = saved_row()
|
||||||
|
row["doctor"] = None
|
||||||
|
row["candidate"]["unit"] = None
|
||||||
|
panel.set_batch(saved_batch([row]))
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "—"
|
||||||
|
assert "单位未注明" in panel.herb_table.item(0, 2).text()
|
||||||
|
assert panel.chart.groups[0][0] is None
|
||||||
|
assert "未明确" in panel.chart.accessibleDescription()
|
||||||
|
|
||||||
|
|
||||||
|
def test_incomparable_report_retains_original_doses_without_bars(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["models"]["qwen"]["comparison"].update(status="not_comparable", score=99, reason="dose_basis_mismatch")
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert "剂量基准不同" in panel.status_label.text()
|
||||||
|
assert not panel.chart.bars_enabled
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "30 克 / 每剂"
|
||||||
|
assert all(scale is None for scale, _rows, _maximum in panel.chart.groups)
|
||||||
|
assert "99" not in panel.chart.accessibleDescription()
|
||||||
|
|
||||||
|
|
||||||
|
def test_units_and_bases_have_independent_scales_and_mismatched_rows_are_excluded(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
rows = [saved_row("黄芪"), saved_row("白术", unit="mg", doctor=1000), saved_row("茯苓", basis="per_day", doctor=60), saved_row("甘草")]
|
||||||
|
rows[-1]["candidate"]["dose_basis"] = "per_day"
|
||||||
|
panel.set_batch(saved_batch(rows))
|
||||||
|
assert [scale for scale, _members, _maximum in panel.chart.groups] == [("克", "每剂"), ("毫克", "每剂"), ("克", "每日"), None]
|
||||||
|
assert panel.chart.groups[0][2] == 30
|
||||||
|
assert panel.chart.groups[1][2] == 1000
|
||||||
|
assert "单位或剂量基准不同" in panel.chart.accessibleDescription()
|
||||||
|
assert "每日" in panel.herb_table.item(3, 2).text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_auxiliary_and_processing_rows_never_merge(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
rows = [saved_row("甘草", formula="主方"), saved_row("甘草", formula="辅方"), saved_row("甘草", processing="炙品")]
|
||||||
|
rows[2]["candidate"]["processing"] = "生品"
|
||||||
|
panel.set_batch(saved_batch(rows))
|
||||||
|
assert panel.herb_table.rowCount() == 3
|
||||||
|
assert "主方" in panel.herb_table.item(0, 0).text()
|
||||||
|
assert "辅方" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert panel.chart.rows[2].scale is None
|
||||||
|
assert "炮制" in panel.chart.accessibleDescription()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("skipped_index", [0, 1])
|
||||||
|
def test_zero_based_trace_preserves_candidate_herbs_skipped_during_normalization(panel: PrescriptionComparisonPanel, skipped_index: int) -> None:
|
||||||
|
normalized = saved_row("黄芪")
|
||||||
|
normalized["candidate"]["source_rows"] = [1 - skipped_index]
|
||||||
|
data = saved_batch([normalized])
|
||||||
|
original = deepcopy(data["models"]["qwen"]["candidate"]["herbs"][0])
|
||||||
|
excluded = {**original, "name": "黄芪", "processing": "炮制待核对", "dosage": 8, "instructions": "先煎 30 分钟"}
|
||||||
|
herbs = [original]
|
||||||
|
herbs.insert(skipped_index, excluded)
|
||||||
|
data["models"]["qwen"]["candidate"]["herbs"] = herbs
|
||||||
|
# Even a contradictory comparable status must not grant the omitted raw herb a bar.
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||||
|
assert "未纳入对比" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert "炮制待核对" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert panel.herb_table.item(1, 1).text() == "—"
|
||||||
|
assert panel.herb_table.item(1, 2).text() == "8 克 / 每剂"
|
||||||
|
assert panel.chart.rows[1].scale is None
|
||||||
|
assert "先煎 30 分钟" in panel.chart.accessibleDescription()
|
||||||
|
assert "原方 2 项" in panel.count_label.text() and "未纳入 1 项" in panel.count_label.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_merged_source_rows_cover_each_original_once_and_keep_original_doses(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
merged = saved_row("黄芪", candidate=10)
|
||||||
|
merged["candidate"]["source_rows"] = [0, 1]
|
||||||
|
data = saved_batch([merged])
|
||||||
|
original = data["models"]["qwen"]["candidate"]["herbs"][0]
|
||||||
|
data["models"]["qwen"]["candidate"]["herbs"] = [{**original, "name": "北芪", "dosage": 4}, {**original, "dosage": 6}, {**original, "name": "未识别药材", "dosage": 5}]
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "10 克 / 每剂"
|
||||||
|
tooltip = panel.herb_table.item(0, 0).toolTip()
|
||||||
|
assert "第 1 项 北芪 4 克 / 每剂" in tooltip
|
||||||
|
assert "第 2 项 黄芪 6 克 / 每剂" in tooltip
|
||||||
|
assert "未识别药材" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert "未纳入对比" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert "原方 3 项" in panel.count_label.text() and "对比 1 项" in panel.count_label.text()
|
||||||
|
panel.search.setText("北芪")
|
||||||
|
assert panel.herb_table.rowCount() == 1
|
||||||
|
assert "黄芪" in panel.herb_table.item(0, 0).text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_missing_trace_retains_full_original_separately_without_name_matching(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
data = saved_batch([saved_row("黄芪")])
|
||||||
|
model = data["models"]["qwen"]
|
||||||
|
del model["comparison"]["rows"][0]["candidate"]["source_rows"]
|
||||||
|
original = model["candidate"]["herbs"][0]
|
||||||
|
model["candidate"]["herbs"] = [{**original, "dosage": 4}, {**original, "name": "炮制待核对药材", "dosage": 6}]
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 3
|
||||||
|
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||||
|
for index, dose in ((1, "4 克 / 每剂"), (2, "6 克 / 每剂")):
|
||||||
|
assert "原方附列" in panel.herb_table.item(index, 0).text()
|
||||||
|
assert panel.herb_table.item(index, 1).text() == "—"
|
||||||
|
assert panel.herb_table.item(index, 2).text() == dose
|
||||||
|
assert panel.chart.rows[index].scale is None
|
||||||
|
assert "对应关系未保存" in panel.status_label.text()
|
||||||
|
assert "原方附列 2 项" in panel.count_label.text()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("trace", [[True], [1], [-1], ["0"], []])
|
||||||
|
def test_invalid_source_trace_never_hides_an_original_herb(panel: PrescriptionComparisonPanel, trace: list[Any]) -> None:
|
||||||
|
data = saved_batch([saved_row("黄芪")])
|
||||||
|
data["models"]["qwen"]["comparison"]["rows"][0]["candidate"]["source_rows"] = trace
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
assert "原方附列" in panel.herb_table.item(1, 0).text()
|
||||||
|
assert panel.chart.rows[1].scale is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_normalized_usage_keeps_all_special_instructions_visible_and_accessible(panel: PrescriptionComparisonPanel) -> None:
|
||||||
|
row = saved_row("石膏")
|
||||||
|
row["doctor"]["usage"] = {"decoction_instruction": "先煎 30 分钟", "special_usage": "布包煎", "usage_time": "饭后", "usage_way": "温服"}
|
||||||
|
row["candidate"]["instructions"] = "后下 5 分钟"
|
||||||
|
row["candidate"]["usage"] = {"instructions": "后下 5 分钟", "decoction_instruction": "另煎", "special_usage": "分次兑服", "usage_instruction": "服前摇匀", "usage_time": "睡前", "usage_way": "温服"}
|
||||||
|
panel.set_batch(saved_batch([row]))
|
||||||
|
name_item = panel.herb_table.item(0, 0)
|
||||||
|
for instruction in ("先煎 30 分钟", "布包煎", "饭后", "温服", "后下 5 分钟", "另煎", "分次兑服", "服前摇匀", "睡前"):
|
||||||
|
assert instruction in name_item.toolTip()
|
||||||
|
assert instruction in name_item.data(Qt.ItemDataRole.AccessibleDescriptionRole)
|
||||||
|
assert instruction in panel.chart.accessibleDescription()
|
||||||
|
assert "先煎 30 分钟" in name_item.text() and "后下 5 分钟" in name_item.text()
|
||||||
|
assert panel.chart.rows[0].candidate.instructions.count("后下 5 分钟") == 1
|
||||||
|
assert panel.herb_table.rowHeight(0) >= panel.herb_table.fontMetrics().height() * 3 + 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_clicking_a_table_row_scrolls_to_its_chart_group(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||||
|
rows = [saved_row(f"药材{index}", unit="g" if index % 2 == 0 else "mg") for index in range(20)]
|
||||||
|
panel.set_batch(saved_batch(rows))
|
||||||
|
application.processEvents()
|
||||||
|
item = panel.herb_table.item(11, 0)
|
||||||
|
panel.herb_table.scrollToItem(item)
|
||||||
|
application.processEvents()
|
||||||
|
panel.chart_scroll.verticalScrollBar().setValue(0)
|
||||||
|
QTest.mouseClick(panel.herb_table.viewport(), Qt.MouseButton.LeftButton, pos=panel.herb_table.visualItemRect(item).center())
|
||||||
|
application.processEvents()
|
||||||
|
assert panel.herb_table.currentRow() == 11
|
||||||
|
# Ten gram rows precede the milligram group; the clicked row is its sixth member.
|
||||||
|
expected_y = 8 + panel.chart.GROUP_HEIGHT * 2 + panel.chart.ROW_HEIGHT * 15
|
||||||
|
assert panel.chart_scroll.verticalScrollBar().value() == expected_y
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("field", ["processing", "formula_type", "administration_route", "group"])
|
||||||
|
def test_unrecognized_identity_values_do_not_become_equal_after_localization(panel: PrescriptionComparisonPanel, field: str) -> None:
|
||||||
|
row = saved_row()
|
||||||
|
row["doctor"][field] = "unknown_first"
|
||||||
|
row["candidate"][field] = "unknown_second"
|
||||||
|
panel.set_batch(saved_batch([row]))
|
||||||
|
assert panel.chart.rows[0].scale is None
|
||||||
|
assert "unknown_first" not in panel.chart.accessibleDescription()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", [None, "适量", -3, float("nan"), float("inf"), True])
|
||||||
|
def test_invalid_or_missing_doses_never_produce_numeric_bars(panel: PrescriptionComparisonPanel, application: QApplication, value: Any) -> None:
|
||||||
|
panel.set_batch(saved_batch([saved_row(doctor=value)]))
|
||||||
|
assert panel.chart.rows[0].doctor.number is None
|
||||||
|
assert panel.chart.groups[0][2] == 15
|
||||||
|
application.processEvents()
|
||||||
|
assert not panel.chart.grab().isNull()
|
||||||
|
if value is None or value is True:
|
||||||
|
assert panel.herb_table.item(0, 1).text() == "—"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("status", "expected"), [("running", "正在生成"), ("failed", "未完成"), ("future_state", "状态未确认")])
|
||||||
|
def test_processing_failed_and_unknown_model_states_are_explicit(panel: PrescriptionComparisonPanel, status: str, expected: str) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["models"]["qwen"].update(status=status, candidate=None, comparison=None)
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert expected in panel.status_label.text()
|
||||||
|
assert panel.empty_label.isVisible()
|
||||||
|
assert panel.herb_table.rowCount() == 0
|
||||||
|
assert not panel.chart.bars_enabled
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("validity", ["stale", "source_updated", "future_validity"])
|
||||||
|
def test_outdated_and_unknown_validity_only_show_saved_original_values(panel: PrescriptionComparisonPanel, validity: str) -> None:
|
||||||
|
data = saved_batch()
|
||||||
|
data["validity"] = validity
|
||||||
|
panel.set_batch(data)
|
||||||
|
assert not panel.chart.bars_enabled
|
||||||
|
assert "历史原值" in panel.status_label.text()
|
||||||
|
assert panel.herb_table.rowCount() == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_refresh_preserves_search_model_selection_and_both_scroll_positions(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||||
|
data = saved_batch([saved_row(f"黄芪{index}") for index in range(40)])
|
||||||
|
panel.set_batch(data)
|
||||||
|
panel.model_buttons["openai"].click()
|
||||||
|
panel.search.setText("黄芪")
|
||||||
|
panel.herb_table.selectRow(10)
|
||||||
|
application.processEvents()
|
||||||
|
panel.herb_table.verticalScrollBar().setValue(180)
|
||||||
|
panel.chart_scroll.verticalScrollBar().setValue(250)
|
||||||
|
before = (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||||
|
for _ in range(3):
|
||||||
|
panel.set_batch(deepcopy(data))
|
||||||
|
application.processEvents()
|
||||||
|
assert panel.search.text() == "黄芪"
|
||||||
|
assert panel.selected_model == "openai"
|
||||||
|
assert panel.herb_table.currentRow() == 10
|
||||||
|
assert before == (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||||
|
changed = deepcopy(data)
|
||||||
|
changed["models"]["qwen"]["progress"] = {"stage": "completed"}
|
||||||
|
panel.set_batch(changed)
|
||||||
|
application.processEvents()
|
||||||
|
assert panel.search.text() == "黄芪" and panel.selected_model == "openai"
|
||||||
|
assert before == (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||||
|
panel.search.setText("黄芪39")
|
||||||
|
assert panel.herb_table.rowCount() == 1
|
||||||
|
assert len(panel.chart.rows) == 1
|
||||||
|
panel.search.setText("未匹配")
|
||||||
|
assert "没有匹配" in panel.empty_label.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parent_owned_controls_do_not_flash_windows_on_update(application: QApplication) -> None:
|
||||||
|
shown_windows = []
|
||||||
|
|
||||||
|
class WindowObserver(QObject):
|
||||||
|
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
|
||||||
|
if event.type() == QEvent.Type.Show and isinstance(watched, QWidget) and watched.isWindow():
|
||||||
|
shown_windows.append(watched)
|
||||||
|
return False
|
||||||
|
|
||||||
|
observer = WindowObserver()
|
||||||
|
application.installEventFilter(observer)
|
||||||
|
host = QWidget()
|
||||||
|
try:
|
||||||
|
layout = QVBoxLayout(host)
|
||||||
|
widget = PrescriptionComparisonPanel(host)
|
||||||
|
layout.addWidget(widget)
|
||||||
|
widget.set_batch(saved_batch())
|
||||||
|
host.show()
|
||||||
|
application.processEvents()
|
||||||
|
assert shown_windows == [host]
|
||||||
|
shown_windows.clear()
|
||||||
|
widget.set_batch(saved_batch([saved_row("当归")]))
|
||||||
|
widget.model_buttons["openai"].click()
|
||||||
|
widget.set_batch({})
|
||||||
|
application.processEvents()
|
||||||
|
assert shown_windows == []
|
||||||
|
assert all(child.parentWidget() is not None for child in widget.findChildren(QWidget))
|
||||||
|
assert widget.herb_table.rowCount() == 0
|
||||||
|
assert widget.chart.rows == []
|
||||||
|
finally:
|
||||||
|
application.removeEventFilter(observer)
|
||||||
|
host.close()
|
||||||
|
host.deleteLater()
|
||||||
|
application.processEvents()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("size", [(1120, 400), (940, 340)])
|
||||||
|
def test_compact_sizes_keep_table_and_chart_scrollable(panel: PrescriptionComparisonPanel, application: QApplication, size: tuple[int, int]) -> None:
|
||||||
|
panel.parentWidget().resize(*size)
|
||||||
|
panel.set_batch(saved_batch([saved_row(f"药材{index}") for index in range(60)]))
|
||||||
|
application.processEvents()
|
||||||
|
assert panel.width() <= size[0] and panel.height() <= size[1]
|
||||||
|
assert panel.herb_table.viewport().height() >= 65
|
||||||
|
assert panel.herb_table.viewport().width() >= 320
|
||||||
|
assert panel.chart_scroll.viewport().height() >= 90
|
||||||
|
assert panel.herb_table.verticalScrollBar().maximum() > 0
|
||||||
|
assert panel.chart_scroll.verticalScrollBar().maximum() > 0
|
||||||
|
assert not panel.grab().isNull()
|
||||||
|
assert panel.herb_table.item(0, 2).data(Qt.ItemDataRole.AccessibleTextRole) == "15 克 / 每剂"
|
||||||
|
assert "药材59" in panel.chart.accessibleDescription()
|
||||||
@@ -0,0 +1,478 @@
|
|||||||
|
"""The composed pages must show the saved batch exactly, including what is missing from it."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from PySide6.QtWidgets import QApplication, QLabel
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs import issued_prescription_ai_pages as pages
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def application() -> QApplication:
|
||||||
|
return QApplication.instance() or QApplication([])
|
||||||
|
|
||||||
|
|
||||||
|
def comparison_rows(model: str) -> list[dict[str, Any]]:
|
||||||
|
doses = {"qwen": {"生地黄": 15, "生麦冬": 12, "麸炒白术": 12}, "openai": {"生地黄": 12, "茯苓": 8}}[model]
|
||||||
|
contributions = {"qwen": {"生地黄": 0.94, "生麦冬": 1.0}, "openai": {"生地黄": 0.75}}[model]
|
||||||
|
doctor = {"生地黄": 16, "生麦冬": 12, "红参片": 6, "茯苓": 10}
|
||||||
|
rows = []
|
||||||
|
for name in sorted(set(doses) | set(doctor)):
|
||||||
|
rows.append({
|
||||||
|
"name": name, "unit": "g",
|
||||||
|
"doctor_dosage": doctor.get(name),
|
||||||
|
"candidate_dosage": doses.get(name),
|
||||||
|
"contribution": contributions.get(name),
|
||||||
|
})
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def batch(**overrides: Any) -> dict[str, Any]:
|
||||||
|
value = {
|
||||||
|
"id": 13, "status": "success", "coverage_status": "partial", "comparison_type": "latest_context",
|
||||||
|
"source_summary": {"attachment_count": 4, "diagnoses_count": 1, "video_calls_count": 3,
|
||||||
|
"source_record_count": 9},
|
||||||
|
"missing": [{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True},
|
||||||
|
{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True},
|
||||||
|
{"code": "ARCHIVE_SYNC_WATERMARK_UNAVAILABLE", "critical": False}],
|
||||||
|
"models": {
|
||||||
|
"qwen": {"status": "success", "algorithm_version": "prescription-soft-dice-v1.1.0",
|
||||||
|
"prompt_version": "manual-prescription-required-candidate-v4",
|
||||||
|
"comparison": {"status": "comparable", "rows": comparison_rows("qwen")},
|
||||||
|
"coverage": {"files": [{"status": "processed"}, {"status": "processed"},
|
||||||
|
{"status": "processed"}, {"status": "restricted"}]},
|
||||||
|
"progress": {"stage_label": "处理完成", "elapsed_seconds": 135, "attempt": 1},
|
||||||
|
"usage": {"total_calls": 2, "calls": [
|
||||||
|
{"stage": "text:0", "ok": True, "latency_ms": 3400, "file_count": 0,
|
||||||
|
"usage": {"completion_tokens": 1020}, "error_code": ""},
|
||||||
|
{"stage": "final", "ok": False, "latency_ms": 14400, "file_count": 0,
|
||||||
|
"usage": {"completion_tokens": 5617}, "error_code": "INVALID_REPORT_OUTPUT"}]}},
|
||||||
|
"openai": {"status": "success", "comparison": {"status": "comparable", "rows": comparison_rows("openai")},
|
||||||
|
"progress": {"stage_label": "处理完成", "elapsed_seconds": 593, "attempt": 1},
|
||||||
|
"usage": {"calls": []}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
value.update(overrides)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- candidates page
|
||||||
|
@pytest.fixture
|
||||||
|
def per_herb(application: QApplication) -> pages.CandidatesPage:
|
||||||
|
widget = pages.CandidatesPage()
|
||||||
|
widget.resize(1200, 700)
|
||||||
|
widget.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _dose_cells(page: pages.CandidatesPage, row: int) -> tuple[str, str]:
|
||||||
|
return (page.table.cellWidget(row, 1).dose, page.table.cellWidget(row, 2).dose)
|
||||||
|
|
||||||
|
|
||||||
|
def _verdict(page: pages.CandidatesPage, row: int) -> str:
|
||||||
|
return page.table.cellWidget(row, 4).findChildren(QLabel)[0].text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_merges_both_models_into_one_row(per_herb: pages.CandidatesPage) -> None:
|
||||||
|
per_herb.set_batch(batch())
|
||||||
|
names = [per_herb.table.item(row, 0).text() for row in range(per_herb.table.rowCount())]
|
||||||
|
assert names.count("生地黄") == 1
|
||||||
|
row = names.index("生地黄")
|
||||||
|
assert _dose_cells(per_herb, row) == ("15 g", "12 g")
|
||||||
|
assert per_herb.table.item(row, 3).text() == "16 g"
|
||||||
|
assert per_herb.table.cellWidget(row, 1).contribution == 0.94
|
||||||
|
assert per_herb.table.cellWidget(row, 2).contribution == 0.75
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_marks_absence_without_inventing_a_dose(per_herb: pages.CandidatesPage) -> None:
|
||||||
|
per_herb.set_batch(batch())
|
||||||
|
names = [per_herb.table.item(row, 0).text() for row in range(per_herb.table.rowCount())]
|
||||||
|
only_doctor = names.index("红参片")
|
||||||
|
assert _dose_cells(per_herb, only_doctor) == ("—", "—")
|
||||||
|
assert per_herb.table.cellWidget(only_doctor, 1).contribution is None
|
||||||
|
assert _verdict(per_herb, only_doctor) == "仅医方使用"
|
||||||
|
added = names.index("麸炒白术")
|
||||||
|
assert per_herb.table.item(added, 3).text() == "未收录"
|
||||||
|
assert _verdict(per_herb, added) == "仅 千问 收录"
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_conclusion_states_a_dose_gap_over_the_threshold(per_herb: pages.CandidatesPage) -> None:
|
||||||
|
per_herb.set_batch(batch())
|
||||||
|
names = [per_herb.table.item(row, 0).text() for row in range(per_herb.table.rowCount())]
|
||||||
|
row = names.index("生地黄")
|
||||||
|
assert _verdict(per_herb, row) == "两模型均收录" # 医方 16,两侧差 1 与 4,未过 5 克阈值
|
||||||
|
source = batch()
|
||||||
|
for model in source["models"].values():
|
||||||
|
for entry in model["comparison"]["rows"]:
|
||||||
|
if entry["name"] == "生地黄":
|
||||||
|
entry["candidate_dosage"] = 9
|
||||||
|
per_herb.set_batch(source)
|
||||||
|
names = [per_herb.table.item(index, 0).text() for index in range(per_herb.table.rowCount())]
|
||||||
|
assert _verdict(per_herb, names.index("生地黄")) == "剂量分歧 7 g"
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_filters_count_and_narrow_the_table(
|
||||||
|
per_herb: pages.CandidatesPage, application: QApplication) -> None:
|
||||||
|
per_herb.set_batch(batch())
|
||||||
|
total = per_herb.table.rowCount()
|
||||||
|
assert per_herb.filter_buttons["all"].text() == f"全部 {total}"
|
||||||
|
per_herb.search.setText("生地黄")
|
||||||
|
application.processEvents()
|
||||||
|
assert per_herb.table.rowCount() == 1
|
||||||
|
per_herb.search.clear()
|
||||||
|
per_herb.filter_buttons["doctor"].click()
|
||||||
|
application.processEvents()
|
||||||
|
assert 0 < per_herb.table.rowCount() < total
|
||||||
|
assert all(_verdict(per_herb, row) in {"仅医方使用", "两模型均未收录"}
|
||||||
|
for row in range(per_herb.table.rowCount()))
|
||||||
|
per_herb.filter_buttons["single"].click()
|
||||||
|
application.processEvents()
|
||||||
|
assert all("仅 " in _verdict(per_herb, row) for row in range(per_herb.table.rowCount()))
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_cards_show_each_saved_prescription(per_herb: pages.CandidatesPage) -> None:
|
||||||
|
per_herb.set_batch(batch(doctor_snapshot={"prescription": {
|
||||||
|
"herbs": [{"name": "生地黄", "dosage": 16, "unit": "g"}, {"name": "红参片", "dosage": 6, "unit": "g"}],
|
||||||
|
"prescription_type": "浓缩水丸", "dose_count": 1, "usage_instruction": "每日1剂,水煎分服。"}}))
|
||||||
|
doctor = per_herb.cards["doctor"]
|
||||||
|
assert doctor.summary.text() == "2 味 · 浓缩水丸"
|
||||||
|
assert "生地黄" in doctor.herbs.text() and "16 g" in doctor.herbs.text()
|
||||||
|
assert "每日1剂" in doctor.usage.text()
|
||||||
|
assert per_herb.cards["qwen"].summary.text() == "尚无候选方"
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_card_grows_for_a_long_usage_note(per_herb: pages.CandidatesPage,
|
||||||
|
application: QApplication) -> None:
|
||||||
|
"""A card must not cap itself and cut the herb list or the 方义 in half."""
|
||||||
|
|
||||||
|
note = "方中生黄芪益气固表,生地黄、生麦冬滋阴清热。" * 8
|
||||||
|
per_herb.set_batch(batch(doctor_snapshot={"prescription": {
|
||||||
|
"herbs": [{"name": f"药{index}", "dosage": 15, "unit": "g"} for index in range(20)],
|
||||||
|
"prescription_type": "浓缩水丸", "dose_count": 1, "usage_instruction": note}}))
|
||||||
|
per_herb.resize(1100, 700)
|
||||||
|
application.processEvents()
|
||||||
|
card = per_herb.cards["doctor"]
|
||||||
|
assert "另有 15 味" in card.herbs.text()
|
||||||
|
assert card.usage.height() >= card.usage.heightForWidth(card.usage.width())
|
||||||
|
assert card.height() >= card.herbs.height() + card.usage.height()
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_herb_says_when_no_prescription_was_saved(per_herb: pages.CandidatesPage) -> None:
|
||||||
|
per_herb.set_batch({})
|
||||||
|
assert per_herb.cards["doctor"].summary.text() == "未保存原方"
|
||||||
|
assert per_herb.cards["doctor"].herbs.text() == "尚未保存药味"
|
||||||
|
assert per_herb.table.rowCount() == 0
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- sources page
|
||||||
|
@pytest.fixture
|
||||||
|
def sources(application: QApplication) -> pages.SourcesPage:
|
||||||
|
widget = pages.SourcesPage()
|
||||||
|
widget.resize(900, 600)
|
||||||
|
widget.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_groups_gaps_by_type_and_keeps_criticality(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch(batch())
|
||||||
|
rows = {sources.gaps.item(row, 0).text(): (sources.gaps.item(row, 1).text(), sources.gaps.item(row, 2).text())
|
||||||
|
for row in range(sources.gaps.rowCount())}
|
||||||
|
transcript = next(key for key in rows if "转写" in key)
|
||||||
|
assert rows[transcript] == ("关键", "2")
|
||||||
|
archive = next(key for key in rows if "归档" in key)
|
||||||
|
assert rows[archive][0] == "一般"
|
||||||
|
|
||||||
|
|
||||||
|
def _composition(sources: pages.SourcesPage) -> dict[str, str]:
|
||||||
|
rows = {}
|
||||||
|
for index in range(sources.composition_layout.count()):
|
||||||
|
widget = sources.composition_layout.itemAt(index).widget()
|
||||||
|
labels = widget.findChildren(QLabel)
|
||||||
|
rows[labels[0].text()] = labels[-1].text()
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_reports_attachment_reading_and_composition(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch(batch())
|
||||||
|
assert "模型实际读取 3 个" in sources.attachment_note.text()
|
||||||
|
assert sources.waffle.accessibleDescription() == "附件 4 个:已读 3,受限或不支持 1"
|
||||||
|
assert _composition(sources)["问诊通话"] == "3"
|
||||||
|
assert "soft-dice" in sources.meta.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_reports_what_each_model_managed_to_read(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch(batch())
|
||||||
|
rows = {sources.per_model.item(row, 0).text(): (sources.per_model.item(row, 1).text(),
|
||||||
|
sources.per_model.item(row, 3).text())
|
||||||
|
for row in range(sources.per_model.rowCount())}
|
||||||
|
assert rows["千问"] == ("3", "75%")
|
||||||
|
assert "已读取 3" in sources.attachment_legend.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_meta_states_the_cutoff_and_reads_codes_in_chinese(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch(batch(cutoff_at="2026-09-10 15:29"))
|
||||||
|
text = sources.meta.text()
|
||||||
|
assert "资料截止:2026-09-10 15:29" in text
|
||||||
|
assert "覆盖状态:部分资料缺失" in text # partial 在覆盖语境里说的是资料,不是进度
|
||||||
|
assert "对照类型:最新资料对照" in text
|
||||||
|
sources.set_batch(batch())
|
||||||
|
assert "资料截止:—" in sources.meta.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_stays_empty_without_a_batch(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch({})
|
||||||
|
assert _composition(sources) == {}
|
||||||
|
assert sources.gaps.rowCount() == 0
|
||||||
|
assert sources.attachment_note.text() == "本次没有附件"
|
||||||
|
assert not sources.gap_note.isVisible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sources_puts_critical_gaps_first_and_counts_them(sources: pages.SourcesPage) -> None:
|
||||||
|
sources.set_batch(batch())
|
||||||
|
assert "关键" in sources.gaps.item(0, 1).text()
|
||||||
|
assert "3 项 · 关键 2" in sources.gap_title.findChildren(QLabel)[-1].text()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- progress page
|
||||||
|
@pytest.fixture
|
||||||
|
def progress(application: QApplication) -> pages.ProgressPage:
|
||||||
|
widget = pages.ProgressPage()
|
||||||
|
widget.resize(900, 600)
|
||||||
|
widget.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_progress_lists_every_call_with_its_outcome(progress: pages.ProgressPage) -> None:
|
||||||
|
progress.set_batch(batch())
|
||||||
|
assert progress.calls.rowCount() == 2
|
||||||
|
assert progress.calls.item(0, 0).text() == "千问"
|
||||||
|
assert progress.calls.item(0, 1).text() == "文字资料 1" # 阶段键不直接露出
|
||||||
|
assert progress.calls.item(1, 1).text() == "生成候选与报告"
|
||||||
|
assert progress.calls.item(0, 3).text() == "3.4 s"
|
||||||
|
assert progress.calls.item(0, 6).text() == "通过"
|
||||||
|
assert progress.calls.item(1, 5).text() == "5617"
|
||||||
|
# 最慢的一次调用占满耗时分布条,其余按比例
|
||||||
|
assert progress.calls.cellWidget(1, 2)._fraction == 1.0
|
||||||
|
assert progress.calls.cellWidget(0, 2)._fraction < 0.3
|
||||||
|
assert "INVALID_REPORT_OUTPUT" not in progress.calls.item(1, 5).text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_progress_shows_each_model_stage_and_attempt(progress: pages.ProgressPage) -> None:
|
||||||
|
progress.set_batch(batch())
|
||||||
|
assert "处理完成" in progress.stage_labels["qwen"].text()
|
||||||
|
assert "第 1 次尝试" in progress.stage_labels["qwen"].text()
|
||||||
|
assert "已用时 2 分 15 秒" in progress.stage_labels["qwen"].text()
|
||||||
|
assert "已用时 9 分 53 秒" in progress.stage_labels["openai"].text()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- history page
|
||||||
|
@pytest.fixture
|
||||||
|
def history(application: QApplication) -> pages.HistoryPage:
|
||||||
|
widget = pages.HistoryPage()
|
||||||
|
widget.resize(900, 600)
|
||||||
|
widget.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_orders_by_time_and_keeps_uncomparable_slots_empty(history: pages.HistoryPage) -> None:
|
||||||
|
history.set_history([
|
||||||
|
{"id": 13, "created_at": 300, "status": "success", "comparison_type": "latest_context",
|
||||||
|
"models": {"qwen": {"score": 18.9, "comparison_status": "comparable",
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.1.0"},
|
||||||
|
"openai": {"score": 16.7, "comparison_status": "comparable"}}},
|
||||||
|
{"id": 11, "created_at": 100, "status": "failed",
|
||||||
|
"models": {"qwen": {"score": None, "comparison_status": "not_comparable"},
|
||||||
|
"openai": {"score": None, "comparison_status": "not_comparable"}}},
|
||||||
|
])
|
||||||
|
description = history.chart.accessibleDescription()
|
||||||
|
assert description.index("11") < description.index("13") # oldest first on the chart
|
||||||
|
assert "11 千问 — OpenAI —" in description
|
||||||
|
assert history.table.item(0, 0).text() == "#13" # newest first in the list
|
||||||
|
assert history.table.item(0, 3).text() == "18.9%"
|
||||||
|
assert history.table.item(1, 3).text() == "—"
|
||||||
|
assert history.table.item(0, 5).text() == "v1.1.0"
|
||||||
|
assert history.chart.has_data()
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_reads_the_score_from_either_payload_shape(history: pages.HistoryPage) -> None:
|
||||||
|
"""列表接口把分数摊平,详情接口留在 comparison 里,两种都要认。"""
|
||||||
|
history.set_history([
|
||||||
|
{"id": 40, "created_at": "2026-09-10 15:29:00", "status": "success", "comparison_type": "non_independent",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "comparable", "score": 94.44}},
|
||||||
|
"openai": {"comparison": {"status": "not_comparable", "score": 93.3}}}},
|
||||||
|
{"id": 39, "created_at": "2026-09-09 09:00:00", "status": "success",
|
||||||
|
"models": {"qwen": {"score": 42.9, "comparison_status": "comparable"}}},
|
||||||
|
])
|
||||||
|
assert history.table.item(0, 0).text() == "#40"
|
||||||
|
assert history.table.item(0, 3).text() == "94.4%"
|
||||||
|
assert history.table.item(0, 4).text() == "—" # 不可比就不给分,哪怕载荷里带着数字
|
||||||
|
assert "分层" in history.strata.text() or "同一套" in history.strata.text()
|
||||||
|
assert history.table.item(1, 3).text() == "42.9%"
|
||||||
|
description = history.chart.accessibleDescription()
|
||||||
|
assert description.index("39") < description.index("40")
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_without_any_comparable_batch_draws_nothing(history: pages.HistoryPage) -> None:
|
||||||
|
history.set_history([{"id": 1, "created_at": 1, "models": {"qwen": {"comparison_status": "not_comparable"}}}])
|
||||||
|
assert not history.chart.has_data()
|
||||||
|
assert history.table.rowCount() == 1
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- statistics panel
|
||||||
|
@pytest.fixture
|
||||||
|
def statistics(application: QApplication) -> pages.StatisticsPanel:
|
||||||
|
widget = pages.StatisticsPanel()
|
||||||
|
widget.resize(1000, 600)
|
||||||
|
widget.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def statistics_payload() -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"total_count": 10, "patient_count": 8,
|
||||||
|
"doctors": [
|
||||||
|
{"doctor_id": 26, "doctor_name": "何医生", "total_count": 6, "patient_count": 5, "paired_count": 3,
|
||||||
|
"models": {"qwen": {"eligible_count": 4, "mean": 18.4, "median": 16.9,
|
||||||
|
"excluded_reasons": {"SOURCE_HISTORY_VERSIONS_UNAVAILABLE": 2}},
|
||||||
|
"openai": {"eligible_count": 3, "mean": 21.0, "median": 19.6,
|
||||||
|
"excluded_reasons": {"SOURCE_HISTORY_VERSIONS_UNAVAILABLE": 3}}},
|
||||||
|
"review": {"evaluated_count": 0, "qualified_count": 0, "qualified_rate": None}},
|
||||||
|
{"doctor_id": 31, "doctor_name": "李医生", "total_count": 4, "patient_count": 3, "paired_count": 1,
|
||||||
|
"models": {"qwen": {"eligible_count": 2, "mean": 25.0, "excluded_reasons": {"incomplete_coverage": 2}},
|
||||||
|
"openai": {"eligible_count": 0, "mean": None, "excluded_reasons": {"incomplete_coverage": 4}}},
|
||||||
|
"review": {"evaluated_count": 2, "qualified_count": 1, "qualified_rate": 50.0}},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_headline_counts_and_coverage(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
assert statistics.kpi_values["events"].text() == "10"
|
||||||
|
assert "涉及患者 8 人" in statistics.kpi_notes["events"].text()
|
||||||
|
assert statistics.kpi_values["qwen"].text() == "6"
|
||||||
|
assert "覆盖率 60.0%" in statistics.kpi_notes["qwen"].text()
|
||||||
|
assert statistics.kpi_values["openai"].text() == "3"
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_review_rate_needs_a_review_sample(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
payload = statistics_payload()
|
||||||
|
for doctor in payload["doctors"]:
|
||||||
|
doctor["review"] = {"evaluated_count": 0, "qualified_count": 0, "qualified_rate": None}
|
||||||
|
statistics.set_statistics(payload)
|
||||||
|
assert statistics.kpi_values["review"].text() == "—"
|
||||||
|
assert "尚未建立复核样本" in statistics.kpi_notes["review"].text()
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
assert statistics.kpi_values["review"].text() == "50.0%"
|
||||||
|
|
||||||
|
|
||||||
|
def _bars(layout) -> list[str]:
|
||||||
|
return [layout.itemAt(index).widget().accessibleDescription() for index in range(layout.count())]
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_funnel_and_exclusions_are_aggregated(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
funnel = _bars(statistics.funnel_layout)
|
||||||
|
assert "范围内开方事件 10" in funnel
|
||||||
|
assert "千问 有效基线比较 6" in funnel
|
||||||
|
assert "两模型配对共同样本 4" in funnel
|
||||||
|
reasons = _bars(statistics.exclusion_layout)
|
||||||
|
assert any(text.endswith(" 5") for text in reasons) # 来源历史版本无法重建 2 + 3
|
||||||
|
assert all("SOURCE_HISTORY" not in text for text in reasons)
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_distribution_sums_the_saved_bins(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
payload = statistics_payload()
|
||||||
|
payload["doctors"][0]["models"]["qwen"]["distribution"] = {"[0,20)": 3, "[20,40)": 1}
|
||||||
|
payload["doctors"][1]["models"]["qwen"]["distribution"] = {"[0,20)": 2}
|
||||||
|
statistics.set_statistics(payload)
|
||||||
|
assert statistics.distribution.has_data()
|
||||||
|
assert "千问 5/1/0/0/0" in statistics.distribution.accessibleDescription()
|
||||||
|
assert statistics.distribution.isVisible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_draws_no_distribution_without_samples(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
assert not statistics.distribution.has_data()
|
||||||
|
assert statistics.chart_empty.isVisible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_summary_row_pairs_mean_with_median(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
assert statistics.summary_values["qwen"].text() == "21.7% / 16.9%"
|
||||||
|
assert statistics.summary_values["paired"].text() == "4 例"
|
||||||
|
|
||||||
|
|
||||||
|
def test_statistics_lists_each_doctor_without_ranking(statistics: pages.StatisticsPanel) -> None:
|
||||||
|
statistics.set_statistics(statistics_payload())
|
||||||
|
assert statistics.doctors.rowCount() == 2
|
||||||
|
assert statistics.doctors.item(0, 0).text() == "何医生"
|
||||||
|
assert statistics.doctors.item(0, 3).text() == "4 / 18.4%"
|
||||||
|
assert statistics.doctors.item(1, 4).text() == "0 / —"
|
||||||
|
assert statistics.doctors.item(1, 6).text() == "50.0%"
|
||||||
|
assert "不是医生准确率" in statistics.footnote.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_progress_counts_the_batch_in_the_stat_row(progress: pages.ProgressPage) -> None:
|
||||||
|
progress.set_batch(batch())
|
||||||
|
assert progress.stat_values["calls"].text() == "2 次"
|
||||||
|
assert progress.stat_values["failures"].text() == "1 次"
|
||||||
|
assert progress.stat_values["repairs"].text() == "0 次"
|
||||||
|
assert progress.stat_values["elapsed"].text() == "9 分 53 秒"
|
||||||
|
|
||||||
|
|
||||||
|
def test_progress_derives_its_stages_from_the_saved_calls(progress: pages.ProgressPage) -> None:
|
||||||
|
progress.set_batch(batch())
|
||||||
|
names = []
|
||||||
|
layout = progress.stage_lists["qwen"]
|
||||||
|
for index in range(layout.count()):
|
||||||
|
widget = layout.itemAt(index).widget()
|
||||||
|
names.append(widget.findChildren(QLabel)[1].text())
|
||||||
|
assert names == ["文字资料分析", "生成候选与报告"]
|
||||||
|
assert progress.stage_lists["openai"].count() == 0 # 没有调用记录就不编造阶段
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_names_the_version_change_between_two_batches(history: pages.HistoryPage) -> None:
|
||||||
|
history.set_history([
|
||||||
|
{"id": 5, "created_at": "2026-09-10 15:29:00", "status": "success",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "comparable", "score": 15.3},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.1.0",
|
||||||
|
"prompt_version": "v4"}}},
|
||||||
|
{"id": 4, "created_at": "2026-09-10 15:18:00", "status": "success",
|
||||||
|
"models": {"qwen": {"comparison": {"status": "comparable", "score": 18.9},
|
||||||
|
"algorithm_version": "prescription-soft-dice-v1.0.1",
|
||||||
|
"prompt_version": "v3"}}},
|
||||||
|
])
|
||||||
|
text = history.strata.text()
|
||||||
|
assert "比较算法 v1.0.1 → v1.1.0" in text
|
||||||
|
assert "提示词 v3 → v4" in text
|
||||||
|
assert "不能直接相减" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_says_when_every_batch_shares_one_version(history: pages.HistoryPage) -> None:
|
||||||
|
history.set_history([
|
||||||
|
{"id": 2, "created_at": "2026-09-10 15:29:00",
|
||||||
|
"models": {"qwen": {"algorithm_version": "prescription-soft-dice-v1.1.0"}}},
|
||||||
|
{"id": 1, "created_at": "2026-09-10 14:29:00",
|
||||||
|
"models": {"qwen": {"algorithm_version": "prescription-soft-dice-v1.1.0"}}},
|
||||||
|
])
|
||||||
|
assert "全部批次使用同一套算法" in history.strata.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_shows_why_a_batch_has_no_score(history: pages.HistoryPage) -> None:
|
||||||
|
history.set_history([{"id": 2, "created_at": "2026-09-10 13:25:00", "status": "failed",
|
||||||
|
"models": {"qwen": {"error_message": "模型返回未通过校验"}}}])
|
||||||
|
assert "模型返回未通过校验" in history.table.item(0, 2).text()
|
||||||
|
assert history.table.item(0, 3).text() == "—"
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
"""Data provenance and native UI checks for the prescription overview page."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from PySide6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
|
||||||
|
|
||||||
|
from doctor_workstation.ui.dialogs.issued_prescription_ai_workspace import (
|
||||||
|
PrescriptionReviewWorkspace,
|
||||||
|
checklist_items,
|
||||||
|
diagnosis_text,
|
||||||
|
dose_deltas,
|
||||||
|
gap_counts,
|
||||||
|
review_rows,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def batch(count: int = 3) -> dict:
|
||||||
|
rows = []
|
||||||
|
herbs = []
|
||||||
|
for index in range(count):
|
||||||
|
herb = {"name": f"药材{index}", "dosage": "15.00", "unit": "g", "dose_basis": "per_dose", "formula_type": "主方", "processing": "生品"}
|
||||||
|
rows.append({"key": f"saved-identity-{index}", "name": herb["name"], "doctor": {**herb, "dosage": "30.00"}, "candidate": {**herb, "source_rows": [index]}, "match_type": "matched"})
|
||||||
|
herbs.append(herb)
|
||||||
|
model = {"status": "succeeded", "report": {"diagnosis": "已保存的辨证意见", "summary": "不能当作诊断的摘要", "missing_information": ["缺少舌脉记录"]}, "candidate": {"status": "available_for_review", "herbs": herbs}, "comparison": {"status": "comparable", "rows": rows}}
|
||||||
|
return {"id": 4, "validity": "current", "models": {"qwen": deepcopy(model), "openai": deepcopy(model)}, "doctor_snapshot": {"patient": {"name": "测试患者", "gender": "male", "age": 50}, "diagnosis": {"western_diagnosis": "已记录西医诊断", "tcm_diagnosis": "已记录中医诊断", "syndrome": "已记录证候"}, "prescription": {"herbs": deepcopy(herbs), "usage_instruction": "水煎服", "usage_days": 7, "times_per_day": 2}}}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def application():
|
||||||
|
return QApplication.instance() or QApplication([])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def workspace(application):
|
||||||
|
host = QWidget()
|
||||||
|
host.resize(1024, 760)
|
||||||
|
layout = QVBoxLayout(host)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
widget = PrescriptionReviewWorkspace(host)
|
||||||
|
layout.addWidget(widget)
|
||||||
|
host.show()
|
||||||
|
application.processEvents()
|
||||||
|
yield widget
|
||||||
|
host.close()
|
||||||
|
host.deleteLater()
|
||||||
|
application.processEvents()
|
||||||
|
|
||||||
|
|
||||||
|
def test_three_series_need_saved_unique_identity_and_same_baseline():
|
||||||
|
source = batch()
|
||||||
|
before = deepcopy(source)
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert source == before
|
||||||
|
assert len(rows) == 3
|
||||||
|
assert all(set(row.doses) == {"doctor", "qwen", "openai"} for row in rows)
|
||||||
|
assert rows[0].scale == ("克", "每剂")
|
||||||
|
assert rows[0].changed
|
||||||
|
assert "15.00 克 / 每剂" in rows[0].description
|
||||||
|
assert "候选原方记录:第 1 项 药材0" in rows[0].description
|
||||||
|
assert "saved-identity" not in rows[0].description
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mutation", ["no_keys", "different_baseline", "duplicate_keys", "different_units", "different_identity"])
|
||||||
|
def test_no_guessed_cross_model_join(mutation):
|
||||||
|
source = batch(1)
|
||||||
|
target = source["models"]["openai"]["comparison"]["rows"][0]
|
||||||
|
if mutation == "no_keys":
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0].pop("key")
|
||||||
|
elif mutation == "different_baseline":
|
||||||
|
target["doctor"]["dosage"] = 31
|
||||||
|
elif mutation == "duplicate_keys":
|
||||||
|
source["models"]["openai"]["comparison"]["rows"].append(deepcopy(target))
|
||||||
|
elif mutation == "different_units":
|
||||||
|
target["candidate"]["unit"] = "mg"
|
||||||
|
else:
|
||||||
|
target["candidate"]["processing"] = "炙品"
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert len(rows) >= 2
|
||||||
|
assert all(len(row.entries) == 1 for row in rows)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ["nan", "-3", "1/2", "Infinity", None])
|
||||||
|
def test_invalid_numbers_and_missing_are_never_zero(value):
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["candidate"]["dosage"] = value
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert rows[0].scale is None
|
||||||
|
assert not rows[0].changed
|
||||||
|
if value is None:
|
||||||
|
assert rows[0].entries["qwen"].candidate.label == "—"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("field,value", [("validity", "stale"), ("validity", "source_updated"), ("validity", None), ("status", "running"), ("status", "failed"), ("comparison", "not_comparable"), ("candidate", "withheld_for_risk")])
|
||||||
|
def test_pending_historical_and_error_are_text_only(field, value):
|
||||||
|
source = batch()
|
||||||
|
if field == "validity":
|
||||||
|
source[field] = value
|
||||||
|
else:
|
||||||
|
for model in source["models"].values():
|
||||||
|
if field == "status":
|
||||||
|
model[field] = value
|
||||||
|
else:
|
||||||
|
model[field]["status"] = value
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert rows
|
||||||
|
assert all(row.scale is None for row in rows)
|
||||||
|
assert all(not row.changed for row in rows)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unaccounted_candidates_and_missing_trace_survive():
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["candidate"]["herbs"].append({"name": "未规范炮制药", "dosage": "2.5", "unit": "g"})
|
||||||
|
model["comparison"]["rows"][0]["candidate"].pop("source_rows")
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert len(rows) == 5
|
||||||
|
originals = [row for row in rows if next(iter(row.entries.values())).origin != "comparison"]
|
||||||
|
assert len(originals) == 4
|
||||||
|
assert all(row.scale is None for row in originals)
|
||||||
|
assert sum(row.name == "未规范炮制药" for row in originals) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_diagnosis_never_fabricated_from_summary():
|
||||||
|
assert diagnosis_text({"report": {"summary": "摘要疾病"}}) == "诊断意见未保存"
|
||||||
|
assert diagnosis_text({"report": {"diagnosis": "辨证原文"}}) == "辨证原文"
|
||||||
|
assert diagnosis_text({"report": {"diagnosis": {"western_diagnosis": "诊断原值"}}}) == "西医诊断:诊断原值"
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_side_is_not_a_zero_dose_difference():
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["doctor"] = None
|
||||||
|
rows, _ = review_rows(source)
|
||||||
|
assert rows[0].doctor is None
|
||||||
|
assert not rows[0].changed
|
||||||
|
|
||||||
|
|
||||||
|
def test_historical_report_states_itself_instead_of_reporting_zero_differences(workspace):
|
||||||
|
source = batch()
|
||||||
|
source["validity"] = "stale"
|
||||||
|
workspace.set_batch(source)
|
||||||
|
assert "历史或失效报告" in workspace.summary_text
|
||||||
|
assert workspace.doses.rows == [] # 不可比就不画,不是画成 0
|
||||||
|
assert workspace.doses.empty.isVisible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_每味药与原方的距离按共同标尺画出(workspace):
|
||||||
|
workspace.set_batch(batch())
|
||||||
|
assert [delta.name for delta in workspace.doses.rows] == ["药材0", "药材1", "药材2"]
|
||||||
|
assert all(delta.deltas["qwen"] == -15 for delta in workspace.doses.rows)
|
||||||
|
assert "3 项剂量差异" in workspace.summary_text
|
||||||
|
axis = workspace.doses.body.findChildren(QWidget)
|
||||||
|
described = [widget.accessibleDescription() for widget in axis if widget.accessibleName() == "剂量差异条"]
|
||||||
|
assert "药材0 千问 −15克 OpenAI −15克" in described
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_herb_one_model_never_listed_is_marked_absent_not_zero(workspace):
|
||||||
|
source = batch(1)
|
||||||
|
source["models"]["openai"]["comparison"]["rows"][0]["candidate"] = None
|
||||||
|
source["models"]["openai"]["candidate"]["herbs"] = []
|
||||||
|
workspace.set_batch(source)
|
||||||
|
delta = workspace.doses.rows[0]
|
||||||
|
assert delta.deltas["openai"] is None
|
||||||
|
assert delta.badge == "OpenAI 未收录"
|
||||||
|
assert delta.deltas["qwen"] == -15
|
||||||
|
|
||||||
|
|
||||||
|
def test_an_unreadable_saved_dose_takes_the_whole_row_off_the_chart(workspace):
|
||||||
|
source = batch(1)
|
||||||
|
source["models"]["openai"]["comparison"]["rows"][0]["candidate"]["dosage"] = "nan"
|
||||||
|
workspace.set_batch(source)
|
||||||
|
assert workspace.doses.rows == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_an_incomparable_basis_keeps_the_row_off_the_chart(workspace):
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["doctor"]["dose_basis"] = "per_day"
|
||||||
|
workspace.set_batch(source)
|
||||||
|
assert workspace.doses.rows == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_pending_model_adds_no_row_and_no_difference(workspace):
|
||||||
|
source = batch(1)
|
||||||
|
source["models"]["openai"] = {"status": "running"}
|
||||||
|
workspace.set_batch(source)
|
||||||
|
assert len(workspace.doses.rows) == 1
|
||||||
|
assert workspace.doses.rows[0].deltas["openai"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_new_herbs_carry_their_full_dose_and_say_which_model_added_them(workspace):
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["doctor"] = None
|
||||||
|
workspace.set_batch(source)
|
||||||
|
delta = workspace.doses.rows[0]
|
||||||
|
assert delta.doctor is None
|
||||||
|
assert delta.deltas["qwen"] == 15
|
||||||
|
|
||||||
|
|
||||||
|
def test_gap_counts_split_the_saved_gaps_into_three_buckets() -> None:
|
||||||
|
source = batch(1)
|
||||||
|
source["missing"] = [{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True},
|
||||||
|
{"code": "ATTACHMENT_STORAGE_RESTRICTED"},
|
||||||
|
{"code": "ARCHIVE_SYNC_WATERMARK_UNAVAILABLE"}]
|
||||||
|
assert gap_counts(source) == {"critical": 1, "attachment": 1, "other": 1}
|
||||||
|
assert gap_counts(batch(1)) == {"critical": 0, "attachment": 0, "other": 0}
|
||||||
|
|
||||||
|
|
||||||
|
def _conclusion_texts(workspace) -> list[str]:
|
||||||
|
"""The numbered sentences, skipping the hairlines the design puts between them."""
|
||||||
|
|
||||||
|
texts = []
|
||||||
|
for index in range(workspace.conclusions.body_layout.count()):
|
||||||
|
widget = workspace.conclusions.body_layout.itemAt(index).widget()
|
||||||
|
labels = widget.findChildren(QLabel) if widget is not None else []
|
||||||
|
if labels:
|
||||||
|
texts.append(labels[-1].text())
|
||||||
|
return texts
|
||||||
|
|
||||||
|
|
||||||
|
def test_conclusions_state_the_counts_they_were_built_from(workspace) -> None:
|
||||||
|
workspace.set_batch(batch(3))
|
||||||
|
texts = _conclusion_texts(workspace)
|
||||||
|
assert any("两个模型都给出了候选方" in text for text in texts)
|
||||||
|
assert any("剂量偏差共" in text for text in texts)
|
||||||
|
assert any("没有记录资料缺口" in text for text in texts)
|
||||||
|
|
||||||
|
|
||||||
|
def test_conclusions_never_invent_a_score_comparison(workspace) -> None:
|
||||||
|
source = batch(1)
|
||||||
|
source["models"]["openai"]["comparison"]["status"] = "not_comparable"
|
||||||
|
workspace.set_batch(source)
|
||||||
|
texts = _conclusion_texts(workspace)
|
||||||
|
assert any("只有可比的一侧有分数" in text for text in texts)
|
||||||
|
|
||||||
|
|
||||||
|
def test_attribution_groups_every_herb_exactly_once(workspace) -> None:
|
||||||
|
source = batch(2)
|
||||||
|
source["models"]["openai"]["candidate"]["herbs"] = [{"name": "药材0", "dosage": "15.00", "unit": "g"}]
|
||||||
|
workspace.set_batch(source)
|
||||||
|
assert workspace.attribution.rows["all"]["count"].text() == "1 味"
|
||||||
|
assert workspace.attribution.rows["doctor_only"]["count"].text() == "0 味"
|
||||||
|
assert workspace.attribution.rows["openai_only"]["count"].text() == "0 味"
|
||||||
|
# 药材1 只有医方与千问共用,四个分组都不含它,标题必须说明这一点
|
||||||
|
assert "合计 2 味" in workspace.attribution.total_note.text()
|
||||||
|
assert "另 1 味为医方与单一模型共用" in workspace.attribution.total_note.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_risk_panel_flags_only_large_changes(workspace) -> None:
|
||||||
|
small = batch(1)
|
||||||
|
for model in small["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["candidate"]["dosage"] = "29.00"
|
||||||
|
workspace.set_batch(small)
|
||||||
|
assert workspace.risks.empty.isVisible() # 差 1 克不进清单
|
||||||
|
large = batch(1)
|
||||||
|
for model in large["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["candidate"]["dosage"] = "12.00"
|
||||||
|
workspace.set_batch(large)
|
||||||
|
assert workspace.risks.body.isVisible()
|
||||||
|
assert "2 项" in workspace.risks.hint.text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_checklist_merges_the_models_and_keeps_the_severe_items_first():
|
||||||
|
source = batch(1)
|
||||||
|
source["models"]["qwen"]["report"]["risk_assessment"] = [{"level": "high", "label": "血压数据缺失"}]
|
||||||
|
source["missing"] = [{"code": "TRANSCRIPT_NOT_VERIFIED_COMPLETE", "critical": True}]
|
||||||
|
items = checklist_items(source)
|
||||||
|
assert items[0][0] == "血压数据缺失"
|
||||||
|
assert items[0][1] == "千问 关键"
|
||||||
|
shared = next(item for item in items if item[0] == "缺少舌脉记录")
|
||||||
|
assert shared[1] == "千问、OpenAI"
|
||||||
|
assert any("转写" in item[0] for item in items)
|
||||||
|
|
||||||
|
|
||||||
|
def test_checklist_shows_the_saved_items_and_says_so_when_empty(workspace):
|
||||||
|
workspace.set_batch(batch())
|
||||||
|
assert "1 条 · 按严重度排序" in workspace.checklist.count_note.text()
|
||||||
|
assert not workspace.checklist.empty.isVisible()
|
||||||
|
workspace.set_batch({"id": 9, "validity": "current", "models": {}})
|
||||||
|
assert workspace.checklist.empty.isVisible()
|
||||||
|
assert workspace.checklist.count_note.text() == "暂无待确认项"
|
||||||
|
|
||||||
|
|
||||||
|
def test_rerendering_leaves_no_stale_row_widgets(workspace, application):
|
||||||
|
workspace.set_batch(batch(20))
|
||||||
|
application.processEvents()
|
||||||
|
assert len(_axes(workspace)) == 20
|
||||||
|
workspace.set_batch(batch(3))
|
||||||
|
application.processEvents()
|
||||||
|
assert len(_axes(workspace)) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def _axes(workspace):
|
||||||
|
return [widget for widget in workspace.doses.body.findChildren(QWidget)
|
||||||
|
if widget.accessibleName() == "剂量差异条" and widget.parentWidget() is not None]
|
||||||
|
|
||||||
|
|
||||||
|
def test_page_fits_a_1024_window_without_horizontal_scrolling(workspace, application):
|
||||||
|
workspace.set_batch(batch())
|
||||||
|
application.processEvents()
|
||||||
|
assert workspace.minimumSizeHint().width() <= 900
|
||||||
|
assert workspace.width() == 1024
|
||||||
|
assert workspace.doses.scroll.horizontalScrollBarPolicy() == Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
||||||
|
assert workspace.checklist.scroll.horizontalScrollBarPolicy() == Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
||||||
|
|
||||||
|
|
||||||
|
def test_dose_deltas_ignore_rows_without_a_shared_scale():
|
||||||
|
source = batch(1)
|
||||||
|
for model in source["models"].values():
|
||||||
|
model["comparison"]["rows"][0]["candidate"]["unit"] = "mg"
|
||||||
|
rows, _states = review_rows(source)
|
||||||
|
assert dose_deltas(rows) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_review_slot_has_no_visible_parentless_widget(workspace, application):
|
||||||
|
before = {widget for widget in application.topLevelWidgets() if widget.isVisible()}
|
||||||
|
first = QLabel("第一模型复核", workspace)
|
||||||
|
second = QLabel("双模型复核", workspace)
|
||||||
|
workspace.set_review_widget(first)
|
||||||
|
workspace.set_review_widget(second)
|
||||||
|
application.processEvents()
|
||||||
|
assert second.parentWidget() is workspace.checklist.review_slot
|
||||||
|
assert not first.isVisible()
|
||||||
|
assert first.parentWidget() is workspace.checklist.review_slot
|
||||||
|
assert {widget for widget in application.topLevelWidgets() if widget.isVisible()} == before
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ from doctor_workstation.ui.pages import prescription_library as library_module
|
|||||||
from doctor_workstation.ui.pages import prescriptions as prescriptions_module
|
from doctor_workstation.ui.pages import prescriptions as prescriptions_module
|
||||||
from doctor_workstation.ui.pages.prescription_library import PrescriptionLibraryPage
|
from doctor_workstation.ui.pages.prescription_library import PrescriptionLibraryPage
|
||||||
from doctor_workstation.ui.pages.prescriptions import PrescriptionsPage
|
from doctor_workstation.ui.pages.prescriptions import PrescriptionsPage
|
||||||
from doctor_workstation.ui.widgets import BusinessPager
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
|
|||||||
@@ -1,495 +0,0 @@
|
|||||||
vite v6.4.2 building for production...
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) outDir D:\web\zyt\artifacts\appointment-modes\admin-build is not inside project root and will not be emptied.
|
|
||||||
Use --emptyOutDir to override.
|
|
||||||
|
|
||||||
|
|
||||||
transforming...
|
|
||||||
✓ 4061 modules transformed.
|
|
||||||
|
|
||||||
WARN node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js (3199:26): Use of eval in "node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
||||||
|
|
||||||
rendering chunks...
|
|
||||||
computing gzip size...
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/default_avatar-C6VB7PGm.png 6.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/no_perms-jDxcYpYC.png 14.62 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/index.html 31.87 kB │ gzip: 16.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/login_bg-BkIjQ0FB.png 59.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/red3-NOuWP8DK.png 105.00 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pink3-BxZ4Y6CS.png 108.36 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/blue3-D3K9OqGO.png 108.98 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yellow3-C_qd9cqN.png 109.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/green3-CPorIQiC.png 109.99 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/purple3-BGd0LxTa.png 110.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/my_topbg-BiU0PleK.png 142.47 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/red2-Dw8p71sP.png 750.50 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/purple2-C0tQkldV.png 752.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/green2-C-VRKLSN.png 756.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yellow2-B-WtqITJ.png 762.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pink2-BpEp33zy.png 806.48 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/blue2-CRQPdLZd.png 806.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/red1-C6Y3UuNB.png 1,660.63 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yellow1-Ebw0T5sw.png 1,662.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pink1-BWNZrP7C.png 1,668.56 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/blue1-gLOo1H0w.png 1,676.54 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/purple1-BpMq9FWz.png 1,680.01 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/green1-h1zqes95.png 1,688.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CmFE2aZQ.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CjSHFu-R.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Oppei429.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/CaseRecordList-Cl-c3q7S.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DietRecordList-Cc-xUr2B.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/roster-CqNlv_rj.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/BloodRecordList-DhVK-Y_e.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B3imPWFk.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list-BluNBZln.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ExerciseRecordList-DGtgtzou.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CObVTcPU.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-D-D8bVPM.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pc_details-nlJo1_D0.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AppointmentRecordPanel-DC22GDgn.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/theme-picker-BsELUxM9.css 0.11 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-Cl-9UIip.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AssignLogPanel-DkQMoEkX.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-DXAsZ7EV.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-DaxRy47P.css 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CAJZpU7c.css 0.14 kB │ gzip: 0.12 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-DxNvUNZR.css 0.15 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useListTimeFilter-DI6SIumd.css 0.16 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-C1NtFi7N.css 0.16 kB │ gzip: 0.11 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-Bd3rHJ5J.css 0.18 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/change-password-DRfsLJ26.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-Dy_alH9u.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/decoration-img-C5XvHl9_.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/account_cost-fbCWUO9k.css 0.20 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-57RdOFNo.css 0.23 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/error-Cz3CexuM.css 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-D4atl_7z.css 0.25 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-lNortPsv.css 0.27 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/setting-CxqqGetv.css 0.27 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DX96drV3.css 0.28 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-DYFusokT.css 0.29 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/OrderActionHost-DiqybThz.css 0.31 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bk2rMrqw.css 0.32 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-D0f1Mn00.css 0.33 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/prescription-drawer-BjwiqvPc.css 0.35 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/bind-work-wechat-BX1gqwq1.css 0.35 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DhR2yeiO.css 0.39 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DoFDMZdP.css 0.40 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DBNvK3ZK.css 0.45 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientOrderList-D8-uh2iu.css 0.45 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DiagnosisTodoList-D0SBmTHG.css 0.46 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/CallRecordPanel-TsjZtTO8.css 0.47 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C_LxAEkS.css 0.48 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/login-Bt4SvQsz.css 0.52 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-sMbqkta2.css 0.55 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/RecordingPlaybackBlock-B3KYFgvg.css 0.58 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/medicine-DO6x6zrS.css 0.58 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/preview-pc-BRQDo0AR.css 0.67 kB │ gzip: 0.36 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BzrSkGWL.css 0.67 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tabbar-DJsOahKR.css 0.69 kB │ gzip: 0.31 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/qywx-Dym8iFe0.css 0.74 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AssistantWatchCallDialog-BIPZRgaH.css 0.76 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/picker-JBYDNsl5.css 0.84 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-phone-CctIyraX.css 0.94 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/preview-C7oaKmYo.css 0.98 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bidrbn2Z.css 1.16 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/TrackingNoteTimeline-B9T8pFso.css 1.18 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/RecordingVideoPlayer-4DegHAwm.css 1.19 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-55HtuLpn.css 1.21 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientInfoCard-n6_UPo9e.css 1.32 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/TrackingMatrix-zyxtXYPb.css 1.32 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/SendPanel-DaGPWlQG.css 1.39 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/picker-XZbUgFct.css 1.57 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionOrderDetailDrawer-B4rggqa1.css 1.73 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ImChatRecordPanel-aX8zpWRQ.css 1.74 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BYOmK9R5.css 1.93 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-OgnT0gUo.css 1.98 kB │ gzip: 0.56 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/MessageBubble-BwtdPM6K.css 2.04 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BmDW9vcs.css 2.44 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientCaseCard-C3Mob6yG.css 2.49 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tongji-C0VnzFxy.css 2.49 kB │ gzip: 0.77 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/dept-tongji-ky7rOYoi.css 2.67 kB │ gzip: 0.72 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/readonly-CdP3eZN0.css 2.69 kB │ gzip: 0.92 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/NoteTimeline-I2FSxX4s.css 2.74 kB │ gzip: 0.84 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/patient-call-k8V9hV2G.css 2.85 kB │ gzip: 0.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DailyMatrix-DCZnxI8i.css 2.87 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PaibanPanel-aqm8azGc.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/paiban-BTUHWpUu.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/appointment-g8BrHfhi.css 3.43 kB │ gzip: 0.93 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionAiBatchGenerateDialog-CKkykmdr.css 3.45 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/h5-BSFcFSny.css 3.52 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bk7b_sL2.css 3.57 kB │ gzip: 0.96 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/add-ByXRFm8M.css 3.60 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PromotionAutomationForm-BMhNCJa-.css 3.64 kB │ gzip: 1.03 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-B6IOi2bJ.css 3.78 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/WelcomeMessageEditor-DD5mXWP2.css 4.41 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionAiReportDialog-CV6AuHV3.css 4.46 kB │ gzip: 1.11 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CMsGEvq_.css 5.19 kB │ gzip: 1.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-dept-node-ChUPkaxq.css 5.47 kB │ gzip: 1.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/OrderPanel-BzElZPEq.css 5.67 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Co57tWbg.css 6.11 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list-D5BdlpZJ.css 6.51 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DtDaTXOB.css 7.01 kB │ gzip: 1.69 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-dept-card-naMqyghz.css 7.09 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-AnyUcdTT.css 7.21 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bw7BJ7QR.css 8.03 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DONCMk_U.css 8.04 kB │ gzip: 2.32 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/progress-99NT9ysL.css 8.14 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Cmf8QBsF.css 8.75 kB │ gzip: 1.78 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CSIcxqFk.css 8.85 kB │ gzip: 2.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B_6Taw4k.css 8.97 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-DOd7Lhrw.css 9.47 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Dnn9ddtX.css 9.49 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-D9Td5Y2a.css 9.52 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ProgressPanel-C_aSqDJh.css 10.10 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-I_vRP8ff.css 10.57 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DV5JwFnL.css 11.95 kB │ gzip: 2.61 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-cz2HBbSE.css 13.65 kB │ gzip: 3.05 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/commission-settlement-Gy2UsUPr.css 15.55 kB │ gzip: 3.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list_h5-Cs3mUmOa.css 17.70 kB │ gzip: 3.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/order_list-CMCIYVRT.css 18.15 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Dgjf9Hxv.css 18.86 kB │ gzip: 3.83 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/WecomFloatingWidgetBuilder-DRWt4rvh.css 19.20 kB │ gzip: 4.00 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index_h5-6234yJkC.css 19.63 kB │ gzip: 3.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yeji-7-gSVFmd.css 25.24 kB │ gzip: 4.60 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BSycZlsS.css 45.91 kB │ gzip: 9.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/order_list_h5-DB3qJv9G.css 48.13 kB │ gzip: 7.68 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/.pnpm-B3v8nGpq.css 723.50 kB │ gzip: 100.49 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/getExposeType-BhVtb25-.js 0.07 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-qqeREw9s.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-DcOELR-K.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-tHefvLXa.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/MediaSourceSelect-8A82adC4.js 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/code-preview-DebWNacj.js 0.16 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-B5lAmlRD.js 0.18 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/refund-log-Dte4TYSk.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/account-adjust-B00EjgEk.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-o13C0dUX.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-Bw34X5yl.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-fJdDXmQk.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-ZSwm2OnL.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/perm-CxGp7d9E.js 0.20 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/diabetes-discovery-display-B_wmGXQJ.js 0.20 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionOrderTimeDialog-DhGQ2qc5.js 0.20 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/weapp-AW0lTawR.js 0.20 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/GancaoSubmissionReconcileButton-BUP3IK_K.js 0.21 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-n8Jap8KU.js 0.21 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-DobNH3vu.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-B6XQI8aF.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-DyTWIq8-.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-DcP285OM.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CpN5NTKb.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CscKSbQv.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DG0CrN4I.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-awcvFaxy.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DvEPH3sF.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-menu-form-DpRW2C_C.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useLockFn-DZaCbVGv.js 0.22 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/appointment-type-DJT6wPaT.js 0.23 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-D9QQ1l2O.js 0.23 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/auth-C5D7rPvf.js 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CBO2zICf.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/relations-add-D1VlWUUa.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mobile-style-DwsiSMiK.js 0.26 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CHvGoI1A.js 0.27 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/cost-edit-Bg-Uyd2c.js 0.30 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yeji-edit-C9uvAsWe.js 0.30 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/404-3rkYd7tU.js 0.31 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/data-table-SmfBi9Sg.js 0.31 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-menu-form-edit-C7hYj3ER.js 0.33 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/medicine-Dd2rx3Ot.js 0.35 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/consumer-B3GKjsdE.js 0.35 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/user-Ca4bJD6d.js 0.37 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/role-DTJVNPTP.js 0.39 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useDictOptions-Dn9M7zwp.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BzeCW0Uz.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/admin-BOSvnXa7.js 0.42 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-s8eO9hBN.js 0.44 kB │ gzip: 0.36 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pay-CYh6c1Jc.js 0.45 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/department-jIolW8HD.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-DxVtimm9.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-COxSBtU5.js 0.47 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-C-UmNM7-.js 0.48 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/post-BM66vrTr.js 0.49 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/overflow-CvDxI8pV.js 0.49 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/decoration-DcKIqlQW.js 0.50 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/message-NjmKbrE0.js 0.50 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/403-Bd7P9v6E.js 0.52 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/footer.vue_vue_type_script_setup_true_lang-CwusgJ4Z.js 0.53 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-C-oRPuSk.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-CY72vprk.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/add-nav-BdWkHCFM.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-DnglhtO0.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-set-O2cSQVkP.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-D3WqzAia.js 0.55 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/patient-vjBAK2HA.js 0.56 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/fans-eQIJEPPO.js 0.60 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-DZ31aYPg.js 0.60 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-lqO8LPXd.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-LZa4samX.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/diag-display-DCz_VAqj.js 0.61 kB │ gzip: 0.39 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content.vue_vue_type_script_setup_true_lang-0Lb4ZZ78.js 0.61 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C9CTu6xY.js 0.63 kB │ gzip: 0.43 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CFowjcfc.js 0.63 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/link-DGRRT5eg.js 0.64 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/asset-D92fftiF.js 0.67 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content.vue_vue_type_script_setup_true_lang-Bt_MGGD6.js 0.69 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useMediaSourceOptions-B0L3IlgH.js 0.70 kB │ gzip: 0.42 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content.vue_vue_type_script_setup_true_lang-4NVOwZND.js 0.70 kB │ gzip: 0.45 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/website-CBS8EOwn.js 0.74 kB │ gzip: 0.24 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-C47G34h8.js 0.76 kB │ gzip: 0.49 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/usePaging-oaNM6A9T.js 0.77 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/self_input_stats-BMoDYPed.js 0.79 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/decoration-img-ChFP1uE8.js 0.81 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/dict-DqKNj29A.js 0.81 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/code-D879PDoD.js 0.82 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/blood-thresholds-CJ1fqMjh.js 0.83 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-NopGY6qR.js 0.84 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content.vue_vue_type_script_setup_true_lang-1ur9wwmZ.js 0.86 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_script_setup_true_lang-kAZgL5aN.js 0.86 kB │ gzip: 0.52 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_script_setup_true_lang-CDLrQsa9.js 0.88 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/error-Bn-Kyayl.js 0.89 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/theme-picker-BiL-qD2M.js 0.92 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/wecomOauthPostMessage-C_e6VGrO.js 0.95 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_script_setup_true_lang-Cmgm_Z5P.js 0.98 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/wx_oa-B5L52qTa.js 1.04 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/article-hs8A9Jdu.js 1.07 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-setting.vue_vue_type_script_setup_true_lang-ClYX-hV1.js 1.08 kB │ gzip: 0.61 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/rich_text-Dk0jS3Pc.js 1.10 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/cache-CWFtbfTm.js 1.13 kB │ gzip: 0.69 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DCmzItce.js 1.21 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/popover_input-pPM8UqwF.js 1.25 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/finance-DA6_SyDw.js 1.30 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pc-DQbWYqv7.js 1.31 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/doctor-DyZGuUZC.js 1.32 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-menu-form-edit.vue_vue_type_script_setup_true_lang-C21u_UoH.js 1.33 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-BAht1aZ2.js 1.33 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-CeY2TUv-.js 1.37 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-BJGtt-xN.js 1.38 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/file-BTnyaqZX.js 1.41 kB │ gzip: 0.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/upload-DHrBQCOO.js 1.41 kB │ gzip: 0.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content-NSr7rO7a.js 1.42 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/code-preview.vue_vue_type_script_setup_true_lang-C4bf87lU.js 1.44 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DLNXfvlb.js 1.44 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_style_index_0_lang-mxcCSl8u.js 1.46 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-phone-DVZEALSU.js 1.47 kB │ gzip: 0.80 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-BePIgx8X.js 1.50 kB │ gzip: 0.76 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-BxpNhh7J.js 1.55 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/MediaSourceSelect.vue_vue_type_script_setup_true_lang-xuHF5G6T.js 1.58 kB │ gzip: 0.80 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientInfoCard-oWJA6Uib.js 1.60 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-BACvjW0Z.js 1.62 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/style-DHri-b9V.js 1.62 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/refund-log.vue_vue_type_script_setup_true_lang-C9XoZuna.js 1.66 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/statistics-B3M41WAU.js 1.69 kB │ gzip: 1.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DNKk0KAC.js 1.71 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/order-D2TrFdV8.js 1.74 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/environment-C74pQttO.js 1.74 kB │ gzip: 0.74 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/setup-Dz_APrpo.js 1.77 kB │ gzip: 1.00 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useMenuOa-DFdWMJUR.js 1.82 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-setting-D_7bJ_WP.js 1.88 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BzDASedl.js 1.90 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_name_articleColumnEdit_lang-E0vpVDJX.js 1.94 kB │ gzip: 1.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B9qvdXe9.js 2.03 kB │ gzip: 0.98 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B3x_V6St.js 2.04 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/TrackingNoteTimeline-B7ZehsXP.js 2.08 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DMVXI7dX.js 2.09 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-CFyAuY4i.js 2.09 kB │ gzip: 0.95 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/open_setting-521tiVc_.js 2.10 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-0BnrgRc3.js 2.12 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/account-adjust.vue_vue_type_script_setup_true_lang-CpPfqdLb.js 2.16 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/filing-Cqmunz7R.js 2.17 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-ClW9LZFF.js 2.18 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/icon-qQvuPtJo.js 2.23 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/content.vue_vue_type_script_setup_true_lang-Dur4SMQq.js 2.24 kB │ gzip: 1.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-vpDrHDku.js 2.28 kB │ gzip: 1.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-C366kQIF.js 2.29 kB │ gzip: 1.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_script_setup_true_lang-BBhGStE3.js 2.37 kB │ gzip: 1.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/RecordingPlaybackBlock-BOTDmjKh.js 2.40 kB │ gzip: 1.19 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/auth.vue_vue_type_script_setup_true_lang-CWND1gXs.js 2.40 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/stats-BZP8tLtW.js 2.45 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/data-table.vue_vue_type_script_setup_true_lang-DS49GzuV.js 2.51 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-attr-BzWXYYXP.js 2.51 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-Dhx2yo7b.js 2.52 kB │ gzip: 1.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/h5-p3Zf_YQP.js 2.56 kB │ gzip: 1.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/change-password-YIODZgIx.js 2.57 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/im-business-message-parse-kN-fWnvR.js 2.60 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/add-nav.vue_vue_type_script_setup_true_lang-DMG92gvp.js 2.61 kB │ gzip: 1.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tabbar-C4RTjgc5.js 2.66 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/file-CT3DBwWs.js 2.73 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-uPRl_lL4.js 2.74 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionOrderTimeDialog.vue_vue_type_script_setup_true_lang-VQL80FaN.js 2.77 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/oa-menu-form.vue_vue_type_script_setup_true_lang-BeAQ08_1.js 2.77 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/CaseRecordList-LSZveKRb.js 2.80 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-CkZpjrdt.js 2.81 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C659Qo0h.js 2.85 kB │ gzip: 1.34 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/follow_reply-DVMErqzs.js 2.85 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-D1k3lHzy.js 2.86 kB │ gzip: 1.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/cost-edit.vue_vue_type_script_setup_true_lang-CVc0n4pV.js 2.86 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-lmr2i5Bj.js 2.89 kB │ gzip: 1.41 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/default_reply-CpyjKv3V.js 2.91 kB │ gzip: 1.54 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-gdH1LeQj.js 2.96 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/protocol-D4wxifpO.js 2.98 kB │ gzip: 1.10 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-PJQQwYnV.js 2.99 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-dept-node-Drt73fzr.js 3.03 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/relations-add.vue_vue_type_script_setup_true_lang-C8d76FGV.js 3.04 kB │ gzip: 1.26 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/picker.vue_vue_type_script_setup_true_lang-BIYyttaB.js 3.05 kB │ gzip: 1.50 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AssignLogPanel-D2r9RPoC.js 3.06 kB │ gzip: 1.48 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/keyword_reply-DBgqHDjZ.js 3.11 kB │ gzip: 1.60 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-DweAr7Ym.js 3.12 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BUsM_LI5.js 3.21 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-CnIVKgZ-.js 3.25 kB │ gzip: 1.47 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/menu-set.vue_vue_type_script_setup_true_lang-BRrEuHV-.js 3.26 kB │ gzip: 1.40 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/pc_details-B_6wZRHo.js 3.39 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DqGrHNcO.js 3.39 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-phcPclpA.js 3.42 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CUge-qSO.js 3.43 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-BkwnyzI2.js 3.44 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/balance_details-Ds1LzmaH.js 3.48 kB │ gzip: 1.63 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DtdgFl7O.js 3.48 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index.vue_vue_type_script_setup_true_lang-cQgN4IT9.js 3.52 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/journal-CY6ZpS6p.js 3.73 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-DKvnMaof.js 3.73 kB │ gzip: 1.57 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/detail-BV7O2HIL.js 3.76 kB │ gzip: 1.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AssistantWatchCallDialog-BuTdyKef.js 3.78 kB │ gzip: 1.85 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/CallRecordPanel-BSTysfqz.js 3.78 kB │ gzip: 1.77 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-bI1lUcbW.js 3.91 kB │ gzip: 1.71 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/preview-pc-BY7HFTA8.js 3.98 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/AppointmentRecordPanel-BbOdj1-7.js 4.02 kB │ gzip: 1.73 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-C8TwWqpc.js 4.11 kB │ gzip: 1.65 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CxWRNLqZ.js 4.11 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/first_visit-CvA69-XM.js 4.13 kB │ gzip: 0.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/detail-BRablUBf.js 4.15 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/MessageBubble-DgY38P4R.js 4.17 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bh5w94E3.js 4.22 kB │ gzip: 1.92 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/useListTimeFilter-COfv9A2s.js 4.26 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B2NSVhqr.js 4.37 kB │ gzip: 2.14 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/bind-work-wechat-DP4Gin1a.js 4.40 kB │ gzip: 2.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/recharge_record-BzIkkNm9.js 4.43 kB │ gzip: 1.87 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B4J-ZHfW.js 4.45 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BwbCBYM9.js 4.47 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-exKfRfkT.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Bs0vH486.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/preview-Dh1zljh0.js 4.49 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CvCnQtvN.js 4.51 kB │ gzip: 1.75 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/RecordingVideoPlayer-JNdaKBs5.js 4.53 kB │ gzip: 2.26 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ImChatRecordPanel-6cheoyXp.js 4.54 kB │ gzip: 2.40 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BLgjrOkJ.js 4.55 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/paiban-idrEqd0C.js 4.56 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DAvdgv_l.js 4.57 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/login_register-BcBecvF5.js 4.59 kB │ gzip: 2.01 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/dept-tongji-CwUw39uH.js 4.66 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C0B1ZpBc.js 4.76 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-Dozjlzsq.js 4.92 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-BBC7JIUD.js 5.04 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CeStvPlb.js 5.18 kB │ gzip: 2.03 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PaibanPanel-CwUhJWnn.js 5.28 kB │ gzip: 2.43 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-gh639-1m.js 5.32 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-rDSQ6cWW.js 5.53 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-CfJ7Ipck.js 5.55 kB │ gzip: 2.56 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yeji-edit.vue_vue_type_script_setup_true_lang-Dw9aZRHs.js 5.56 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mobile-style.vue_vue_type_script_setup_true_lang-BGsSkFG1.js 5.59 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ExerciseRecordList-oXdkNbAH.js 5.62 kB │ gzip: 2.15 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/attr-853P--U5.js 5.62 kB │ gzip: 2.24 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tongji-BTDH_24t.js 5.66 kB │ gzip: 2.31 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientOrderList-Cy0KYlPt.js 5.82 kB │ gzip: 2.84 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DietRecordList-BeSIO_4H.js 5.84 kB │ gzip: 2.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/mubiao-dept-card-C1gPiZEq.js 5.91 kB │ gzip: 2.38 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/login-DJgbVJrh.js 6.03 kB │ gzip: 2.72 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/refund_record-MA_WT5dD.js 6.27 kB │ gzip: 2.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DiagnosisTodoList-CIc3FdV9.js 6.34 kB │ gzip: 2.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-D4UoI3lC.js 6.35 kB │ gzip: 3.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/information-BZXrACCh.js 6.36 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/NoteTimeline-B64KEfoD.js 6.48 kB │ gzip: 2.60 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/account_cost-DdPYY7oD.js 6.56 kB │ gzip: 2.86 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PatientCaseCard-QLnHkNyv.js 6.61 kB │ gzip: 2.34 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-hHC-HY2w.js 6.61 kB │ gzip: 2.62 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-6aflqrhb.js 6.77 kB │ gzip: 2.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/readonly-Bc7S11g_.js 6.98 kB │ gzip: 2.69 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/picker-BA72xnJ0.js 7.32 kB │ gzip: 3.18 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CKaLV_7V.js 7.35 kB │ gzip: 2.66 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/setting-DK-S6SHN.js 7.35 kB │ gzip: 2.93 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/weapp-BaIevkiG.js 7.39 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/prescription-order-utils-CXfs2Bra.js 7.62 kB │ gzip: 3.11 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-RSmge3tZ.js 7.65 kB │ gzip: 3.13 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/config-h4uBlWvN.js 7.77 kB │ gzip: 2.59 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/TrackingMatrix-Bq97RGBJ.js 7.84 kB │ gzip: 3.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/patient-call-CzyPR-gp.js 7.85 kB │ gzip: 3.24 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/dayjs-CF5xpNFg.js 8.07 kB │ gzip: 3.45 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tcm-DVMUVofm.js 8.99 kB │ gzip: 1.57 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-vUD1ND3r.js 9.00 kB │ gzip: 2.83 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tim-upload-plugin-B7yxlBgj.js 9.47 kB │ gzip: 3.61 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BsQ-IFkz.js 9.49 kB │ gzip: 3.84 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/BloodRecordList-lzEwKV5j.js 9.58 kB │ gzip: 3.33 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/medicine-D_iyqZcd.js 9.74 kB │ gzip: 3.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-Chzff2sc.js 9.99 kB │ gzip: 3.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CUd56fl0.js 10.06 kB │ gzip: 3.98 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DfXvRPp3.js 10.33 kB │ gzip: 3.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionAiBatchGenerateDialog-DD3meqcJ.js 10.36 kB │ gzip: 4.29 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/SendPanel-DDd8ec_S.js 10.37 kB │ gzip: 3.57 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/progress-DuXbNWsm.js 11.18 kB │ gzip: 4.59 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list-Cw7NQEcD.js 11.18 kB │ gzip: 4.03 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DGvtXd9k.js 11.30 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/appointment-Cm3Hfa5N.js 11.34 kB │ gzip: 4.24 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-BDOteF3G.js 11.44 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/OrderPanel-Blnuj7Qu.js 11.74 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/picker-I1e_QZKi.js 12.15 kB │ gzip: 4.49 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/ProgressPanel-2qbsbc6G.js 12.58 kB │ gzip: 4.48 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/prescription-drawer-Dql8RbMo.js 12.76 kB │ gzip: 3.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CcMovsjP.js 13.00 kB │ gzip: 4.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-B_Kh6a1u.js 13.11 kB │ gzip: 3.91 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/h5-BpUP9yLV.js 13.83 kB │ gzip: 4.40 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CWnyJh4D.js 14.26 kB │ gzip: 5.90 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/guahao-DEn7doZO.js 15.06 kB │ gzip: 4.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/WecomFloatingWidgetBuilder-CUAXb84X.js 15.22 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BrOS-AAf.js 15.62 kB │ gzip: 6.44 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BRhh-vvk.js 15.90 kB │ gzip: 5.20 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-b7xryZ1R.js 15.90 kB │ gzip: 4.70 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CMnX8dfi.js 16.34 kB │ gzip: 6.06 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-NRre3yrk.js 16.35 kB │ gzip: 5.11 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CRhDpVNm.js 16.60 kB │ gzip: 6.07 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionAiReportDialog-DAmQQSVZ.js 17.15 kB │ gzip: 6.46 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DQb8OBRa.js 17.84 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/roster-BVq4roST.js 18.09 kB │ gzip: 5.71 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DXdrxgPI.js 18.19 kB │ gzip: 6.88 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PromotionAutomationForm-kf-hJGbx.js 18.23 kB │ gzip: 6.37 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CVeeMncf.js 18.60 kB │ gzip: 5.23 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/WelcomeMessageEditor-D6ufstYr.js 19.11 kB │ gzip: 6.95 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/OrderActionHost-CdrbUm1n.js 21.16 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CS6MIRqR.js 21.91 kB │ gzip: 7.87 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/add-C1r7FTAR.js 22.29 kB │ gzip: 5.36 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-BpYt_Hee.js 22.96 kB │ gzip: 8.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/DailyMatrix-BPhRn5lY.js 24.97 kB │ gzip: 7.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list_h5-cNTYH_iO.js 27.86 kB │ gzip: 9.43 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/qywx-CJq7NVl4.js 30.35 kB │ gzip: 10.01 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/list-DSAjNXOT.js 30.35 kB │ gzip: 9.74 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/commission-settlement-B1m1GnhE.js 35.83 kB │ gzip: 9.75 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/edit-C-_be8Ci.js 35.86 kB │ gzip: 9.02 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CmQWbBsG.js 36.70 kB │ gzip: 11.81 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index_h5-BSNbLohA.js 37.92 kB │ gzip: 11.61 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CnqPWEF7.js 37.98 kB │ gzip: 13.55 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-CAiJ9mjS.js 40.10 kB │ gzip: 11.87 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/PrescriptionOrderDetailDrawer-BwYuljL9.js 48.77 kB │ gzip: 13.48 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-Am7Jc2zt.js 51.46 kB │ gzip: 15.16 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-QkQBeLjo.js 55.03 kB │ gzip: 15.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/tim-profanity-filter-plugin-BJ7z5puq.js 55.90 kB │ gzip: 21.04 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-DpMMQbZW.js 69.54 kB │ gzip: 21.09 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/rtc-detect-CAvkmauD.js 74.80 kB │ gzip: 25.93 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/yeji-D8HJelzr.js 87.40 kB │ gzip: 23.27 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C4cvelAi.js 88.42 kB │ gzip: 25.65 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/order_list-D_i5USUp.js 114.89 kB │ gzip: 31.80 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/order_list_h5-B8Us1G-6.js 139.58 kB │ gzip: 36.82 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/@tencentcloud/chat-uikit-engine-BJz4IWsw.js 162.84 kB │ gzip: 41.72 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/index-C1oKTAGC.js 290.22 kB │ gzip: 92.00 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/@tencentcloud/chat-Bg29VzHQ.js 725.88 kB │ gzip: 178.89 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/@tencentcloud/call-engine-js-CRx4G1jN.js 2,290.42 kB │ gzip: 749.64 kB
|
|
||||||
../artifacts/appointment-modes/admin-build/assets/.pnpm-BHOjf1ZS.js 18,525.72 kB │ gzip: 5,519.46 kB
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
|
||||||
- Using dynamic import() to code-split the application
|
|
||||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
|
||||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
|
||||||
|
|
||||||
✓ built in 1m 53s
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
src/components/chat-dialog/ChatMessageItem.vue(29,7): error TS7022: 'attrs' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
|
||||||
src/components/editor/index.vue(29,44): error TS7016: Could not find a declaration file for module '@wangeditor/editor-for-vue'. 'D:/web/zyt/admin/node_modules/.pnpm/@wangeditor+editor-for-vue@_d49ef1161b4f4b880c450fdbfe3a0001/node_modules/@wangeditor/editor-for-vue/dist/index.esm.js' implicitly has an 'any' type.
|
|
||||||
There are types at 'D:/web/zyt/admin/node_modules/@wangeditor/editor-for-vue/dist/src/index.d.ts', but this result could not be resolved when respecting package.json "exports". The '@wangeditor/editor-for-vue' library may need to update its package.json or typings.
|
|
||||||
src/components/link/mini-program.vue(11,30): error TS7006: Parameter 'value' implicitly has an 'any' type.
|
|
||||||
src/components/link/mini-program.vue(22,30): error TS7006: Parameter 'value' implicitly has an 'any' type.
|
|
||||||
src/components/link/mini-program.vue(33,30): error TS7006: Parameter 'value' implicitly has an 'any' type.
|
|
||||||
src/components/link/mini-program.vue(48,31): error TS7006: Parameter 'value' implicitly has an 'any' type.
|
|
||||||
src/utils/call-local-recorder.ts(335,26): error TS2339: Property 'captureStream' does not exist on type 'HTMLVideoElement'.
|
|
||||||
src/views/asset/user/index.vue(230,20): error TS2339: Property 'remark' does not exist on type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1952,9): error TS2322: Type '{ name: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ name: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1958,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1986,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2001,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2016,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2256,32): error TS2304: Cannot find name 'searchPatientsAPI'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(1939,68): error TS7006: Parameter 'r' implicitly has an 'any' type.
|
|
||||||
src/views/consumer/prescription/order_list.vue(2379,9): error TS2322: Type '{ value: any; label: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ value: any; label: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(3775,13): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(497,96): error TS7006: Parameter 'cmd' implicitly has an 'any' type.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(566,47): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(576,58): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(586,47): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(596,59): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(611,46): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(621,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; diagnosis_id?: number | undefined; pay_order_ids?: number[] | undefined; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; diagnosis_id?: number | undefined; pay_order_ids?: number[] | undefined; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(631,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(641,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(2509,68): error TS7006: Parameter 'r' implicitly has an 'any' type.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(2848,9): error TS2322: Type '{ value: any; label: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ value: any; label: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(4694,13): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/decoration/component/tabbar/pc/attr.vue(10,18): error TS2345: Argument of type '{ modelValue: any; }' is not assignable to parameter of type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Record<...>'.
|
|
||||||
Property 'itemData' is missing in type '{ modelValue: any; }' but required in type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; }'.
|
|
||||||
src/views/decoration/component/tabbar/pc/attr.vue(13,18): error TS2345: Argument of type '{ modelValue: any; }' is not assignable to parameter of type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Record<...>'.
|
|
||||||
Property 'itemData' is missing in type '{ modelValue: any; }' but required in type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; }'.
|
|
||||||
src/views/decoration/component/widgets/middle-banner/content.vue(6,33): error TS2339: Property 'height' does not exist on type '{}'.
|
|
||||||
src/views/doctor/dept-tongji.vue(131,31): error TS7006: Parameter 'depts' implicitly has an 'any' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(132,17): error TS7034: Variable 'result' implicitly has type 'any[]' in some locations where its type cannot be determined.
|
|
||||||
src/views/doctor/dept-tongji.vue(133,27): error TS7006: Parameter 'dept' implicitly has an 'any' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(136,30): error TS7005: Variable 'result' implicitly has an 'any[]' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(139,20): error TS7005: Variable 'result' implicitly has an 'any[]' type.
|
|
||||||
src/views/doctor/tongji.vue(230,48): error TS2769: No overload matches this call.
|
|
||||||
Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'.
|
|
||||||
Types of parameters 'total' and 'previousValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'.
|
|
||||||
Types of parameters 'count' and 'currentValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
src/views/doctor/tongji.vue(236,51): error TS2769: No overload matches this call.
|
|
||||||
Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'.
|
|
||||||
Types of parameters 'total' and 'previousValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'.
|
|
||||||
Types of parameters 'count' and 'currentValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
src/views/first_visit/my_patients/components/OrderActionHost.vue(13,28): error TS7006: Parameter 'command' implicitly has an 'any' type.
|
|
||||||
src/views/first_visit/my_patients/components/OrderPanel.vue(202,40): error TS7006: Parameter 'command' implicitly has an 'any' type.
|
|
||||||
src/views/first_visit/wecom_promotion/index.vue(121,43): error TS7006: Parameter 'checked' implicitly has an 'any' type.
|
|
||||||
src/views/first_visit/wecom_promotion/index.vue(217,226): error TS7006: Parameter 'value' implicitly has an 'any' type.
|
|
||||||
src/views/order/index.vue(446,33): error TS2367: This comparison appears to be unintentional because the types '"supplement"' and '"normal"' have no overlap.
|
|
||||||
src/views/order/index.vue(1542,13): error TS2367: This comparison appears to be unintentional because the types '"supplement"' and '"normal"' have no overlap.
|
|
||||||
src/views/tcm/appointment/list.vue(266,69): error TS7006: Parameter 'cmd' implicitly has an 'any' type.
|
|
||||||
src/views/tcm/diagnosis/add.vue(744,49): error TS2349: This expression is not callable.
|
|
||||||
Type 'String' has no call signatures.
|
|
||||||
src/views/tcm/diagnosis/add.vue(772,45): error TS2349: This expression is not callable.
|
|
||||||
Type 'String' has no call signatures.
|
|
||||||
src/views/tcm/diagnosis/components/BloodRecordList.vue(416,5): error TS2322: Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(145,54): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(146,50): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(147,47): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(148,47): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(154,26): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/index.vue(392,69): error TS7006: Parameter 'cmd' implicitly has an 'any' type.
|
|
||||||
src/views/tcm/follow/index.vue(239,69): error TS7006: Parameter 'cmd' implicitly has an 'any' type.
|
|
||||||
src/views/workbench/index.vue(541,18): error TS7006: Parameter 'd' implicitly has an 'any' type.
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
vite v6.4.2 building for production...
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) outDir D:\web\zyt\artifacts\im-account-filter\admin-build is not inside project root and will not be emptied.
|
|
||||||
Use --emptyOutDir to override.
|
|
||||||
|
|
||||||
|
|
||||||
transforming...
|
|
||||||
✓ 4063 modules transformed.
|
|
||||||
|
|
||||||
WARN node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js (3199:26): Use of eval in "node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
||||||
|
|
||||||
rendering chunks...
|
|
||||||
computing gzip size...
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/default_avatar-C6VB7PGm.png 6.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/no_perms-jDxcYpYC.png 14.62 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/index.html 31.87 kB │ gzip: 16.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/login_bg-BkIjQ0FB.png 59.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/red3-NOuWP8DK.png 105.00 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pink3-BxZ4Y6CS.png 108.36 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/blue3-D3K9OqGO.png 108.98 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yellow3-C_qd9cqN.png 109.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/green3-CPorIQiC.png 109.99 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/purple3-BGd0LxTa.png 110.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/my_topbg-BiU0PleK.png 142.47 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/red2-Dw8p71sP.png 750.50 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/purple2-C0tQkldV.png 752.33 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/green2-C-VRKLSN.png 756.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yellow2-B-WtqITJ.png 762.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pink2-BpEp33zy.png 806.48 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/blue2-CRQPdLZd.png 806.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/red1-C6Y3UuNB.png 1,660.63 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yellow1-Ebw0T5sw.png 1,662.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pink1-BWNZrP7C.png 1,668.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/blue1-gLOo1H0w.png 1,676.54 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/purple1-BpMq9FWz.png 1,680.01 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/green1-h1zqes95.png 1,688.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CmFE2aZQ.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CjSHFu-R.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Oppei429.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/CaseRecordList-Cl-c3q7S.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DietRecordList-Cc-xUr2B.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/roster-CqNlv_rj.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/BloodRecordList-DhVK-Y_e.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-B3imPWFk.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list-BluNBZln.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ExerciseRecordList-DGtgtzou.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CObVTcPU.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-D-D8bVPM.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pc_details-nlJo1_D0.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AppointmentRecordPanel-DC22GDgn.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/theme-picker-BsELUxM9.css 0.11 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-Cl-9UIip.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AssignLogPanel-DkQMoEkX.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DXAsZ7EV.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DaxRy47P.css 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CAJZpU7c.css 0.14 kB │ gzip: 0.12 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DxNvUNZR.css 0.15 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useListTimeFilter-DI6SIumd.css 0.16 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-C1NtFi7N.css 0.16 kB │ gzip: 0.11 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-Bd3rHJ5J.css 0.18 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/change-password-DRfsLJ26.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-Dy_alH9u.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/decoration-img-C5XvHl9_.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/account_cost-fbCWUO9k.css 0.20 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-57RdOFNo.css 0.23 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/error-Cz3CexuM.css 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-D4atl_7z.css 0.25 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-lNortPsv.css 0.27 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/setting-CxqqGetv.css 0.27 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DX96drV3.css 0.28 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-DYFusokT.css 0.29 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/OrderActionHost-DiqybThz.css 0.31 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bk2rMrqw.css 0.32 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-D0f1Mn00.css 0.33 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/prescription-drawer-BjwiqvPc.css 0.35 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/bind-work-wechat-BX1gqwq1.css 0.35 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DhR2yeiO.css 0.39 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DoFDMZdP.css 0.40 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DBNvK3ZK.css 0.45 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientOrderList-D8-uh2iu.css 0.45 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DiagnosisTodoList-D0SBmTHG.css 0.46 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/CallRecordPanel-TsjZtTO8.css 0.47 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-C_LxAEkS.css 0.48 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/login-Bt4SvQsz.css 0.52 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-sMbqkta2.css 0.55 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/RecordingPlaybackBlock-B3KYFgvg.css 0.58 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/medicine-DO6x6zrS.css 0.58 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/preview-pc-BRQDo0AR.css 0.67 kB │ gzip: 0.36 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BzrSkGWL.css 0.67 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tabbar-DJsOahKR.css 0.69 kB │ gzip: 0.31 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/qywx-Dym8iFe0.css 0.74 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AssistantWatchCallDialog-BIPZRgaH.css 0.76 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/picker-JBYDNsl5.css 0.84 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-phone-CctIyraX.css 0.94 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/preview-C7oaKmYo.css 0.98 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bidrbn2Z.css 1.16 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/TrackingNoteTimeline-B9T8pFso.css 1.18 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/RecordingVideoPlayer-4DegHAwm.css 1.19 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-55HtuLpn.css 1.21 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientInfoCard-n6_UPo9e.css 1.32 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/TrackingMatrix-zyxtXYPb.css 1.32 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/SendPanel-DaGPWlQG.css 1.39 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/picker-XZbUgFct.css 1.57 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionOrderDetailDrawer-B4rggqa1.css 1.73 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ImChatRecordPanel-CNRZQxnD.css 1.85 kB │ gzip: 0.61 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BYOmK9R5.css 1.93 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-OgnT0gUo.css 1.98 kB │ gzip: 0.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/MessageBubble-BwtdPM6K.css 2.04 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BmDW9vcs.css 2.44 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientCaseCard-C3Mob6yG.css 2.49 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tongji-C0VnzFxy.css 2.49 kB │ gzip: 0.77 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/dept-tongji-ky7rOYoi.css 2.67 kB │ gzip: 0.72 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/readonly-CdP3eZN0.css 2.69 kB │ gzip: 0.92 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/NoteTimeline-I2FSxX4s.css 2.74 kB │ gzip: 0.84 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/patient-call-k8V9hV2G.css 2.85 kB │ gzip: 0.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DailyMatrix-DCZnxI8i.css 2.87 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PaibanPanel-aqm8azGc.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/paiban-BTUHWpUu.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/appointment-g8BrHfhi.css 3.43 kB │ gzip: 0.93 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionAiBatchGenerateDialog-CKkykmdr.css 3.45 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/h5-BSFcFSny.css 3.52 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bk7b_sL2.css 3.57 kB │ gzip: 0.96 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/add-ByXRFm8M.css 3.60 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PromotionAutomationForm-BMhNCJa-.css 3.64 kB │ gzip: 1.03 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-B6IOi2bJ.css 3.78 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/WelcomeMessageEditor-DD5mXWP2.css 4.41 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionAiReportDialog-CV6AuHV3.css 4.46 kB │ gzip: 1.11 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CMsGEvq_.css 5.19 kB │ gzip: 1.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-dept-node-ChUPkaxq.css 5.47 kB │ gzip: 1.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/OrderPanel-BzElZPEq.css 5.67 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Co57tWbg.css 6.11 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list-D5BdlpZJ.css 6.51 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DtDaTXOB.css 7.01 kB │ gzip: 1.69 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-dept-card-naMqyghz.css 7.09 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-AnyUcdTT.css 7.21 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bw7BJ7QR.css 8.03 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-ma9pGSsz.css 8.04 kB │ gzip: 2.32 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/progress-99NT9ysL.css 8.14 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Cmf8QBsF.css 8.75 kB │ gzip: 1.78 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CSIcxqFk.css 8.85 kB │ gzip: 2.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-B_6Taw4k.css 8.97 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-DOd7Lhrw.css 9.47 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Dnn9ddtX.css 9.49 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-D9Td5Y2a.css 9.52 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ProgressPanel-C_aSqDJh.css 10.10 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-I_vRP8ff.css 10.57 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DV5JwFnL.css 11.95 kB │ gzip: 2.61 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-cz2HBbSE.css 13.65 kB │ gzip: 3.05 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/commission-settlement-Gy2UsUPr.css 15.55 kB │ gzip: 3.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list_h5-Cs3mUmOa.css 17.70 kB │ gzip: 3.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/order_list-CMCIYVRT.css 18.15 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Dgjf9Hxv.css 18.86 kB │ gzip: 3.83 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/WecomFloatingWidgetBuilder-DRWt4rvh.css 19.20 kB │ gzip: 4.00 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index_h5-6234yJkC.css 19.63 kB │ gzip: 3.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yeji-7-gSVFmd.css 25.24 kB │ gzip: 4.60 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BSycZlsS.css 45.91 kB │ gzip: 9.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/order_list_h5-DB3qJv9G.css 48.13 kB │ gzip: 7.68 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/.pnpm-B3v8nGpq.css 723.50 kB │ gzip: 100.49 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/getExposeType-BhVtb25-.js 0.07 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-qqeREw9s.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-DcOELR-K.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-tHefvLXa.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/MediaSourceSelect-8A82adC4.js 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/code-preview-DBRXIZ5s.js 0.16 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-BQzWQW42.js 0.18 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/refund-log-BKxGpbZJ.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/account-adjust-B9vE51aJ.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-BKAOFNqu.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-BRF9xP1V.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-C0v_tOz5.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-CU8EgS8j.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/perm-ReZ1LYgU.js 0.20 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/diabetes-discovery-display-B_wmGXQJ.js 0.20 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionOrderTimeDialog-CO8lEk6t.js 0.20 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/weapp-CSMt578I.js 0.20 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/GancaoSubmissionReconcileButton-DaOrcVD6.js 0.21 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-B9L9tFty.js 0.21 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-Cnhi669r.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-RsdNi0sG.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-BIVwVbdj.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CS1FpnKo.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CsLOq8cA.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-DFJuQUt4.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DG0CrN4I.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-awcvFaxy.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DvEPH3sF.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-menu-form-C0NXOJbg.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useLockFn-DZaCbVGv.js 0.22 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/appointment-type-DJT6wPaT.js 0.23 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-DsY37x6w.js 0.23 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/auth-CQMXVsq4.js 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-DVCS2-tX.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/relations-add-TgfEUFsn.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mobile-style-A6-Ytfdg.js 0.26 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-C7l-AQkg.js 0.27 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/cost-edit-FBwF0u_p.js 0.30 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yeji-edit-DZBTmpJN.js 0.30 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/404-CHX28Ct_.js 0.31 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/data-table-B2Ely9V2.js 0.31 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-menu-form-edit-CuxIy3Cx.js 0.33 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/medicine-BKSr1vD6.js 0.35 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/consumer-CUp4ANbV.js 0.35 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/user-Bf1uVga9.js 0.37 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/role-BF3CqZnw.js 0.39 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useDictOptions-Dm28dNNo.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CxDFxkgf.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/admin-C8Vnlftq.js 0.42 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DT1XKlpO.js 0.44 kB │ gzip: 0.36 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pay-DcNRfvav.js 0.45 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/department-CpbHoJWl.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-fRjuc9D6.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-lHNz_rVU.js 0.47 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-CqNjJZSp.js 0.48 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/post-CwbznGRQ.js 0.49 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/overflow-B3CCpSAq.js 0.49 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/decoration-aYJgFVdU.js 0.50 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/message-CFGRf72h.js 0.50 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/403-CmUBOX6U.js 0.52 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/footer.vue_vue_type_script_setup_true_lang-BQ_JwEY8.js 0.53 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-CzKaNt79.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-CkhITkGu.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/add-nav-BSigI979.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DgGA7umv.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-set-BEKLkStG.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-DED_RFBQ.js 0.55 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/patient-10cOiqNf.js 0.56 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/fans-BuQxLAMK.js 0.60 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-B9aKQy30.js 0.60 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-6r7TVAax.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-AkWNVJlI.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/diag-display-DCz_VAqj.js 0.61 kB │ gzip: 0.39 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content.vue_vue_type_script_setup_true_lang-Cb2RlgSQ.js 0.61 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-hADFb-Qb.js 0.63 kB │ gzip: 0.43 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CnTFyK4a.js 0.63 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/link-BSeCb4pE.js 0.64 kB │ gzip: 0.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/asset-CzzFecaT.js 0.67 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content.vue_vue_type_script_setup_true_lang-DygM2blB.js 0.69 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useMediaSourceOptions-BbeqKRaz.js 0.70 kB │ gzip: 0.42 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content.vue_vue_type_script_setup_true_lang-BybcODyl.js 0.70 kB │ gzip: 0.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/website-CnBMCdv4.js 0.74 kB │ gzip: 0.24 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-ChKBqme9.js 0.76 kB │ gzip: 0.49 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/usePaging-oaNM6A9T.js 0.77 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/self_input_stats-D9U_aByK.js 0.79 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/decoration-img-C3rZgZnn.js 0.81 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/dict-Cpp_Mw13.js 0.81 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/code-SnwCPhUk.js 0.82 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/blood-thresholds-CJ1fqMjh.js 0.83 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-vVE2p2bn.js 0.84 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content.vue_vue_type_script_setup_true_lang-Cis0yx5N.js 0.86 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_script_setup_true_lang-kAZgL5aN.js 0.86 kB │ gzip: 0.52 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_script_setup_true_lang-CDLrQsa9.js 0.88 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/error-Dv5eyRBh.js 0.89 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/theme-picker-DOZzD0eg.js 0.92 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/wecomOauthPostMessage-C_e6VGrO.js 0.95 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_script_setup_true_lang-Cmgm_Z5P.js 0.98 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/wx_oa-BpHzQG4c.js 1.04 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/article--pPmZn0N.js 1.07 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-setting.vue_vue_type_script_setup_true_lang-IVCzoaL5.js 1.08 kB │ gzip: 0.61 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/rich_text-CsKSftnY.js 1.10 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/cache-DgX09o9S.js 1.13 kB │ gzip: 0.69 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CUJ5A7oy.js 1.21 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/popover_input-pPM8UqwF.js 1.25 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/finance-DuFx0ydf.js 1.30 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pc-DkNtz_df.js 1.31 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/doctor-D9Ckz5qF.js 1.32 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-menu-form-edit.vue_vue_type_script_setup_true_lang-3C4xDi6c.js 1.33 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-DTJHaQ97.js 1.33 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-BBtVqQrt.js 1.37 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-zXgrFnfv.js 1.38 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/file-DgUwoHFQ.js 1.41 kB │ gzip: 0.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/upload-zCiopoTX.js 1.41 kB │ gzip: 0.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content-B0Nx2Xp7.js 1.42 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/code-preview.vue_vue_type_script_setup_true_lang-D9DFyk1p.js 1.44 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bj-CUIgW.js 1.44 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_style_index_0_lang-DBzsCZdN.js 1.46 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-phone-zZyXZcXj.js 1.47 kB │ gzip: 0.80 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-DpFiCsnZ.js 1.50 kB │ gzip: 0.76 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-CO308tu7.js 1.55 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/MediaSourceSelect.vue_vue_type_script_setup_true_lang-xuHF5G6T.js 1.58 kB │ gzip: 0.80 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientInfoCard-CGWScS-V.js 1.60 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-D1E0COys.js 1.62 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/style-CLznS8nw.js 1.62 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/refund-log.vue_vue_type_script_setup_true_lang-B_lDKpjw.js 1.66 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/statistics-f_KiXaUV.js 1.69 kB │ gzip: 1.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-Dd6cWsZ6.js 1.71 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/order-D_FCirhb.js 1.74 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/environment-B_XTqIWk.js 1.74 kB │ gzip: 0.74 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/setup-DgUzoDWf.js 1.77 kB │ gzip: 1.00 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useMenuOa-DLMZQo_g.js 1.82 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-setting-Cs0gMOw0.js 1.88 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Cy7DHPGZ.js 1.90 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_name_articleColumnEdit_lang-CkPUppPd.js 1.94 kB │ gzip: 1.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bp595dP4.js 2.03 kB │ gzip: 0.98 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-7aWdYRG3.js 2.04 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/TrackingNoteTimeline-CrRi6X48.js 2.08 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CdAj-Aii.js 2.09 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-Ms8VOaHv.js 2.09 kB │ gzip: 0.95 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/open_setting-Dxyl8ZOh.js 2.10 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BNJ0ZVyH.js 2.12 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/account-adjust.vue_vue_type_script_setup_true_lang-DTPZREhE.js 2.16 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/filing-D4NTcggm.js 2.17 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BucNbXmG.js 2.18 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/icon-Do497UAk.js 2.23 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/content.vue_vue_type_script_setup_true_lang-C0NsmCvz.js 2.24 kB │ gzip: 1.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-BnPsXfZU.js 2.28 kB │ gzip: 1.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CymXj7F7.js 2.29 kB │ gzip: 1.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_script_setup_true_lang-BBhGStE3.js 2.37 kB │ gzip: 1.15 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/RecordingPlaybackBlock-AeIjtk_8.js 2.40 kB │ gzip: 1.19 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/auth.vue_vue_type_script_setup_true_lang-DmZ0-3eO.js 2.40 kB │ gzip: 1.34 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/stats-nEmR8lop.js 2.45 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/data-table.vue_vue_type_script_setup_true_lang-2DOwK9aa.js 2.51 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-attr-B1hCdPcf.js 2.51 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-DIEkzadS.js 2.52 kB │ gzip: 1.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/h5-BuxdT1Ge.js 2.56 kB │ gzip: 1.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/change-password-CTHR5iAx.js 2.57 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/im-business-message-parse-kN-fWnvR.js 2.60 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/add-nav.vue_vue_type_script_setup_true_lang-B_WQB6lM.js 2.61 kB │ gzip: 1.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tabbar-C1-HGN3_.js 2.66 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/file-Ctv52jTJ.js 2.73 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CQlLYE6x.js 2.74 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionOrderTimeDialog.vue_vue_type_script_setup_true_lang-CVFb32gJ.js 2.77 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/oa-menu-form.vue_vue_type_script_setup_true_lang-iYtuB3lb.js 2.77 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/CaseRecordList-tMfop0Ax.js 2.80 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DPF_SVr9.js 2.81 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bs1CWyht.js 2.85 kB │ gzip: 1.34 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/follow_reply-CLLbFSdU.js 2.85 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DatyJB_Z.js 2.86 kB │ gzip: 1.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/cost-edit.vue_vue_type_script_setup_true_lang-C4-qihtL.js 2.86 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DCzKMVHe.js 2.89 kB │ gzip: 1.41 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/default_reply-CCbjFiSS.js 2.91 kB │ gzip: 1.54 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-YlyRN4kl.js 2.96 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/protocol-BzB8ADIy.js 2.98 kB │ gzip: 1.10 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-C14Yv8Im.js 2.99 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-dept-node-B0ePzrLt.js 3.03 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/relations-add.vue_vue_type_script_setup_true_lang-p4eU2-OM.js 3.04 kB │ gzip: 1.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/picker.vue_vue_type_script_setup_true_lang-vT5soSYR.js 3.05 kB │ gzip: 1.50 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AssignLogPanel-CFZgXVh2.js 3.06 kB │ gzip: 1.48 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/keyword_reply-BdymY522.js 3.11 kB │ gzip: 1.60 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CC56Yug7.js 3.12 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BiWT4KtO.js 3.21 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-SMNg0bD8.js 3.25 kB │ gzip: 1.47 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/menu-set.vue_vue_type_script_setup_true_lang-C_CMPELM.js 3.26 kB │ gzip: 1.40 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/pc_details-Dz3ryfVw.js 3.39 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DzhB4nYz.js 3.39 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DaO3Dr3X.js 3.42 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-D9uB9rBL.js 3.43 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-Bosm-6EE.js 3.44 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/balance_details-C4aiXjPv.js 3.48 kB │ gzip: 1.63 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-ClmX7Grr.js 3.48 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index.vue_vue_type_script_setup_true_lang-CPBIijoV.js 3.52 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/journal-D8rzfTjM.js 3.73 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CZ-8SnyI.js 3.73 kB │ gzip: 1.57 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/detail-BtrR1i5l.js 3.76 kB │ gzip: 1.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AssistantWatchCallDialog-WFQy_w1O.js 3.78 kB │ gzip: 1.85 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/CallRecordPanel-C6S8lX6p.js 3.78 kB │ gzip: 1.77 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-BqH5-oDz.js 3.91 kB │ gzip: 1.71 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/preview-pc-Xf5C6ZTV.js 3.98 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/AppointmentRecordPanel-Dz-s2ev7.js 4.02 kB │ gzip: 1.73 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-C_uAPVti.js 4.11 kB │ gzip: 1.65 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CfYI3w0V.js 4.11 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/first_visit-CL3LsNli.js 4.13 kB │ gzip: 0.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/detail-BKl3pxSr.js 4.15 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/MessageBubble-Bdhrgfj-.js 4.17 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-LDzctaXO.js 4.22 kB │ gzip: 1.92 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/useListTimeFilter-CH6UBG2R.js 4.26 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DRzquyS9.js 4.37 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/bind-work-wechat-CROd0N9y.js 4.40 kB │ gzip: 2.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/recharge_record-enX6gukh.js 4.43 kB │ gzip: 1.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-rTwl4NKT.js 4.45 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DfAjN1ed.js 4.47 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DztYvhgb.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Crm6rZwf.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/preview-B82ShBae.js 4.49 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CNaaHiYx.js 4.51 kB │ gzip: 1.74 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/RecordingVideoPlayer-DjZKFdeH.js 4.53 kB │ gzip: 2.26 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CGc_5of6.js 4.55 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/paiban-PtPXBv3F.js 4.56 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DD7jgAVQ.js 4.57 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/login_register-BNOOfIjY.js 4.59 kB │ gzip: 2.01 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/dept-tongji-Dinbag9H.js 4.66 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DqeWJjfV.js 4.76 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-CHccJy1G.js 4.92 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CzQiz2K9.js 5.04 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-Ddc6tfnt.js 5.18 kB │ gzip: 2.03 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PaibanPanel-CrkOl1_i.js 5.28 kB │ gzip: 2.43 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-gYY-Ihui.js 5.32 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BQCUmdVA.js 5.53 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-ClqsT4Lq.js 5.55 kB │ gzip: 2.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yeji-edit.vue_vue_type_script_setup_true_lang-C_B8uAfu.js 5.56 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mobile-style.vue_vue_type_script_setup_true_lang-BmffPg5D.js 5.59 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ExerciseRecordList-BObPeBus.js 5.62 kB │ gzip: 2.16 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/attr-Dzwjk7L0.js 5.62 kB │ gzip: 2.24 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tongji-CX_AZXQC.js 5.66 kB │ gzip: 2.31 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientOrderList-BHvkaD04.js 5.82 kB │ gzip: 2.83 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DietRecordList-COIYJ7tp.js 5.84 kB │ gzip: 2.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/mubiao-dept-card-C2KWHSmH.js 5.91 kB │ gzip: 2.38 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/login-BQTyEEgd.js 6.03 kB │ gzip: 2.72 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/refund_record-B1htcVBe.js 6.27 kB │ gzip: 2.33 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DiagnosisTodoList-CIiI2vXk.js 6.34 kB │ gzip: 2.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DBgVsArC.js 6.35 kB │ gzip: 3.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/information-H4cElfMD.js 6.36 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/NoteTimeline-D2Cn-t49.js 6.48 kB │ gzip: 2.60 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/account_cost-D_MeM5Ko.js 6.56 kB │ gzip: 2.86 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PatientCaseCard-DKT_4Y7L.js 6.61 kB │ gzip: 2.34 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Cz1l9OWu.js 6.61 kB │ gzip: 2.62 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-CHjcZeLZ.js 6.77 kB │ gzip: 2.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/readonly-DUZeV_w4.js 6.98 kB │ gzip: 2.69 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/picker-BmZLoYfG.js 7.32 kB │ gzip: 3.18 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-BLYfnDac.js 7.35 kB │ gzip: 2.66 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/setting-rhpIbXGO.js 7.35 kB │ gzip: 2.93 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/weapp-9bAJJTz2.js 7.39 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/prescription-order-utils-CXfs2Bra.js 7.62 kB │ gzip: 3.11 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-kR5rYZYe.js 7.65 kB │ gzip: 3.13 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/config-3N1TQJj9.js 7.77 kB │ gzip: 2.59 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/TrackingMatrix-B607GGU9.js 7.84 kB │ gzip: 3.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/patient-call-XoEtFLU4.js 7.85 kB │ gzip: 3.24 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/dayjs-CF5xpNFg.js 8.07 kB │ gzip: 3.45 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-BxkGCzTr.js 9.00 kB │ gzip: 2.83 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tcm-ChtuqNhm.js 9.44 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tim-upload-plugin-B7yxlBgj.js 9.47 kB │ gzip: 3.61 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Fl6nmxEe.js 9.49 kB │ gzip: 3.85 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/BloodRecordList-BIMdWKO9.js 9.58 kB │ gzip: 3.33 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/medicine-tIfSC35q.js 9.74 kB │ gzip: 3.56 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-DA50wNyU.js 9.99 kB │ gzip: 3.55 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-C26qtC74.js 10.06 kB │ gzip: 3.98 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-eQPLcJEP.js 10.33 kB │ gzip: 3.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionAiBatchGenerateDialog-Bh8cBZ2U.js 10.36 kB │ gzip: 4.29 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/SendPanel-Nh2wEP27.js 10.37 kB │ gzip: 3.57 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ImChatRecordPanel-CY_60n0E.js 10.42 kB │ gzip: 4.54 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/progress-DuOm10jY.js 11.18 kB │ gzip: 4.59 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list-CVyT8pTR.js 11.18 kB │ gzip: 4.03 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BbWzj1v3.js 11.30 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/appointment-Cs4RxO0A.js 11.34 kB │ gzip: 4.24 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-BLc8zvwf.js 11.44 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/OrderPanel-BrM0G-6H.js 11.74 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/picker-MDXg6BK8.js 12.15 kB │ gzip: 4.49 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/ProgressPanel-BdB2pJX6.js 12.58 kB │ gzip: 4.48 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/prescription-drawer-BWDDYnw4.js 12.76 kB │ gzip: 3.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BbU9WSws.js 13.00 kB │ gzip: 4.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DCXj6gvc.js 13.11 kB │ gzip: 3.91 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/h5-CaqOq4yE.js 13.83 kB │ gzip: 4.41 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BCAQC4rg.js 14.26 kB │ gzip: 5.90 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/WecomFloatingWidgetBuilder-DMflyfHq.js 15.22 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/guahao-BYCsWxhO.js 15.46 kB │ gzip: 4.47 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-B9EPXS4Q.js 15.62 kB │ gzip: 6.44 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-1kRmD1tW.js 15.90 kB │ gzip: 5.20 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-CltFqeY9.js 15.90 kB │ gzip: 4.70 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DhIFawvT.js 16.34 kB │ gzip: 6.06 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-Bg-fEUBq.js 16.35 kB │ gzip: 5.11 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-D_XrhTnj.js 16.60 kB │ gzip: 6.07 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionAiReportDialog-DFaqRfkq.js 17.15 kB │ gzip: 6.46 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index--Gnpl1A-.js 17.84 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/roster-swtnbZqH.js 18.09 kB │ gzip: 5.72 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-txlKB4-2.js 18.19 kB │ gzip: 6.88 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PromotionAutomationForm-7RvW6GYj.js 18.23 kB │ gzip: 6.36 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DM2LKeNd.js 18.60 kB │ gzip: 5.23 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/WelcomeMessageEditor-s-2890d7.js 19.11 kB │ gzip: 6.95 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/OrderActionHost-DRYzGZA4.js 21.16 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DXrt0yLp.js 21.91 kB │ gzip: 7.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/add-Dtdqf7rg.js 22.29 kB │ gzip: 5.36 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-IWiFfZkH.js 22.96 kB │ gzip: 8.02 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/DailyMatrix-Cs-u0Y_n.js 24.97 kB │ gzip: 7.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list_h5-Dd9_81WR.js 27.86 kB │ gzip: 9.43 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/qywx-CMKsxFl1.js 30.35 kB │ gzip: 10.01 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/list-BSx_1xK9.js 30.35 kB │ gzip: 9.74 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/commission-settlement-Bo-HY-oY.js 35.83 kB │ gzip: 9.75 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/edit-Bar3Z5T5.js 35.86 kB │ gzip: 9.03 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-B0VJhQYg.js 36.70 kB │ gzip: 11.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index_h5-n5_UHvfv.js 37.92 kB │ gzip: 11.61 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-BJi-R8ym.js 39.27 kB │ gzip: 14.01 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DzMN_XCD.js 40.10 kB │ gzip: 11.87 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/PrescriptionOrderDetailDrawer-xamaMnut.js 48.77 kB │ gzip: 13.48 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-E1I45ddz.js 51.46 kB │ gzip: 15.17 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-C0h-ySsY.js 55.03 kB │ gzip: 15.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/tim-profanity-filter-plugin-BJ7z5puq.js 55.90 kB │ gzip: 21.04 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-B_S7y1Ml.js 69.54 kB │ gzip: 21.09 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/rtc-detect-CAvkmauD.js 74.80 kB │ gzip: 25.93 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/yeji-DL63xmk5.js 87.40 kB │ gzip: 23.27 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-DqAUwhoT.js 88.42 kB │ gzip: 25.66 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/order_list-hscOFw30.js 114.89 kB │ gzip: 31.81 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/order_list_h5-ArM4QkBN.js 139.58 kB │ gzip: 36.82 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/@tencentcloud/chat-uikit-engine-BJz4IWsw.js 162.84 kB │ gzip: 41.72 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/index-FUvp6yIk.js 290.22 kB │ gzip: 92.03 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/@tencentcloud/chat-Bg29VzHQ.js 725.88 kB │ gzip: 178.89 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/@tencentcloud/call-engine-js-CRx4G1jN.js 2,290.42 kB │ gzip: 749.64 kB
|
|
||||||
../artifacts/im-account-filter/admin-build/assets/.pnpm-BHOjf1ZS.js 18,525.72 kB │ gzip: 5,519.46 kB
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
|
||||||
- Using dynamic import() to code-split the application
|
|
||||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
|
||||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
|
||||||
|
|
||||||
✓ built in 1m 50s
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
vite v6.4.2 building for production...
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) outDir D:\web\zyt\artifacts\im-chat-history\admin-build is not inside project root and will not be emptied.
|
|
||||||
Use --emptyOutDir to override.
|
|
||||||
|
|
||||||
|
|
||||||
transforming...
|
|
||||||
✓ 4063 modules transformed.
|
|
||||||
|
|
||||||
WARN node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js (3199:26): Use of eval in "node_modules/.pnpm/tcplayer.js@5.3.4-beta.32/node_modules/tcplayer.js/dist/tcplayer.v5.3.4.min.js" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
||||||
|
|
||||||
rendering chunks...
|
|
||||||
computing gzip size...
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/default_avatar-C6VB7PGm.png 6.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/no_perms-jDxcYpYC.png 14.62 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/index.html 31.87 kB │ gzip: 16.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/login_bg-BkIjQ0FB.png 59.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/red3-NOuWP8DK.png 105.00 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pink3-BxZ4Y6CS.png 108.36 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/blue3-D3K9OqGO.png 108.98 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yellow3-C_qd9cqN.png 109.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/green3-CPorIQiC.png 109.99 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/purple3-BGd0LxTa.png 110.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/my_topbg-BiU0PleK.png 142.47 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/red2-Dw8p71sP.png 750.50 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/purple2-C0tQkldV.png 752.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/green2-C-VRKLSN.png 756.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yellow2-B-WtqITJ.png 762.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pink2-BpEp33zy.png 806.48 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/blue2-CRQPdLZd.png 806.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/red1-C6Y3UuNB.png 1,660.63 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yellow1-Ebw0T5sw.png 1,662.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pink1-BWNZrP7C.png 1,668.56 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/blue1-gLOo1H0w.png 1,676.54 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/purple1-BpMq9FWz.png 1,680.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/green1-h1zqes95.png 1,688.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CmFE2aZQ.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CjSHFu-R.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Oppei429.css 0.04 kB │ gzip: 0.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/CaseRecordList-Cl-c3q7S.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DietRecordList-Cc-xUr2B.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/roster-CqNlv_rj.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/BloodRecordList-DhVK-Y_e.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-B3imPWFk.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list-BluNBZln.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ExerciseRecordList-DGtgtzou.css 0.05 kB │ gzip: 0.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-CObVTcPU.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-D-D8bVPM.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pc_details-nlJo1_D0.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AppointmentRecordPanel-DC22GDgn.css 0.09 kB │ gzip: 0.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/theme-picker-BsELUxM9.css 0.11 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-Cl-9UIip.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AssignLogPanel-DkQMoEkX.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-DXAsZ7EV.css 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-DaxRy47P.css 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-CAJZpU7c.css 0.14 kB │ gzip: 0.12 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-DxNvUNZR.css 0.15 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useListTimeFilter-DI6SIumd.css 0.16 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-C1NtFi7N.css 0.16 kB │ gzip: 0.11 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-Bd3rHJ5J.css 0.18 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/change-password-DRfsLJ26.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-Dy_alH9u.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/decoration-img-C5XvHl9_.css 0.19 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/account_cost-fbCWUO9k.css 0.20 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-57RdOFNo.css 0.23 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/error-Cz3CexuM.css 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-D4atl_7z.css 0.25 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-lNortPsv.css 0.27 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/setting-CxqqGetv.css 0.27 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DX96drV3.css 0.28 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-DYFusokT.css 0.29 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/OrderActionHost-DiqybThz.css 0.31 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Bk2rMrqw.css 0.32 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-D0f1Mn00.css 0.33 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/prescription-drawer-BjwiqvPc.css 0.35 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/bind-work-wechat-BX1gqwq1.css 0.35 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DhR2yeiO.css 0.39 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DoFDMZdP.css 0.40 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DBNvK3ZK.css 0.45 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientOrderList-D8-uh2iu.css 0.45 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DiagnosisTodoList-D0SBmTHG.css 0.46 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/CallRecordPanel-TsjZtTO8.css 0.47 kB │ gzip: 0.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-C_LxAEkS.css 0.48 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/login-Bt4SvQsz.css 0.52 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-sMbqkta2.css 0.55 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/RecordingPlaybackBlock-B3KYFgvg.css 0.58 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/medicine-DO6x6zrS.css 0.58 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/preview-pc-BRQDo0AR.css 0.67 kB │ gzip: 0.36 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BzrSkGWL.css 0.67 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tabbar-DJsOahKR.css 0.69 kB │ gzip: 0.31 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/qywx-Dym8iFe0.css 0.74 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AssistantWatchCallDialog-BIPZRgaH.css 0.76 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/picker-JBYDNsl5.css 0.84 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-phone-CctIyraX.css 0.94 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/preview-C7oaKmYo.css 0.98 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Bidrbn2Z.css 1.16 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/TrackingNoteTimeline-B9T8pFso.css 1.18 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/RecordingVideoPlayer-4DegHAwm.css 1.19 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-55HtuLpn.css 1.21 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientInfoCard-n6_UPo9e.css 1.32 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/TrackingMatrix-zyxtXYPb.css 1.32 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/SendPanel-DaGPWlQG.css 1.39 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/picker-XZbUgFct.css 1.57 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionOrderDetailDrawer-B4rggqa1.css 1.73 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ImChatRecordPanel-DUX4tYwU.css 1.85 kB │ gzip: 0.61 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BYOmK9R5.css 1.93 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-OgnT0gUo.css 1.98 kB │ gzip: 0.56 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/MessageBubble-BwtdPM6K.css 2.04 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BmDW9vcs.css 2.44 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientCaseCard-C3Mob6yG.css 2.49 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tongji-C0VnzFxy.css 2.49 kB │ gzip: 0.77 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/dept-tongji-ky7rOYoi.css 2.67 kB │ gzip: 0.72 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/readonly-CdP3eZN0.css 2.69 kB │ gzip: 0.92 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/NoteTimeline-I2FSxX4s.css 2.74 kB │ gzip: 0.84 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/patient-call-k8V9hV2G.css 2.85 kB │ gzip: 0.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DailyMatrix-DCZnxI8i.css 2.87 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PaibanPanel-aqm8azGc.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/paiban-BTUHWpUu.css 3.35 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/appointment-g8BrHfhi.css 3.43 kB │ gzip: 0.93 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionAiBatchGenerateDialog-CKkykmdr.css 3.45 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/h5-BSFcFSny.css 3.52 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Bk7b_sL2.css 3.57 kB │ gzip: 0.96 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/add-ByXRFm8M.css 3.60 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PromotionAutomationForm-BMhNCJa-.css 3.64 kB │ gzip: 1.03 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-B6IOi2bJ.css 3.78 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/WelcomeMessageEditor-DD5mXWP2.css 4.41 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionAiReportDialog-CV6AuHV3.css 4.46 kB │ gzip: 1.11 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CMsGEvq_.css 5.19 kB │ gzip: 1.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-dept-node-ChUPkaxq.css 5.47 kB │ gzip: 1.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/OrderPanel-BzElZPEq.css 5.67 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Co57tWbg.css 6.11 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list-D5BdlpZJ.css 6.51 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DtDaTXOB.css 7.01 kB │ gzip: 1.69 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-dept-card-naMqyghz.css 7.09 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-AnyUcdTT.css 7.21 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Bw7BJ7QR.css 8.03 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-D4TKAF49.css 8.04 kB │ gzip: 2.32 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/progress-99NT9ysL.css 8.14 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Cmf8QBsF.css 8.75 kB │ gzip: 1.78 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CSIcxqFk.css 8.85 kB │ gzip: 2.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-B_6Taw4k.css 8.97 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DOd7Lhrw.css 9.47 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dnn9ddtX.css 9.49 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-D9Td5Y2a.css 9.52 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ProgressPanel-C_aSqDJh.css 10.10 kB │ gzip: 2.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-I_vRP8ff.css 10.57 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DV5JwFnL.css 11.95 kB │ gzip: 2.61 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-cz2HBbSE.css 13.65 kB │ gzip: 3.05 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/commission-settlement-Gy2UsUPr.css 15.55 kB │ gzip: 3.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list_h5-Cs3mUmOa.css 17.70 kB │ gzip: 3.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/order_list-CMCIYVRT.css 18.15 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dgjf9Hxv.css 18.86 kB │ gzip: 3.83 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/WecomFloatingWidgetBuilder-DRWt4rvh.css 19.20 kB │ gzip: 4.00 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index_h5-6234yJkC.css 19.63 kB │ gzip: 3.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yeji-7-gSVFmd.css 25.24 kB │ gzip: 4.60 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BSycZlsS.css 45.91 kB │ gzip: 9.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/order_list_h5-DB3qJv9G.css 48.13 kB │ gzip: 7.68 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/.pnpm-B3v8nGpq.css 723.50 kB │ gzip: 100.49 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/getExposeType-BhVtb25-.js 0.07 kB │ gzip: 0.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-qqeREw9s.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-DcOELR-K.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-tHefvLXa.js 0.13 kB │ gzip: 0.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/MediaSourceSelect-8A82adC4.js 0.14 kB │ gzip: 0.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/code-preview-CRYwsie5.js 0.16 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-CnxBj2N-.js 0.18 kB │ gzip: 0.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/refund-log-D0z9hzwY.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/account-adjust-D_iiZRxf.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-GwXp2MfM.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-V0f1_vdW.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-DtM3yPIw.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-Ba0l8P4X.js 0.19 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/perm-CiI_vrea.js 0.20 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/diabetes-discovery-display-B_wmGXQJ.js 0.20 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionOrderTimeDialog-BHoWMYDV.js 0.20 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/weapp-Iyi9vyFf.js 0.20 kB │ gzip: 0.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/GancaoSubmissionReconcileButton-CCqEzOaM.js 0.21 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-CNCHtrEi.js 0.21 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-B0tfK27i.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-zzzTd8tZ.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-cLpVnAEl.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-BFh3g8dC.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-CBF3zoKz.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-BwLHbRaK.js 0.21 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DG0CrN4I.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-awcvFaxy.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DvEPH3sF.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-menu-form-r5keepdo.js 0.22 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useLockFn-DZaCbVGv.js 0.22 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/appointment-type-DJT6wPaT.js 0.23 kB │ gzip: 0.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DTpkRNnx.js 0.23 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/auth-D69rZ0QF.js 0.24 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DKHY8c-x.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/relations-add-eMBobvC6.js 0.25 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mobile-style-M0fJyJft.js 0.26 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-Dl6ZYHd6.js 0.27 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/cost-edit-HlyKgBPk.js 0.30 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yeji-edit-DiKvabrc.js 0.30 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/404-Bx67M9_i.js 0.31 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/data-table-DiwFgvTm.js 0.31 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-menu-form-edit-BJ1TaJrJ.js 0.33 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/medicine-ClhF6dhx.js 0.35 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/consumer-DvvPd1hy.js 0.35 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/user-BoFAWLZw.js 0.37 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/role-DursS8TF.js 0.39 kB │ gzip: 0.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useDictOptions-DGLBVObI.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-offYjbU6.js 0.42 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/admin-Dswuerz6.js 0.42 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-CMc73Fka.js 0.44 kB │ gzip: 0.35 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pay-XwSNNoxK.js 0.45 kB │ gzip: 0.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/department-BG0AgOKP.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-B2wXzt_G.js 0.46 kB │ gzip: 0.19 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-BkJ1RaU_.js 0.47 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-Cy0U3F6F.js 0.48 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/post-CQN2ASdU.js 0.49 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/overflow-Ccm0BASr.js 0.49 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/decoration-qD9r-XC7.js 0.50 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/message-4zgGHQuK.js 0.50 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/403-NUKlpGTJ.js 0.52 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/footer.vue_vue_type_script_setup_true_lang-DSCEzizm.js 0.53 kB │ gzip: 0.38 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-IKnWwccM.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-CbwBYdrP.js 0.54 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/add-nav-CKob9pi_.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-BPe9H2z1.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-set-Cdq4oYg2.js 0.54 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-B0hrD1LU.js 0.55 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/patient-x1uj0g5K.js 0.56 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/fans-Bko_8S6a.js 0.60 kB │ gzip: 0.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-DlaxhiVa.js 0.60 kB │ gzip: 0.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-S1Kh6jUC.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-BqqVaNlV.js 0.61 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/diag-display-DCz_VAqj.js 0.61 kB │ gzip: 0.39 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content.vue_vue_type_script_setup_true_lang-DMxWOKx5.js 0.61 kB │ gzip: 0.41 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BhE11Nq_.js 0.63 kB │ gzip: 0.43 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DIWWWv4S.js 0.63 kB │ gzip: 0.32 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/link-gNOTfPzX.js 0.64 kB │ gzip: 0.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/asset-lYonyybX.js 0.67 kB │ gzip: 0.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content.vue_vue_type_script_setup_true_lang-D1LuuMC-.js 0.69 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useMediaSourceOptions-fw385QtK.js 0.70 kB │ gzip: 0.42 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content.vue_vue_type_script_setup_true_lang-BfBIkn5E.js 0.70 kB │ gzip: 0.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/website-BKsM8bHj.js 0.74 kB │ gzip: 0.24 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-BNR0wsyZ.js 0.76 kB │ gzip: 0.49 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/usePaging-oaNM6A9T.js 0.77 kB │ gzip: 0.47 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/self_input_stats-Bh9Ivy5F.js 0.79 kB │ gzip: 0.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/decoration-img-CHn5E-6-.js 0.81 kB │ gzip: 0.50 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/dict-C49DwoYN.js 0.81 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/code-DKMUKFzD.js 0.82 kB │ gzip: 0.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/blood-thresholds-CJ1fqMjh.js 0.83 kB │ gzip: 0.34 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-BdQq_NlD.js 0.84 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content.vue_vue_type_script_setup_true_lang-CFOyeTvK.js 0.86 kB │ gzip: 0.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_script_setup_true_lang-kAZgL5aN.js 0.86 kB │ gzip: 0.52 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_script_setup_true_lang-CDLrQsa9.js 0.88 kB │ gzip: 0.51 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/error-4ClSi6LT.js 0.89 kB │ gzip: 0.60 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/theme-picker-M1AQ2XQj.js 0.92 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/wecomOauthPostMessage-C_e6VGrO.js 0.95 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_script_setup_true_lang-Cmgm_Z5P.js 0.98 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/wx_oa-CsUXz3M_.js 1.04 kB │ gzip: 0.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/article-B_VIFIGv.js 1.07 kB │ gzip: 0.26 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-setting.vue_vue_type_script_setup_true_lang-Cn7UpFyE.js 1.08 kB │ gzip: 0.62 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/rich_text-C8iWxnva.js 1.10 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/cache-BZx4Ny8O.js 1.13 kB │ gzip: 0.69 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-lM29fXUc.js 1.21 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/popover_input-pPM8UqwF.js 1.25 kB │ gzip: 0.59 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/finance-B4gS023c.js 1.30 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pc-BVO2bNRL.js 1.31 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/doctor-3GXATh2M.js 1.32 kB │ gzip: 0.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-menu-form-edit.vue_vue_type_script_setup_true_lang-C3xkO5fZ.js 1.33 kB │ gzip: 0.78 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-Db4P_VPl.js 1.33 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-gqAvW-3z.js 1.37 kB │ gzip: 0.70 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-DlEk_FSe.js 1.38 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/file-BOrBUJT2.js 1.41 kB │ gzip: 0.63 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/upload-DooW1Wwa.js 1.41 kB │ gzip: 0.64 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content-CLc2dzGE.js 1.42 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/code-preview.vue_vue_type_script_setup_true_lang-BN1CyAsN.js 1.44 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CSekGh8y.js 1.44 kB │ gzip: 0.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_style_index_0_lang-BR-rP4B6.js 1.46 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-phone-B-GLqF9L.js 1.47 kB │ gzip: 0.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-Ch8kaUiR.js 1.50 kB │ gzip: 0.76 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-BwPV9PE0.js 1.55 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/MediaSourceSelect.vue_vue_type_script_setup_true_lang-xuHF5G6T.js 1.58 kB │ gzip: 0.80 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientInfoCard-BwfsUM2s.js 1.60 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-D7iG4611.js 1.62 kB │ gzip: 0.71 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/style-BSaMeNII.js 1.62 kB │ gzip: 0.88 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/refund-log.vue_vue_type_script_setup_true_lang-BNZvq8OU.js 1.66 kB │ gzip: 0.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/statistics-D4bqVAIO.js 1.69 kB │ gzip: 1.02 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-BucqEaGC.js 1.71 kB │ gzip: 0.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/order-D6xPdSYq.js 1.74 kB │ gzip: 0.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/environment-FBaSCNXw.js 1.74 kB │ gzip: 0.74 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/setup-xqfHpnLl.js 1.77 kB │ gzip: 1.00 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useMenuOa-C9nb3qNM.js 1.82 kB │ gzip: 0.85 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-setting-CSC9RHvg.js 1.88 kB │ gzip: 0.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CmqXvX0o.js 1.90 kB │ gzip: 0.99 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_name_articleColumnEdit_lang-Dj2cjLjD.js 1.94 kB │ gzip: 1.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-dsbqhbDF.js 2.03 kB │ gzip: 0.98 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-e3LZNL22.js 2.04 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/TrackingNoteTimeline-B5IIeBTZ.js 2.08 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-irEiZ2to.js 2.09 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-BTdpdc9s.js 2.09 kB │ gzip: 0.95 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/open_setting-CbyEUjYD.js 2.10 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-D-oRNzvN.js 2.12 kB │ gzip: 1.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/account-adjust.vue_vue_type_script_setup_true_lang-CIazW94a.js 2.16 kB │ gzip: 1.12 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/filing-BBoLgS0g.js 2.17 kB │ gzip: 1.17 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dy_DI6fL.js 2.18 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/icon-JBSUrucJ.js 2.23 kB │ gzip: 0.82 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/content.vue_vue_type_script_setup_true_lang-ef0Do_pT.js 2.24 kB │ gzip: 1.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-B04f-PF6.js 2.28 kB │ gzip: 1.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CKpR0wBb.js 2.29 kB │ gzip: 1.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_script_setup_true_lang-BBhGStE3.js 2.37 kB │ gzip: 1.15 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/RecordingPlaybackBlock-Cvp4VJaq.js 2.40 kB │ gzip: 1.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/auth.vue_vue_type_script_setup_true_lang-CSADBu_h.js 2.40 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/stats-BwkMZNKj.js 2.45 kB │ gzip: 0.58 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/data-table.vue_vue_type_script_setup_true_lang-BdfIrilq.js 2.51 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-attr-I62kgtSB.js 2.51 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-D1CNqt95.js 2.52 kB │ gzip: 1.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/h5-DUfSYLLB.js 2.56 kB │ gzip: 1.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/change-password-BHBSZ_1x.js 2.57 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/im-business-message-parse-kN-fWnvR.js 2.60 kB │ gzip: 1.28 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/add-nav.vue_vue_type_script_setup_true_lang-TLGujWib.js 2.61 kB │ gzip: 1.26 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tabbar-BGzVO5aw.js 2.66 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/file-CIbk4Ntr.js 2.73 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CRrevJfY.js 2.74 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionOrderTimeDialog.vue_vue_type_script_setup_true_lang-CLtuslQR.js 2.77 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/oa-menu-form.vue_vue_type_script_setup_true_lang-B49nVddb.js 2.77 kB │ gzip: 1.08 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/CaseRecordList-CoSK0NHb.js 2.80 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-CoDIyiGp.js 2.81 kB │ gzip: 1.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CsWG06Br.js 2.85 kB │ gzip: 1.34 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/follow_reply-rMkYpNN5.js 2.85 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BIkjSt8G.js 2.86 kB │ gzip: 1.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/cost-edit.vue_vue_type_script_setup_true_lang-B8oLeJca.js 2.86 kB │ gzip: 1.35 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-By9ydUIZ.js 2.89 kB │ gzip: 1.41 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/default_reply-D6ouDPpq.js 2.91 kB │ gzip: 1.54 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-Deve6q4X.js 2.96 kB │ gzip: 1.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/protocol-BFlJmc-k.js 2.98 kB │ gzip: 1.10 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-DCtCwWvi.js 2.99 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-dept-node-BaFPJoMU.js 3.03 kB │ gzip: 1.25 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/relations-add.vue_vue_type_script_setup_true_lang-B0HRNpRz.js 3.04 kB │ gzip: 1.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/picker.vue_vue_type_script_setup_true_lang-CusCTJJ4.js 3.05 kB │ gzip: 1.50 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AssignLogPanel-DRsSJqIX.js 3.06 kB │ gzip: 1.48 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/keyword_reply-7EuDiKuz.js 3.11 kB │ gzip: 1.60 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-DJE0T4Q1.js 3.12 kB │ gzip: 1.51 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CeqsxLC-.js 3.21 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DYd7907D.js 3.25 kB │ gzip: 1.47 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/menu-set.vue_vue_type_script_setup_true_lang-GLaRZwMy.js 3.26 kB │ gzip: 1.40 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/pc_details-Bw7VUj2g.js 3.39 kB │ gzip: 1.49 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CHQBJpbo.js 3.39 kB │ gzip: 1.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dj5Yh3rP.js 3.42 kB │ gzip: 1.53 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-B19ngLYq.js 3.43 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/GancaoSubmissionReconcileButton.vue_vue_type_script_setup_true_lang-B20SThVv.js 3.44 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/balance_details-D_KfXFYa.js 3.48 kB │ gzip: 1.63 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-D7yxqlrs.js 3.48 kB │ gzip: 1.64 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index.vue_vue_type_script_setup_true_lang-BKPP98Vt.js 3.52 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/journal-BQXRw-rc.js 3.73 kB │ gzip: 1.46 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-wbPWradd.js 3.73 kB │ gzip: 1.57 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/detail-DPEZNO-R.js 3.76 kB │ gzip: 1.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AssistantWatchCallDialog-Do4wtgqb.js 3.78 kB │ gzip: 1.85 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/CallRecordPanel-0Um7IKOD.js 3.78 kB │ gzip: 1.77 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-Bv_AGp7e.js 3.91 kB │ gzip: 1.71 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/preview-pc-vQFYLbHC.js 3.98 kB │ gzip: 1.63 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/AppointmentRecordPanel-C_BICu-f.js 4.02 kB │ gzip: 1.73 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-g-evO6wb.js 4.11 kB │ gzip: 1.65 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-fFppMfCb.js 4.11 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/first_visit-DrLCEbkH.js 4.13 kB │ gzip: 0.89 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/detail-DdD6RcHV.js 4.15 kB │ gzip: 1.56 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/MessageBubble-DEoXDLTv.js 4.17 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-F345h9xq.js 4.22 kB │ gzip: 1.92 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/useListTimeFilter-i-LKEU1K.js 4.26 kB │ gzip: 1.52 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CWKqi4D6.js 4.37 kB │ gzip: 2.14 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/bind-work-wechat-BV_lfAa8.js 4.40 kB │ gzip: 2.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/recharge_record-saR6x18Z.js 4.43 kB │ gzip: 1.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DuwLBHge.js 4.45 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dv70S3A7.js 4.47 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DJa1-cVO.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Tejy5EwF.js 4.49 kB │ gzip: 1.96 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/preview-zbOEK628.js 4.49 kB │ gzip: 1.70 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-Ba54tb4y.js 4.51 kB │ gzip: 1.75 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/RecordingVideoPlayer-B-dcy3dw.js 4.53 kB │ gzip: 2.26 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DRTC0exf.js 4.55 kB │ gzip: 1.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/paiban-pudpsS38.js 4.56 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-C7KZ_Ik4.js 4.57 kB │ gzip: 1.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/login_register-CGqb90Lj.js 4.59 kB │ gzip: 2.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/dept-tongji-BOgCLB2J.js 4.66 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BpKwfCQA.js 4.76 kB │ gzip: 2.05 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr.vue_vue_type_script_setup_true_lang-CNcDtJ9x.js 4.92 kB │ gzip: 2.04 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-C1amYZ9l.js 5.04 kB │ gzip: 1.99 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-CAgpj-eS.js 5.18 kB │ gzip: 2.03 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PaibanPanel-DZNODK98.js 5.28 kB │ gzip: 2.43 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DXb87qjA.js 5.32 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DkaxoKzQ.js 5.53 kB │ gzip: 2.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-CHwIUtxK.js 5.55 kB │ gzip: 2.56 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yeji-edit.vue_vue_type_script_setup_true_lang-Bu0RgavZ.js 5.56 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mobile-style.vue_vue_type_script_setup_true_lang-B-q3QQVT.js 5.59 kB │ gzip: 1.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ExerciseRecordList-DZ-SHSRX.js 5.62 kB │ gzip: 2.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/attr-CRSc5FiL.js 5.62 kB │ gzip: 2.24 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tongji-CQw3u6Fc.js 5.66 kB │ gzip: 2.31 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientOrderList-DPhs-MJN.js 5.82 kB │ gzip: 2.84 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DietRecordList-DK7RU2Fo.js 5.84 kB │ gzip: 2.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/mubiao-dept-card-XFpSnNvn.js 5.91 kB │ gzip: 2.38 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/login-mU_6coFj.js 6.03 kB │ gzip: 2.72 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/refund_record-WGOu0QAJ.js 6.27 kB │ gzip: 2.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DiagnosisTodoList-DFhubbCI.js 6.34 kB │ gzip: 2.88 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BDHQ8kpu.js 6.35 kB │ gzip: 3.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/information-cQxR_gym.js 6.36 kB │ gzip: 1.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/NoteTimeline-C_UaNOKv.js 6.48 kB │ gzip: 2.60 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/account_cost-Bo5VIFr8.js 6.56 kB │ gzip: 2.86 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PatientCaseCard-B7o30YLL.js 6.61 kB │ gzip: 2.34 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DcDTRBFW.js 6.61 kB │ gzip: 2.62 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CVeY18TE.js 6.77 kB │ gzip: 2.89 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/readonly-Bt9YZALs.js 6.98 kB │ gzip: 2.69 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/picker-BAPV7c7C.js 7.32 kB │ gzip: 3.18 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit.vue_vue_type_script_setup_true_lang-Dz5fUrfr.js 7.35 kB │ gzip: 2.66 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/setting-Hgl4zct0.js 7.35 kB │ gzip: 2.93 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/weapp-BvXTEjc6.js 7.39 kB │ gzip: 2.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/prescription-order-utils-CXfs2Bra.js 7.62 kB │ gzip: 3.11 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BBTpOFL8.js 7.65 kB │ gzip: 3.13 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/config-CV0P9oRu.js 7.77 kB │ gzip: 2.59 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/TrackingMatrix-Av_7LhBk.js 7.84 kB │ gzip: 3.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/patient-call-Cg9Vaj8C.js 7.85 kB │ gzip: 3.24 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/dayjs-CF5xpNFg.js 8.07 kB │ gzip: 3.45 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-da9KFXx_.js 9.00 kB │ gzip: 2.83 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ImChatRecordPanel-CsmMG9cI.js 9.30 kB │ gzip: 4.21 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tcm-BuqVT7wS.js 9.44 kB │ gzip: 1.79 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tim-upload-plugin-B7yxlBgj.js 9.47 kB │ gzip: 3.61 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DALWbhiS.js 9.49 kB │ gzip: 3.85 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/BloodRecordList-_QjjAMCZ.js 9.58 kB │ gzip: 3.33 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/medicine-jP_YfcAn.js 9.74 kB │ gzip: 3.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-pzxY3-e8.js 9.99 kB │ gzip: 3.55 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DP7qunZa.js 10.06 kB │ gzip: 3.98 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-hTQz2IcE.js 10.33 kB │ gzip: 3.89 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionAiBatchGenerateDialog-CZ1Wvfc7.js 10.36 kB │ gzip: 4.29 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/SendPanel-CabEk305.js 10.37 kB │ gzip: 3.57 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/progress-BsQKnwsp.js 11.18 kB │ gzip: 4.59 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list-BHd-eM8G.js 11.18 kB │ gzip: 4.03 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index--2yxCiVu.js 11.30 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/appointment-ByorQLwa.js 11.34 kB │ gzip: 4.24 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-Qv0aOhsU.js 11.44 kB │ gzip: 3.76 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/OrderPanel-B9ccVKPw.js 11.74 kB │ gzip: 4.22 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/picker-BwMDQMg3.js 12.15 kB │ gzip: 4.49 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/ProgressPanel-D3QNGTTf.js 12.58 kB │ gzip: 4.48 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/prescription-drawer-Dpvk2MZD.js 12.76 kB │ gzip: 3.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CgZP_eau.js 13.00 kB │ gzip: 4.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-gQ8YYUNH.js 13.11 kB │ gzip: 3.91 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/h5-DlI3J5NN.js 13.83 kB │ gzip: 4.40 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-_UZVnWu_.js 14.26 kB │ gzip: 5.90 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/guahao-DlzdlKLt.js 15.06 kB │ gzip: 4.44 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/WecomFloatingWidgetBuilder-BUV-hVOm.js 15.22 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DYfZFMZH.js 15.62 kB │ gzip: 6.43 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-yIo8VvbW.js 15.90 kB │ gzip: 5.20 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-DyWYmoYJ.js 15.90 kB │ gzip: 4.71 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-nZMrOmT0.js 16.34 kB │ gzip: 6.06 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-COnlKS9k.js 16.35 kB │ gzip: 5.11 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DdycTSXR.js 16.60 kB │ gzip: 6.07 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionAiReportDialog-CRUja6D5.js 17.15 kB │ gzip: 6.47 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-9rPpXRqm.js 17.84 kB │ gzip: 6.02 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/roster-5q1rfTpI.js 18.09 kB │ gzip: 5.72 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dw8UNAuL.js 18.19 kB │ gzip: 6.88 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PromotionAutomationForm-BA0NIzNP.js 18.23 kB │ gzip: 6.37 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DL3Rz2aL.js 18.60 kB │ gzip: 5.23 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/WelcomeMessageEditor-CRBBUPRj.js 19.11 kB │ gzip: 6.95 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/OrderActionHost-B-jmhVnH.js 21.16 kB │ gzip: 6.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-C_so5ska.js 21.91 kB │ gzip: 7.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/add-EcOqBIiP.js 22.29 kB │ gzip: 5.36 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-PgI4nM1G.js 22.96 kB │ gzip: 8.02 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/DailyMatrix-HANRgYOy.js 24.97 kB │ gzip: 7.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list_h5-DoSV8WSE.js 27.86 kB │ gzip: 9.42 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/qywx-DffvrfUX.js 30.35 kB │ gzip: 10.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/list-QcqwAhrg.js 30.35 kB │ gzip: 9.73 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/commission-settlement-C2o50eF8.js 35.83 kB │ gzip: 9.75 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/edit-Drj7L2C8.js 35.86 kB │ gzip: 9.02 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-DUf7A4Dd.js 36.70 kB │ gzip: 11.81 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index_h5-B07JsZ1O.js 37.92 kB │ gzip: 11.61 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-BrMxtLph.js 39.24 kB │ gzip: 14.01 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CFzaCYqK.js 40.10 kB │ gzip: 11.87 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/PrescriptionOrderDetailDrawer-D-NYIIFH.js 48.77 kB │ gzip: 13.48 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-CX4B8xwM.js 51.46 kB │ gzip: 15.16 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Cu5qMzfF.js 55.03 kB │ gzip: 15.89 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/tim-profanity-filter-plugin-BJ7z5puq.js 55.90 kB │ gzip: 21.04 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-gt2MSDnW.js 69.54 kB │ gzip: 21.09 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/rtc-detect-CAvkmauD.js 74.80 kB │ gzip: 25.93 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/yeji-Cw0Su2A3.js 87.40 kB │ gzip: 23.27 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Dz7niwME.js 88.42 kB │ gzip: 25.66 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/order_list-D_zw0l1b.js 114.89 kB │ gzip: 31.80 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/order_list_h5-CCe4ZA_s.js 139.58 kB │ gzip: 36.82 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/@tencentcloud/chat-uikit-engine-BJz4IWsw.js 162.84 kB │ gzip: 41.72 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/index-Cs3PThsC.js 290.22 kB │ gzip: 92.04 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/@tencentcloud/chat-Bg29VzHQ.js 725.88 kB │ gzip: 178.89 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/@tencentcloud/call-engine-js-CRx4G1jN.js 2,290.42 kB │ gzip: 749.64 kB
|
|
||||||
../artifacts/im-chat-history/admin-build/assets/.pnpm-BHOjf1ZS.js 18,525.72 kB │ gzip: 5,519.46 kB
|
|
||||||
|
|
||||||
WARN
|
|
||||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
|
||||||
- Using dynamic import() to code-split the application
|
|
||||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
|
||||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
|
||||||
|
|
||||||
✓ built in 1m 55s
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
✔ type labels and video capability preserve legacy video while rejecting text and unknown (1.042ms)
|
|
||||||
✔ rendered real chat template keeps text tools while excluding call controls in text mode (201.7263ms)
|
|
||||||
✔ text mode hides all live call entries and refuses direct group invocation (52.6597ms)
|
|
||||||
✔ all SDK outgoing paths stop before touching SDK when current appointment is text (50.1941ms)
|
|
||||||
✔ video preflight uses actual appointment and prevents a server-side change to text (44.9134ms)
|
|
||||||
✔ historical phone is audio-only; missing server confirmation never enables video (45.8624ms)
|
|
||||||
✔ singleton call guard switches to current chat and cancels a pending old context (2.7073ms)
|
|
||||||
ℹ tests 7
|
|
||||||
ℹ suites 0
|
|
||||||
ℹ pass 7
|
|
||||||
ℹ fail 0
|
|
||||||
ℹ cancelled 0
|
|
||||||
ℹ skipped 0
|
|
||||||
ℹ todo 0
|
|
||||||
ℹ duration_ms 1045.4102
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
src/components/chat-dialog/ChatMessageItem.vue(29,7): error TS7022: 'attrs' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
|
||||||
src/components/editor/index.vue(29,44): error TS7016: Could not find a declaration file for module '@wangeditor/editor-for-vue'. 'D:/web/zyt/admin/node_modules/.pnpm/@wangeditor+editor-for-vue@_d49ef1161b4f4b880c450fdbfe3a0001/node_modules/@wangeditor/editor-for-vue/dist/index.esm.js' implicitly has an 'any' type.
|
|
||||||
There are types at 'D:/web/zyt/admin/node_modules/@wangeditor/editor-for-vue/dist/src/index.d.ts', but this result could not be resolved when respecting package.json "exports". The '@wangeditor/editor-for-vue' library may need to update its package.json or typings.
|
|
||||||
src/utils/call-local-recorder.ts(335,26): error TS2339: Property 'captureStream' does not exist on type 'HTMLVideoElement'.
|
|
||||||
src/views/asset/user/index.vue(230,20): error TS2339: Property 'remark' does not exist on type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1150,42): error TS2322: Type '(value: string[]) => void' is not assignable to type '(value: CascaderValue | null | undefined) => any'.
|
|
||||||
Types of parameters 'value' and 'value' are incompatible.
|
|
||||||
Type 'CascaderValue | null | undefined' is not assignable to type 'string[]'.
|
|
||||||
Type 'undefined' is not assignable to type 'string[]'.
|
|
||||||
src/views/consumer/prescription/index.vue(1952,9): error TS2322: Type '{ name: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ name: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1958,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(1986,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2001,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2016,13): error TS2322: Type '{ name: string; children: { name: string; children: { name: string; }[]; }[]; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/index.vue(2256,32): error TS2304: Cannot find name 'searchPatientsAPI'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(253,26): error TS2322: Type 'unknown[]' is not assignable to type 'TreeNodeData[]'.
|
|
||||||
Type 'unknown' is not assignable to type 'TreeNodeData'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(983,45): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | null | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(1181,41): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | null | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(1939,68): error TS7006: Parameter 'r' implicitly has an 'any' type.
|
|
||||||
src/views/consumer/prescription/order_list.vue(2379,9): error TS2322: Type '{ value: any; label: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ value: any; label: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/order_list.vue(3775,13): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(271,26): error TS2322: Type 'unknown[]' is not assignable to type 'TreeNodeData[]'.
|
|
||||||
Type 'unknown' is not assignable to type 'TreeNodeData'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(566,47): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(576,58): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(586,47): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(596,59): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(611,46): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(621,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; diagnosis_id?: number | undefined; pay_order_ids?: number[] | undefined; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; diagnosis_id?: number | undefined; pay_order_ids?: number[] | undefined; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(631,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(641,53): error TS2345: Argument of type 'Record<string, any>' is not assignable to parameter of type '{ id: number; }'.
|
|
||||||
Property 'id' is missing in type 'Record<string, any>' but required in type '{ id: number; }'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(1550,45): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | null | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(1738,41): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | null | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(2509,68): error TS7006: Parameter 'r' implicitly has an 'any' type.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(2848,9): error TS2322: Type '{ value: any; label: any; code: any; children: any; }[]' is not assignable to type 'never[]'.
|
|
||||||
Type '{ value: any; label: any; code: any; children: any; }' is not assignable to type 'never'.
|
|
||||||
src/views/consumer/prescription/order_list_h5.vue(4694,13): error TS2322: Type 'string | number | undefined' is not assignable to type 'number | undefined'.
|
|
||||||
Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/decoration/component/tabbar/pc/attr.vue(10,18): error TS2345: Argument of type '{ modelValue: any; }' is not assignable to parameter of type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Record<...>'.
|
|
||||||
Property 'itemData' is missing in type '{ modelValue: any; }' but required in type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; }'.
|
|
||||||
src/views/decoration/component/tabbar/pc/attr.vue(13,18): error TS2345: Argument of type '{ modelValue: any; }' is not assignable to parameter of type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Record<...>'.
|
|
||||||
Property 'itemData' is missing in type '{ modelValue: any; }' but required in type '{ readonly modelValue: any[]; readonly max?: number | undefined; readonly min?: number | undefined; readonly itemData: any; readonly "onUpdate:modelValue"?: ((value: any) => any) | undefined; }'.
|
|
||||||
src/views/decoration/component/widgets/middle-banner/content.vue(6,33): error TS2339: Property 'height' does not exist on type '{}'.
|
|
||||||
src/views/doctor/dept-tongji.vue(131,31): error TS7006: Parameter 'depts' implicitly has an 'any' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(132,17): error TS7034: Variable 'result' implicitly has type 'any[]' in some locations where its type cannot be determined.
|
|
||||||
src/views/doctor/dept-tongji.vue(133,27): error TS7006: Parameter 'dept' implicitly has an 'any' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(136,30): error TS7005: Variable 'result' implicitly has an 'any[]' type.
|
|
||||||
src/views/doctor/dept-tongji.vue(139,20): error TS7005: Variable 'result' implicitly has an 'any[]' type.
|
|
||||||
src/views/doctor/tongji.vue(94,58): error TS2322: Type 'string' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
src/views/doctor/tongji.vue(125,58): error TS2322: Type 'string' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
src/views/doctor/tongji.vue(230,48): error TS2769: No overload matches this call.
|
|
||||||
Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'.
|
|
||||||
Types of parameters 'total' and 'previousValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'.
|
|
||||||
Types of parameters 'count' and 'currentValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
src/views/doctor/tongji.vue(236,51): error TS2769: No overload matches this call.
|
|
||||||
Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'.
|
|
||||||
Types of parameters 'total' and 'previousValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error.
|
|
||||||
Argument of type '(total: number, count: number) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'.
|
|
||||||
Types of parameters 'count' and 'currentValue' are incompatible.
|
|
||||||
Type 'unknown' is not assignable to type 'number'.
|
|
||||||
src/views/fans/h5.vue(43,47): error TS2322: Type '"" | "danger" | "info"' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
Type '""' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
src/views/fans/h5.vue(167,51): error TS2322: Type 'string' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
src/views/fans/index.vue(158,34): error TS2322: Type 'string' is not assignable to type '"primary" | "success" | "warning" | "danger" | "info" | undefined'.
|
|
||||||
src/views/message/notice/index.vue(12,44): error TS2322: Type '(opts?: { silent?: boolean; }) => Promise<any>' is not assignable to type '(name: TabPaneName) => any'.
|
|
||||||
Types of parameters 'opts' and 'name' are incompatible.
|
|
||||||
Type 'TabPaneName' is not assignable to type '{ silent?: boolean | undefined; } | undefined'.
|
|
||||||
Type 'string' has no properties in common with type '{ silent?: boolean | undefined; }'.
|
|
||||||
src/views/order/index.vue(446,33): error TS2367: This comparison appears to be unintentional because the types '"supplement"' and '"normal"' have no overlap.
|
|
||||||
src/views/order/index.vue(1542,13): error TS2367: This comparison appears to be unintentional because the types '"supplement"' and '"normal"' have no overlap.
|
|
||||||
src/views/organization/department/edit.vue(20,29): error TS2353: Object literal may only specify known properties, and 'value' does not exist in type 'TreeOptionProps'.
|
|
||||||
src/views/permission/admin/edit.vue(44,29): error TS2353: Object literal may only specify known properties, and 'value' does not exist in type 'TreeOptionProps'.
|
|
||||||
src/views/tcm/appointment/list.vue(90,26): error TS2322: Type 'unknown[]' is not assignable to type 'TreeNodeData[]'.
|
|
||||||
Type 'unknown' is not assignable to type 'TreeNodeData'.
|
|
||||||
src/views/tcm/diagnosis/add.vue(744,49): error TS2349: This expression is not callable.
|
|
||||||
Type 'String' has no call signatures.
|
|
||||||
src/views/tcm/diagnosis/add.vue(772,45): error TS2349: This expression is not callable.
|
|
||||||
Type 'String' has no call signatures.
|
|
||||||
src/views/tcm/diagnosis/components/BloodRecordList.vue(416,5): error TS2322: Type 'string' is not assignable to type 'number'.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(145,54): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(146,50): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(147,47): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(148,47): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/tcm/diagnosis/components/RecordingVideoPlayer.vue(154,26): error TS2554: Expected 0 arguments, but got 1.
|
|
||||||
src/views/workbench/index.vue(541,18): error TS7006: Parameter 'd' implicitly has an 'any' type.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
@@ -1 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,558 +0,0 @@
|
|||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error think\db\exception\PDOException@PDOConnection.php:836
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,830 +0,0 @@
|
|||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
PRESCRIPTION_AI storage_or_configuration_error
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"openai","enabled":true,"processed":true}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"lane": "prepare",
|
||||||
|
"instance": 1,
|
||||||
|
"pid": 1160,
|
||||||
|
"stdout": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\prepare-1-parallel-20260910-171432.stdout.log",
|
||||||
|
"stderr": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\prepare-1-parallel-20260910-171432.stderr.log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lane": "qwen",
|
||||||
|
"instance": 1,
|
||||||
|
"pid": 25112,
|
||||||
|
"stdout": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\qwen-1-parallel-20260910-171432.stdout.log",
|
||||||
|
"stderr": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\qwen-1-parallel-20260910-171432.stderr.log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lane": "qwen",
|
||||||
|
"instance": 2,
|
||||||
|
"pid": 25352,
|
||||||
|
"stdout": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\qwen-2-parallel-20260910-171432.stdout.log",
|
||||||
|
"stderr": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\qwen-2-parallel-20260910-171432.stderr.log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lane": "openai",
|
||||||
|
"instance": 1,
|
||||||
|
"pid": 25156,
|
||||||
|
"stdout": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\openai-1-parallel-20260910-171432.stdout.log",
|
||||||
|
"stderr": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\openai-1-parallel-20260910-171432.stderr.log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lane": "openai",
|
||||||
|
"instance": 2,
|
||||||
|
"pid": 25188,
|
||||||
|
"stdout": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\openai-2-parallel-20260910-171432.stdout.log",
|
||||||
|
"stderr": "D:\\web\\zyt\\artifacts\\prescription-ai-runtime\\openai-2-parallel-20260910-171432.stderr.log"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
PRESCRIPTION_AI {"lane":"prepare","enabled":true,"processed":true}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
29924
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1 +1 @@
|
|||||||
28108
|
20460
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"qwen","enabled":true,"processed":true}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
PRESCRIPTION_AI {"lane":"qwen","enabled":true,"processed":true}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user