| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783 |
- <script setup lang="ts">
- import { ref, computed, watch, reactive, onMounted } from 'vue';
- import { message } from 'ant-design-vue';
- import dayjs, { type Dayjs } from 'dayjs';
- import type { VxeGridInstance, VxeGridProps } from 'vxe-table';
- import {
- todayOrderMethod, pendingOrderMethod, allPieOrderMethod, todayPhysicalOrderMethod, pendingPhysicalOrderMethod, allPhysicalOrderMethod,
- getOrderLiaisonListMethod, confirmPieOrderMethod, confirmPhysicalOrderMethod, getPieOrderCountMethod
- } from '@/request/api/order.api';
- import type { OrderModel, OrderQuery, OrderLiaisonListModel } from '@/model/order.model';
- import DateTimePicker from './DateTimePicker.vue';
- defineOptions({
- name: 'DispatchOrderPanel',
- });
- interface Institution extends OrderLiaisonListModel {
- address: string;
- }
- const props = defineProps<{
- orderType: 'offline' | 'physical'; // 线下服务 | 实体商品
- }>();
- // 当前选中的子tab
- const activeSubTab = ref<'pending' | 'today' | 'all'>('pending');
- // 订单列表数据
- const orderList = ref<OrderModel[]>([]);
- // 可派单机构列表
- const institutionList = ref<Institution[]>([]);
- // 当前选中的订单
- const selectedOrderId = ref<number | null>(null);
- // 订单计数
- const orderCounts = ref({
- offlineCount: 0, // 线下服务订单总和
- allPieOfflineOrderCount: 0, // 线下服务——全部指派订单数量
- pendPieOfflineOrderCount: 0, // 线下服务——待指派订单数量
- todayPieOfflineOrderCount: 0, // 线下服务——今日指派订单数量
- onlineCount: 0, // 实体商品数量订单总和
- allPieOnlineOrderCount: 0, // 实体商品——全部指派订单数量
- pendPieOnlineOrderCount: 0, // 实体商品——待指派订单数量
- todayPieOnlineOrderCount: 0, // 实体商品——今日指派订单数量
- });
- // 当前订单的分配机构输入值
- const assignedInstitutionMap = ref<Record<number, string>>({});
- // 日期时间选择弹窗相关
- const dateTimePickerVisible = ref(false);
- const currentPickerOrderId = ref<number | null>(null);
- const pickerType = ref<'date' | 'time'>('date'); // 选择器类型:日期或时间
- const isFromTimeButton = ref(false); // 是否从时间按钮进入
- // 加载状态
- const loading = ref(false);
- const institutionLoading = ref(false);
- // 订单列表分页配置
- const orderPageConfig = reactive({
- currentPage: 1,
- pageSize: 10,
- total: 0,
- });
- // 机构列表分页配置
- const institutionPageConfig = reactive({
- currentPage: 1,
- pageSize: 10,
- total: 0,
- });
- // 搜索条件
- const searchForm = reactive({
- customerName: '',
- contactPhone: '',
- orderTypes: [] as string[], // 线下服务: 'booked' | 'verified' | 实体商品: 'pendingShip' | 'shipped'
- });
- // 获取可派单机构名称列表
- const availableInstitutionNames = computed(() => {
- return allInstitutionData.value.map(inst => inst.name);
- });
- // 获取订单的完整地址
- function getOrderAddress(order: OrderModel): string {
- const addressParts = [
- order.provinceName,
- order.cityName,
- order.areaName,
- order.detailAddress,
- ].filter(Boolean);
- return addressParts.join('');
- }
- function formatCustomerInfo(order: OrderModel): string {
- const parts: string[] = [];
- if (order.liaison) {
- parts.push(order.liaison);
- }
- if (order.phone) {
- parts.push(order.phone);
- }
- const address = getOrderAddress(order);
- if (address) {
- parts.push(address);
- }
- return parts.join(',');
- }
- // 计算结束时间
- function calculateEndTime(order: OrderModel): string | null {
- if (!order.arrangeTime) return null;
- // 如果没有服务时长,无法计算结束时间
- if (!order.offlineDuration) return null;
- // 根据开始时间和服务时长计算结束时间
- const durationMinutes = parseDurationToMinutes(order.offlineDuration);
- if (durationMinutes > 0) {
- const start = dayjs(order.arrangeTime, 'HH:mm');
- const end = start.add(durationMinutes, 'minute');
- return end.format('HH:mm');
- }
- return null;
- }
- // 判断订单是否已核销(线下服务)
- function isOrderVerified(order: OrderModel): boolean {
- return props.orderType === 'offline' && order.type === 3;
- }
- // 判断订单是否已发货(实体商品)
- function isOrderShipped(order: OrderModel): boolean {
- return props.orderType === 'physical' && order.type === 3;
- }
- // 右侧机构表格配置
- const gridRef = ref<VxeGridInstance<Institution>>();
- const gridOptions = reactive<VxeGridProps<Institution>>({
- border: true,
- autoResize: true,
- height: '100%',
- scrollY: { enabled: true },
- columns: [
- {
- field: 'action',
- title: '操作',
- width: 150,
- align: 'center',
- showOverflow: false,
- cellRender: {
- name: 'VxeButton',
- props: {
- type: 'primary',
- size: 'large',
- content: '指派',
- },
- events: {
- click({ row }: { row: Institution }) {
- handleAssignInstitution(row.name);
- },
- },
- },
- },
- { field: 'name', title: '机构名称' },
- { field: 'detailAddress', title: '地址' },
- { field: 'phone', title: '联系电话' },
- { field: 'todayOrderQuantity', title: '当日线下订单数' },
- ],
- data: [],
- pagerConfig: {
- enabled: false,
- },
- });
- // 初始化
- onMounted(async () => {
- await loadOrderCounts();
- await loadOrderList();
- if (orderList.value.length > 0) {
- selectedOrderId.value = getOrderId(orderList.value[0]);
- if (activeSubTab.value !== 'pending') {
- orderList.value.forEach(order => {
- if (order.conditioningProgramSupplierName) {
- const orderId = getOrderId(order);
- assignedInstitutionMap.value[orderId] = order.conditioningProgramSupplierName;
- }
- });
- }
- // 选中订单后再加载机构列表
- await loadInstitutionList();
- }
- });
- // 监听子tab切换
- watch(activeSubTab, async () => {
- orderPageConfig.currentPage = 1;
- allInstitutionData.value = [];
- institutionList.value = [];
- gridRef.value?.loadData([]);
- await loadOrderCounts();
- await loadOrderList();
- if (orderList.value.length > 0) {
- selectedOrderId.value = getOrderId(orderList.value[0]);
- if (activeSubTab.value !== 'pending') {
- orderList.value.forEach(order => {
- if (order.conditioningProgramSupplierName) {
- const orderId = getOrderId(order);
- assignedInstitutionMap.value[orderId] = order.conditioningProgramSupplierName;
- }
- });
- } else {
- orderList.value.forEach(order => {
- const orderId = getOrderId(order);
- assignedInstitutionMap.value[orderId] = '';
- });
- }
- await loadInstitutionList();
- } else {
- selectedOrderId.value = null;
- allInstitutionData.value = [];
- institutionList.value = [];
- gridRef.value?.loadData([]);
- }
- });
- // 监听订单类型切换
- watch(() => props.orderType, async () => {
- activeSubTab.value = 'pending';
- selectedOrderId.value = null;
- assignedInstitutionMap.value = {};
- orderPageConfig.currentPage = 1;
- searchForm.customerName = '';
- searchForm.contactPhone = '';
- searchForm.orderTypes = [];
- allInstitutionData.value = [];
- institutionList.value = [];
- gridRef.value?.loadData([]);
- await loadOrderCounts();
- await loadOrderList();
- if (orderList.value.length > 0) {
- const firstOrder = orderList.value[0];
- const orderId = firstOrder.id ?? firstOrder.patientConditioningProgramId;
- selectedOrderId.value = orderId;
- const verifyOrder = orderList.value.find(order => {
- const orderId = order.id ?? order.patientConditioningProgramId;
- return orderId === selectedOrderId.value;
- });
- if (!verifyOrder) {
- console.error('错误:选中的订单ID不在订单列表中!', {
- selectedOrderId: selectedOrderId.value,
- orderListIds: orderList.value.map(o => o.id ?? o.patientConditioningProgramId),
- });
- }
- if (activeSubTab.value !== 'pending') {
- orderList.value.forEach(order => {
- if (order.conditioningProgramSupplierName) {
- const orderId = getOrderId(order);
- assignedInstitutionMap.value[orderId] = order.conditioningProgramSupplierName;
- }
- });
- } else {
- // 待指派订单tab:清空所有已分配的机构
- orderList.value.forEach(order => {
- const orderId = getOrderId(order);
- assignedInstitutionMap.value[orderId] = '';
- });
- }
- // 选中订单后必须加载机构列表(切换订单类型时)
- await loadInstitutionList();
- } else {
- // 如果没有订单,清空机构列表
- allInstitutionData.value = [];
- institutionPageConfig.total = 0;
- institutionList.value = [];
- gridRef.value?.loadData([]);
- }
- }, { immediate: false });
- function buildOrderQuery(): OrderQuery | undefined {
- if (activeSubTab.value !== 'all') {
- return undefined;
- }
- const query: Partial<OrderQuery> = {};
- // 联系人(客户姓名)
- if (searchForm.customerName) {
- query.liaison = searchForm.customerName;
- }
- // 电话
- if (searchForm.contactPhone) {
- query.phone = searchForm.contactPhone;
- }
- // 订单类型转换
- if (searchForm.orderTypes && searchForm.orderTypes.length > 0) {
- const types: string[] = [];
- if (props.orderType === 'offline') {
- // 线下服务
- if (searchForm.orderTypes.includes('booked')) {
- types.push('2'); // 已派单但未核销
- }
- if (searchForm.orderTypes.includes('verified')) {
- types.push('3'); // 已核销
- }
- } else if (props.orderType === 'physical') {
- // 实体商品
- if (searchForm.orderTypes.includes('pendingShip')) {
- types.push('2'); // 已派单但未核销(待发货)
- }
- if (searchForm.orderTypes.includes('shipped')) {
- types.push('3'); // 已核销(已发货)
- }
- }
- if (types.length > 0) {
- query.types = types;
- }
- }
- return Object.keys(query).length > 0 ? (query as OrderQuery) : undefined;
- }
- // 加载订单列表
- async function loadOrderList() {
- loading.value = true;
- try {
- let response;
- if (props.orderType === 'offline') {
- // 线下服务接口
- if (activeSubTab.value === 'pending') {
- // 待指派订单
- response = await pendingOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize);
- } else if (activeSubTab.value === 'today') {
- // 今日指派订单
- response = await todayOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize);
- } else if (activeSubTab.value === 'all') {
- // 全部指派订单
- const query = buildOrderQuery();
- response = await allPieOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize, query);
- } else {
- return;
- }
- } else {
- // 实体商品接口
- if (activeSubTab.value === 'pending') {
- // 待指派订单
- response = await pendingPhysicalOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize);
- } else if (activeSubTab.value === 'today') {
- // 今日指派订单
- response = await todayPhysicalOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize);
- } else if (activeSubTab.value === 'all') {
- // 全部指派订单
- const query = buildOrderQuery();
- response = await allPhysicalOrderMethod(orderPageConfig.currentPage, orderPageConfig.pageSize, query);
- } else {
- return;
- }
- }
- orderList.value = response.data;
- orderPageConfig.total = response.total;
- } catch (error) {
- orderList.value = [];
- orderPageConfig.total = 0;
- } finally {
- loading.value = false;
- }
- }
- const allInstitutionData = ref<Institution[]>([]);
- function getInstitutionAddress(institution: OrderLiaisonListModel): string {
- const addressParts = [
- institution.cityName,
- institution.areaName,
- institution.detailAddress,
- ].filter(Boolean);
- return addressParts.join('');
- }
- function transformToInstitution(item: OrderLiaisonListModel): Institution {
- return {
- ...item,
- address: getInstitutionAddress(item),
- };
- }
- // 加载可派单机构列表
- async function loadInstitutionList() {
- institutionLoading.value = true;
- allInstitutionData.value = [];
- institutionList.value = [];
- gridRef.value?.loadData([]);
- try {
- const selectedOrder = selectedOrderId.value
- ? orderList.value.find(order => {
- const orderId = order.id ?? order.patientConditioningProgramId;
- return orderId === selectedOrderId.value;
- })
- : null;
- if (!selectedOrder) {
- allInstitutionData.value = [];
- institutionPageConfig.total = 0;
- institutionList.value = [];
- gridRef.value?.loadData([]);
- return;
- }
- if (selectedOrder.institutionId == null || selectedOrder.conditioningProgramType == null) {
- allInstitutionData.value = [];
- institutionPageConfig.total = 0;
- institutionList.value = [];
- gridRef.value?.loadData([]);
- return;
- }
- if (props.orderType === 'offline') {
- if (!selectedOrder.arrangeDate || !selectedOrder.arrangeTime) {
- allInstitutionData.value = [];
- institutionPageConfig.total = 0;
- institutionList.value = [];
- gridRef.value?.loadData([]);
- // 提示用户需要先选择日期和时间(仅线下服务)
- if (!selectedOrder.arrangeDate && !selectedOrder.arrangeTime) {
- message.error('请先选择日期和时间');
- } else if (!selectedOrder.arrangeDate) {
- message.error('请先选择日期');
- } else if (!selectedOrder.arrangeTime) {
- message.error('请先选择时间');
- }
- return;
- }
- }
- const data: Partial<any> = {
- institutionId: selectedOrder.institutionId,
- conditioningProgramType: selectedOrder.conditioningProgramType,
- ...(props.orderType === 'offline' && selectedOrder.arrangeDate && selectedOrder.arrangeTime
- ? { timeStart: `${selectedOrder.arrangeDate} ${selectedOrder.arrangeTime}` }
- : {}),
- // 线下服务 type=2,实体商品 type=1
- type: props.orderType === 'offline' ? 2 : 1
- };
- // 获取可派单机构列表
- const response = await getOrderLiaisonListMethod(data);
- const responseData = Array.isArray(response) ? response : (response?.data || []);
- const transformedData = responseData.map(transformToInstitution);
- allInstitutionData.value = transformedData;
- institutionList.value = transformedData;
- // 更新表格数据
- gridRef.value?.loadData(institutionList.value);
- } catch (error) {
- allInstitutionData.value = [];
- institutionPageConfig.total = 0;
- institutionList.value = [];
- } finally {
- institutionLoading.value = false;
- }
- }
- // 处理日期按钮点击
- function handleDateBtnClick(orderId: number) {
- const order = orderList.value.find(o => getOrderId(o) === orderId);
- if (!order) return;
- if (selectedOrderId.value !== orderId) {
- selectedOrderId.value = orderId;
- loadInstitutionList();
- }
- currentPickerOrderId.value = orderId;
- pickerType.value = 'date';
- isFromTimeButton.value = false;
- dateTimePickerVisible.value = true;
- }
- // 处理时间按钮点击
- function handleTimeBtnClick(orderId: number) {
- const order = orderList.value.find(o => getOrderId(o) === orderId);
- if (!order) return;
- if (selectedOrderId.value !== orderId) {
- selectedOrderId.value = orderId;
- loadInstitutionList();
- }
- currentPickerOrderId.value = orderId;
- isFromTimeButton.value = true;
- if (order.arrangeDate) {
- pickerType.value = 'time';
- } else {
- pickerType.value = 'date'; // 先选择日期
- }
- dateTimePickerVisible.value = true;
- }
- // 处理日期时间选择器的事件
- function handleDateTimeDateSelect(date: Dayjs) {
- if (currentPickerOrderId.value !== null) {
- handleDateChange(currentPickerOrderId.value, date);
- if (isFromTimeButton.value) {
- pickerType.value = 'time';
- }
- }
- }
- function handleDateTimeTimeSelect(time: string, date: Dayjs) {
- if (currentPickerOrderId.value !== null) {
- const order = orderList.value.find(o => getOrderId(o) === currentPickerOrderId.value);
- if (order) {
- if (!order.arrangeDate) {
- order.arrangeDate = date.format('YYYY-MM-DD');
- }
- // 更新时间
- handleTimeChange(currentPickerOrderId.value, dayjs(time, 'HH:mm'), order);
- }
- }
- }
- function handleDateTimeDateConfirm(date: Dayjs) {
- handleDateTimeDateSelect(date);
- }
- function handleDateTimeClose() {
- dateTimePickerVisible.value = false;
- currentPickerOrderId.value = null;
- isFromTimeButton.value = false;
- }
- // 处理日期修改
- function handleDateChange(orderId: number, date: Dayjs | null) {
- if (!date) return;
- // 更新订单的日期
- const orderIndex = orderList.value.findIndex(o => getOrderId(o) === orderId);
- if (orderIndex !== -1) {
- const order = orderList.value[orderIndex];
- const newDateStr = date.format('YYYY-MM-DD');
- // 如果日期相同,不需要更新
- if (order.arrangeDate !== newDateStr) {
- order.arrangeDate = newDateStr;
- orderList.value = [...orderList.value];
- }
- }
- // 清空分配机构
- assignedInstitutionMap.value[orderId] = '';
- loadInstitutionList();
- }
- function handleTimeChange(orderId: number, startTime: any, order: OrderModel) {
- if (!startTime) return;
- // 更新订单的开始时间
- const targetOrder = orderList.value.find(o => getOrderId(o) === orderId);
- if (!targetOrder) return;
- const durationSource = targetOrder.offlineDuration || order.offlineDuration;
- const start = dayjs.isDayjs(startTime) ? startTime : dayjs(startTime);
- targetOrder.arrangeTime = start.format('HH:mm');
- if (durationSource) {
- const durationMinutes = parseDurationToMinutes(durationSource);
- if (durationMinutes > 0) {
- const end = start.add(durationMinutes, 'minute');
- targetOrder.arrangeEndTime = end.format('HH:mm');
- }
- }
- // 清空分配机构
- assignedInstitutionMap.value[orderId] = '';
- // 刷新可派单机构列表
- loadInstitutionList();
- }
- // 解析服务时长为分钟数
- function parseDurationToMinutes(duration: string): number {
- if (!duration) return 0;
- const cleanDuration = duration.trim();
- const numericValue = parseFloat(cleanDuration);
- if (!isNaN(numericValue) && cleanDuration === numericValue.toString()) {
- return numericValue;
- }
- const minutesMatch = cleanDuration.match(/([\d.]+)\s*分钟/);
- if (minutesMatch) {
- return Math.round(parseFloat(minutesMatch[1]));
- }
- const hoursMatch = cleanDuration.match(/([\d.]+)\s*小时/);
- if (hoursMatch) {
- return Math.round(parseFloat(hoursMatch[1]) * 60);
- }
- const hoursMinutesMatch = cleanDuration.match(/([\d.]+)\s*小时\s*([\d.]+)\s*分钟/);
- if (hoursMinutesMatch) {
- const hours = parseFloat(hoursMinutesMatch[1]);
- const minutes = parseFloat(hoursMinutesMatch[2]);
- return Math.round(hours * 60 + minutes);
- }
- return 0;
- }
- function handleLastInstitutionClick(orderId: number, institutionName: string) {
- if (!institutionName) return;
- if (selectedOrderId.value !== orderId) {
- selectedOrderId.value = orderId;
- loadInstitutionList();
- }
- if (!availableInstitutionNames.value.includes(institutionName)) {
- message.warning('供应商不可选择,且不填充此机构');
- return;
- }
- assignedInstitutionMap.value[orderId] = institutionName;
- }
- function handleSupplierClick(orderId: number, supplierName: string) {
- if (!supplierName) return;
- if (selectedOrderId.value !== orderId) {
- selectedOrderId.value = orderId;
- loadInstitutionList();
- }
- if (!availableInstitutionNames.value.includes(supplierName)) {
- message.warning('供应商不可选择,且不填充此机构');
- return;
- }
- assignedInstitutionMap.value[orderId] = supplierName;
- }
- function handleAssignInstitution(institutionName: string) {
- if (selectedOrderId.value !== null) {
- const selectedOrder = orderList.value.find(order => {
- const orderId = order.id ?? order.patientConditioningProgramId;
- return orderId === selectedOrderId.value;
- });
- if (selectedOrder && (isOrderVerified(selectedOrder))) {
- message.warning('已核销的订单不能指派');
- return;
- }
- if (selectedOrder && (isOrderShipped(selectedOrder))) {
- message.warning('已发货的订单不能指派');
- return;
- }
- assignedInstitutionMap.value[selectedOrderId.value] = institutionName;
- }
- }
- function validateAndGetInstitution(orderId: number) {
- const institutionName = assignedInstitutionMap.value[orderId];
- if (!institutionName) {
- message.warning('请先选择分配机构');
- return null;
- }
- const institution = allInstitutionData.value.find(inst => inst.name === institutionName);
- if (!institution) {
- message.error('未找到对应的供应商信息');
- return null;
- }
- return { institutionName, conditioningProgramSupplierId: institution.id };
- }
- function validateAndGetOrder(orderId: number) {
- const orderIndex = orderList.value.findIndex(order => getOrderId(order) === orderId);
- if (orderIndex === -1) {
- message.error('未找到对应的订单');
- return null;
- }
- return { orderIndex, order: orderList.value[orderIndex] };
- }
- // 验证线下服务订单的必要信息
- function validateOfflineOrder(order: OrderModel) {
- if (!order.id) {
- message.error('订单缺少必要信息(id)');
- return null;
- }
- if (!order.arrangeDate || !order.arrangeTime) {
- message.error('请先选择日期和时间');
- return null;
- }
- const endTime = order.arrangeEndTime || (order.offlineDuration ? calculateEndTime(order) : null);
- if (!endTime) {
- message.error('无法计算服务结束时间,请检查服务时长设置');
- return null;
- }
- return {
- id: order.id,
- pieTimeStart: `${order.arrangeDate} ${order.arrangeTime}`,
- pieTimeEnd: `${order.arrangeDate} ${endTime}`,
- };
- }
- // 验证实体商品订单的必要信息
- function validatePhysicalOrder(order: OrderModel) {
- if (!order.patientConditioningProgramId) {
- message.error('订单缺少必要信息(patientConditioningProgramId)');
- return null;
- }
- return {
- patientConditioningProgramId: order.patientConditioningProgramId,
- };
- }
- async function callConfirmOrderAPI(order: OrderModel, conditioningProgramSupplierId: number) {
- if (props.orderType === 'offline') {
- const offlineData = validateOfflineOrder(order);
- if (!offlineData) return false;
- await confirmPieOrderMethod({
- id: offlineData.id,
- conditioningProgramSupplierId,
- pieTimeStart: offlineData.pieTimeStart,
- pieTimeEnd: offlineData.pieTimeEnd,
- });
- } else {
- const physicalData = validatePhysicalOrder(order);
- if (!physicalData) return false;
- await confirmPhysicalOrderMethod({
- patientConditioningProgramId: physicalData.patientConditioningProgramId,
- conditioningProgramSupplierId,
- });
- }
- return true;
- }
- // 处理订单列表更新
- async function handleOrderListAfterAssign(orderId: number, orderIndex: number) {
- if (activeSubTab.value !== 'pending') {
- // 如果当前在"今日指派订单"或"全部指派订单"tab,订单保留但显示已指派状态
- return;
- }
- // 如果当前在"待指派订单"tab,指派成功后从列表中移除该订单
- orderList.value.splice(orderIndex, 1);
- // 如果移除后列表为空,取消选中
- if (orderList.value.length === 0) {
- selectedOrderId.value = null;
- return;
- }
- // 如果移除的是当前选中的订单,选中第一个订单
- if (selectedOrderId.value === orderId) {
- selectedOrderId.value = orderList.value[0] ? getOrderId(orderList.value[0]) : null;
- await loadInstitutionList();
- }
- }
- // 处理确认指派
- async function handleConfirmAssign(orderId: number) {
- // 如果订单未选中,自动选中该订单并加载机构列表
- if (selectedOrderId.value !== orderId) {
- selectedOrderId.value = orderId;
- await loadInstitutionList();
- }
- // 验证并获取机构信息
- const institutionInfo = validateAndGetInstitution(orderId);
- if (!institutionInfo) return;
- // 验证并获取订单信息
- const orderInfo = validateAndGetOrder(orderId);
- if (!orderInfo) return;
- const { orderIndex, order } = orderInfo;
- const originalOrder = { ...order };
- // 先更新本地数据
- order.conditioningProgramSupplierName = institutionInfo.institutionName;
- order.conditioningProgramSupplierId = institutionInfo.conditioningProgramSupplierId;
- try {
- const success = await callConfirmOrderAPI(order, institutionInfo.conditioningProgramSupplierId);
- if (!success) {
- // 验证失败
- orderList.value[orderIndex] = originalOrder;
- return;
- }
- // 处理订单列表更新
- await handleOrderListAfterAssign(orderId, orderIndex);
- // 清空分配机构输入框
- assignedInstitutionMap.value[orderId] = '';
- // 静默更新订单计数(不显示loading,避免闪烁)
- loadOrderCounts().catch(err => {
- console.error('更新订单计数失败:', err);
- });
- message.success('派单成功');
- } catch (error) {
- //API调用失败,恢复原始数据
- orderList.value[orderIndex] = originalOrder;
- }
- }
- // 处理取消
- function handleCancel(orderId: number) {
- assignedInstitutionMap.value[orderId] = '';
- }
- function getOrderId(order: OrderModel): number {
- return order.id ?? order.patientConditioningProgramId;
- }
- // 判断订单是否被选中
- function isOrderSelected(order: OrderModel): boolean {
- const orderId = getOrderId(order);
- const result = selectedOrderId.value === orderId;
- return result;
- }
- // 处理订单选中
- function handleOrderSelect(order: OrderModel) {
- const orderId = getOrderId(order);
- if (selectedOrderId.value === orderId) {
- return;
- }
- selectedOrderId.value = orderId;
- loadInstitutionList();
- }
- // 处理查询
- async function handleSearch() {
- // 重置到第一页
- orderPageConfig.currentPage = 1;
- await loadOrderList();
- }
- // 处理重置
- function handleReset() {
- searchForm.customerName = '';
- searchForm.contactPhone = '';
- searchForm.orderTypes = [];
- orderPageConfig.currentPage = 1;
- loadOrderList();
- }
- // 处理订单列表分页变化
- function handleOrderPageChange({ page, pageSize }: { page?: number; pageSize?: number }) {
- if (page !== undefined) {
- orderPageConfig.currentPage = page;
- }
- if (pageSize !== undefined) {
- orderPageConfig.pageSize = pageSize;
- }
- loadOrderList();
- }
- // 获取订单数量
- const pendingCount = computed(() => {
- return props.orderType === 'offline'
- ? orderCounts.value.pendPieOfflineOrderCount
- : orderCounts.value.pendPieOnlineOrderCount;
- });
- const todayCount = computed(() => {
- return props.orderType === 'offline'
- ? orderCounts.value.todayPieOfflineOrderCount
- : orderCounts.value.todayPieOnlineOrderCount;
- });
- const allCount = computed(() => {
- return props.orderType === 'offline'
- ? orderCounts.value.allPieOfflineOrderCount
- : orderCounts.value.allPieOnlineOrderCount;
- });
- // 加载订单计数
- async function loadOrderCounts() {
- try {
- const response: any = await getPieOrderCountMethod();
- const data = response?.data ?? response;
- orderCounts.value = {
- offlineCount: Number(data?.offlineCount) || 0, // 线下服务订单总和
- allPieOfflineOrderCount: Number(data?.allPieOfflineOrderCount) || 0, // 线下服务——全部指派订单数量
- pendPieOfflineOrderCount: Number(data?.pendPieOfflineOrderCount) || 0, // 线下服务——待指派订单数量
- todayPieOfflineOrderCount: Number(data?.todayPieOfflineOrderCount) || 0, // 线下服务——今日指派订单数量
- onlineCount: Number(data?.onlineCount) || 0, // 实体商品数量订单总和
- allPieOnlineOrderCount: Number(data?.allPieOnlineOrderCount) || 0, // 实体商品——全部指派订单数量
- pendPieOnlineOrderCount: Number(data?.pendPieOnlineOrderCount) || 0, // 实体商品——待指派订单数量
- todayPieOnlineOrderCount: Number(data?.todayPieOnlineOrderCount) || 0, // 实体商品——今日指派订单数量
- };
- } catch (error) {
- // 失败时使用默认值
- orderCounts.value = {
- offlineCount: 0, // 线下服务数量
- allPieOfflineOrderCount: 0, // 线下服务——全部指派订单数量
- pendPieOfflineOrderCount: 0, // 线下服务——待指派订单数量
- todayPieOfflineOrderCount: 0, // 线下服务——今日指派订单数量
- onlineCount: 0, // 实体商品数量
- allPieOnlineOrderCount: 0, // 实体商品——全部指派订单数量
- pendPieOnlineOrderCount: 0, // 实体商品——待指派订单数量
- todayPieOnlineOrderCount: 0, // 实体商品——今日指派订单数量
- };
- }
- }
- // 计算总订单数(直接使用后端返回的总和)
- const offlineCount = computed(() => {
- return orderCounts.value.offlineCount;
- });
- const onlineCount = computed(() => {
- return orderCounts.value.onlineCount;
- });
- // 暴露方法给父组件
- defineExpose({
- offlineCount,
- onlineCount,
- refresh: () => {
- loadOrderList();
- loadInstitutionList();
- },
- });
- </script>
- <template>
- <div class="dispatch-order-panel">
- <!-- 主要内容区域 -->
- <div class="main-content">
- <!-- 左侧区域 -->
- <div class="left-panel">
- <!-- 子tab -->
- <div class="sub-tabs">
- <div class="sub-tab" :class="{ active: activeSubTab === 'pending' }" @click="activeSubTab = 'pending'">
- 待指派订单 ({{ pendingCount }})
- </div>
- <div class="sub-tab" :class="{ active: activeSubTab === 'today' }" @click="activeSubTab = 'today'">
- 今日指派订单 ({{ todayCount }})
- </div>
- <div class="sub-tab" :class="{ active: activeSubTab === 'all' }" @click="activeSubTab = 'all'">
- 全部指派订单 ({{ allCount }})
- </div>
- </div>
- <!-- 订单列表 -->
- <div class="order-list-panel">
- <!-- 固定头部区域 -->
- <div class="order-list-header">
- <!-- 警告 - 今日指派和全部指派都显示 -->
- <div v-if="(activeSubTab === 'today' || activeSubTab === 'all')" class="warning-banner">
- <span class="warning-text">
- {{ props.orderType === 'physical' ? '已发货不能修改' : '距离开始时间1小时内,修改请先联系商家和客户' }}
- </span>
- </div>
- <!-- 搜索 - 仅全部指派时显示 -->
- <div v-if="activeSubTab === 'all'" class="search-section">
- <div class="search-form">
- <div class="search-item">
- <span class="search-label">客户姓名:</span>
- <a-input v-model:value="searchForm.customerName" placeholder="请输入" class="search-input" />
- </div>
- <div class="search-item">
- <span class="search-label">联系电话:</span>
- <a-input v-model:value="searchForm.contactPhone" placeholder="请输入" class="search-input" />
- </div>
- <div class="search-item">
- <span class="search-label">订单类型:</span>
- <a-checkbox-group v-model:value="searchForm.orderTypes" class="checkbox-group">
- <!-- 线下服务显示:已预约、已核销 -->
- <template v-if="props.orderType === 'offline'">
- <a-checkbox value="booked">已预约</a-checkbox>
- <a-checkbox value="verified">已核销</a-checkbox>
- </template>
- <!-- 实体商品显示:待发货、已发货 -->
- <template v-else-if="props.orderType === 'physical'">
- <a-checkbox value="pendingShip">待发货</a-checkbox>
- <a-checkbox value="shipped">已发货</a-checkbox>
- </template>
- </a-checkbox-group>
- </div>
- <div class="search-actions">
- <a-button type="primary" @click="handleSearch">查询</a-button>
- <a-button @click="handleReset">重置</a-button>
- </div>
- </div>
- </div>
- </div>
- <!-- 可滚动的订单列表内容 -->
- <div class="order-list-content">
- <div v-if="loading" class="loading">加载中...</div>
- <div v-else-if="orderList.length === 0" class="empty">暂无订单</div>
- <template v-else>
- <div v-for="(order, index) in orderList" :key="getOrderId(order)" class="order-card"
- :class="{ active: isOrderSelected(order) }" @click="handleOrderSelect(order)">
- <!-- 已核销/已发货印章 -->
- <!-- 线下服务:已核销状态显示已核销图标 -->
- <!-- 实体商品:已发货状态显示已发货图标 -->
- <div v-if="isOrderVerified(order) || isOrderShipped(order)" class="verified-badge">
- <img v-if="isOrderVerified(order)" src="@/assets/images/verify.png" alt="已核销"
- style="width: 100px; height: 100px;" />
- <img v-else-if="isOrderShipped(order)" src="@/assets/images/shipment.png" alt="已发货"
- style="width: 100px; height: 100px;" />
- </div>
- <!-- 左侧:图钉图标和数字 -->
- <div class="order-header-left">
- <div class="pin-icon-wrapper">
- <div class="pin-icon-inner">
- <span class="pin-icon-number">{{ index + 1 }}</span>
- </div>
- </div>
- <span class="service-name">
- {{ order.conditioningProgramName }}
- <span v-if="order.conditioningProgramType">({{ order.conditioningProgramType }})</span>
- </span>
- </div>
- <!-- 右侧:日期和时间按钮(仅线下服务显示) -->
- <div v-if="props.orderType === 'offline'" class="order-header-right">
- <!-- 日期按钮 -->
- <div v-if="isOrderVerified(order) || isOrderShipped(order)"
- class="date-display-btn date-display-btn-disabled">
- {{ order.arrangeDate || '--' }}
- </div>
- <template v-else>
- <div class="date-display-btn" @click.stop="handleDateBtnClick(getOrderId(order))">
- {{ order.arrangeDate || '选择日期' }}
- </div>
- </template>
- <!-- 分隔符 -->
- <span class="separator-dash">-</span>
- <!-- 时间范围按钮 -->
- <div v-if="isOrderVerified(order) || isOrderShipped(order)"
- class="time-range-display-btn time-range-display-btn-disabled">
- <template v-if="order.arrangeTime">
- {{ order.arrangeTime }}
- <template v-if="calculateEndTime(order)">-{{ calculateEndTime(order) }}</template>
- </template>
- <template v-else>--</template>
- </div>
- <div v-else class="time-range-display-btn" @click.stop="handleTimeBtnClick(getOrderId(order))">
- <template v-if="order.arrangeTime">
- {{ order.arrangeTime }}
- <template v-if="calculateEndTime(order)">-{{ calculateEndTime(order) }}</template>
- </template>
- <template v-else>选择时间</template>
- </div>
- </div>
- <!-- 客户信息 -->
- <div class="customer-info">
- {{ formatCustomerInfo(order) }}
- </div>
- <!-- 分配机构 -->
- <div class="assign-section">
- <span class="assign-label">分配机构:</span>
- <a-input
- :value="assignedInstitutionMap[getOrderId(order)] || (activeSubTab !== 'pending' ? (order.conditioningProgramSupplierName || '') : '')"
- placeholder="请点击右侧指派按钮或点击上次/供应商选择分配机构" class="assign-input" readonly
- :disabled="isOrderVerified(order) || isOrderShipped(order)" />
- <!-- <a-button
- v-if="!isOrderVerified(order) && !isOrderShipped(order)"
- type="default"
- size="small"
- @click.stop="handleCancel(getOrderId(order))"
- >
- 取消
- </a-button> -->
- <a-button v-if="!isOrderVerified(order) && !isOrderShipped(order)" type="primary" size="small"
- @click.stop="handleConfirmAssign(getOrderId(order))">
- 确认指派
- </a-button>
- </div>
- <!-- 上次机构和供应商 -->
- <div class="institution-options">
- <div v-if="order.preConditioningProgramSupplierName" class="institution-option"
- :class="{ disabled: isOrderVerified(order) || isOrderShipped(order) }"
- @click.stop="!(isOrderVerified(order) || isOrderShipped(order)) && handleLastInstitutionClick(getOrderId(order), order.preConditioningProgramSupplierName)">
- 上次:{{ order.preConditioningProgramSupplierName }}
- </div>
- <div v-if="order.conditioningProgramSupplierName" class="institution-option"
- :class="{ disabled: isOrderVerified(order) || isOrderShipped(order) }"
- @click.stop="!(isOrderVerified(order) || isOrderShipped(order)) && handleSupplierClick(getOrderId(order), order.conditioningProgramSupplierName)">
- 供应商:{{ order.conditioningProgramSupplierName }}
- </div>
- </div>
- <!-- 订单信息 -->
- <div class="order-info">
- <div class="order-info-item" v-if="order.orderNo">订单编号: {{ order.orderNo }}</div>
- <div class="order-info-item" v-if="order.applyTime">用户操作时间: {{ order.applyTime }}</div>
- <div class="order-info-item" v-if="activeSubTab !== 'pending' && order.pieBy">派单员: {{ order.pieBy }}
- </div>
- <div class="order-info-item" v-if="order.offlineDuration">服务时长: {{ order.offlineDuration }}分钟</div>
- </div>
- </div>
- </template>
- </div>
- <!-- 订单列表分页器 -->
- <div class="order-pager-wrapper">
- <vxe-pager v-model:current-page="orderPageConfig.currentPage" v-model:page-size="orderPageConfig.pageSize"
- :total="orderPageConfig.total"
- :layouts="['PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
- @page-change="handleOrderPageChange" />
- </div>
- </div>
- </div>
- <!-- 右侧可派单机构列表 -->
- <div class="institution-panel">
- <div class="panel-title">可派单机构</div>
- <div class="institution-grid-wrapper">
- <vxe-grid ref="gridRef" v-bind="gridOptions" :loading="institutionLoading" />
- </div>
- </div>
- </div>
- <!-- 日期时间选择弹窗 -->
- <DateTimePicker v-model:visible="dateTimePickerVisible" :picker-type="pickerType"
- :initial-date="currentPickerOrderId ? (orderList.find(o => getOrderId(o) === currentPickerOrderId)?.arrangeDate || null) : null"
- :initial-time="currentPickerOrderId ? (orderList.find(o => getOrderId(o) === currentPickerOrderId)?.arrangeTime || null) : null"
- :is-from-time-button="isFromTimeButton" @date-select="handleDateTimeDateSelect"
- @time-select="handleDateTimeTimeSelect" @date-confirm="handleDateTimeDateConfirm" @close="handleDateTimeClose">
- </DateTimePicker>
- </div>
- </template>
- <style scoped lang="scss">
- .dispatch-order-panel {
- display: flex;
- flex-direction: column;
- height: 100%;
- min-height: 0;
- .main-content {
- display: flex;
- gap: 16px;
- flex: 1;
- min-height: 0;
- overflow: hidden;
- align-items: stretch;
- .left-panel {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- overflow: hidden;
- border: 1px solid #8bc34a;
- .sub-tabs {
- display: flex;
- gap: 0;
- margin-bottom: 0;
- background: #8bc34a;
- padding: 0;
- align-items: flex-end;
- box-sizing: border-box;
- .sub-tab {
- padding: 12px 20px;
- cursor: pointer;
- color: #fff;
- background: #8bc34a;
- border: none;
- border-bottom: 3px solid transparent;
- transition: all 0.3s;
- font-size: 14px;
- position: relative;
- white-space: nowrap;
- &.active {
- background: #fff;
- color: #52c41a;
- border-bottom-color: #52c41a;
- border-bottom-width: 3px;
- font-weight: 500;
- }
- &:hover:not(.active) {
- background: #a5d6a7;
- }
- }
- }
- .order-list-panel {
- flex: 1;
- min-height: 0;
- display: flex;
- flex-direction: column;
- overflow: hidden;
-
- .order-list-header {
- flex-shrink: 0;
- padding: 16px;
- padding-bottom: 0;
- background: #fff;
- .warning-banner {
- margin-bottom: 16px;
- border-radius: 4px;
- .warning-text {
- color: #ff4d4f;
- font-size: 14px;
- font-weight: 500;
- }
- }
- .search-section {
- margin-bottom: 16px;
- padding: 16px;
- background: #fff;
- border: 1px solid #e8e8e8;
- border-radius: 4px;
- .search-form {
- display: flex;
- align-items: center;
- gap: 16px;
- flex-wrap: wrap;
- .search-item {
- display: flex;
- align-items: center;
- gap: 8px;
- .search-label {
- white-space: nowrap;
- font-size: 14px;
- color: #333;
- }
- .search-input {
- width: 180px;
- }
- }
- .checkbox-group {
- display: flex;
- gap: 8px;
- }
- .search-actions {
- display: flex;
- gap: 8px;
- :deep(.ant-btn) {
- height: 32px;
- padding: 4px 15px;
- font-size: 14px;
- }
- }
- }
- }
- }
- // 可滚动的订单列表内容
- .order-list-content {
- flex: 1;
- min-height: 0;
- overflow-y: auto;
- overflow-x: hidden;
- padding: 16px;
- padding-top: 0;
- padding-right: 24px;
- .loading,
- .empty {
- text-align: center;
- padding: 40px;
- color: #999;
- }
- .order-card {
- padding: 16px;
- margin-bottom: 16px;
- border: 1px solid #e8e8e8;
- border-radius: 4px;
- background: #fff;
- cursor: pointer;
- transition: all 0.3s;
- position: relative;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
- overflow: visible;
- &.active {
- background: rgba(24, 144, 255, 0.08);
- box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
- }
- .verified-badge {
- position: absolute;
- bottom: 35px;
- right: 16px;
- width: 100px;
- height: 100px;
- display: flex;
- align-items: center;
- justify-content: center;
- pointer-events: none;
- z-index: 1;
- &::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
- &::after {
- content: '';
- position: absolute;
- top: 12px;
- left: 12px;
- width: calc(100% - 24px);
- height: calc(100% - 24px);
- }
- span {
- position: relative;
- z-index: 1;
- display: inline-block;
- &::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 50px;
- height: 50px;
- border: 1px solid #5a9fe3;
- border-radius: 50%;
- opacity: 0.6;
- }
- }
- }
- .order-header-left {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 12px;
- .pin-icon-wrapper {
- width: 24px;
- height: 32px;
- position: relative;
- .pin-icon-inner {
- position: absolute;
- top: 0;
- left: 0;
- width: 25px;
- height: 25px;
- transform: rotate(-45deg);
- transform-origin: 15px 14px;
- &::before {
- content: '';
- position: absolute;
- width: 25px;
- height: 25px;
- background: #5a9fe3;
- border-radius: 50% 50% 50% 0;
- top: 0;
- left: 0;
- }
- .pin-icon-number {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%) rotate(45deg);
- z-index: 1;
- color: #fff;
- font-size: 12px;
- font-weight: 600;
- line-height: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 16px;
- height: 16px;
- }
- }
- &::after {
- content: '';
- position: absolute;
- width: 0;
- height: 0;
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 6px solid #5a9fe3;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%);
- z-index: 0;
- }
- }
- .service-name {
- font-size: 16px;
- font-weight: 500;
- color: #333;
- }
- }
- .order-header-right {
- position: absolute;
- top: 16px;
- right: 16px;
- display: flex;
- gap: 8px;
- align-items: center;
- z-index: 10;
- visibility: visible;
- opacity: 1;
- .date-display-btn {
- background: #8bc34a;
- color: #fff;
- padding: 4px 11px;
- border-radius: 4px;
- font-size: 14px;
- line-height: 1.5715;
- cursor: pointer;
- user-select: none;
- white-space: nowrap;
- min-width: 110px;
- text-align: center;
- transition: background-color 0.3s;
- &:hover {
- background: #7cb342;
- }
- &.date-display-btn-disabled {
- cursor: default;
- opacity: 0.8;
- }
- }
- .separator-dash {
- color: #999;
- font-size: 14px;
- margin: 0 4px;
- }
- .time-range-display-btn {
- background: #8bc34a;
- color: #fff;
- padding: 4px 11px;
- border-radius: 4px;
- font-size: 14px;
- line-height: 1.5715;
- cursor: pointer;
- user-select: none;
- white-space: nowrap;
- min-width: 100px;
- text-align: center;
- transition: background-color 0.3s;
- &:hover {
- background: #7cb342;
- }
- &.time-range-display-btn-disabled {
- cursor: default;
- opacity: 0.8;
- }
- }
- .hidden-date-picker,
- .hidden-time-picker {
- position: absolute;
- opacity: 0;
- pointer-events: none;
- width: 0;
- height: 0;
- overflow: hidden;
- }
- }
- .customer-info {
- font-size: 14px;
- color: #5da1f9;
- margin-bottom: 12px;
- line-height: 1.6;
- }
- .assign-section {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 8px;
- flex-wrap: wrap;
- .assign-label {
- white-space: nowrap;
- color: #333;
- font-size: 14px;
- margin-right: 4px;
- width: 72px;
- flex-shrink: 0;
- }
- .assign-input {
- flex: 1;
- min-width: 200px;
- max-width: 300px;
- :deep(.ant-input) {
- border-color: #52c41a;
- cursor: default;
- &[readonly] {
- background-color: #fafafa;
- cursor: not-allowed;
- }
- &:disabled {
- background-color: #f5f5f5;
- cursor: not-allowed;
- }
- }
- }
- :deep(.ant-btn) {
- height: 32px;
- padding: 4px 15px;
- font-size: 14px;
- }
- }
- .assigned-institution {
- font-size: 14px;
- color: #666;
- margin-bottom: 12px;
- padding-left: 80px;
- }
- .institution-options {
- display: flex;
- gap: 12px;
- margin-bottom: 12px;
- flex-wrap: wrap;
- margin-left: 85px;
- .institution-option {
- padding: 4px 12px;
- border-radius: 4px;
- font-size: 13px;
- color: #5da1f9;
- cursor: pointer;
- transition: all 0.3s;
- &.disabled {
- opacity: 0.5;
- cursor: not-allowed;
- color: #999;
- }
- }
- }
- .order-info {
- font-size: 13px;
- color: black;
- line-height: 1.8;
- display: flex;
- .order-info-item {
- margin-right: 30px;
- }
- div {
- margin-bottom: 4px;
- }
- }
- }
- .order-pager-wrapper {
- flex-shrink: 0;
- border-top: 1px solid #e8e8e8;
- background: #fff;
- padding: 8px 16px;
- margin-top: 16px;
- }
- }
- }
- }
- .institution-panel {
- flex: 1;
- min-height: 0;
- border: 1px solid #e8e8e8;
- border-radius: 4px;
- background: #fff;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- .panel-title {
- padding: 12px 16px;
- font-size: 16px;
- font-weight: 500;
- border-bottom: 1px solid #e8e8e8;
- background: #5a9fe3;
- color: white;
- height: 48px;
- display: flex;
- align-items: center;
- box-sizing: border-box;
- flex-shrink: 0;
- }
- .institution-grid-wrapper {
- flex: 1;
- min-height: 0;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
- .institution-pager-wrapper {
- flex-shrink: 0;
- border-top: 1px solid #e8e8e8;
- background: #fff;
- padding: 8px 16px;
- }
- :deep(.vxe-grid) {
- flex: 1;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- min-height: 0;
- height: 100%;
- // 表格主体区域
- .vxe-table--wrapper {
- flex: 1;
- overflow: hidden;
- min-height: 0;
- height: 100%;
- display: flex;
- flex-direction: column;
- }
- .vxe-table {
- height: 100%;
- display: flex;
- flex-direction: column;
- }
- .vxe-table--header-wrapper {
- flex-shrink: 0;
- position: relative;
- z-index: 10;
- }
- .vxe-table--header {
- background: #fafafa;
- .vxe-header--column {
- font-weight: 500;
- color: #333;
- }
- }
- .vxe-table--body-wrapper {
- flex: 1;
- min-height: 0;
- position: relative;
- }
- :deep(.vxe-table--body-wrapper),
- :deep(.vxe-table--body),
- :deep(.vxe-table--body-wrapper > *),
- :deep([class*="scroll"]) {
- &::-webkit-scrollbar {
- width: 8px;
- }
- &::-webkit-scrollbar-thumb {
- background: #d9d9d9;
- border-radius: 4px;
- }
- &::-webkit-scrollbar-thumb:hover {
- background: #bfbfbf;
- }
- }
- .vxe-table--body {
- .vxe-body--row {
- &:hover {
- background: #f5f5f5;
- }
- .vxe-body--column {
- padding: 12px 8px;
- }
- }
- }
- .vxe-button {
- padding: 4px 15px;
- height: 35px;
- font-size: 14px;
- width: 100px;
- background: #1890ff;
- color: white;
- }
- }
- }
- }
- }
- </style>
|