feat:优化了订单、诊所搜索组件、药品这些交互
This commit is contained in:
@@ -13,11 +13,11 @@ defineOptions({
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
/** 运单号:变化时按前缀自动匹配快递公司(用户手动改选后不再覆盖) */
|
||||
trackingNo?: string;
|
||||
value?: string;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits<{
|
||||
@@ -27,9 +27,9 @@ const emits = defineEmits<{
|
||||
const mValue = useVModel(props, 'value', emits, { passive: true });
|
||||
|
||||
type ExpressCompanyOption = {
|
||||
code: string;
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
const open = ref(false);
|
||||
@@ -40,7 +40,10 @@ const searchInputRef = ref<InstanceType<typeof Input> | null>(null);
|
||||
/** 用户是否已手动选择/清空;为 true 时不再按单号自动覆盖 */
|
||||
const userPicked = ref(false);
|
||||
|
||||
function filterExpressCompanyOptions(keyword: string, list: ExpressCompanyOption[]) {
|
||||
function filterExpressCompanyOptions(
|
||||
keyword: string,
|
||||
list: ExpressCompanyOption[],
|
||||
) {
|
||||
const q = keyword.trim().toLowerCase();
|
||||
if (!q) return list;
|
||||
return list.filter((item) => {
|
||||
@@ -61,7 +64,7 @@ const selectedOption = computed(() =>
|
||||
const displayText = computed(() => {
|
||||
const item = selectedOption.value;
|
||||
if (!item) return '';
|
||||
return item.code ? `${item.name || '-'}(${item.code})` : (item.name || '-');
|
||||
return item.code ? `${item.name || '-'}(${item.code})` : item.name || '-';
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -69,7 +72,7 @@ const displayText = computed(() => {
|
||||
* 规则:尚未手动改选,或当前选中仍与规则结果一致时才覆盖
|
||||
*/
|
||||
function tryAutoMatchByTrackingNo() {
|
||||
if (!options.value.length) return;
|
||||
if (options.value.length === 0) return;
|
||||
const matched = matchExpressByTrackingNo(props.trackingNo, options.value);
|
||||
if (!matched?.code) return;
|
||||
const matchedCode = matched.code;
|
||||
@@ -110,7 +113,7 @@ function onOpenChange(next: boolean) {
|
||||
open.value = next;
|
||||
if (next) {
|
||||
searchKeyword.value = '';
|
||||
if (!options.value.length) {
|
||||
if (options.value.length === 0) {
|
||||
loadOptions();
|
||||
}
|
||||
nextTick(() => searchInputRef.value?.focus?.());
|
||||
@@ -140,9 +143,9 @@ onMounted(() => {
|
||||
<template>
|
||||
<Popover
|
||||
:open="open"
|
||||
trigger="click"
|
||||
placement="bottomLeft"
|
||||
overlay-class-name="express-company-select-popover"
|
||||
placement="bottomLeft"
|
||||
trigger="click"
|
||||
@open-change="onOpenChange"
|
||||
>
|
||||
<template #content>
|
||||
@@ -151,17 +154,17 @@ onMounted(() => {
|
||||
ref="searchInputRef"
|
||||
v-model:value="searchKeyword"
|
||||
allow-clear
|
||||
placeholder="搜索公司名称或编码"
|
||||
class="express-search"
|
||||
placeholder="搜索公司名称或编码"
|
||||
/>
|
||||
<Spin :spinning="loading">
|
||||
<div v-if="filteredOptions.length" class="express-list">
|
||||
<div v-if="filteredOptions.length > 0" class="express-list">
|
||||
<button
|
||||
v-for="item in filteredOptions"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="express-item"
|
||||
:class="{ active: item.code === mValue }"
|
||||
class="express-item"
|
||||
type="button"
|
||||
@click="selectOption(item)"
|
||||
>
|
||||
<div class="express-meta">
|
||||
@@ -170,20 +173,20 @@ onMounted(() => {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<Empty v-else description="无匹配快递公司" class="express-empty" />
|
||||
<Empty v-else class="express-empty" description="无匹配快递公司" />
|
||||
</Spin>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
:class="{ disabled, placeholder: !displayText }"
|
||||
class="express-trigger"
|
||||
:class="{ disabled: disabled, placeholder: !displayText }"
|
||||
>
|
||||
<Input
|
||||
:value="displayText"
|
||||
readonly
|
||||
:disabled="disabled"
|
||||
:placeholder="placeholder || '请选择快递公司'"
|
||||
:value="displayText"
|
||||
class="express-trigger-input"
|
||||
readonly
|
||||
>
|
||||
<template v-if="displayText && !disabled" #suffix>
|
||||
<span class="clear-btn" @click="clearSelection">×</span>
|
||||
@@ -196,6 +199,8 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.express-trigger {
|
||||
width: 100%;
|
||||
/* min-width: fit-content; */
|
||||
min-width: 250px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.express-trigger.disabled {
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 门店多选搜索组件
|
||||
* - 普通输入框输入,下方气泡卡片展示匹配结果(名称 / 拼音首拼)
|
||||
* - 选中后在输入框下方用可关闭 Tag 展示「名称【id】」
|
||||
* - 搜索结果默认高亮第一项,Enter 可直接选中
|
||||
* - 样式使用主题 CSS 变量,自动适配暗色
|
||||
*/
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { LoadingOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { useDebounceFn, useVModel } from '@vueuse/core';
|
||||
import { Empty, Input, Spin, Tag } from 'ant-design-vue';
|
||||
|
||||
import { searchStoreOption } from '#/views/system/store/api';
|
||||
|
||||
defineOptions({
|
||||
name: 'StoreMultiSearch',
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
export type StoreSearchItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
type?: number;
|
||||
shouzimu?: string;
|
||||
};
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 已选门店 ID 列表 */
|
||||
value?: number[];
|
||||
/** 限定类型:0诊所 1药店;不传则诊所+药店都可搜 */
|
||||
storeType?: 0 | 1;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 外部已选门店详情(打开绑定弹窗时回填名称用)
|
||||
* 仅用于 Tag 展示,不改变 value
|
||||
*/
|
||||
initialItems?: StoreSearchItem[];
|
||||
}>(),
|
||||
{
|
||||
value: () => [],
|
||||
storeType: undefined,
|
||||
placeholder: '输入名称或拼音首拼搜索',
|
||||
disabled: false,
|
||||
initialItems: () => [],
|
||||
},
|
||||
);
|
||||
|
||||
const emits = defineEmits<{
|
||||
'update:value': [value: number[]];
|
||||
}>();
|
||||
|
||||
const mValue = useVModel(props, 'value', emits, {
|
||||
passive: true,
|
||||
defaultValue: [],
|
||||
});
|
||||
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
const searchKeyword = ref('');
|
||||
const loading = ref(false);
|
||||
const showDropdown = ref(false);
|
||||
const highlightIndex = ref(-1);
|
||||
const options = ref<StoreSearchItem[]>([]);
|
||||
/** 已选门店详情(用于 Tag 展示名称) */
|
||||
const selectedMap = ref<Record<number, StoreSearchItem>>({});
|
||||
|
||||
const selectedIds = computed(() =>
|
||||
Array.isArray(mValue.value) ? mValue.value.map(Number) : [],
|
||||
);
|
||||
|
||||
const selectedItems = computed(() =>
|
||||
selectedIds.value.map((id) => {
|
||||
const cached = selectedMap.value[id];
|
||||
return cached || { id, name: `门店${id}` };
|
||||
}),
|
||||
);
|
||||
|
||||
/** 下拉中过滤掉已选 */
|
||||
const visibleOptions = computed(() =>
|
||||
options.value.filter((item) => !selectedIds.value.includes(item.id)),
|
||||
);
|
||||
|
||||
/**
|
||||
* 将初始/回填门店写入 selectedMap,便于 Tag 显示真实名称
|
||||
*/
|
||||
function mergeInitialItems(items: StoreSearchItem[]) {
|
||||
if (!items?.length) return;
|
||||
const next = { ...selectedMap.value };
|
||||
for (const item of items) {
|
||||
if (item?.id) {
|
||||
next[item.id] = item;
|
||||
}
|
||||
}
|
||||
selectedMap.value = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求后端门店搜索;有结果时默认高亮第一项
|
||||
*/
|
||||
async function fetchOptions() {
|
||||
const keyword = searchKeyword.value.trim();
|
||||
if (!keyword) {
|
||||
options.value = [];
|
||||
showDropdown.value = false;
|
||||
highlightIndex.value = -1;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
showDropdown.value = true;
|
||||
try {
|
||||
const res = await searchStoreOption({
|
||||
keyword,
|
||||
...(props.storeType === 0 || props.storeType === 1
|
||||
? { type: props.storeType }
|
||||
: {}),
|
||||
limit: 20,
|
||||
});
|
||||
options.value = res?.items ?? [];
|
||||
// 默认高亮第一项,Enter 可直接选中
|
||||
highlightIndex.value = options.value.length > 0 ? 0 : -1;
|
||||
} catch {
|
||||
options.value = [];
|
||||
highlightIndex.value = -1;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedFetch = useDebounceFn(fetchOptions, 300);
|
||||
|
||||
function handleInput() {
|
||||
debouncedFetch();
|
||||
}
|
||||
|
||||
function handleFocus() {
|
||||
if (visibleOptions.value.length > 0) {
|
||||
showDropdown.value = true;
|
||||
if (highlightIndex.value < 0) {
|
||||
highlightIndex.value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中一项:追加到 value,缓存名称,清空输入继续搜
|
||||
*/
|
||||
function selectOption(item: StoreSearchItem) {
|
||||
if (selectedIds.value.includes(item.id)) return;
|
||||
selectedMap.value = { ...selectedMap.value, [item.id]: item };
|
||||
mValue.value = [...selectedIds.value, item.id];
|
||||
searchKeyword.value = '';
|
||||
options.value = [];
|
||||
showDropdown.value = false;
|
||||
highlightIndex.value = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消某个已选门店
|
||||
*/
|
||||
function removeSelected(id: number) {
|
||||
mValue.value = selectedIds.value.filter((x) => x !== id);
|
||||
const next = { ...selectedMap.value };
|
||||
delete next[id];
|
||||
selectedMap.value = next;
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (!showDropdown.value || visibleOptions.value.length === 0) return;
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
highlightIndex.value = Math.min(
|
||||
Math.max(highlightIndex.value, 0) + 1,
|
||||
visibleOptions.value.length - 1,
|
||||
);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
highlightIndex.value = Math.max(highlightIndex.value - 1, 0);
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
{
|
||||
const idx = highlightIndex.value >= 0 ? highlightIndex.value : 0;
|
||||
const item = visibleOptions.value[idx];
|
||||
if (item) selectOption(item);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
showDropdown.value = false;
|
||||
highlightIndex.value = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (containerRef.value && !containerRef.value.contains(e.target as Node)) {
|
||||
showDropdown.value = false;
|
||||
highlightIndex.value = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
mergeInitialItems(props.initialItems || []);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.storeType,
|
||||
() => {
|
||||
options.value = [];
|
||||
showDropdown.value = false;
|
||||
searchKeyword.value = '';
|
||||
highlightIndex.value = -1;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.initialItems,
|
||||
(items) => mergeInitialItems(items || []),
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="containerRef" class="store-multi-search">
|
||||
<Input
|
||||
v-model:value="searchKeyword"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
allow-clear
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@keydown="handleKeydown"
|
||||
>
|
||||
<template #prefix>
|
||||
<LoadingOutlined v-if="loading" class="text-muted-foreground" />
|
||||
<SearchOutlined v-else class="text-muted-foreground" />
|
||||
</template>
|
||||
</Input>
|
||||
|
||||
<!-- 气泡建议列表 -->
|
||||
<div v-if="showDropdown" class="store-multi-search__dropdown">
|
||||
<Spin :spinning="loading">
|
||||
<Empty
|
||||
v-if="!loading && visibleOptions.length === 0"
|
||||
:image="Empty.PRESENTED_IMAGE_SIMPLE"
|
||||
description="暂无匹配门店"
|
||||
class="py-3"
|
||||
/>
|
||||
<div
|
||||
v-for="(item, index) in visibleOptions"
|
||||
:key="item.id"
|
||||
class="store-multi-search__option"
|
||||
:class="{ 'is-active': index === highlightIndex }"
|
||||
@mousedown.prevent="selectOption(item)"
|
||||
>
|
||||
<div class="store-multi-search__option-name">
|
||||
{{ item.name }}【{{ item.id }}】
|
||||
</div>
|
||||
<div v-if="item.shouzimu" class="store-multi-search__option-sub">
|
||||
首拼:{{ item.shouzimu }}
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
<!-- 已选 Tag -->
|
||||
<div v-if="selectedItems.length > 0" class="store-multi-search__tags">
|
||||
<Tag
|
||||
v-for="item in selectedItems"
|
||||
:key="item.id"
|
||||
closable
|
||||
color="blue"
|
||||
@close="() => removeSelected(item.id)"
|
||||
>
|
||||
{{ item.name }}【{{ item.id }}】
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 只用框架主题变量,随亮暗/主题色自动适配 */
|
||||
.store-multi-search {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.store-multi-search__dropdown {
|
||||
position: absolute;
|
||||
z-index: 1050;
|
||||
top: calc(100% + 4px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
padding: 6px;
|
||||
border-radius: 8px;
|
||||
background: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow:
|
||||
0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||
0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.store-multi-search__option {
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover,
|
||||
&.is-active {
|
||||
background: hsl(var(--accent-hover));
|
||||
}
|
||||
}
|
||||
|
||||
.store-multi-search__option-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.store-multi-search__option-sub {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.store-multi-search__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -5,4 +5,5 @@ export type CustomComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'ApiTreeSelect'
|
||||
| 'IconPicker'
|
||||
| 'StoreMultiSearch'
|
||||
| 'WarehouseAdminDrugSearch';
|
||||
|
||||
511
apps/web-antd/src/components/store-card/StoreCardModal.vue
Normal file
511
apps/web-antd/src/components/store-card/StoreCardModal.vue
Normal file
@@ -0,0 +1,511 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 门店详情弹窗(诊所/药店)
|
||||
* 布局对齐医生档案:顶部统计 + 左侧竖向 Tabs
|
||||
*/
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
DatePicker,
|
||||
Descriptions,
|
||||
Empty,
|
||||
Image,
|
||||
Select,
|
||||
Spin,
|
||||
Table,
|
||||
Tabs,
|
||||
Tag,
|
||||
message,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import DoctorCardModal from '#/views/doctor/doctor/components/DoctorCardModal.vue';
|
||||
import { getStoreBankCardReportDetail } from '#/views/system/store-bank-card/api';
|
||||
import {
|
||||
getStoreCardApi,
|
||||
getStoreCardStatsApi,
|
||||
getStoreDrugsByTypeApi,
|
||||
openQrCodeApi,
|
||||
} from '#/views/system/store/api';
|
||||
import { getSalespersonList } from '#/views/system/store/api/salesperson';
|
||||
import { resolveAvatarUrl } from '#/views/system/store-input/utils/inputUser';
|
||||
|
||||
const RangePicker = DatePicker.RangePicker;
|
||||
|
||||
const storeId = ref(0);
|
||||
const loading = ref(false);
|
||||
const activeTab = ref('basic');
|
||||
const cardData = ref<any>(null);
|
||||
const searchTime = ref<[Dayjs, Dayjs]>([
|
||||
dayjs().startOf('month'),
|
||||
dayjs(),
|
||||
]);
|
||||
|
||||
const bankLoading = ref(false);
|
||||
const bankDetail = ref<any>(null);
|
||||
|
||||
const drugType = ref(2);
|
||||
const drugLoading = ref(false);
|
||||
const drugList = ref<any[]>([]);
|
||||
|
||||
const promoterLoading = ref(false);
|
||||
const promoterList = ref<any[]>([]);
|
||||
|
||||
const generatingQr = ref(false);
|
||||
|
||||
const store = computed(() => cardData.value?.store ?? {});
|
||||
const stats = computed(() => cardData.value?.stats_summary ?? {});
|
||||
const doctorList = computed(() => cardData.value?.doctor_list ?? []);
|
||||
const isClinic = computed(() => Number(store.value?.type) === 0);
|
||||
const isPharmacy = computed(() => Number(store.value?.type) === 1);
|
||||
const modalTitle = computed(() => {
|
||||
const name = store.value?.name;
|
||||
if (!name) return '门店详情';
|
||||
return isPharmacy.value ? `药店详情 · ${name}` : `诊所详情 · ${name}`;
|
||||
});
|
||||
|
||||
const drugTypeOptions = [
|
||||
{ label: '中药', value: 1 },
|
||||
{ label: '西药', value: 2 },
|
||||
{ label: '保健食品', value: 3 },
|
||||
{ label: '产品服务包', value: 5 },
|
||||
{ label: '非药品', value: 6 },
|
||||
{ label: '医疗器械', value: 7 },
|
||||
];
|
||||
|
||||
const drugColumns = [
|
||||
{ title: '药品名称', dataIndex: 'drug_name', key: 'drug_name', ellipsis: true },
|
||||
{ title: '规格', dataIndex: 'specification', key: 'specification', width: 140 },
|
||||
{ title: '售价', dataIndex: 'price', key: 'price', width: 100 },
|
||||
{ title: '进价', dataIndex: 'buy_price', key: 'buy_price', width: 100 },
|
||||
];
|
||||
|
||||
const promoterColumns = [
|
||||
{ title: 'ID', dataIndex: 'id', key: 'id', width: 80 },
|
||||
{ title: '姓名', dataIndex: 'name', key: 'name' },
|
||||
{ title: '手机号', dataIndex: 'phone', key: 'phone', width: 140 },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
||||
];
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
class: 'w-[900px] xl:w-[1100px]',
|
||||
fullscreenButton: true,
|
||||
footer: false,
|
||||
draggable: true,
|
||||
closeOnClickModal: false,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) return;
|
||||
const data = modalApi.getData<Record<string, any>>() || {};
|
||||
storeId.value = Number(data.storeId || data.id || 0);
|
||||
activeTab.value = 'basic';
|
||||
searchTime.value = [dayjs().startOf('month'), dayjs()];
|
||||
bankDetail.value = null;
|
||||
drugList.value = [];
|
||||
promoterList.value = [];
|
||||
if (storeId.value) {
|
||||
loadCard();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [DoctorModal, DoctorModalApi] = useVbenModal({
|
||||
connectedComponent: DoctorCardModal,
|
||||
});
|
||||
|
||||
function searchTimeParam(): [string, string] {
|
||||
return [
|
||||
searchTime.value[0].format('YYYY-MM-DD 00:00:00'),
|
||||
searchTime.value[1].format('YYYY-MM-DD 23:59:59'),
|
||||
];
|
||||
}
|
||||
|
||||
/** 加载门店卡片概览 */
|
||||
async function loadCard() {
|
||||
if (!storeId.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getStoreCardApi({
|
||||
id: storeId.value,
|
||||
search_time: searchTimeParam(),
|
||||
});
|
||||
cardData.value = res;
|
||||
if (isPharmacy.value) {
|
||||
drugType.value = 2;
|
||||
}
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '加载门店详情失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅刷新统计 */
|
||||
async function refreshStats() {
|
||||
if (!storeId.value) return;
|
||||
try {
|
||||
const res = await getStoreCardStatsApi({
|
||||
id: storeId.value,
|
||||
search_time: searchTimeParam(),
|
||||
});
|
||||
if (cardData.value) {
|
||||
cardData.value = {
|
||||
...cardData.value,
|
||||
stats_summary: res.stats_summary,
|
||||
search_time: res.search_time,
|
||||
};
|
||||
}
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '刷新统计失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadBank() {
|
||||
if (!storeId.value || bankDetail.value) return;
|
||||
bankLoading.value = true;
|
||||
try {
|
||||
bankDetail.value = await getStoreBankCardReportDetail(storeId.value);
|
||||
} catch {
|
||||
bankDetail.value = null;
|
||||
} finally {
|
||||
bankLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDrugs() {
|
||||
if (!storeId.value) return;
|
||||
drugLoading.value = true;
|
||||
try {
|
||||
const res = await getStoreDrugsByTypeApi(storeId.value, drugType.value);
|
||||
const list = Array.isArray(res) ? res : res?.items || res?.list || [];
|
||||
drugList.value = (list || []).map((item: any) => {
|
||||
const drug = item.drug || {};
|
||||
return {
|
||||
id: item.id,
|
||||
drug_name: drug.drug_name || item.drug_name || '-',
|
||||
specification: drug.specification || item.specification || '-',
|
||||
price: item.price ?? '-',
|
||||
buy_price: item.buy_price ?? '-',
|
||||
};
|
||||
});
|
||||
} catch {
|
||||
drugList.value = [];
|
||||
} finally {
|
||||
drugLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPromoters() {
|
||||
if (!storeId.value || promoterList.value.length > 0) return;
|
||||
promoterLoading.value = true;
|
||||
try {
|
||||
const res = await getSalespersonList({
|
||||
store_id: storeId.value,
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
});
|
||||
const list = res?.items || res?.list?.items || res || [];
|
||||
promoterList.value = Array.isArray(list) ? list : [];
|
||||
} catch {
|
||||
promoterList.value = [];
|
||||
} finally {
|
||||
promoterLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onTabChange(key: string | number) {
|
||||
activeTab.value = String(key);
|
||||
if (key === 'bank') loadBank();
|
||||
if (key === 'drugs') loadDrugs();
|
||||
if (key === 'promoters') loadPromoters();
|
||||
}
|
||||
|
||||
function openDoctor(suId: number) {
|
||||
DoctorModalApi.setData({ su_id: suId, hideStoresTab: true, readonly: true });
|
||||
DoctorModalApi.open();
|
||||
}
|
||||
|
||||
async function ensureQrCode() {
|
||||
if (cardData.value?.qr_code || store.value?.qr_code) return;
|
||||
generatingQr.value = true;
|
||||
try {
|
||||
await openQrCodeApi(storeId.value);
|
||||
await loadCard();
|
||||
message.success('二维码已生成');
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '生成二维码失败');
|
||||
} finally {
|
||||
generatingQr.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clinicTypeText(v: number) {
|
||||
if (v === 1) return '西医诊所';
|
||||
if (v === 2) return '中医诊所';
|
||||
return '未设置';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="modalTitle">
|
||||
<Spin :spinning="loading">
|
||||
<div v-if="cardData" class="mb-4 flex flex-wrap items-center justify-between gap-3 px-1">
|
||||
<div class="text-sm text-muted-foreground">统计时间范围</div>
|
||||
<RangePicker
|
||||
v-model:value="searchTime"
|
||||
format="YYYY-MM-DD"
|
||||
@change="refreshStats"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 顶部统计 -->
|
||||
<div
|
||||
v-if="cardData"
|
||||
class="mb-6 grid grid-cols-1 gap-4 px-1 md:grid-cols-3"
|
||||
>
|
||||
<div
|
||||
class="relative flex flex-col overflow-hidden rounded-xl border border-blue-100 bg-blue-50 p-5 shadow-sm transition-all group hover:shadow-md dark:border-blue-800/50 dark:bg-blue-900/20"
|
||||
>
|
||||
<span class="text-sm font-semibold text-blue-600/80 dark:text-blue-300/80">
|
||||
销售额
|
||||
</span>
|
||||
<div class="mt-2 text-2xl font-bold text-blue-900 dark:text-blue-100">
|
||||
<span class="text-lg">¥</span>
|
||||
{{ Number(stats.sales_amount ?? 0).toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative flex flex-col overflow-hidden rounded-xl border border-emerald-100 bg-emerald-50 p-5 shadow-sm transition-all group hover:shadow-md dark:border-emerald-800/50 dark:bg-emerald-900/20"
|
||||
>
|
||||
<span class="text-sm font-semibold text-emerald-600/80 dark:text-emerald-300/80">
|
||||
利润
|
||||
</span>
|
||||
<div class="mt-2 text-2xl font-bold text-emerald-900 dark:text-emerald-100">
|
||||
<span class="text-lg">¥</span>
|
||||
{{ Number(stats.profit ?? 0).toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative flex flex-col overflow-hidden rounded-xl border border-purple-100 bg-purple-50 p-5 shadow-sm transition-all group hover:shadow-md dark:border-purple-800/50 dark:bg-purple-900/20"
|
||||
>
|
||||
<span class="text-sm font-semibold text-purple-600/80 dark:text-purple-300/80">
|
||||
处方量
|
||||
</span>
|
||||
<div class="mt-2 text-2xl font-bold text-purple-900 dark:text-purple-100">
|
||||
{{ stats.prescription_count ?? 0 }}
|
||||
<span class="text-xs font-normal text-purple-700/60 dark:text-purple-300/60">
|
||||
张
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs
|
||||
:active-key="activeTab"
|
||||
tab-position="left"
|
||||
class="custom-vertical-tabs min-h-[480px]"
|
||||
@change="onTabChange"
|
||||
>
|
||||
<Tabs.TabPane key="basic" tab="门店基本信息">
|
||||
<div class="pl-4">
|
||||
<Descriptions bordered :column="2" size="small">
|
||||
<Descriptions.Item label="名称">
|
||||
{{ store.name || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="ID">{{ store.id }}</Descriptions.Item>
|
||||
<Descriptions.Item label="类型">
|
||||
{{ isPharmacy ? '药店' : '诊所' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item v-if="isClinic" label="诊所类型">
|
||||
{{ clinicTypeText(Number(store.clinic_type)) }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="联系人">
|
||||
{{ store.contact || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="手机号">
|
||||
{{ store.mobile || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="地址" :span="2">
|
||||
{{ store.position || store.address || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="ERP ID">
|
||||
{{ store.erp_id || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="MES ID">
|
||||
{{ store.mes_id || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="银行卡报备">
|
||||
{{ store.bank_card_report_status_text || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="业务员">
|
||||
{{ store.new_admin?.nick_name || '-' }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="bank" tab="银行卡">
|
||||
<div class="pl-4">
|
||||
<Spin :spinning="bankLoading">
|
||||
<template v-if="bankDetail">
|
||||
<Descriptions bordered :column="2" size="small">
|
||||
<Descriptions.Item label="户名">
|
||||
{{ bankDetail.bank_user_name || bankDetail.account_name || store.bank_user_name || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="卡号">
|
||||
{{ bankDetail.bank_card || bankDetail.card_no || store.bank_card || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="开户行">
|
||||
{{ bankDetail.bank_name || store.bank_name || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="联行号">
|
||||
{{ bankDetail.bank_no || store.bank_no || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="报备状态">
|
||||
{{ bankDetail.report_status_text || store.bank_card_report_status_text || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="账户类型">
|
||||
{{ bankDetail.bank_account_type ?? store.bank_account_type ?? '-' }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</template>
|
||||
<Empty v-else description="暂无银行卡报备信息,展示门店预留信息">
|
||||
<Descriptions bordered :column="2" size="small" class="mt-4 text-left">
|
||||
<Descriptions.Item label="户名">
|
||||
{{ store.bank_user_name || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="卡号">
|
||||
{{ store.bank_card || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="开户行">
|
||||
{{ store.bank_name || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="联行号">
|
||||
{{ store.bank_no || '-' }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Empty>
|
||||
</Spin>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane v-if="isClinic" key="doctors" tab="诊所医生团队">
|
||||
<div class="pl-4">
|
||||
<Empty v-if="doctorList.length === 0" description="暂无医生" />
|
||||
<div v-else class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<div
|
||||
v-for="item in doctorList"
|
||||
:key="item.su_id"
|
||||
class="flex items-center gap-3 rounded-lg border border-gray-100 p-3 dark:border-slate-700"
|
||||
>
|
||||
<Avatar :src="resolveAvatarUrl(item.doctor?.avatar)" :size="48" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<Button type="link" class="!px-0" @click="openDoctor(item.doctor?.su_id || item.su_id)">
|
||||
{{ item.doctor?.name || '未知医生' }}
|
||||
</Button>
|
||||
<div class="text-xs text-muted-foreground">
|
||||
{{ item.doctor?.mobile || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane v-if="isPharmacy" key="drugs" tab="药店药品">
|
||||
<div class="pl-4">
|
||||
<div class="mb-3 flex items-center gap-2">
|
||||
<span class="text-sm text-muted-foreground">药品类型</span>
|
||||
<Select
|
||||
v-model:value="drugType"
|
||||
:options="drugTypeOptions"
|
||||
style="width: 160px"
|
||||
@change="loadDrugs"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:columns="drugColumns"
|
||||
:data-source="drugList"
|
||||
:loading="drugLoading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
:scroll="{ y: 360 }"
|
||||
/>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="qr" tab="门店二维码">
|
||||
<div class="flex flex-col items-center gap-4 py-6 pl-4">
|
||||
<template v-if="cardData?.qr_code || store.qr_code">
|
||||
<Image
|
||||
:src="cardData?.qr_code || store.qr_code"
|
||||
:width="200"
|
||||
:preview="true"
|
||||
/>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ store.name }} 门店二维码
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Empty description="尚未生成二维码" />
|
||||
<Button type="primary" :loading="generatingQr" @click="ensureQrCode">
|
||||
生成二维码
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="promoters" tab="门店推广员">
|
||||
<div class="pl-4">
|
||||
<Table
|
||||
:columns="promoterColumns"
|
||||
:data-source="promoterList"
|
||||
:loading="promoterLoading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
:scroll="{ y: 360 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
{{ record.name || record.nick_name || '-' }}
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<Tag>{{ record.status ?? '-' }}</Tag>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Spin>
|
||||
</Modal>
|
||||
<DoctorModal />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.custom-vertical-tabs .ant-tabs-nav) {
|
||||
width: 140px;
|
||||
}
|
||||
:deep(.custom-vertical-tabs .ant-tabs-tab) {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
:deep(.custom-vertical-tabs .ant-tabs-tab-active) {
|
||||
background-color: var(
|
||||
--ant-primary-color-active-deprecated-f-12,
|
||||
rgba(22, 119, 255, 0.08)
|
||||
);
|
||||
}
|
||||
</style>
|
||||
@@ -57,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}快递公司`" class="w-[30%]">
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}快递公司`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -37,7 +37,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="处方溯源">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="处方溯源">
|
||||
<PrescriptionSourceContent :data="data" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -21,7 +21,7 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
{ field: 'prescription_no', align: 'left', title: '处方订单号' },
|
||||
{ field: 'doctor_info.name', align: 'left', title: '开方医生' },
|
||||
{ field: 'user_patient.name', align: 'left', title: '就诊人名称' },
|
||||
{ field: 'store.name', align: 'left', title: '开方诊所' },
|
||||
{ field: 'store.name', align: 'left', title: '开方诊所', slots: { default: 'store-name' } },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{
|
||||
field: 'is_online',
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Button, Tabs, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import StoreCardModal from '#/components/store-card/StoreCardModal.vue';
|
||||
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
|
||||
|
||||
import { getPrescriptionListApi } from './api';
|
||||
@@ -31,6 +32,16 @@ const gridEvents: VxeGridListeners<any> = {
|
||||
},
|
||||
};
|
||||
|
||||
const [StoreCardModalComp, StoreCardModalApi] = useVbenModal({
|
||||
connectedComponent: StoreCardModal,
|
||||
});
|
||||
|
||||
function openStoreCard(storeId?: number) {
|
||||
if (!storeId) return;
|
||||
StoreCardModalApi.setData({ storeId: Number(storeId) });
|
||||
StoreCardModalApi.open();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions: {
|
||||
@@ -82,6 +93,7 @@ const openPrescriptionSourceModal = (id: number) => {
|
||||
<Page auto-content-height title="处方管理">
|
||||
<PrescriptionDetailModal />
|
||||
<PrescriptionSourceModal />
|
||||
<StoreCardModalComp />
|
||||
<div class="mb-3">
|
||||
<Tabs :active-key="statusTab" @change="onStatusTabChange">
|
||||
<Tabs.TabPane key="0" tab="待审核" />
|
||||
@@ -99,6 +111,18 @@ const openPrescriptionSourceModal = (id: number) => {
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #store-name="{ row }">
|
||||
<Button
|
||||
v-if="row.store?.id || row.store_id"
|
||||
type="link"
|
||||
size="small"
|
||||
class="!px-0"
|
||||
@click="openStoreCard(row.store?.id || row.store_id)"
|
||||
>
|
||||
{{ row.store?.name || '—' }}
|
||||
</Button>
|
||||
<span v-else>{{ row.store?.name || '—' }}</span>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<div>
|
||||
<Tag v-if="row.prescription_type === 1" color="orange">中药</Tag>
|
||||
|
||||
@@ -249,7 +249,7 @@ function prescriptionStatusColor() {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="订单详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="订单详情">
|
||||
<div v-if="data" class="flex flex-col gap-4">
|
||||
<Space>
|
||||
<Button type="primary" size="small" @click="openOrderTrace">
|
||||
|
||||
@@ -65,7 +65,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`订单:【 ${orderNo} 】发货`" class="w-[30%]">
|
||||
<Modal :title="`订单:【 ${orderNo} 】发货`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -54,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
title="退款"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import {getOrderStatusOption} from "#/views/business/order/product-order/api";
|
||||
import {getStoreOption} from "#/views/system/store/api";
|
||||
import { getOrderStatusOption } from '#/views/business/order/product-order/api';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
@@ -18,22 +17,13 @@ export const formOptions: VbenFormProps = {
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
// 门店多选:名称/拼音首拼气泡选择,诊所+药店
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
// showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: `${item.name}【${item.id}】`,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择',
|
||||
placeholder: '输入名称或拼音首拼',
|
||||
},
|
||||
fieldName: 'store_id',
|
||||
defaultValue: [],
|
||||
fieldName: 'store_ids',
|
||||
label: '门店',
|
||||
},
|
||||
{
|
||||
@@ -41,8 +31,6 @@ export const formOptions: VbenFormProps = {
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
// showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
@@ -117,21 +105,16 @@ export const formOptions: VbenFormProps = {
|
||||
{
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
fieldName: 'search_time',
|
||||
label: '时间范围',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
// submitOnChange: true,
|
||||
// submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ import type { QuickDiscountOption } from '#/utils/pricePercentAdjust';
|
||||
import { normalizeQuickOptions } from '#/utils/pricePercentAdjust';
|
||||
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
|
||||
import DoctorCardModal from '#/views/doctor/doctor/components/DoctorCardModal.vue';
|
||||
import StoreCardModal from '#/components/store-card/StoreCardModal.vue';
|
||||
import {
|
||||
WxUserPatientDetailModal,
|
||||
WxUserPatientsModal,
|
||||
@@ -138,8 +139,8 @@ function buildFormOptionsFromRoute(): VbenFormProps {
|
||||
const rawTimeScope = route.query.time_scope;
|
||||
const timeScope = Array.isArray(rawTimeScope) ? rawTimeScope[0] : rawTimeScope;
|
||||
const schema = (formOptions.schema ?? []).map((item) => {
|
||||
if (item.fieldName === 'store_id') {
|
||||
return { ...item, defaultValue: Number(storeId) };
|
||||
if (item.fieldName === 'store_ids') {
|
||||
return { ...item, defaultValue: [Number(storeId)] };
|
||||
}
|
||||
if (item.fieldName === 'search_time') {
|
||||
if (timeScope === 'month') {
|
||||
@@ -256,12 +257,15 @@ const collapseAll = () => {
|
||||
const overviewItems = ref<AnalysisOverviewItem[]>([]);
|
||||
const income = ref(0);
|
||||
const total = ref(0);
|
||||
/** 按当前列表筛选条件刷新顶部销售金额/收益(门店参数后端字段名为 id) */
|
||||
/** 按当前列表筛选条件刷新顶部销售金额/收益(支持门店多选 store_ids) */
|
||||
const saleAmount = (formValues?: Record<string, any>) => {
|
||||
const values = formValues ?? gridApi.formApi.latestSubmissionValues ?? {};
|
||||
saleAmountApi({
|
||||
search_time: values.search_time,
|
||||
id: values.store_id,
|
||||
store_ids: values.store_ids,
|
||||
id: Array.isArray(values.store_ids) && values.store_ids.length === 1
|
||||
? values.store_ids[0]
|
||||
: undefined,
|
||||
}).then((res) => {
|
||||
income.value = res.income;
|
||||
total.value = res.total;
|
||||
@@ -292,6 +296,16 @@ const [DoctorCardModals, DoctorCardModalApi] = useVbenModal({
|
||||
connectedComponent: DoctorCardModal,
|
||||
});
|
||||
|
||||
const [StoreCardModalComp, StoreCardModalApi] = useVbenModal({
|
||||
connectedComponent: StoreCardModal,
|
||||
});
|
||||
|
||||
function openStoreCard(storeId?: number) {
|
||||
if (!storeId) return;
|
||||
StoreCardModalApi.setData({ storeId: Number(storeId) });
|
||||
StoreCardModalApi.open();
|
||||
}
|
||||
|
||||
const [PatientsModal, PatientsModalApi] = useVbenModal({
|
||||
connectedComponent: WxUserPatientsModal,
|
||||
});
|
||||
@@ -818,6 +832,7 @@ const openOrderAmountVerify = () => {
|
||||
</AntdModal>
|
||||
<RefundModal />
|
||||
<DoctorCardModals />
|
||||
<StoreCardModalComp />
|
||||
<PatientsModal />
|
||||
<PatientDetailModal />
|
||||
<ExportModal />
|
||||
@@ -883,7 +898,18 @@ const openOrderAmountVerify = () => {
|
||||
<Tag v-else color="default">线下就诊</Tag>
|
||||
</div>
|
||||
<div class="font-medium">{{ row.order_no }}</div>
|
||||
<div class="text-xs text-gray-500">{{ row.store?.name || '—' }}</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
<Button
|
||||
v-if="row.store?.id"
|
||||
type="link"
|
||||
size="small"
|
||||
class="!h-auto !px-0"
|
||||
@click="openStoreCard(row.store.id)"
|
||||
>
|
||||
{{ row.store?.name || '—' }}
|
||||
</Button>
|
||||
<span v-else>{{ row.store?.name || '—' }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">下单时间: {{ row.created_at || '—' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -54,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
title="退款"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const gridOptions: VxeGridProps<RegisterOrderItem> = {
|
||||
{ field: 'doctor_info.name', title: '开方医生' },
|
||||
{ field: 'doctor_info.depart.name', title: '科室' },
|
||||
{ field: 'user_patient.name', title: '就诊人名称' },
|
||||
{ field: 'store.name', title: '开方诊所' },
|
||||
{ field: 'store.name', title: '开方诊所', slots: { default: 'store-name' } },
|
||||
{ field: 'salesperson', title: '推广员', slots: { default: 'salesperson' } },
|
||||
{ field: 'type', title: '挂号类型', slots: { default: 'type' } },
|
||||
{ field: 'prescription', title: '处方', width: 240, slots: { default: 'prescription'} },
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Button, Image, message, Modal as AntdModal, Tag } from "ant-design-vue"
|
||||
|
||||
import { useVbenVxeGrid } from "#/adapter/vxe-table";
|
||||
import { TableAction } from "#/components/table-action";
|
||||
import StoreCardModal from "#/components/store-card/StoreCardModal.vue";
|
||||
import { simulatePayApi } from "#/views/business/order/api/order-ops";
|
||||
import PrescriptionDetail from "#/views/doctor/doctor-reception/components/PrescriptionDetail.vue";
|
||||
|
||||
@@ -38,6 +39,16 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridEvents
|
||||
});
|
||||
|
||||
const [StoreCardModalComp, StoreCardModalApi] = useVbenModal({
|
||||
connectedComponent: StoreCardModal,
|
||||
});
|
||||
|
||||
function openStoreCard(storeId?: number) {
|
||||
if (!storeId) return;
|
||||
StoreCardModalApi.setData({ storeId: Number(storeId) });
|
||||
StoreCardModalApi.open();
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
onMounted(() => {
|
||||
@@ -108,6 +119,7 @@ function formatRegisterPrice(price: unknown) {
|
||||
<Page auto-content-height title="订单管理">
|
||||
<PrescriptionDetailModal />
|
||||
<RefundModal />
|
||||
<StoreCardModalComp />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction :actions="[]" :drop-down-actions="[]">
|
||||
@@ -119,6 +131,18 @@ function formatRegisterPrice(price: unknown) {
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #store-name="{ row }">
|
||||
<Button
|
||||
v-if="row.store?.id || row.store_id"
|
||||
type="link"
|
||||
size="small"
|
||||
class="!px-0"
|
||||
@click="openStoreCard(row.store?.id || row.store_id)"
|
||||
>
|
||||
{{ row.store?.name || "—" }}
|
||||
</Button>
|
||||
<span v-else>{{ row.store?.name || "—" }}</span>
|
||||
</template>
|
||||
<template #prescription="{ row }">
|
||||
<div v-for="item in row.prescription" :key="item.id">
|
||||
<Button type="link" @click="openPrescriptionDetail(item.id)">
|
||||
|
||||
@@ -80,7 +80,7 @@ const handleChange = (info: { file: UploadFile }) => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="上传Excel">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="上传Excel">
|
||||
<!-- 新增:显示文件选择状态 -->
|
||||
<div v-if="isFileSelected" class="mb-4 p-3 bg-green-50 border border-green-200 rounded">
|
||||
<p class="text-green-700 text-sm">已选择文件:{{ selectedFile?.name }}</p>
|
||||
|
||||
@@ -123,7 +123,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}中药`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -102,7 +102,7 @@ const handleChange = (info: { file: UploadFile }) => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="上传Excel">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="上传Excel">
|
||||
<!-- 显示文件选择状态提示 -->
|
||||
<div
|
||||
v-if="isFileSelected"
|
||||
|
||||
@@ -142,7 +142,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}保健食品`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -73,7 +73,7 @@ const handleChange = (info: { file: UploadFile }) => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="上传Excel">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="上传Excel">
|
||||
<div
|
||||
v-if="isFileSelected"
|
||||
class="mb-4 rounded border border-green-200 bg-green-50 p-3"
|
||||
|
||||
@@ -96,7 +96,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}医疗器械`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -73,7 +73,7 @@ const handleChange = (info: { file: UploadFile }) => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="上传Excel">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="上传Excel">
|
||||
<div
|
||||
v-if="isFileSelected"
|
||||
class="mb-4 rounded border border-green-200 bg-green-50 p-3"
|
||||
|
||||
@@ -96,7 +96,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}非药品`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -66,7 +66,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}产品服务包`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -80,7 +80,7 @@ const handleChange = (info: { file: UploadFile }) => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="上传Excel">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="上传Excel">
|
||||
<!-- 新增:显示文件选择状态 -->
|
||||
<div
|
||||
v-if="isFileSelected"
|
||||
|
||||
@@ -152,7 +152,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}西(中成)药`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -100,7 +100,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="编辑药方" class="w-[80%]">
|
||||
<Modal title="编辑药方" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<div v-if="prescriptionType === 'chinese'">
|
||||
<ChineseDrugEditor
|
||||
ref="chineseDrugEditorRef"
|
||||
|
||||
@@ -178,7 +178,7 @@ function bindPrescriptionTypeChange() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="`${isUpdate ? '编辑' : '新增'}特色方`" class="w-[80%]">
|
||||
<Modal :title="`${isUpdate ? '编辑' : '新增'}特色方`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
<div v-if="prescriptionType === 'chinese'" class="mt-4 border-t pt-4">
|
||||
<ChineseDrugEditor
|
||||
|
||||
@@ -58,7 +58,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}轮播图`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -132,7 +132,7 @@ const exportWarehouseDrugManagementTemplate = () => {
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="importType === 1 ? '上传Excel' : titles" class="w-[30%]">
|
||||
<Modal :title="importType === 1 ? '上传Excel' : titles" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Button
|
||||
v-if="importType === 1"
|
||||
type="link"
|
||||
|
||||
@@ -112,7 +112,7 @@ const exportWarehouseDrugManagementStoreTemplate = () => {
|
||||
<template>
|
||||
<Modal
|
||||
:title="importType === 1 ? '上传Excel' : '批量修改价格'"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Button
|
||||
v-if="importType === 1"
|
||||
|
||||
@@ -126,7 +126,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}仓库药品`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -115,7 +115,7 @@ const exportWarehouseDrugManagementStoreTemplate = () => {
|
||||
<template>
|
||||
<Modal
|
||||
:title="importType === 1 ? '上传Excel' : '批量修改价格'"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Button
|
||||
v-if="importType === 1"
|
||||
|
||||
@@ -126,7 +126,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}仓库药品`"
|
||||
class="w-[30%]"
|
||||
class="w-[80%] md:w-[50%] lg:w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 医生绑定诊所弹窗:使用门店多选搜索(名称/首拼),替代全量 Select
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Select } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import StoreMultiSearch from '#/components/form/components/store-multi-search.vue';
|
||||
import { updateDoctorStoreBindApi } from '#/views/doctor/doctor/api';
|
||||
import { getStoreOption } from '#/views/system/store/api';
|
||||
|
||||
const gridApi = ref();
|
||||
const data = ref();
|
||||
const optioinData = ref();
|
||||
const data = ref<{ su_id?: number; stores?: number[] }>({ stores: [] });
|
||||
/** 打开时回填已绑门店名称 */
|
||||
const initialItems = ref<{ id: number; name: string }[]>([]);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
@@ -21,7 +25,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
onConfirm: async () => {
|
||||
updateDoctorStoreBindApi({
|
||||
doctor_id: data.value.su_id,
|
||||
store_ids: data.value.stores,
|
||||
store_ids: data.value.stores || [],
|
||||
}).then(() => {
|
||||
modalApi.close();
|
||||
message.success('保存成功');
|
||||
@@ -30,41 +34,35 @@ const [Modal, modalApi] = useVbenModal({
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
const { values } = modalApi.getData<Record<string, any>>();
|
||||
gridApi.value = modalApi.getData()?.gridApi ?? null;
|
||||
const { values } = modalApi.getData<Record<string, any>>() || {};
|
||||
if (values) {
|
||||
// 拷贝 values
|
||||
data.value = JSON.parse(JSON.stringify(values));
|
||||
data.value.stores = data.value.stores.map((item: any) => item.store_id);
|
||||
getStoreOptionFun();
|
||||
const copied = JSON.parse(JSON.stringify(values));
|
||||
const storesRaw = copied.stores || [];
|
||||
initialItems.value = storesRaw.map((item: any) => ({
|
||||
id: Number(item.store_id ?? item.id),
|
||||
name: String(item.store?.name || item.name || `门店${item.store_id ?? item.id}`),
|
||||
}));
|
||||
data.value = {
|
||||
su_id: copied.su_id,
|
||||
stores: storesRaw.map((item: any) => Number(item.store_id ?? item.id)),
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const getStoreOptionFun = () => {
|
||||
getStoreOption({}).then((res) => {
|
||||
optioinData.value = res.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Modal class="w-[60%]" title="西(中成)药列表">-->
|
||||
<Modal class="w-[60%]" title="绑定诊所">
|
||||
<div style="width: 60%; margin: 30px auto;">
|
||||
<Select
|
||||
<div style="width: 80%; margin: 30px auto">
|
||||
<StoreMultiSearch
|
||||
v-if="data"
|
||||
v-model:value="data.stores"
|
||||
:options="optioinData"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
:initial-items="initialItems"
|
||||
:store-type="0"
|
||||
placeholder="输入诊所名称或拼音首拼搜索"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import {getStoreOption} from "#/views/system/store/api";
|
||||
import {getDoctorTitleOptionApi, getPharmacistTitleOptionApi} from "#/views/doctor/pharmacist/api";
|
||||
import {getDepartOptionApi} from "#/views/doctor/doctor/api";
|
||||
|
||||
@@ -27,25 +26,12 @@ export const modalFormProps: VbenFormProps = {
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择',
|
||||
model: 'multiple',
|
||||
placeholder: '输入诊所名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: [],
|
||||
fieldName: 'stores',
|
||||
formItemClass: 'col-span-6',
|
||||
label: '诊所',
|
||||
@@ -113,25 +99,12 @@ export const infoModalFormProps: VbenFormProps = {
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择诊所',
|
||||
model: 'multiple',
|
||||
placeholder: '输入诊所名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: [],
|
||||
dependencies: {
|
||||
show: (values) => {
|
||||
return values.id == null;
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 药师绑定诊所弹窗:使用门店多选搜索(名称/首拼),替代全量 Select
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Select } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { getStoreOption } from '#/views/system/store/api';
|
||||
import StoreMultiSearch from '#/components/form/components/store-multi-search.vue';
|
||||
|
||||
import { updatePharmacistStoreBindApi } from '../api';
|
||||
|
||||
const gridApi = ref();
|
||||
const data = ref();
|
||||
const optioinData = ref();
|
||||
const data = ref<{ su_id?: number; store_ids?: number[] }>({ store_ids: [] });
|
||||
/** 打开时回填已绑门店名称 */
|
||||
const initialItems = ref<{ id: number; name: string }[]>([]);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
@@ -22,7 +26,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
onConfirm: async () => {
|
||||
updatePharmacistStoreBindApi({
|
||||
doctor_id: data.value.su_id,
|
||||
store_ids: data.value.store_ids,
|
||||
store_ids: data.value.store_ids || [],
|
||||
}).then(() => {
|
||||
modalApi.close();
|
||||
message.success('保存成功');
|
||||
@@ -31,41 +35,35 @@ const [Modal, modalApi] = useVbenModal({
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
const { values } = modalApi.getData<Record<string, any>>();
|
||||
gridApi.value = modalApi.getData()?.gridApi ?? null;
|
||||
const { values } = modalApi.getData<Record<string, any>>() || {};
|
||||
if (values) {
|
||||
// 拷贝 values
|
||||
data.value = JSON.parse(JSON.stringify(values));
|
||||
data.value.store_ids = data.value.bind_stores.map((item: any) => item.id);
|
||||
getStoreOptionFun();
|
||||
const copied = JSON.parse(JSON.stringify(values));
|
||||
const bindStores = copied.bind_stores || [];
|
||||
initialItems.value = bindStores.map((item: any) => ({
|
||||
id: Number(item.id),
|
||||
name: String(item.name || `门店${item.id}`),
|
||||
}));
|
||||
data.value = {
|
||||
su_id: copied.su_id,
|
||||
store_ids: bindStores.map((item: any) => Number(item.id)),
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const getStoreOptionFun = () => {
|
||||
getStoreOption({}).then((res) => {
|
||||
optioinData.value = res.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Modal class="w-[60%]" title="西(中成)药列表">-->
|
||||
<Modal class="w-[60%]" title="绑定诊所">
|
||||
<div style="width: 60%; margin: 30px auto;">
|
||||
<Select
|
||||
<div style="width: 80%; margin: 30px auto">
|
||||
<StoreMultiSearch
|
||||
v-if="data"
|
||||
v-model:value="data.store_ids"
|
||||
:options="optioinData"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
:initial-items="initialItems"
|
||||
:store-type="0"
|
||||
placeholder="输入诊所名称或拼音首拼搜索"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import {getStoreOption} from "#/views/system/store/api";
|
||||
import {getPharmacistTitleOptionApi} from "#/views/doctor/pharmacist/api";
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
@@ -26,25 +25,12 @@ export const modalFormProps: VbenFormProps = {
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择',
|
||||
model: 'multiple',
|
||||
placeholder: '输入诊所名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: [],
|
||||
fieldName: 'stores',
|
||||
formItemClass: 'col-span-6',
|
||||
label: '诊所',
|
||||
@@ -112,25 +98,12 @@ export const infoModalFormProps: VbenFormProps = {
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择诊所',
|
||||
model: 'multiple',
|
||||
placeholder: '输入诊所名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: [],
|
||||
dependencies: {
|
||||
show: (values) => {
|
||||
return values.id == null;
|
||||
|
||||
@@ -60,6 +60,7 @@ const infoModal = (id) => {
|
||||
<Tag v-if="row.type === 'refund'" color="purple">出账</Tag>
|
||||
<Tag v-else-if="row.type === 'enter'" color="green">入账</Tag>
|
||||
<Tag v-else-if="row.type === 'withdraw'" color="orange">提现</Tag>
|
||||
<Tag v-else-if="row.type === 'charge'" color="red">代付手续费</Tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
|
||||
@@ -60,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="申请提现">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="申请提现">
|
||||
<InvalidatedForm />
|
||||
<SettlementForm />
|
||||
</Modal>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getStoreOption } from '#/views/system/store/api';
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
@@ -8,20 +7,13 @@ export const formOptions: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
// 门店多选:诊所+药店
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '输入名称或拼音首拼',
|
||||
},
|
||||
// defaultValue: 0,
|
||||
fieldName: 'store_id',
|
||||
defaultValue: [],
|
||||
fieldName: 'store_ids',
|
||||
label: '诊所',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="type === 1 ? '作废订单' : '结算订单'" class="w-[30%]">
|
||||
<Modal :title="type === 1 ? '作废订单' : '结算订单'" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<InvalidatedForm v-if="type === 1" />
|
||||
<SettlementForm v-else />
|
||||
</Modal>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
import {getStoreOption} from "#/views/system/store/api";
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
@@ -8,20 +7,13 @@ export const formOptions: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
// 门店多选:诊所+药店
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
api: getStoreOption,
|
||||
placeholder: '输入名称或拼音首拼',
|
||||
},
|
||||
// defaultValue: 0,
|
||||
fieldName: 'store_id',
|
||||
defaultValue: [],
|
||||
fieldName: 'store_ids',
|
||||
label: '诊所',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
DatePicker,
|
||||
Input,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Space,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import StoreMultiSearch from '#/components/form/components/store-multi-search.vue';
|
||||
import {
|
||||
getReconciliationDrugList,
|
||||
getReconciliationDrugOptions,
|
||||
@@ -89,7 +89,8 @@ const activeTab = ref('store');
|
||||
|
||||
/** 整页共用时间 */
|
||||
const searchTime = ref<[Dayjs, Dayjs]>([dayjs().startOf('month'), dayjs()]);
|
||||
const storeName = ref('');
|
||||
/** 对账诊所 Tab 多选门店(诊所+药店) */
|
||||
const storeIds = ref<number[]>([]);
|
||||
const excludeZeroSales = ref(loadExcludeZeroSales());
|
||||
|
||||
type ReconciliationSelectOption = { value: number; label: string };
|
||||
@@ -199,7 +200,7 @@ async function fetchStoreList() {
|
||||
const res = await getReconciliationStoreList({
|
||||
page: 1,
|
||||
pageSize: STORE_LIST_PAGE_SIZE,
|
||||
store_name: storeName.value,
|
||||
store_ids: storeIds.value,
|
||||
search_time: searchTimeParam(),
|
||||
exclude_zero_sales: isStoreClinicUser.value ? 0 : excludeZeroSales.value,
|
||||
});
|
||||
@@ -391,14 +392,11 @@ watch(byOrder, () => {
|
||||
<Tabs v-model:active-key="activeTab" type="card" :destroy-inactive-tab-pane="false">
|
||||
<TabPane key="store" tab="诊所">
|
||||
<div class="mb-3 flex flex-wrap items-end gap-3">
|
||||
<div>
|
||||
<div class="mb-1 text-xs text-muted-foreground">诊所名称</div>
|
||||
<Input
|
||||
v-model:value="storeName"
|
||||
allow-clear
|
||||
placeholder="诊所名称或拼音首拼"
|
||||
class="w-52"
|
||||
@press-enter="queryStore"
|
||||
<div class="min-w-[260px] flex-1">
|
||||
<div class="mb-1 text-xs text-muted-foreground">门店</div>
|
||||
<StoreMultiSearch
|
||||
v-model:value="storeIds"
|
||||
placeholder="输入名称或拼音首拼"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showExcludeZeroSalesFilter">
|
||||
|
||||
@@ -60,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="`提现审核【${type === 1 ? '拒绝' : '通过'}】`" class="w-[30%]">
|
||||
<Modal :title="`提现审核【${type === 1 ? '拒绝' : '通过'}】`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<WithdrawalAuditForm />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -57,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="编辑银行卡账户">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="编辑银行卡账户">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -61,7 +61,7 @@ const setAmount = (amount: number) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[30%]" title="申请提现">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="申请提现">
|
||||
<Form />
|
||||
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<Button type="primary" @click="setAmount(1000)"> 1000 </Button>
|
||||
|
||||
@@ -39,7 +39,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="Api访问日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="Api访问日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 2, xs: 2 }"
|
||||
@@ -50,6 +50,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<span>{{ getApiOpLogOperatorLabel(data) }}</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="URL">{{ data.url }}</Descriptions.Item>
|
||||
<Descriptions.Item label="请求方式">
|
||||
<span v-if="data.method === 1">GET</span>
|
||||
<span v-else-if="data.method === 2">POST</span>
|
||||
<span v-else>未知</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="控制器">
|
||||
{{ data.controller }}
|
||||
</Descriptions.Item>
|
||||
|
||||
@@ -29,10 +29,26 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'platform_type',
|
||||
label: '接口来源',
|
||||
},
|
||||
{
|
||||
// 请求方式:与后端 xk_api_op_log.method(1=GET 2=POST)一致
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: [
|
||||
{ label: 'GET', value: 1 },
|
||||
{ label: 'POST', value: 2 },
|
||||
],
|
||||
placeholder: '全部',
|
||||
},
|
||||
fieldName: 'method',
|
||||
label: '请求方式',
|
||||
},
|
||||
{
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
// defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
fieldName: 'search_time',
|
||||
|
||||
@@ -32,6 +32,12 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
slots: { default: 'admin' },
|
||||
},
|
||||
{ field: 'url', title: '访问路由' },
|
||||
{
|
||||
field: 'method',
|
||||
title: '请求方式',
|
||||
slots: { default: 'method' },
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
field: 'platform_type',
|
||||
title: '接口来源',
|
||||
|
||||
@@ -75,6 +75,11 @@ const showModal = (data = {}, isUpdate = false) => {
|
||||
<template #platform_type="{ row }">
|
||||
<span>{{ getApiOpLogPlatformLabel(row.platform_type) }}</span>
|
||||
</template>
|
||||
<template #method="{ row }">
|
||||
<Tag v-if="row.method === 1" color="blue">GET</Tag>
|
||||
<Tag v-else-if="row.method === 2" color="green">POST</Tag>
|
||||
<Tag v-else color="default">未知</Tag>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #equipment="{ row }">
|
||||
<Icon :icon="getIcon(row.equipment)" :size="20" />
|
||||
|
||||
@@ -55,7 +55,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="监管回调详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="监管回调详情">
|
||||
<Spin :spinning="loading">
|
||||
<Descriptions bordered :column="2" class="mb-4">
|
||||
<Descriptions.Item label="记录ID">{{ data.id }}</Descriptions.Item>
|
||||
|
||||
@@ -30,7 +30,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="分账日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="分账日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||||
|
||||
@@ -32,7 +32,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="Api访问日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="Api访问日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||||
|
||||
@@ -30,7 +30,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="操作日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="操作日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||||
|
||||
@@ -30,7 +30,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="订单日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="订单日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||||
|
||||
@@ -30,7 +30,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="处方日志详情">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="处方日志详情">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Descriptions
|
||||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||||
|
||||
@@ -67,7 +67,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal title="拒绝审核" class="w-[30%]">
|
||||
<Modal title="拒绝审核" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -37,7 +37,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="处方溯源">
|
||||
<Modal class="w-[80%] md:w-[50%] lg:w-[30%]" title="处方溯源">
|
||||
<PrescriptionSourceContent :data="data" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -51,7 +51,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`订单:【 ${orderNo} 】发货`" class="w-[30%]">
|
||||
<Modal :title="`订单:【 ${orderNo} 】发货`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -122,9 +122,9 @@ function openDetail(row: Record<string, any>, scrollToLogistics = false) {
|
||||
onShip:
|
||||
Number(row?.warehouse_is_send ?? 0) === 0 || Number(row?.status) === 1
|
||||
? () => {
|
||||
detailModalApi.close();
|
||||
wareSend(row);
|
||||
}
|
||||
detailModalApi.close();
|
||||
wareSend(row);
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
detailModalApi.open();
|
||||
@@ -139,7 +139,7 @@ function productRows(row: Record<string, any>) {
|
||||
function qtyText(p: Record<string, any>) {
|
||||
const n = p.number ?? '-';
|
||||
if (p.dosage != null && p.dosage !== '' && Number(p.dosage) !== 1) {
|
||||
return `${n}×${p.dosage}`;
|
||||
return `${n}`;
|
||||
}
|
||||
return String(n);
|
||||
}
|
||||
@@ -156,159 +156,139 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="我的订单">
|
||||
<FormModal />
|
||||
<DetailModal />
|
||||
<div class="flex flex-col gap-4">
|
||||
<Tabs
|
||||
:active-key="statusTab"
|
||||
type="card"
|
||||
@change="handleTabChange"
|
||||
>
|
||||
<Tabs.TabPane key="1" tab="待发货" />
|
||||
<Tabs.TabPane key="2" tab="待收货" />
|
||||
<Tabs.TabPane key="7" tab="确认收货" />
|
||||
</Tabs>
|
||||
<Page auto-content-height class="bg-[#f7f8fa] dark:bg-[#0a0a0a] transition-colors duration-300" title="我的订单">
|
||||
<FormModal/>
|
||||
<DetailModal/>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<Input
|
||||
v-model:value="orderNo"
|
||||
allow-clear
|
||||
class="w-64"
|
||||
placeholder="输入订单号"
|
||||
@press-enter="handleSearch"
|
||||
/>
|
||||
<Button type="primary" @click="handleSearch">查询</Button>
|
||||
<span class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
当前:{{ tabLabel }}
|
||||
</span>
|
||||
<div class="flex flex-col gap-6 p-2 md:p-4">
|
||||
<!-- 顶部操作栏 -->
|
||||
<div class="flex flex-col gap-4 rounded-2xl bg-white dark:bg-[#141414] px-6 py-4 shadow-sm dark:shadow-none dark:border dark:border-[#303030] md:flex-row md:items-center md:justify-between transition-colors duration-300">
|
||||
<Tabs :active-key="statusTab" :tab-bar-style="{ margin: 0, border: 'none' }" @change="handleTabChange" size="large">
|
||||
<Tabs.TabPane key="1" tab="待发货"/>
|
||||
<Tabs.TabPane key="2" tab="待收货"/>
|
||||
<Tabs.TabPane key="7" tab="确认收货"/>
|
||||
</Tabs>
|
||||
|
||||
<Input.Search @search="handleSearch" allow-clear class="w-full md:w-80" enter-button placeholder="请输入订单号快捷查询" size="large" v-model:value="orderNo"/>
|
||||
</div>
|
||||
|
||||
<Spin :spinning="loading">
|
||||
<div
|
||||
v-if="list.length"
|
||||
class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"
|
||||
class="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3"
|
||||
>
|
||||
<Card
|
||||
v-for="row in list"
|
||||
:key="row.id"
|
||||
class="overflow-hidden"
|
||||
size="small"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-medium">{{ row.order_no || '—' }}</span>
|
||||
<Tag :color="statusMeta[row.status]?.color || 'default'">
|
||||
{{ statusMeta[row.status]?.text || row.status }}
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<span class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
{{ row.created_at || '' }}
|
||||
</span>
|
||||
</template>
|
||||
<Card :body-style="{ padding: 0 }" :key="row.id" class="overflow-hidden rounded-2xl border-transparent dark:border-[#303030] bg-white dark:bg-[#141414] shadow-sm dark:shadow-none transition-all hover:-translate-y-1 hover:shadow-md dark:hover:border-gray-500" v-for="row in list">
|
||||
|
||||
<div class="space-y-2 text-sm">
|
||||
<div>
|
||||
<span class="text-[hsl(var(--muted-foreground))]">收件人:</span>
|
||||
{{ row.express_name || '—' }}
|
||||
<span class="ml-2">{{ row.express_mobile || '' }}</span>
|
||||
<!-- 卡片 Header -->
|
||||
<div class="flex items-center justify-between border-b border-gray-100 dark:border-[#2a2a2a] px-5 py-4 transition-colors duration-300">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">订单号</span>
|
||||
<span class="text-base font-semibold text-gray-800 dark:text-gray-200">{{ row.order_no || '—' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[hsl(var(--muted-foreground))]">地址:</span>
|
||||
{{ addressText(row) }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-[hsl(var(--muted-foreground))]">
|
||||
本仓商品:
|
||||
</div>
|
||||
<Tag :color="statusMeta[row.status]?.color || 'default'" class="m-0 rounded-full border-none px-3 py-1 font-medium shadow-sm dark:shadow-none">
|
||||
{{ statusMeta[row.status]?.text || row.status }}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<!-- 商品展示区 -->
|
||||
<div class="bg-gray-50/60 dark:bg-[#1a1a1a] px-5 py-4 transition-colors duration-300">
|
||||
<div v-if="productRows(row).length" class="flex flex-col gap-4">
|
||||
<div
|
||||
v-if="productRows(row).length"
|
||||
class="flex flex-col gap-2"
|
||||
v-for="p in productRows(row)"
|
||||
:key="p.id"
|
||||
class="flex items-start gap-3"
|
||||
>
|
||||
<div
|
||||
v-for="p in productRows(row)"
|
||||
:key="p.id"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<Image
|
||||
:src="p.drug?.image || p.drug_image || ''"
|
||||
:width="40"
|
||||
:height="40"
|
||||
class="shrink-0 rounded object-cover"
|
||||
fallback="/img/empty.png"
|
||||
/>
|
||||
<div class="min-w-0 flex-1 text-xs">
|
||||
<div class="truncate font-medium">
|
||||
{{ p.drug_name || p.drug?.drug_name || '-' }}
|
||||
</div>
|
||||
<div class="text-[hsl(var(--muted-foreground))]">
|
||||
规格:{{ p.drug?.specification || '-' }}
|
||||
· 数量:{{ qtyText(p) }}
|
||||
</div>
|
||||
<Image :height="68" :src="p.drug?.image || p.drug_image || ''" :width="68" class="shrink-0 rounded-xl border border-gray-100 dark:border-transparent bg-white dark:bg-[#2a2a2a] object-cover p-1 shadow-sm dark:shadow-none" fallback="/img/empty.png"/>
|
||||
<div class="min-w-0 flex-1 pt-1">
|
||||
<div class="line-clamp-2 text-sm font-medium text-gray-800 dark:text-gray-200 transition-colors hover:text-blue-600 dark:hover:text-blue-400 cursor-pointer">
|
||||
{{ p.drug_name || p.drug?.drug_name || '-' }}
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-between">
|
||||
<span class="rounded bg-white dark:bg-[#2a2a2a] px-2 py-0.5 text-xs text-gray-500 dark:text-gray-400 shadow-sm dark:shadow-none border border-gray-100 dark:border-transparent">
|
||||
{{ p.drug?.specification || '默认规格' }}
|
||||
</span>
|
||||
<span class="text-base font-bold text-gray-800 dark:text-gray-200">x{{ qtyText(p) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[hsl(var(--muted-foreground))]">物流:</span>
|
||||
<div v-else class="text-sm text-gray-400 dark:text-gray-600 py-2">— 该订单暂无商品明细 —</div>
|
||||
</div>
|
||||
|
||||
<!-- 配送信息区 -->
|
||||
<div class="px-5 py-4 text-sm space-y-3 transition-colors duration-300">
|
||||
<div class="flex items-start">
|
||||
<span class="w-16 shrink-0 text-gray-400 dark:text-gray-500">收件人</span>
|
||||
<div class="flex-1 text-gray-800 dark:text-gray-200">
|
||||
<span class="font-medium mr-2">{{ row.express_name || '—' }}</span>
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ row.express_mobile || '' }}</span>
|
||||
<div class="mt-1 text-gray-500 dark:text-gray-400 line-clamp-2">{{ addressText(row) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="w-16 shrink-0 text-gray-400 dark:text-gray-500">物流</span>
|
||||
<span
|
||||
:class="
|
||||
row.logistics_summary === '未发货'
|
||||
? 'text-[hsl(var(--muted-foreground))]'
|
||||
: ''
|
||||
"
|
||||
class="flex-1 truncate"
|
||||
:class="row.logistics_summary === '未发货' ? 'text-gray-400 dark:text-gray-500' : 'text-blue-500 dark:text-blue-400 cursor-pointer hover:underline'"
|
||||
>
|
||||
{{ row.logistics_summary || '未发货' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #actions>
|
||||
<!-- 底部操作区 (已修复 v-if 报错问题) -->
|
||||
<div class="flex items-center justify-between border-t border-gray-50 dark:border-[#2a2a2a] px-5 py-3 transition-colors duration-300">
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">{{ row.created_at || '' }}</span>
|
||||
<Space>
|
||||
<Button size="small" type="link" @click="openDetail(row, false)">
|
||||
详情
|
||||
<Button @click="openDetail(row, false)" shape="round" size="middle">
|
||||
订单详情
|
||||
</Button>
|
||||
<!-- 修复了此处的 v-if 闭合错误 -->
|
||||
<Button
|
||||
v-if="
|
||||
Number(row.is_send) === 1 ||
|
||||
Number(row.status) >= 2 ||
|
||||
!!row.waybill_no
|
||||
"
|
||||
size="small"
|
||||
type="link"
|
||||
v-if="Number(row.is_send) === 1 || Number(row.status) >= 2 || !!row.waybill_no"
|
||||
shape="round"
|
||||
size="middle"
|
||||
@click="openDetail(row, true)"
|
||||
>
|
||||
物流
|
||||
查看物流
|
||||
</Button>
|
||||
<Button
|
||||
v-if="Number(row.warehouse_is_send ?? 0) === 0"
|
||||
size="small"
|
||||
type="link"
|
||||
class="shadow-md shadow-blue-500/20 dark:shadow-none"
|
||||
shape="round"
|
||||
size="middle"
|
||||
type="primary"
|
||||
@click="wareSend(row)"
|
||||
>
|
||||
发货
|
||||
立即发货
|
||||
</Button>
|
||||
</Space>
|
||||
</template>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<Empty v-else class="py-16" :description="`${tabLabel}暂无订单`" />
|
||||
|
||||
<Empty :description="`当前暂无${tabLabel}订单`" :image="Empty.PRESENTED_IMAGE_SIMPLE" class="my-20" v-else/>
|
||||
</Spin>
|
||||
|
||||
<div v-if="pager.total > 0" class="flex justify-end">
|
||||
<Pagination
|
||||
v-model:current="pager.currentPage"
|
||||
v-model:page-size="pager.pageSize"
|
||||
:total="pager.total"
|
||||
show-size-changer
|
||||
:page-size-options="['12', '24', '48']"
|
||||
@change="loadList"
|
||||
@show-size-change="loadList"
|
||||
/>
|
||||
<!-- 分页区域 -->
|
||||
<div v-if="pager.total > 0" class="flex justify-end pb-8">
|
||||
<Pagination :page-size-options="['12', '24', '48']" :total="pager.total" @change="loadList" @show-size-change="loadList" show-quick-jumper show-size-changer v-model:current="pager.currentPage" v-model:page-size="pager.pageSize"/>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.ant-tabs-nav::before) {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
:deep(.ant-tabs-tab) {
|
||||
@apply dark:text-gray-400;
|
||||
}
|
||||
:deep(.ant-tabs-tab-active) {
|
||||
@apply dark:!text-blue-400;
|
||||
}
|
||||
:deep(.ant-card-body) {
|
||||
@apply dark:bg-[#141414];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getDepartOptionApi } from '#/views/doctor/doctor/api';
|
||||
import { getDoctorTitleOptionApi } from '#/views/doctor/pharmacist/api';
|
||||
import { getStoreOption } from '#/views/system/store/api';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12',
|
||||
@@ -25,21 +24,12 @@ export const modalFormProps: VbenFormProps = {
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: (input: string, option: any) =>
|
||||
option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
|
||||
showSearch: true,
|
||||
afterFetch: (data: { id: number; name: string }[]) =>
|
||||
data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
api: getStoreOption,
|
||||
placeholder: '请选择诊所',
|
||||
mode: 'multiple',
|
||||
placeholder: '输入诊所名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: [],
|
||||
fieldName: 'stores',
|
||||
formItemClass: 'col-span-12',
|
||||
label: '关联诊所',
|
||||
|
||||
@@ -7,24 +7,15 @@ export const formOptions: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
// 药店名称搜索字段
|
||||
component: 'VbenInput',
|
||||
// 药店多选:仅搜 type=1,名称/首拼合一
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
placeholder: '输入名称',
|
||||
placeholder: '输入名称或拼音首拼',
|
||||
storeType: 1,
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'name',
|
||||
label: '药店名称', // 已从"诊所名称"改为"药店名称"
|
||||
},
|
||||
{
|
||||
// 药店首字母搜索字段
|
||||
component: 'VbenInput',
|
||||
componentProps: {
|
||||
placeholder: '输入名称',
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'shouzimu',
|
||||
label: '药店首字母', // 已从"诊所首字母"改为"药店首字母"
|
||||
defaultValue: [],
|
||||
fieldName: 'store_ids',
|
||||
label: '药店',
|
||||
},
|
||||
{
|
||||
// 联系人手机号搜索字段
|
||||
@@ -36,7 +27,6 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'mobile',
|
||||
label: '联系人手机号',
|
||||
},
|
||||
// 注意:已移除类型筛选字段,因为药店管理页面固定显示type=1的数据
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
|
||||
@@ -37,6 +37,7 @@ import StoreConfigTogglesCell from '#/views/system/store/components/cells/StoreC
|
||||
import BindConsultationModal from '#/views/system/store/components/BindConsultationModal.vue';
|
||||
import DrugPriceModal from '#/views/system/store/components/DrugPriceModal.vue';
|
||||
import StoreExternalFieldModal from '#/views/system/store/components/StoreExternalFieldModal.vue';
|
||||
import StoreCardModal from '#/components/store-card/StoreCardModal.vue';
|
||||
import BankCardReportModal from '#/views/system/store-bank-card/components/BankCardReportModal.vue';
|
||||
import BankCardStatusModal from '#/views/system/store-bank-card/components/BankCardStatusModal.vue';
|
||||
import BankCardStoreEditModal from '#/views/system/store-bank-card/components/BankCardStoreEditModal.vue';
|
||||
@@ -97,6 +98,16 @@ const [QrCodePreviewModal, QrCodePreviewApi] = useVbenModal({
|
||||
connectedComponent: QrCodePreview,
|
||||
});
|
||||
|
||||
const [StoreCardModalComp, StoreCardModalApi] = useVbenModal({
|
||||
connectedComponent: StoreCardModal,
|
||||
});
|
||||
|
||||
function openStoreCard(row: Record<string, any>) {
|
||||
if (!row?.id) return;
|
||||
StoreCardModalApi.setData({ storeId: Number(row.id) });
|
||||
StoreCardModalApi.open();
|
||||
}
|
||||
|
||||
// 打开二维码预览弹窗
|
||||
const openQrCodeModal = (url, title, address) => {
|
||||
QrCodePreviewApi.setData({
|
||||
@@ -376,6 +387,7 @@ const batchSyncDrugPrice = () => {
|
||||
<BankCardReportModalComponent />
|
||||
<BankCardStatusModalComponent />
|
||||
<BankCardStoreEditModalComponent />
|
||||
<StoreCardModalComp />
|
||||
<QrCodePreviewModal />
|
||||
<DrugPriceModalComponent />
|
||||
<BindConsultationModalComponent />
|
||||
@@ -436,6 +448,7 @@ const batchSyncDrugPrice = () => {
|
||||
variant="pharmacy"
|
||||
:on-copy="copyText"
|
||||
:on-edit-external-field="showStoreExternalFieldModal"
|
||||
:on-open-store="openStoreCard"
|
||||
/>
|
||||
</template>
|
||||
<template #store_config_1="{ row }">
|
||||
|
||||
@@ -54,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}平台`" class="w-[30%]">
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}平台`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -54,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}角色`" class="w-[30%]">
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}角色`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -56,6 +56,19 @@ export async function getStoreOption(data: any) {
|
||||
return requestClient.get<any>(`${prefix}option`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店关键词搜索(名称/拼音首拼),供多选搜索气泡列表
|
||||
*/
|
||||
export async function searchStoreOption(params: {
|
||||
keyword: string;
|
||||
type?: 0 | 1;
|
||||
limit?: number;
|
||||
}) {
|
||||
return requestClient.get<{
|
||||
items: { id: number; name: string; type: number; shouzimu: string }[];
|
||||
}>(`${prefix}search-option`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取诊所详情
|
||||
* @param id
|
||||
@@ -64,6 +77,26 @@ export async function getStoreInfo(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店详情卡片(概览 + 统计 + 医生团队)
|
||||
*/
|
||||
export async function getStoreCardApi(params: {
|
||||
id: number;
|
||||
search_time?: [string, string];
|
||||
}) {
|
||||
return requestClient.get<any>(`${prefix}card`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店详情卡片统计刷新
|
||||
*/
|
||||
export async function getStoreCardStatsApi(params: {
|
||||
id: number;
|
||||
search_time?: [string, string];
|
||||
}) {
|
||||
return requestClient.get<any>(`${prefix}card-stats`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增诊所
|
||||
* @param data
|
||||
|
||||
@@ -87,7 +87,7 @@ const rowImageSrc = (row: DrugItem) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="修改药品价格" class="w-[80%]">
|
||||
<Modal title="修改药品价格" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<div>
|
||||
<Tabs v-model:activeKey="activeTab">
|
||||
<TabPane
|
||||
|
||||
@@ -12,6 +12,8 @@ defineProps<{
|
||||
field: 'erp_id' | 'mes_id',
|
||||
label: string,
|
||||
) => void;
|
||||
/** 点击门店名称打开详情卡片 */
|
||||
onOpenStore?: (row: Record<string, any>) => void;
|
||||
}>();
|
||||
|
||||
function getClinicTypeText(clinicType: number) {
|
||||
@@ -23,7 +25,15 @@ function getClinicTypeText(clinicType: number) {
|
||||
|
||||
<template>
|
||||
<div class="store-basic-info">
|
||||
<div class="store-basic-info__name">{{ row.name || '-' }}</div>
|
||||
<Button
|
||||
v-if="onOpenStore"
|
||||
type="link"
|
||||
class="store-basic-info__name !h-auto !px-0 !py-0 text-left"
|
||||
@click="onOpenStore(row)"
|
||||
>
|
||||
{{ row.name || '-' }}
|
||||
</Button>
|
||||
<div v-else class="store-basic-info__name">{{ row.name || '-' }}</div>
|
||||
<div class="store-basic-info__grid">
|
||||
<div class="info-item">
|
||||
<span class="info-item__label">ID</span>
|
||||
|
||||
@@ -7,24 +7,15 @@ export const formOptions: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
// 诊所名称搜索字段
|
||||
component: 'VbenInput',
|
||||
// 诊所多选:仅搜 type=0,名称/首拼合一
|
||||
component: 'StoreMultiSearch',
|
||||
componentProps: {
|
||||
placeholder: '输入名称',
|
||||
placeholder: '输入名称或拼音首拼',
|
||||
storeType: 0,
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'name',
|
||||
label: '诊所名称',
|
||||
},
|
||||
{
|
||||
// 诊所首字母搜索字段
|
||||
component: 'VbenInput',
|
||||
componentProps: {
|
||||
placeholder: '输入名称',
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'shouzimu',
|
||||
label: '诊所首字母',
|
||||
defaultValue: [],
|
||||
fieldName: 'store_ids',
|
||||
label: '诊所',
|
||||
},
|
||||
{
|
||||
// 联系人手机号搜索字段
|
||||
@@ -36,16 +27,6 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'mobile',
|
||||
label: '联系人手机号',
|
||||
},
|
||||
// 注意:已移除类型筛选字段,因为诊所管理页面固定显示type=0的数据
|
||||
// {
|
||||
// component: 'RangePicker',
|
||||
// componentProps: {
|
||||
// format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
// },
|
||||
// // defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
// fieldName: 'search_time',
|
||||
// label: '时间范围',
|
||||
// },
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
|
||||
@@ -39,6 +39,7 @@ import BindConsultationModal from './components/BindConsultationModal.vue';
|
||||
import BindSpecialPrescriptionDoctorModal from './components/BindSpecialPrescriptionDoctorModal.vue';
|
||||
import StoreExternalFieldModal from './components/StoreExternalFieldModal.vue';
|
||||
import SalespersonCommissionDrawer from './components/SalespersonCommissionDrawer.vue';
|
||||
import StoreCardModal from '#/components/store-card/StoreCardModal.vue';
|
||||
import BankCardReportModal from '#/views/system/store-bank-card/components/BankCardReportModal.vue';
|
||||
import BankCardStatusModal from '#/views/system/store-bank-card/components/BankCardStatusModal.vue';
|
||||
import BankCardStoreEditModal from '#/views/system/store-bank-card/components/BankCardStoreEditModal.vue';
|
||||
@@ -89,6 +90,17 @@ const [QrCodePreviewModal, QrCodePreviewApi] = useVbenModal({
|
||||
connectedComponent: QrCodePreview,
|
||||
});
|
||||
|
||||
const [StoreCardModalComp, StoreCardModalApi] = useVbenModal({
|
||||
connectedComponent: StoreCardModal,
|
||||
});
|
||||
|
||||
/** 打开门店详情卡片 */
|
||||
function openStoreCard(row: Record<string, any>) {
|
||||
if (!row?.id) return;
|
||||
StoreCardModalApi.setData({ storeId: Number(row.id) });
|
||||
StoreCardModalApi.open();
|
||||
}
|
||||
|
||||
const openQrCodeModal = (url, title, address) => {
|
||||
QrCodePreviewApi.setData({
|
||||
// 表单值
|
||||
@@ -403,6 +415,7 @@ const handleSwitchClinicType = (row: any) => {
|
||||
<BankCardReportModalComponent />
|
||||
<BankCardStatusModalComponent />
|
||||
<BankCardStoreEditModalComponent />
|
||||
<StoreCardModalComp />
|
||||
<QrCodePreviewModal />
|
||||
<DrugPriceModalComponent />
|
||||
<BindConsultationModalComponent />
|
||||
@@ -466,6 +479,7 @@ const handleSwitchClinicType = (row: any) => {
|
||||
variant="clinic"
|
||||
:on-copy="copyText"
|
||||
:on-edit-external-field="showStoreExternalFieldModal"
|
||||
:on-open-store="openStoreCard"
|
||||
/>
|
||||
</template>
|
||||
<template #store_config_1="{ row }">
|
||||
|
||||
@@ -54,7 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}供应商`" class="w-[30%]">
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}供应商`" class="w-[80%] md:w-[50%] lg:w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user