在线复诊挂号和订单校验
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
This commit is contained in:
@@ -491,7 +491,13 @@ export const usePrescriptionStore = defineStore('prescription', () => {
|
||||
image: data.drug.image,
|
||||
instruction: data.drug.instruction,
|
||||
type: data.drug.type,
|
||||
select_number: 1,
|
||||
select_number: (() => {
|
||||
const raw = Number(
|
||||
data.select_number ?? data.purchase_quantity ?? data.purchase_number,
|
||||
);
|
||||
if (!Number.isFinite(raw) || raw < 1) return 1;
|
||||
return Math.min(9999, Math.max(1, Math.floor(raw)));
|
||||
})(),
|
||||
};
|
||||
|
||||
const newDrugs = [...currentDrugs.value, newProduct];
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const data = computed(() =>
|
||||
typeof props.content === 'string'
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(props.content);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
})()
|
||||
: props.content || {},
|
||||
);
|
||||
|
||||
const answeredLabel = computed(() => {
|
||||
const st = data.value.answer_status;
|
||||
if (st === 'yes' || st === '1') return '是,曾使用过';
|
||||
if (st === 'no' || st === '0') return '否,未使用过';
|
||||
return '';
|
||||
});
|
||||
|
||||
const drugs = computed(() =>
|
||||
Array.isArray(data.value.drugs) ? data.value.drugs : [],
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="follow-up-drug-card">
|
||||
<div class="card-head">
|
||||
<div class="head-icon">
|
||||
<i class="fas fa-user-md"></i>
|
||||
</div>
|
||||
<div class="head-text">
|
||||
<div class="title">医生助理 · 用药确认</div>
|
||||
<div v-if="data.flow === 'follow_up_drug'" class="sub">复诊用药</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="data.question" class="question-block">
|
||||
{{ data.question }}
|
||||
</div>
|
||||
|
||||
<div v-if="drugs.length" class="drug-list">
|
||||
<div class="drug-list-title">涉及药品</div>
|
||||
<ul>
|
||||
<li v-for="(row, idx) in drugs" :key="idx">
|
||||
<span class="name">{{ row.name || '药品' }}</span>
|
||||
<span class="qty">×{{ row.quantity != null ? row.quantity : 1 }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="answeredLabel" class="answer-row">
|
||||
<span class="label">患者回复</span>
|
||||
<span class="value">{{ answeredLabel }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="data.answer_status === 'pending'"
|
||||
class="pending-tip"
|
||||
>
|
||||
待患者在小程序端填写用药信息
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.follow-up-drug-card {
|
||||
@apply w-full max-w-md rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-600 dark:bg-slate-800;
|
||||
}
|
||||
|
||||
.card-head {
|
||||
@apply mb-3 flex items-center gap-3;
|
||||
}
|
||||
|
||||
.head-icon {
|
||||
@apply flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-300;
|
||||
}
|
||||
|
||||
.head-text .title {
|
||||
@apply text-base font-semibold text-slate-800 dark:text-slate-100;
|
||||
}
|
||||
|
||||
.head-text .sub {
|
||||
@apply mt-0.5 text-xs text-slate-500 dark:text-slate-400;
|
||||
}
|
||||
|
||||
.question-block {
|
||||
@apply mb-3 rounded-lg bg-slate-50 p-3 text-sm leading-relaxed text-slate-700 dark:bg-slate-900/60 dark:text-slate-200;
|
||||
}
|
||||
|
||||
.drug-list {
|
||||
@apply mb-3 border-t border-slate-100 pt-3 dark:border-slate-600;
|
||||
}
|
||||
|
||||
.drug-list-title {
|
||||
@apply mb-2 text-xs font-medium uppercase tracking-wide text-slate-500 dark:text-slate-400;
|
||||
}
|
||||
|
||||
.drug-list ul {
|
||||
@apply m-0 list-none space-y-2 p-0;
|
||||
}
|
||||
|
||||
.drug-list li {
|
||||
@apply flex items-center justify-between text-sm text-slate-800 dark:text-slate-100;
|
||||
}
|
||||
|
||||
.drug-list .name {
|
||||
@apply min-w-0 flex-1 truncate pr-2;
|
||||
}
|
||||
|
||||
.drug-list .qty {
|
||||
@apply flex-shrink-0 font-medium text-slate-600 dark:text-slate-300;
|
||||
}
|
||||
|
||||
.answer-row {
|
||||
@apply flex items-center justify-between rounded-lg bg-emerald-50 px-3 py-2 text-sm dark:bg-emerald-900/30;
|
||||
}
|
||||
|
||||
.answer-row .label {
|
||||
@apply text-slate-600 dark:text-slate-400;
|
||||
}
|
||||
|
||||
.answer-row .value {
|
||||
@apply font-medium text-emerald-800 dark:text-emerald-300;
|
||||
}
|
||||
|
||||
.pending-tip {
|
||||
@apply rounded-lg border border-dashed border-amber-200 bg-amber-50 px-3 py-2 text-center text-xs text-amber-800 dark:border-amber-700 dark:bg-amber-900/20 dark:text-amber-200;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
@@ -55,6 +55,7 @@ import { useThemeStore } from '../stores/theme.ts';
|
||||
import { useUserStore as chatUseUserStore } from '../stores/user';
|
||||
import AudioMessage from './AudioMessage.vue';
|
||||
import PatientExperienceCard from '#/views/doctor/online-consultation/components/PatientExperienceCard.vue';
|
||||
import FollowUpDrugAssistantCard from './FollowUpDrugAssistantCard.vue';
|
||||
import ProductCard from '#/views/doctor/online-consultation/components/ProductCard.vue';
|
||||
import EndConsultationCard from '#/views/doctor/online-consultation/components/EndConsultationCard.vue';
|
||||
import TransferPrescriptionMessageCard from '#/views/doctor/online-consultation/components/TransferPrescriptionMessageCard.vue';
|
||||
@@ -154,15 +155,76 @@ const messageTypeName = computed(() => {
|
||||
return getMessageTypeName(props.message.message_type);
|
||||
});
|
||||
|
||||
const getRegisterCardInfo = () => {
|
||||
const content = getParsedContent(props.message.message_type, props.message.message_content);
|
||||
getChatMessageRegisterInfoApi({ id: content.id }).then(
|
||||
(res) => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
props.message.message_content = JSON.stringify(res);
|
||||
},
|
||||
/** type10:接口补全的挂号卡字段(不写回 props,避免破坏响应式) */
|
||||
const registerCardPayload = ref(null);
|
||||
|
||||
const registerCardDisplay = computed(() => {
|
||||
if (props.message.message_type !== 10) {
|
||||
return {};
|
||||
}
|
||||
let base = {};
|
||||
const raw = props.message.message_content || props.message.content || '';
|
||||
try {
|
||||
base =
|
||||
typeof raw === 'string' && raw
|
||||
? JSON.parse(raw)
|
||||
: typeof raw === 'object' && raw
|
||||
? { ...raw }
|
||||
: {};
|
||||
} catch {
|
||||
base = {};
|
||||
}
|
||||
const enrich = registerCardPayload.value;
|
||||
if (enrich && typeof enrich === 'object' && !Array.isArray(enrich)) {
|
||||
return { ...base, ...enrich };
|
||||
}
|
||||
return base;
|
||||
});
|
||||
|
||||
const registerDrugNamesLine = computed(() => {
|
||||
const pc = registerCardDisplay.value;
|
||||
if (!pc || typeof pc !== 'object') return '';
|
||||
const arr = pc.selected_western_drugs;
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
return arr.map((d) => d?.name).filter(Boolean).join('、');
|
||||
}
|
||||
if (pc.drug?.name) return pc.drug.name;
|
||||
return '';
|
||||
});
|
||||
|
||||
const isFollowUpDrugAssistant = computed(() => {
|
||||
if (props.message.message_type !== 11) return false;
|
||||
const pc = parsedContent.value;
|
||||
return !!(pc && typeof pc === 'object' && pc.flow === 'follow_up_drug');
|
||||
});
|
||||
|
||||
function fetchRegisterCard() {
|
||||
if (props.message.message_type !== 10) return;
|
||||
const content = getParsedContent(
|
||||
props.message.message_type,
|
||||
props.message.message_content,
|
||||
);
|
||||
};
|
||||
const id = content && typeof content === 'object' ? content.id : undefined;
|
||||
if (!id) return;
|
||||
getChatMessageRegisterInfoApi({ id }).then((res) => {
|
||||
const body = res?.data?.data ?? res?.data ?? res;
|
||||
registerCardPayload.value =
|
||||
body && typeof body === 'object' && !Array.isArray(body) ? body : null;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [
|
||||
props.message.message_type,
|
||||
props.message.id,
|
||||
props.message.message_content,
|
||||
],
|
||||
() => {
|
||||
registerCardPayload.value = null;
|
||||
fetchRegisterCard();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 接诊操作
|
||||
const acceptRegister = async (registerId, orderNo) => {
|
||||
@@ -176,7 +238,7 @@ const acceptRegister = async (registerId, orderNo) => {
|
||||
receptionApi(registerId).then(async (value) => {
|
||||
// message.success('接诊成功');
|
||||
// 更新消息状态
|
||||
getRegisterCardInfo();
|
||||
fetchRegisterCard();
|
||||
|
||||
const messageObj = {
|
||||
message_type: 0,
|
||||
@@ -188,7 +250,7 @@ const acceptRegister = async (registerId, orderNo) => {
|
||||
read: true,
|
||||
duration: 0,
|
||||
};
|
||||
localStorage.setItem('currentRegisterId', reregisterId);
|
||||
localStorage.setItem('currentRegisterId', String(registerId));
|
||||
// chatStore.currentFriend?.register_id = reregisterId;
|
||||
chatStore.addMessage(messageObj, chatUserStore.currentUser.doctor_id);
|
||||
await sendMessage({
|
||||
@@ -336,9 +398,6 @@ const getSexText = (sex) => {
|
||||
return sex === 1 ? '男' : '女';
|
||||
};
|
||||
|
||||
if (props.message.message_type === 10) {
|
||||
getRegisterCardInfo();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -482,20 +541,20 @@ if (props.message.message_type === 10) {
|
||||
<div
|
||||
:class="{
|
||||
'bg-yellow-100 text-yellow-800':
|
||||
parsedContent.status === 0,
|
||||
'bg-blue-100 text-blue-800': parsedContent.status === 1,
|
||||
'bg-red-100 text-red-800': parsedContent.status === 2,
|
||||
'bg-red-100 text-red-800': parsedContent.status === 3,
|
||||
'bg-gray-100 text-gray-800': parsedContent.status === 4,
|
||||
registerCardDisplay.status === 0,
|
||||
'bg-blue-100 text-blue-800': registerCardDisplay.status === 1,
|
||||
'bg-red-100 text-red-800': registerCardDisplay.status === 2,
|
||||
'bg-red-100 text-red-800': registerCardDisplay.status === 3,
|
||||
'bg-gray-100 text-gray-800': registerCardDisplay.status === 4,
|
||||
'bg-purple-100 text-purple-800':
|
||||
parsedContent.status === 5,
|
||||
registerCardDisplay.status === 5,
|
||||
'bg-indigo-100 text-indigo-800':
|
||||
parsedContent.status === 6,
|
||||
'bg-pink-100 text-pink-800': parsedContent.status === 7,
|
||||
registerCardDisplay.status === 6,
|
||||
'bg-pink-100 text-pink-800': registerCardDisplay.status === 7,
|
||||
}"
|
||||
class="rounded-full px-2 py-1 text-xs font-medium"
|
||||
>
|
||||
{{ registerStatusMap[parsedContent.status] || '未知状态' }}
|
||||
{{ registerStatusMap[registerCardDisplay.status] || '未知状态' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -505,13 +564,13 @@ if (props.message.message_type === 10) {
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-hashtag text-xs"></i>
|
||||
<span class="max-w-[120px] truncate">{{
|
||||
parsedContent.order_no
|
||||
registerCardDisplay.order_no
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-list-ol text-xs"></i>
|
||||
<span>第{{ parsedContent.order_number }}号</span>
|
||||
<span>第{{ registerCardDisplay.order_number }}号</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -525,12 +584,12 @@ if (props.message.message_type === 10) {
|
||||
</div>
|
||||
<div
|
||||
:class="{
|
||||
'bg-red-100 text-red-800': parsedContent.is_pay === 0,
|
||||
'bg-green-100 text-green-800': parsedContent.is_pay === 1,
|
||||
'bg-red-100 text-red-800': registerCardDisplay.is_pay === 0,
|
||||
'bg-green-100 text-green-800': registerCardDisplay.is_pay === 1,
|
||||
}"
|
||||
class="rounded-full px-2 py-1 text-xs font-medium"
|
||||
>
|
||||
{{ payStatusMap[parsedContent.is_pay] || '未知' }}
|
||||
{{ payStatusMap[registerCardDisplay.is_pay] || '未知' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -539,43 +598,70 @@ if (props.message.message_type === 10) {
|
||||
>
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-user text-xs"></i>
|
||||
<span>{{ parsedContent.user_patient?.name || '未知' }}</span>
|
||||
<span>{{ registerCardDisplay.user_patient?.name || '未知' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-birthday-cake text-xs"></i>
|
||||
<span>{{ parsedContent.user_patient?.age || 0 }}岁</span>
|
||||
<span>{{ registerCardDisplay.user_patient?.age || 0 }}岁</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-venus-mars text-xs"></i>
|
||||
<span>{{ getSexText(parsedContent.user_patient?.sex) }}</span>
|
||||
<span>{{ getSexText(registerCardDisplay.user_patient?.sex) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<i class="fas fa-phone text-xs"></i>
|
||||
<span>{{
|
||||
parsedContent.user_patient?.mobile ? '已绑定' : '未绑定'
|
||||
registerCardDisplay.user_patient?.mobile ? '已绑定' : '未绑定'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主诉 / 所选药品 / 数量(与小程序挂号卡对齐) -->
|
||||
<div
|
||||
v-if="registerCardDisplay.chief_complaint || registerDrugNamesLine || (registerCardDisplay.number != null && registerCardDisplay.number !== '')"
|
||||
class="mt-3 space-y-2 rounded-lg border border-gray-100 p-3 text-sm dark:border-gray-600"
|
||||
>
|
||||
<div v-if="registerCardDisplay.chief_complaint">
|
||||
<span class="font-medium text-gray-600 dark:text-gray-300">主诉:</span>
|
||||
<span class="text-gray-800 dark:text-gray-100">{{
|
||||
registerCardDisplay.chief_complaint
|
||||
}}</span>
|
||||
</div>
|
||||
<div v-if="registerDrugNamesLine">
|
||||
<span class="font-medium text-gray-600 dark:text-gray-300">所选药品:</span>
|
||||
<span class="text-gray-800 dark:text-gray-100">{{
|
||||
registerDrugNamesLine
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="registerCardDisplay.number != null && registerCardDisplay.number !== ''"
|
||||
>
|
||||
<span class="font-medium text-gray-600 dark:text-gray-300">数量:</span>
|
||||
<span class="text-gray-800 dark:text-gray-100"
|
||||
>各 {{ registerCardDisplay.number }} 盒</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="mt-3 border-t border-gray-200 pt-3 dark:border-gray-600">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
挂号费用:
|
||||
<span class="text-green-500 dark:text-green-400">¥{{ parsedContent.price }}</span>
|
||||
<span class="text-green-500 dark:text-green-400">¥{{ registerCardDisplay.price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 接诊/拒诊按钮 - 只在待就诊状态显示 -->
|
||||
<div v-if="parsedContent.status === 1" class="mt-2 flex gap-2">
|
||||
<div v-if="registerCardDisplay.status === 1" class="mt-2 flex gap-2">
|
||||
<Button
|
||||
:loading="registerOperating"
|
||||
class="flex-1"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="
|
||||
acceptRegister(parsedContent.id, parsedContent.order_no)
|
||||
acceptRegister(registerCardDisplay.id, registerCardDisplay.order_no)
|
||||
"
|
||||
>
|
||||
<i class="fas fa-check mr-1"></i>
|
||||
@@ -587,7 +673,7 @@ if (props.message.message_type === 10) {
|
||||
danger
|
||||
size="small"
|
||||
@click="
|
||||
rejectRegister(parsedContent.id, parsedContent.order_no)
|
||||
rejectRegister(registerCardDisplay.id, registerCardDisplay.order_no)
|
||||
"
|
||||
>
|
||||
<i class="fas fa-times mr-1"></i>
|
||||
@@ -606,9 +692,9 @@ if (props.message.message_type === 10) {
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<div class="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
创建时间: {{ parsedContent.created_at }}
|
||||
<span v-if="parsedContent.pay_time" class="ml-2">
|
||||
支付时间: {{ formatDateTime(parsedContent.pay_time) }}
|
||||
创建时间: {{ registerCardDisplay.created_at }}
|
||||
<span v-if="registerCardDisplay.pay_time" class="ml-2">
|
||||
支付时间: {{ formatDateTime(registerCardDisplay.pay_time) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -824,6 +910,12 @@ if (props.message.message_type === 10) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- type11:复诊助理用药确认(与药店患者就诊经历卡片分流) -->
|
||||
<FollowUpDrugAssistantCard
|
||||
v-else-if="message.message_type === 11 && isFollowUpDrugAssistant"
|
||||
:content="parsedContent"
|
||||
/>
|
||||
|
||||
<!-- 患者就诊经历卡片 (type=11) -->
|
||||
<PatientExperienceCard
|
||||
v-else-if="message.message_type === 11"
|
||||
|
||||
@@ -93,3 +93,30 @@ export async function exportOrderApi() {
|
||||
export async function updateFreeShipping(data: { id: number; is_free_shipping: number }) {
|
||||
return requestClient.post<any>(`${prefix}update-free-shipping`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 近 N 分钟订单:表内金额与按 OrderItems 重算是否一致(调价/分账同口径)
|
||||
*/
|
||||
export async function getVerifyRecentOrderAmounts(params?: {
|
||||
minutes?: number;
|
||||
mismatch_only?: number;
|
||||
}) {
|
||||
return requestClient.get<{
|
||||
count: number;
|
||||
minutes: number;
|
||||
orders: Array<{
|
||||
expected_items_price: number;
|
||||
expected_total_pay_price: number;
|
||||
items_diff: number;
|
||||
items_match: boolean;
|
||||
items_price: number;
|
||||
ok: boolean;
|
||||
order_id: number;
|
||||
order_no: string;
|
||||
total_diff: number;
|
||||
total_match: boolean;
|
||||
total_pay_price: number;
|
||||
}>;
|
||||
since: number;
|
||||
}>(`${prefix}verify-recent-order-amounts`, { params });
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@vben/common-ui';
|
||||
import { SvgCakeIcon } from '@vben/icons';
|
||||
|
||||
import { Button, Image, message, Switch, Tag } from 'ant-design-vue';
|
||||
import { Button, Image, message, Modal as AntdModal, Space, Switch, Table, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
@@ -19,6 +19,7 @@ import { downloadByData } from '#/util/tool';
|
||||
import {
|
||||
exportOrderApi,
|
||||
getOrderList,
|
||||
getVerifyRecentOrderAmounts,
|
||||
saleAmountApi,
|
||||
updateFreeShipping,
|
||||
} from '#/views/business/order/product-order/api';
|
||||
@@ -178,12 +179,103 @@ const toggleFreeShipping = async (row: any, checked: boolean) => {
|
||||
message.error(error.message || '更新失败');
|
||||
}
|
||||
};
|
||||
|
||||
const verifyOpen = ref(false);
|
||||
const verifyLoading = ref(false);
|
||||
const verifyRows = ref<any[]>([]);
|
||||
const verifyMeta = ref({ minutes: 10, count: 0, since: 0 });
|
||||
const verifyMismatchOnly = ref(false);
|
||||
|
||||
const verifyColumns = [
|
||||
{ title: '订单ID', dataIndex: 'order_id', key: 'order_id', width: 90 },
|
||||
{ title: '订单号', dataIndex: 'order_no', key: 'order_no', width: 200, ellipsis: true },
|
||||
{ title: '药品价(表)', dataIndex: 'items_price', key: 'items_price', width: 100 },
|
||||
{ title: '药品价(算)', dataIndex: 'expected_items_price', key: 'exp_items', width: 100 },
|
||||
{ title: '药品', key: 'items_match', width: 80 },
|
||||
{ title: '应付(表)', dataIndex: 'total_pay_price', key: 'total_pay', width: 100 },
|
||||
{ title: '应付(算)', dataIndex: 'expected_total_pay_price', key: 'exp_total', width: 100 },
|
||||
{ title: '应付', key: 'total_match', width: 80 },
|
||||
{ title: '全OK', key: 'ok', width: 70 },
|
||||
{ title: '药品差', dataIndex: 'items_diff', key: 'items_diff', width: 80 },
|
||||
{ title: '应付差', dataIndex: 'total_diff', key: 'total_diff', width: 80 },
|
||||
];
|
||||
|
||||
const fetchOrderAmountVerify = async () => {
|
||||
verifyLoading.value = true;
|
||||
try {
|
||||
const res = await getVerifyRecentOrderAmounts({
|
||||
minutes: 10,
|
||||
mismatch_only: verifyMismatchOnly.value ? 1 : 0,
|
||||
});
|
||||
verifyMeta.value = {
|
||||
minutes: res.minutes,
|
||||
count: res.count,
|
||||
since: res.since,
|
||||
};
|
||||
verifyRows.value = res.orders ?? [];
|
||||
verifyOpen.value = true;
|
||||
} finally {
|
||||
verifyLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openOrderAmountVerify = () => {
|
||||
verifyMismatchOnly.value = false;
|
||||
fetchOrderAmountVerify();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="订单管理">
|
||||
<FormModal />
|
||||
<Modal />
|
||||
<AntdModal
|
||||
v-model:open="verifyOpen"
|
||||
title="近10分钟订单金额校验"
|
||||
width="min(1100px, 96vw)"
|
||||
:footer="null"
|
||||
destroy-on-close
|
||||
>
|
||||
<Space direction="vertical" style="width: 100%; margin-bottom: 12px">
|
||||
<div>
|
||||
与明细重算口径同调价/分账;已检 {{ verifyMeta.count }} 条;since={{ verifyMeta.since }}(Unix)
|
||||
</div>
|
||||
<Space wrap>
|
||||
<span>仅显示不一致</span>
|
||||
<Switch v-model:checked="verifyMismatchOnly" @update:checked="fetchOrderAmountVerify" />
|
||||
<Button :loading="verifyLoading" type="primary" @click="fetchOrderAmountVerify">
|
||||
重新检测
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
<Table
|
||||
:columns="verifyColumns"
|
||||
:data-source="verifyRows"
|
||||
:loading="verifyLoading"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 1200 }"
|
||||
size="small"
|
||||
row-key="order_id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'items_match'">
|
||||
<Tag :color="record.items_match ? 'success' : 'error'">
|
||||
{{ record.items_match ? '一致' : '否' }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'total_match'">
|
||||
<Tag :color="record.total_match ? 'success' : 'error'">
|
||||
{{ record.total_match ? '一致' : '否' }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'ok'">
|
||||
<Tag :color="record.ok ? 'success' : 'error'">
|
||||
{{ record.ok ? '是' : '否' }}
|
||||
</Tag>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</AntdModal>
|
||||
<RefundModal />
|
||||
<PrescriptionDetailModal />
|
||||
<AnalysisOverview :items="overviewItems" :my-card="false" />
|
||||
@@ -192,6 +284,12 @@ const toggleFreeShipping = async (row: any, checked: boolean) => {
|
||||
<TableAction
|
||||
:flex="false"
|
||||
:actions="[
|
||||
// {
|
||||
// label: '校验近10分钟金额',
|
||||
// type: 'default',
|
||||
// icon: 'ix:search-check',
|
||||
// onClick: openOrderAmountVerify.bind(null),
|
||||
// },
|
||||
{
|
||||
label: '导出',
|
||||
type: 'primary',
|
||||
|
||||
Reference in New Issue
Block a user