feat: 1. 数脉api鉴别身份证。 2. 后台+微信登陆优化
This commit is contained in:
@@ -9,6 +9,20 @@ export async function simulatePayApi(data: {
|
||||
return requestClient.post<any>(`${prefix}simulate-pay`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品订单重新结算:作废残缺分账后按规则重建并入账(防重复加账)
|
||||
*/
|
||||
export async function retrySettleApi(data: { order_id: number }) {
|
||||
return requestClient.post<any>(`${prefix}retry-settle`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 超管手动确认收货 / 重试解冻(自动确认收货队列失败后的人工补偿)
|
||||
*/
|
||||
export async function retryConfirmReceiveApi(data: { order_id: number }) {
|
||||
return requestClient.post<any>(`${prefix}retry-confirm-receive`, data);
|
||||
}
|
||||
|
||||
export async function getOrderTraceApi(params: {
|
||||
order_id: number;
|
||||
scene: 'product' | 'register';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { getOrderPrescriptionTypeOption } from '#/views/business/order/product-order/api';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
@@ -16,29 +17,19 @@ export const formOptions: VbenFormProps = {
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
component: 'VbenSelect',
|
||||
// 与商品订单共用后端 ProductTypeEnum 开方类型,保证筛选项完整
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
showSearch: true,
|
||||
options: [
|
||||
{
|
||||
label: '中药',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '西药',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '中成药',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: '产品服务包',
|
||||
value: 5,
|
||||
},
|
||||
],
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getOrderPrescriptionTypeOption,
|
||||
placeholder: '请选择',
|
||||
},
|
||||
fieldName: 'prescription_type',
|
||||
|
||||
@@ -15,6 +15,16 @@ export async function getOrderList(data: any) {
|
||||
export async function getOrderStatusOption(data: any) {
|
||||
return requestClient.get<any>(`${prefix}status-option`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单/处方类型筛选项(与后端 ProductTypeEnum 开方类型对齐)
|
||||
*/
|
||||
export async function getOrderPrescriptionTypeOption(data?: any) {
|
||||
return requestClient.get<Array<{ id: number; name: string }>>(
|
||||
`${prefix}prescription-type-option`,
|
||||
{ params: data },
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 统计金额
|
||||
* @param data
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
deleteOrderExportScheme,
|
||||
getOrderExportDataApi,
|
||||
getOrderExportSchemeList,
|
||||
getOrderPrescriptionTypeOption,
|
||||
getOrderStatusOption,
|
||||
type OrderExportDataParams,
|
||||
type OrderExportSchemeItem,
|
||||
@@ -69,13 +70,6 @@ const DELIVERY_OPTIONS = [
|
||||
{ label: '诊所自提', value: 1 },
|
||||
];
|
||||
|
||||
const PRESCRIPTION_TYPE_OPTIONS = [
|
||||
{ label: '中药', value: 1 },
|
||||
{ label: '西药', value: 2 },
|
||||
{ label: '中成药', value: 3 },
|
||||
{ label: '产品服务包', value: 5 },
|
||||
];
|
||||
|
||||
const searchTime = ref<[Dayjs, Dayjs]>([dayjs().startOf('month'), dayjs()]);
|
||||
const orderNo = ref('');
|
||||
const storeId = ref<number | undefined>(undefined);
|
||||
@@ -94,6 +88,8 @@ const loadingStep = ref('');
|
||||
|
||||
const storeOptions = ref<{ label: string; value: number }[]>([]);
|
||||
const statusOptions = ref<{ label: string; value: number }[]>([]);
|
||||
/** 订单类型选项:打开弹窗时从后端 ProductTypeEnum 拉取 */
|
||||
const prescriptionTypeOptions = ref<{ label: string; value: number }[]>([]);
|
||||
const fetchingStores = ref(false);
|
||||
const storeExportLocked = ref(false);
|
||||
const lockedStoreName = ref('');
|
||||
@@ -331,6 +327,18 @@ const loadStatusOptions = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 拉取完整订单类型(含非药品、医疗器械等) */
|
||||
const loadPrescriptionTypeOptions = () => {
|
||||
getOrderPrescriptionTypeOption({}).then(
|
||||
(res: { id: number; name: string }[]) => {
|
||||
prescriptionTypeOptions.value = (res || []).map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
function onSelectedKeysChange() {
|
||||
markSchemeModified();
|
||||
}
|
||||
@@ -572,7 +580,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
status_text: labelOf(statusOptions.value, status.value),
|
||||
delivery_method_text: labelOf(DELIVERY_OPTIONS, deliveryMethod.value),
|
||||
prescription_type_text: labelOf(
|
||||
PRESCRIPTION_TYPE_OPTIONS,
|
||||
prescriptionTypeOptions.value,
|
||||
prescriptionType.value,
|
||||
),
|
||||
export_fields_text: selectedFieldLabels(),
|
||||
@@ -620,6 +628,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
loadStores();
|
||||
}
|
||||
loadStatusOptions();
|
||||
loadPrescriptionTypeOptions();
|
||||
void loadSchemes(true);
|
||||
}
|
||||
},
|
||||
@@ -749,7 +758,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
v-model:value="prescriptionType"
|
||||
allow-clear
|
||||
class="w-full"
|
||||
:options="PRESCRIPTION_TYPE_OPTIONS"
|
||||
:options="prescriptionTypeOptions"
|
||||
placeholder="全部"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,10 @@ import { computed, ref } from 'vue';
|
||||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
|
||||
import { Button, Card, Descriptions, Image, Space, Tabs, Tag, message } from 'ant-design-vue';
|
||||
import { Button, Card, Descriptions, Image, Modal as AntdModal, Space, Tabs, Tag, message } from 'ant-design-vue';
|
||||
|
||||
import OrderTraceDrawer from '#/views/business/order/components/order-trace-drawer.vue';
|
||||
import { retrySettleApi } from '#/views/business/order/api/order-ops';
|
||||
|
||||
import OrderPricePercentAdjustDrawer from '#/views/business/order/components/OrderPricePercentAdjustDrawer.vue';
|
||||
import { getOrderPriceAdjustConfig, adjustOrderPercent } from '#/api/order/priceAdjust';
|
||||
@@ -30,6 +31,26 @@ const expressDetail = ref({});
|
||||
// 订单发货方式
|
||||
const deliveryMethod = ref(-1);
|
||||
const syncingLegacy = ref(false);
|
||||
/** 重新结算请求中,防止重复点击 */
|
||||
const retrySettling = ref(false);
|
||||
|
||||
/**
|
||||
* 超管可见:已支付且未取消/未退款时可重新结算(补齐残缺分账)
|
||||
* 与后端一致:仅 role_id=1(SUPPER_ADMIN)
|
||||
*/
|
||||
const canRetrySettle = computed(() => {
|
||||
const roleId = Number(
|
||||
userStore.userInfo?.role_id ?? userStore.userInfo?.roles?.id,
|
||||
);
|
||||
if (roleId !== 1) return false;
|
||||
const row = data.value;
|
||||
if (!row?.id) return false;
|
||||
return (
|
||||
Number(row.is_pay) === 1 &&
|
||||
Number(row.cancel_status) === 0 &&
|
||||
Number(row.refund_status) === 0
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* 超管可见:上门快递且订单有运单时,可手动把历史运单同步到 shipment
|
||||
@@ -123,6 +144,31 @@ function openOrderTrace() {
|
||||
traceDrawerApi.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认后触发后端重新结算:作废残缺分账并按规则重建入账,避免重复加钱
|
||||
*/
|
||||
function handleRetrySettle() {
|
||||
if (!data.value?.id || retrySettling.value) {
|
||||
return;
|
||||
}
|
||||
AntdModal.confirm({
|
||||
title: '重新结算',
|
||||
content: `确认为订单 ${data.value.order_no} 重新结算分账?将作废旧分账后按当前规则重建,已完整的订单不会重复加账。`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
retrySettling.value = true;
|
||||
try {
|
||||
const res = await retrySettleApi({ order_id: Number(data.value.id) });
|
||||
message.success(res?.message || '重新结算成功');
|
||||
await reloadOrder();
|
||||
} finally {
|
||||
retrySettling.value = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
@@ -269,6 +315,14 @@ function prescriptionStatusColor() {
|
||||
<Button type="primary" size="small" @click="openOrderTrace">
|
||||
查看溯源
|
||||
</Button>
|
||||
<Button
|
||||
v-if="canRetrySettle"
|
||||
size="small"
|
||||
:loading="retrySettling"
|
||||
@click="handleRetrySettle"
|
||||
>
|
||||
重新结算
|
||||
</Button>
|
||||
</Space>
|
||||
<h3 class="mt-4">订单信息</h3>
|
||||
<Descriptions
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { getOrderStatusOption } from '#/views/business/order/product-order/api';
|
||||
import {
|
||||
getOrderPrescriptionTypeOption,
|
||||
getOrderStatusOption,
|
||||
} from '#/views/business/order/product-order/api';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
@@ -65,29 +68,19 @@ export const formOptions: VbenFormProps = {
|
||||
label: '发货方式',
|
||||
},
|
||||
{
|
||||
component: 'VbenSelect',
|
||||
// 类型从后端枚举拉取,避免前端写死漏掉非药品/医疗器械等
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
showSearch: true,
|
||||
options: [
|
||||
{
|
||||
label: '中药',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '西药',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '中成药',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: '产品服务包',
|
||||
value: 5,
|
||||
},
|
||||
],
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getOrderPrescriptionTypeOption,
|
||||
placeholder: '请选择',
|
||||
},
|
||||
fieldName: 'prescription_type',
|
||||
|
||||
@@ -43,7 +43,12 @@ import {
|
||||
updateFreeShipping,
|
||||
} from '#/views/business/order/product-order/api';
|
||||
import { getDeliveryWarehouseOptionsByDrugs } from '#/views/doctor/doctor-reception/api';
|
||||
import { simulatePayApi, accrueSalespersonCommissionApi, reverseSalespersonCommissionApi } from '#/views/business/order/api/order-ops';
|
||||
import {
|
||||
accrueSalespersonCommissionApi,
|
||||
retryConfirmReceiveApi,
|
||||
reverseSalespersonCommissionApi,
|
||||
simulatePayApi,
|
||||
} from '#/views/business/order/api/order-ops';
|
||||
import ChinaErpSyncLogDrawer from '#/views/business/order/components/china-erp-sync-log-drawer.vue';
|
||||
import OrderTraceDrawer from '#/views/business/order/components/order-trace-drawer.vue';
|
||||
import OrderPricePercentAdjustDrawer from '#/views/business/order/components/OrderPricePercentAdjustDrawer.vue';
|
||||
@@ -446,6 +451,29 @@ function handleSimulatePay(row: Record<string, any>) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 超管手动确认收货 / 重试解冻(自动确认队列失败后的人工补偿)
|
||||
* 仅 Super Admin 可见;待收货走完整签收,已确认收货仅重试解冻
|
||||
*/
|
||||
function handleRetryConfirmReceive(row: Record<string, any>) {
|
||||
const isWaitReceive = Number(row.status) === 2;
|
||||
AntdModal.confirm({
|
||||
title: isWaitReceive ? '确认收货' : '重试解冻',
|
||||
content: isWaitReceive
|
||||
? `确认为订单 ${row.order_no} 手动确认收货?将更新为已确认收货并解冻供应商/配送仓分账冻结。`
|
||||
: `订单 ${row.order_no} 已确认收货,是否重试解冻分账冻结?(幂等,不会重复加账)`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const res = await retryConfirmReceiveApi({ order_id: Number(row.id) });
|
||||
message.success(
|
||||
res?.message || (isWaitReceive ? '确认收货成功' : '解冻重试成功'),
|
||||
);
|
||||
await gridApi.query();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const accruingOrderIds = ref<number[]>([]);
|
||||
const reversingOrderIds = ref<number[]>([]);
|
||||
|
||||
@@ -1303,6 +1331,19 @@ const openOrderAmountVerify = () => {
|
||||
ifShow: row.is_pay === 0,
|
||||
onClick: handleSimulatePay.bind(null, row),
|
||||
},
|
||||
{
|
||||
// 仅超管:待收货确认收货 / 已确认收货重试解冻
|
||||
label: Number(row.status) === 2 ? '确认收货' : '重试解冻',
|
||||
type: 'link',
|
||||
icon: 'mdi:package-check',
|
||||
auth: ['Super Admin'],
|
||||
ifShow:
|
||||
Number(row.is_pay) === 1 &&
|
||||
Number(row.cancel_status) === 0 &&
|
||||
Number(row.refund_status) === 0 &&
|
||||
(Number(row.status) === 2 || Number(row.status) === 7),
|
||||
onClick: handleRetryConfirmReceive.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '发货',
|
||||
type: 'link',
|
||||
|
||||
@@ -141,8 +141,10 @@ const getDrugListByWesternModal = debounce(async (searchText = '') => {
|
||||
select_number: matched?.select_number || 0,
|
||||
drug: {
|
||||
...item.drug,
|
||||
// 如果已选,使用已选的数量,否则默认为1
|
||||
number: matched?.number || 1,
|
||||
// 中药克数不默认 1,需医生手动填写;西药仍默认 1
|
||||
number:
|
||||
matched?.number ??
|
||||
(Number(type.value) === 1 ? undefined : 1),
|
||||
// 如果已选,同步已选的用法
|
||||
frequency_id: matched?.frequency_id || item.drug.frequency_id,
|
||||
type_id: matched?.type_id || item.drug.type_id,
|
||||
|
||||
@@ -2052,13 +2052,9 @@ const doctorSecondSignModal = ref(false);
|
||||
|
||||
/**
|
||||
* 在新的药品数量框失去焦点后弹出新药品弹窗
|
||||
* 中药克数不设默认值,未填写时不弹确认,避免误加 1g
|
||||
*/
|
||||
function newDrugBlur() {
|
||||
// 如果没输入克数,默认为 1g
|
||||
if (!newDrugInfo.value.number) {
|
||||
newDrugInfo.value.number = 1;
|
||||
}
|
||||
|
||||
if (newDrugInfo.value.id && newDrugInfo.value.name && newDrugInfo.value.number > 0) {
|
||||
confirmModalApi.setData({
|
||||
title: '确认添加',
|
||||
@@ -2121,7 +2117,10 @@ const getDrugListByWesternModal = debounce(async (searchText = '') => {
|
||||
select_number: matched?.select_number || 0,
|
||||
drug: {
|
||||
...item.drug,
|
||||
number: matched?.number || 1,
|
||||
// 中药克数不默认 1,需医生手动填写;西药仍默认 1
|
||||
number:
|
||||
matched?.number ??
|
||||
(activeCategory.value === 1 ? undefined : 1),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -3002,7 +3001,7 @@ watch(
|
||||
</Select>
|
||||
</span>
|
||||
<p class="card-price">
|
||||
¥<span>{{ (drug.price * drug.number).toFixed(2) }}</span>
|
||||
¥<span>{{ (drug.price * (drug.number || 0)).toFixed(2) }}</span>
|
||||
<span
|
||||
v-if="getChineseItemMargin(drug) && getChineseItemMargin(drug) !== '--'"
|
||||
class="ml-2 text-gray-500 text-sm"
|
||||
@@ -3083,7 +3082,7 @@ watch(
|
||||
<p class="card-price">
|
||||
¥<span>{{
|
||||
(
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 1)
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 0)
|
||||
).toFixed(2)
|
||||
}}元</span>
|
||||
</p>
|
||||
@@ -3158,7 +3157,7 @@ watch(
|
||||
<p class="card-price">
|
||||
¥<span>{{
|
||||
(
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 1)
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 0)
|
||||
).toFixed(2)
|
||||
}}元</span>
|
||||
</p>
|
||||
@@ -3233,7 +3232,7 @@ watch(
|
||||
<p class="card-price">
|
||||
¥<span>{{
|
||||
(
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 1)
|
||||
(newDrugInfo.price || 0) * (newDrugInfo.number || 0)
|
||||
).toFixed(2)
|
||||
}}元</span>
|
||||
</p>
|
||||
|
||||
@@ -72,7 +72,7 @@ export const modalFormProps: VbenFormProps = {
|
||||
componentProps: {
|
||||
maxCount: 1,
|
||||
multiple: false,
|
||||
acceptTypes: [1],
|
||||
acceptTypes: [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user