Explorar el Código

Merge tag '2.3.0' into develop

2.3.0
张田田 hace 2 meses
padre
commit
ca541547fa

+ 6 - 9
src/components/EditSupplier.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { nextTick, watch, computed, ref, watchEffect } from 'vue';
+import { nextTick, watch, computed, ref, watchEffect, reactive, onBeforeMount } from 'vue';
 import { VxeUI, type VxeFormProps, type VxeFormListeners } from 'vxe-pc-ui';
 import type { SupplierModel } from '@/model/care.model';
 import { useRequest } from 'alova/client';
@@ -175,7 +175,7 @@ const { loading: submitting, send: submit } = useRequest(supplierEditMethod, { i
 const projectList = ref<Array<{ label: string; value: string }>>([]);
 // 线上权益
 const onlineCPList = ref<Array<{ label: string; value: string }>>([]);
-// 营业时间相关方法
+// 营业时间
 // 全选/取消全选
 const selectAllBusinessHours = computed({
   get: () => {
@@ -187,7 +187,7 @@ const selectAllBusinessHours = computed({
     days.forEach(day => {
       if (model.value.businessHours[day]) {
         model.value.businessHours[day].enabled = value;
-        // 如果启用日期,初始化时间范围值
+        // 初始化时间范围值
         if (value && !timeRangeValues.value[day]) {
           const dayData = model.value.businessHours[day];
           if (dayData.start && dayData.end) {
@@ -289,7 +289,7 @@ initTimeRangeValues();
 
 const formRef = ref<any>(null);
 
-// 基础表单项(不包含营业时间)
+
 const baseFormItems = [
   {
     field: 'name',
@@ -473,7 +473,6 @@ const formProps = reactive<VxeFormProps<FormModel>>({
         validator: () => {
           const depts = model.value.collaborateDepts;
           if (!depts || !Array.isArray(depts) || depts.length === 0) {
-            // throw new Error('请选择合作机构');
             notification.error({
               message: '请选择合作机构',
             });
@@ -525,7 +524,6 @@ const showBusinessHours = computed<boolean>(() => {
   return !!(offlineCPTypes && Array.isArray(offlineCPTypes) && offlineCPTypes.length > 0);
 });
 
-// 跟踪线下服务之前的值,用于检测清除后再次选择的情况
 const prevOfflineCPTypes = ref<string[] | undefined>(undefined);
 let wasCleared = false;
 
@@ -606,7 +604,6 @@ const formEmits: VxeFormListeners<FormModel> = {
   },
 };
 
-// 统一用 change 事件集中处理(父选子,取消独立),避免在 select 阶段改值导致弹层关闭
 function onDeptChange(
   newVal: Array<{ value: string | number; label: string }>,
   _labels: Array<string>,
@@ -619,7 +616,7 @@ function onDeptChange(
 
   const final = new Set(current);
 
-  // 优先使用组件提供的 triggerValue 判定是否为新增(选中)以及哪一个父节点被点击
+  // 判定是否为新增(选中)
   const triggerValue = String(extra?.triggerValue ?? '');
   const isChecked = !!extra?.checked; // true: 勾选, false: 取消
 
@@ -678,7 +675,7 @@ onBeforeMount(async () => {
   if (props.data?.id) load(props.data);
   await getOnlineCPList();
   await getOfflineCPList();
-  // 初始化线下服务的前一个值
+  // 初始化线下服务
   prevOfflineCPTypes.value = formProps.data?.offlineCPTypes ? [...formProps.data.offlineCPTypes] : undefined;
   // 初始化表单
   updateFormItems();

+ 2 - 0
src/model/care.model.ts

@@ -293,6 +293,8 @@ export interface ConditioningRecordListModel {
   orderAmount?: number | string; // 订单金额
   createBy?: string; // 创建人
   estimatedStartDate?: string; // 开始调养日期
+  realProfitSharing?: string; // 实际分账比例
+  profitSharing?:string; // 预计分账比例
 }
 export type ConditioningRecordListQuery = Partial<ConditioningRecordListModel> & {
   orderStatus?: string; // 订单状态

+ 2 - 2
src/order/DateTimePicker.vue

@@ -577,8 +577,8 @@ function handleClose() {
           box-sizing: border-box;
 
           &:hover:not(.disabled) {
-            border-color: #8bc34a !important;
-            background: #f9fff9 !important;
+            // border-color: #8bc34a !important;
+            // background: #f9fff9 !important;
           }
 
           &.active {

+ 71 - 16
src/order/DispatchOrderPanel.vue

@@ -46,6 +46,8 @@ const orderCounts = ref({
 
 // 当前订单的分配机构输入值
 const assignedInstitutionMap = ref<Record<number, string>>({});
+// 记录是否从右边列表选择指派(用于控制按钮显示)
+const assignedFromRightList = ref<Record<number, boolean>>({});
 
 // 日期时间选择弹窗相关
 const dateTimePickerVisible = ref(false);
@@ -168,7 +170,7 @@ const gridOptions = reactive<VxeGridProps<Institution>>({
     { field: 'name', title: '机构名称' },
     { field: 'detailAddress', title: '地址' },
     { field: 'phone', title: '联系电话' },
-    { field: 'todayOrderQuantity', title: '当日线下订单数' },
+    { field: 'todayOrderQuantity', title: '当日订单数' },
   ],
   data: [],
   pagerConfig: {
@@ -201,6 +203,8 @@ watch(activeSubTab, async () => {
   allInstitutionData.value = [];
   institutionList.value = [];
   gridRef.value?.loadData([]);
+  // 切换tab时,清空所有按钮显示标记,这样按钮就不会显示
+  assignedFromRightList.value = {};
   await loadOrderCounts();
   await loadOrderList();
   if (orderList.value.length > 0) {
@@ -216,6 +220,7 @@ watch(activeSubTab, async () => {
       orderList.value.forEach(order => {
         const orderId = getOrderId(order);
         assignedInstitutionMap.value[orderId] = '';
+        assignedFromRightList.value[orderId] = false;
       });
     }
     await loadInstitutionList();
@@ -232,6 +237,8 @@ watch(() => props.orderType, async () => {
   activeSubTab.value = 'pending';
   selectedOrderId.value = null;
   assignedInstitutionMap.value = {};
+  // 切换订单类型时,清空所有按钮显示标记
+  assignedFromRightList.value = {};
   orderPageConfig.currentPage = 1;
   searchForm.customerName = '';
   searchForm.contactPhone = '';
@@ -270,6 +277,7 @@ watch(() => props.orderType, async () => {
       orderList.value.forEach(order => {
         const orderId = getOrderId(order);
         assignedInstitutionMap.value[orderId] = '';
+        assignedFromRightList.value[orderId] = false;
       });
     }
     // 选中订单后必须加载机构列表(切换订单类型时)
@@ -569,6 +577,7 @@ function handleDateChange(orderId: number, date: Dayjs | null) {
 
   // 清空分配机构
   assignedInstitutionMap.value[orderId] = '';
+  assignedFromRightList.value[orderId] = false;
 
   loadInstitutionList();
 }
@@ -596,6 +605,7 @@ function handleTimeChange(orderId: number, startTime: any, order: OrderModel) {
 
   // 清空分配机构
   assignedInstitutionMap.value[orderId] = '';
+  assignedFromRightList.value[orderId] = false;
 
   // 刷新可派单机构列表
   loadInstitutionList();
@@ -646,6 +656,8 @@ function handleLastInstitutionClick(orderId: number, institutionName: string) {
   }
 
   assignedInstitutionMap.value[orderId] = institutionName;
+  // 标记为已填充(可以显示按钮)
+  assignedFromRightList.value[orderId] = true;
 }
 
 function handleSupplierClick(orderId: number, supplierName: string) {
@@ -662,6 +674,8 @@ function handleSupplierClick(orderId: number, supplierName: string) {
   }
 
   assignedInstitutionMap.value[orderId] = supplierName;
+  // 标记为已填充(可以显示按钮)
+  assignedFromRightList.value[orderId] = true;
 }
 
 function handleAssignInstitution(institutionName: string) {
@@ -680,8 +694,9 @@ function handleAssignInstitution(institutionName: string) {
       return;
     }
 
-
     assignedInstitutionMap.value[selectedOrderId.value] = institutionName;
+    // 标记为从右边列表选择
+    assignedFromRightList.value[selectedOrderId.value] = true;
   }
 }
 
@@ -831,6 +846,7 @@ async function handleConfirmAssign(orderId: number) {
 
     // 清空分配机构输入框
     assignedInstitutionMap.value[orderId] = '';
+    assignedFromRightList.value[orderId] = false;
 
     // 静默更新订单计数(不显示loading,避免闪烁)
     loadOrderCounts().catch(err => {
@@ -846,7 +862,9 @@ async function handleConfirmAssign(orderId: number) {
 
 // 处理取消
 function handleCancel(orderId: number) {
-  assignedInstitutionMap.value[orderId] = '';
+  // 只清空分配机构,保持按钮显示(不清空 assignedFromRightList)
+  // 使用 null 表示用户主动清空,这样在非 pending tab 中也不会显示原有的供应商名称
+  assignedInstitutionMap.value[orderId] = null as any;
 }
 
 
@@ -861,6 +879,42 @@ function isOrderSelected(order: OrderModel): boolean {
   return result;
 }
 
+// 获取分配机构显示值
+function getAssignedInstitutionDisplayValue(order: OrderModel): string {
+  const orderId = getOrderId(order);
+  // 检查 key 是否存在(使用 hasOwnProperty 或 in 操作符)
+  const hasKey = orderId in assignedInstitutionMap.value;
+  const assignedValue = assignedInstitutionMap.value[orderId];
+  
+  
+  if (hasKey) {
+    if (assignedValue === null) {
+      return '';
+    }
+    return assignedValue || '';
+  }
+  
+  if (activeSubTab.value !== 'pending') {
+    return order.conditioningProgramSupplierName || '';
+  }
+  
+  return '';
+}
+
+// 判断是否应该显示确认指派按钮
+function shouldShowConfirmAssignButton(order: OrderModel): boolean {
+  // 如果订单已核销或已发货,不显示
+  if (isOrderVerified(order) || isOrderShipped(order)) {
+    return false;
+  }
+
+  const orderId = getOrderId(order);
+  // 检查是否已标记为可显示按钮(从右边列表选择、上次供应商或下面供应商填充)
+  // 只要标记为可显示,就显示按钮(即使后来点击取消清空了输入框)
+  const canShowButton = assignedFromRightList.value[orderId];
+  return canShowButton === true;
+}
+
 // 处理订单选中
 function handleOrderSelect(order: OrderModel) {
   const orderId = getOrderId(order);
@@ -927,11 +981,13 @@ async function loadOrderCounts() {
     const data = response?.data ?? response;
 
     orderCounts.value = {
-      offlineCount: Number(data?.offlineCount) || 0, // 线下服务订单总和
+      // offlineCount: Number(data?.offlineCount) || 0, // 线下服务订单总和
+      offlineCount: Number(data?.pendPieOfflineOrderCount) || 0,
       allPieOfflineOrderCount: Number(data?.allPieOfflineOrderCount) || 0, // 线下服务——全部指派订单数量
       pendPieOfflineOrderCount: Number(data?.pendPieOfflineOrderCount) || 0, // 线下服务——待指派订单数量
       todayPieOfflineOrderCount: Number(data?.todayPieOfflineOrderCount) || 0, // 线下服务——今日指派订单数量
-      onlineCount: Number(data?.onlineCount) || 0, // 实体商品数量订单总和
+      // onlineCount: Number(data?.onlineCount) || 0, // 实体商品数量订单总和
+      onlineCount: Number(data?.pendPieOnlineOrderCount) || 0, // 实体商品数量订单总和
       allPieOnlineOrderCount: Number(data?.allPieOnlineOrderCount) || 0, // 实体商品——全部指派订单数量
       pendPieOnlineOrderCount: Number(data?.pendPieOnlineOrderCount) || 0, // 实体商品——待指派订单数量
       todayPieOnlineOrderCount: Number(data?.todayPieOnlineOrderCount) || 0, // 实体商品——今日指派订单数量
@@ -1106,18 +1162,17 @@ defineExpose({
                 <div class="assign-section">
                   <span class="assign-label">分配机构:</span>
                   <a-input
-                    :value="assignedInstitutionMap[getOrderId(order)] || (activeSubTab !== 'pending' ? (order.conditioningProgramSupplierName || '') : '')"
+                    :value="getAssignedInstitutionDisplayValue(order)"
                     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"
+                  <a-button 
+                    v-if="shouldShowConfirmAssignButton(order)" 
+                    type="default" 
+                    size="small" 
+                    @click.stop="handleCancel(getOrderId(order))">
+                    取消
+                  </a-button>
+                  <a-button v-if="shouldShowConfirmAssignButton(order)" type="primary" size="small"
                     @click.stop="handleConfirmAssign(getOrderId(order))">
                     确认指派
                   </a-button>
@@ -1243,7 +1298,7 @@ defineExpose({
         flex-direction: column;
         overflow: hidden;
 
-     
+
         .order-list-header {
           flex-shrink: 0;
           padding: 16px;

+ 0 - 1
src/pages/index/care/issueService.vue

@@ -1113,7 +1113,6 @@ function openPatientHealthRecord(row: { id: string }, showType: 'analysis' | 'sc
                 <div v-if="row.conditioningProgramDetail?.name === '健康咨询' || row.conditioningProgramDetail?.name === '健康评估'" class="flex items-center">
                   <div class="flex items-center mr-4">
                     <span>每</span>
-                    {{ row.frequencyType }}
                     <a-input
                       v-model:value="row.frequencyType"
                       style="width: 50px"

+ 14 - 0
src/pages/index/equipment/configured.vue

@@ -171,6 +171,10 @@ const gridOptions = reactive<VxeGridProps<DeviceManageModel>>({
   autoResize: false,
   syncResize: true,
   scrollY: { enabled: true, gt: 0 },
+  rowConfig: {
+    isHover: true,
+    isCurrent: true,
+  },
   toolbarConfig: {
     custom: true,
     zoom: true,
@@ -361,6 +365,16 @@ function importOrganization() {
   padding: 0 24px;
   max-height: var(--page-main-container);
 }
+
+/* 行 hover & 选中高亮背景 */
+:deep(.vxe-table .vxe-body--row.row--hover) {
+  background-color: #f5f7ff !important;
+}
+
+:deep(.vxe-table .vxe-body--row.row--current),
+:deep(.vxe-table .vxe-body--row.row--checked) {
+  background-color: #e6f7ff !important;
+}
 .date-range-container {
   display: flex;
   align-items: center;

+ 1 - 0
src/pages/index/order/management.vue

@@ -158,6 +158,7 @@ const gridOptions = reactive<VxeGridProps<ConditioningRecordListModel>>({
     { field: 'progress', title: '调理状态', slots: { default: 'patients' } },
     { field: 'orderStatus', title: '订单状态', slots: { default: 'orderStatus' } },
     { field: 'cost', title: '订单金额' },
+    { field: 'profitSharing', title: '分账' },
     {
       field: 'action',
       title: '操作',

+ 1 - 1
src/pages/index/order/revenueSharing.vue

@@ -239,7 +239,7 @@ function serviceDetail(model?: ConditioningRecordListModel) {
           <span class="order-no-link" @click="serviceDetail(row as any)">{{ row.orderNo }}</span>
         </template>
         <template #revenueStatusCell="{ row }">
-          <span>{{ row.profitSharingStatus === '1' ? '未分账' : row.revenueStatus === '2' ? '已分账' : row.profitSharingStatus === '3' ? '分账异常' : '' }}</span>
+          <span>{{ row.profitSharingStatus === '1' ? '未分账' : row.profitSharingStatus === '2' ? '已分账' : row.profitSharingStatus === '3' ? '分账异常' : '' }}</span>
         </template>
         <template #toolbar-extra>
           <vxe-button style="margin-right: 12px" icon="vxe-icon-repeat" circle @click="refresh(page)"></vxe-button>

+ 263 - 259
src/service/AddItems.vue

@@ -40,13 +40,13 @@ const { loading: branchLoading } = useRequest(branchMethod).onSuccess(({ data })
   const to = (data?: any[]): any[] => {
     return Array.isArray(data)
       ? data.map((item) => {
-          return {
-            ...item,
-            value: item.id,
-            key: item.id.toString(),
-            children: to(item.children),
-          };
-        })
+        return {
+          ...item,
+          value: item.id,
+          key: item.id.toString(),
+          children: to(item.children),
+        };
+      })
       : [];
   };
   branch.value = to(data);
@@ -102,8 +102,6 @@ const form = reactive<any>({
 
 const checkedList = ref<string[]>(['1']); // 默认选中第一个
 
-const deliverArr = ref<string[]>([]);
-
 // 建议频率相关字段
 const suggestedFrequencyUnit = ref<string>(''); // 建议频率单位
 
@@ -114,20 +112,35 @@ const isDerivationModalOpen = ref(false);
 const rules: any = {
   name: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
   conditioningProgramType: [{ required: true, message: '请选择方案类型', trigger: 'change' }],
+  offlineDuration: [{ required: true, message: '请输入服务所需时间', trigger: 'blur' }],
   institutionId: [
     {
-      required: true,
-      message: '请选择机构名称',
       trigger: 'blur',
       validator: async (_rule: any, value: any) => {
         // 如果推导逻辑弹窗打开,跳过验证
         if (isDerivationModalOpen.value) return Promise.resolve();
-        if (!value) return Promise.reject(new Error('请选择机构名称'));
+        if (!value) return Promise.reject('请选择机构名称');
         return Promise.resolve();
       },
     },
   ],
   photo: [{ required: true, message: '请上传商品图片', trigger: 'change' }],
+  isDelivery: [
+    {
+      trigger: 'change',
+      validator: async (_rule: any, value: any) => {
+        // 如果只选择了调理方案项目(包含 '2' 但不包含 '1'),配送不必填
+        if (checkedList.value.includes('2') && !checkedList.value.includes('1')) {
+          return Promise.resolve();
+        }
+        // 项目应用含有服务包项目或商品类型是实体商品时,配送必填
+        if ((checkedList.value.includes('1') || form.sellType === '1') && !value) {
+          return Promise.reject('请选择配送方式');
+        }
+        return Promise.resolve();
+      },
+    },
+  ],
 };
 const showMiniProgramCode = ref<boolean>(false);
 const showBuyLink = ref<boolean>(false);
@@ -161,6 +174,16 @@ const showDelivery = computed(() => {
   return form.sellType === '1';
 });
 
+// 判断配送是否必填(显示红色星号)
+const isDeliveryRequired = computed(() => {
+  // 如果只选择了调理方案项目,不必填
+  if (checkedList.value.includes('2') && !checkedList.value.includes('1')) {
+    return false;
+  }
+  // 项目应用含有服务包项目或商品类型是实体商品时,必填
+  return checkedList.value.includes('1') || form.sellType === '1';
+});
+
 // 线下服务:itemsList 和 system 类型都需要“服务所需时间”
 const showServiceRequiredTime = computed(() => {
   return form.sellType === '2';
@@ -207,10 +230,10 @@ function cancel() {
 }
 function doSubmit() {
   // 手动验证方案类型
-  if (!form.conditioningProgramType) {
-    message.error('请选择方案类型');
-    return;
-  }
+  // if (!form.conditioningProgramType) {
+  //   message.error('请选择方案类型');
+  //   return;
+  // }
 
   // 自定义验证:检查项目应用是否已选择
   if (!checkedList.value || checkedList.value.length === 0) {
@@ -269,15 +292,15 @@ function doSubmit() {
         return;
       }
       // 线下服务:服务所需时间必填
-      if (form.sellType === '2' && !form.offlineDuration) {
-        message.error('请输入服务所需时间');
-        return;
-      }
+      // if (form.sellType === '2' && !form.offlineDuration) {
+      //   message.error('请输入服务所需时间');
+      //   return;
+      // }
       // 实体商品:配送必填(itemsList 和 system 类型都需要)
-      if (form.sellType === '1' && (!deliverArr.value || deliverArr.value.length === 0)) {
-        message.error('请选择配送方式');
-        return;
-      }
+      // if (form.sellType === '1' && (!deliverArr.value || deliverArr.value.length === 0)) {
+      //   message.error('请选择配送方式');
+      //   return;
+      // }
     }
   } else if (form.addType === 'itemsList') {
     // 计价规则非必填时,做全填/全空校验
@@ -321,25 +344,25 @@ function doSubmit() {
 
   // 购买链接与跳转类型联动校验
   if (form.addType === 'itemsList') {
-   
+
     // 有购买链接则必须选择跳转类型
     const hasBuyUrl = !!(form.attrEighth && String(form.attrEighth).trim());
     if (hasBuyUrl && !form.attrNinth) {
       message.error('请选择跳转类型');
       return;
     }
-     // 如果选择了小程序码类型,类型是小程序码  小程序码必填 链接可填
-     if (form.attrNinth === 'miniprogram' && miniProgramCodeList.value.length === 0) {
+    // 如果选择了小程序码类型,类型是小程序码  小程序码必填 链接可填
+    if (form.attrNinth === 'miniprogram' && miniProgramCodeList.value.length === 0) {
       message.error('请上传小程序码');
       return;
     }
   }
 
   // 商品图片必填
-  if (!fileList.value || fileList.value.length === 0) {
-    message.error('请上传商品图片');
-    return;
-  }
+  // if (!fileList.value || fileList.value.length === 0) {
+  //   message.error('请上传商品图片');
+  //   return;
+  // }
 
   formRef.value?.validate().then(() => {
     form.photo = fileList.value[0]?.response?.url || fileList.value[0]?.url || '';
@@ -419,7 +442,7 @@ onMounted(async () => {
     if (res.isForInfer === 'Y') checked.push('2');
     checkedList.value = checked.length > 0 ? checked : ['1', '2'];
     Object.assign(form, res);
-     suggestedFrequencyUnit.value = res.convertUnit; // 建议频率使用单位
+    suggestedFrequencyUnit.value = res.convertUnit; // 建议频率使用单位
     form.cpMedicines = (res.cpMedicines ?? []).map((item: any) => ({
       name: item.name || item.herbName || item.medicineName || '',
       dosage: item.dosage,
@@ -434,36 +457,36 @@ onMounted(async () => {
 
     fileList.value = res.photo
       ? [
-          {
-            uid: '-1',
-            name: 'image.png',
-            status: 'done',
-            url: res.photo,
-            thumbUrl: res.photo,
-          },
-        ]
+        {
+          uid: '-1',
+          name: 'image.png',
+          status: 'done',
+          url: res.photo,
+          thumbUrl: res.photo,
+        },
+      ]
       : [];
     optionsList.value = res.itemImgFirst
       ? [
-          {
-            uid: '-1',
-            name: 'image.png',
-            status: 'done',
-            url: res.itemImgFirst,
-            thumbUrl: res.itemImgFirst,
-          },
-        ]
+        {
+          uid: '-1',
+          name: 'image.png',
+          status: 'done',
+          url: res.itemImgFirst,
+          thumbUrl: res.itemImgFirst,
+        },
+      ]
       : [];
     miniProgramCodeList.value = res.attrTen
       ? [
-          {
-            uid: '-1',
-            name: 'image.png',
-            status: 'done',
-            url: res.attrTen,
-            thumbUrl: res.attrTen,
-          },
-        ]
+        {
+          uid: '-1',
+          name: 'image.png',
+          status: 'done',
+          url: res.attrTen,
+          thumbUrl: res.attrTen,
+        },
+      ]
       : [];
     // 处理视频数据
     if (res.itemVideoFirst) {
@@ -728,7 +751,7 @@ watch(
       // 一口价模式:建议频率单位与一口价单位保持一致
       if (form.cpFixedPricingRule?.convertUnit) {
         suggestedFrequencyUnit.value = form.cpFixedPricingRule.convertUnit;
-      }else{
+      } else {
         suggestedFrequencyUnit.value = '';
       }
     } else if (val === '1') {
@@ -772,21 +795,47 @@ watch(
   (newVal) => {
     if (newVal === '1') {
       // 实体商品:默认支持配送(itemsList / system 都一样)
-      form.isDelivery = 'Y';
-      deliverArr.value = ['Y'];
+      // 但如果只选择了调理方案项目,配送不是必填,不设置默认值,让用户自己选择
+      if (!(checkedList.value.includes('2') && !checkedList.value.includes('1'))) {
+        form.isDelivery = 'Y';
+      }
     } else {
       // 其他类型:清空配送
       form.isDelivery = null;
-      deliverArr.value = [];
     }
   }
 );
-function deliveryChange(value: any) {
-  form.isDelivery = value[value.length - 1];
-}
+
+// 监听项目应用变化,当只选择调理方案项目时,自动设置为一口价
+watch(
+  () => checkedList.value,
+  (newVal) => {
+    // 如果只选择了调理方案项目(包含 '2' 但不包含 '1'),自动设置为一口价
+    if (newVal.includes('2') && !newVal.includes('1')) {
+      form.pricingType = '0';
+      // 如果只选择调理方案项目,配送不是必填,清空默认值让用户自己选择
+      if (form.sellType === '1') {
+        form.isDelivery = null;
+      }
+    } else if (form.sellType === '1' && !form.isDelivery) {
+      // 如果从只选择调理方案项目变为其他情况,且是实体商品,恢复默认值
+      form.isDelivery = 'Y';
+    }
+  }
+);
 function getConditioningProgramSupplier() {
   // 供应商改为可选,不做额外强制联动
 }
+
+// 处理配送选项点击,允许取消选择
+function handleDeliveryClick(value: string) {
+  // 如果点击的是已选中的选项,则取消选择
+  if (form.isDelivery === value) {
+    form.isDelivery = null;
+  } else {
+    form.isDelivery = value;
+  }
+}
 // 项目应用数据
 const plainOptions = [
   { id: '1', name: '服务包项目' },
@@ -868,17 +917,8 @@ function handleDerivation() {
         <a-input v-model:value="form.name" placeholder="请输入" />
       </a-form-item>
       <a-form-item label="方案类型:" name="conditioningProgramType" style="width: 100%" required>
-        <vxe-select
-          v-model="form.conditioningProgramType"
-          :options="typeOptions"
-          placeholder="请选择方案类型"
-          clearable
-          filterable
-          transfer
-          empty-text="暂无数据"
-          @focus="handleSelectClick"
-          style="width: 100%"
-        />
+        <vxe-select v-model="form.conditioningProgramType" :options="typeOptions" placeholder="请选择方案类型" clearable
+          filterable transfer empty-text="暂无数据" @focus="handleSelectClick" style="width: 100%" />
       </a-form-item>
       <a-form-item label="项目应用:" required v-if="form.addType === 'itemsList'">
         <a-checkbox-group v-model:value="checkedList">
@@ -894,10 +934,10 @@ function handleDerivation() {
           <a-radio value="3">线上权益</a-radio>
         </a-radio-group>
       </a-form-item>
-      <a-form-item label="计价规则:" name="pricingType" :required="checkedList.includes('1')">
+      <a-form-item label="计价规则:" name="pricingType" :required="checkedList.includes('1') || (checkedList.includes('2') && !checkedList.includes('1'))">
         <a-radio-group v-model:value="form.pricingType">
           <a-radio value="0">一口价</a-radio>
-          <a-radio value="1">按穴位/经络/部位</a-radio>
+          <a-radio value="1" v-if="!checkedList.includes('2')">按穴位/经络/部位</a-radio>
         </a-radio-group>
       </a-form-item>
       <div class="price-row" v-if="form.pricingType === '0' && form.cpFixedPricingRule">
@@ -909,101 +949,70 @@ function handleDerivation() {
         <a-input v-model:value="form.cpFixedPricingRule.pricingUnit" placeholder="请输入" style="width: 100px" />
 
         <span style="margin-left: 32px">相当于</span>
-        <a-input v-model:value="form.cpFixedPricingRule.convertDose" placeholder="请输入" style="width: 100px; margin-left: 8px" />
-        <vxe-select v-model="form.cpFixedPricingRule.convertUnit" :options="unitOptions" placeholder="请选择" clearable transfer style="width: 100px; margin-left: 8px" />
+        <a-input v-model:value="form.cpFixedPricingRule.convertDose" placeholder="请输入"
+          style="width: 100px; margin-left: 8px" />
+        <vxe-select v-model="form.cpFixedPricingRule.convertUnit" :options="unitOptions" placeholder="请选择" clearable
+          transfer style="width: 100px; margin-left: 8px" />
 
         <span style="color: #aaa; margin-left: 8px">(使用单位)</span>
       </div>
-      <a-form-item label="建议频率:" v-if="form.addType === 'itemsList' || form.addType === 'system'" required>
-        <div class="suggested-frequency-row">
-          <span>每</span>
-          <a-input v-model:value="form.frequencyType" placeholder="请输入" style="width: 100px; margin: 0 8px" />
-          <span>天</span>
-          <a-input v-model:value="form.frequencyMeasure" placeholder="请输入" style="width: 100px; margin: 0 8px" />
-          <vxe-select
-            v-model="suggestedFrequencyUnit"
-            :options="unitOptions"
-            placeholder="请选择"
-            :clearable="form.pricingType !== '0'"
-            transfer
-            style="width: 100px; margin-left: 8px"
-          />
-        </div>
-      </a-form-item>
-      <a-form-item
-        label="服务所需时间:"
-        v-if="(form.addType === 'itemsList' || form.addType === 'system') && showServiceRequiredTime"
-        required
-      >
-        <a-input v-model:value="form.offlineDuration" placeholder="请输入" style="width: 200px" />
-        <span style="margin-left: 8px">分钟</span>
-      </a-form-item>
       <div v-if="form.pricingType === '1'" class="per-rule">
         <div class="price-row">
           <span>计价1:</span>
-          <span class="flex items-center"
-            >当"穴位/经络/部位" ≤
-            <a-input
-              placeholder="请输入"
-              class="w-20 ml-2 mr-2"
-              v-model:value="form.cpDynamicPricingRule[0].min"
-              @change="() => (form.cpDynamicPricingRule[1].max = form.cpDynamicPricingRule[0].min)"
-            />个时,</span
-          >
-          <vxe-select
-            v-model="form.cpDynamicPricingRule[0].priceType"
-            :options="[
-              { label: '单价', value: 0, priceType: 0 },
-              { label: '一口价', value: 1, priceType: 1 },
-            ]"
-            placeholder="请选择"
-            clearable
-            transfer
-            style="width: 100px; margin: 0 4px"
-          />
+          <span class="flex items-center">当"穴位/经络/部位" ≤
+            <a-input placeholder="请输入" class="w-20 ml-2 mr-2" v-model:value="form.cpDynamicPricingRule[0].min"
+              @change="() => (form.cpDynamicPricingRule[1].max = form.cpDynamicPricingRule[0].min)" />个时,</span>
+          <vxe-select v-model="form.cpDynamicPricingRule[0].priceType" :options="[
+            { label: '单价', value: 0, priceType: 0 },
+            { label: '一口价', value: 1, priceType: 1 },
+          ]" placeholder="请选择" clearable transfer style="width: 100px; margin: 0 4px" />
           <span>=</span>
-          <a-input v-model:value="form.cpDynamicPricingRule[0].price" style="width: 80px; margin: 0 4px" placeholder="请输入" />
+          <a-input v-model:value="form.cpDynamicPricingRule[0].price" style="width: 80px; margin: 0 4px"
+            placeholder="请输入" />
           <span>元</span>
         </div>
         <div class="price-row">
           <span>计价2:</span>
-          <span class="flex items-center"
-            >当"穴位/经络/部位" &gt; <a-input placeholder="请输入" class="w-20 ml-2 mr-2" v-model:value="form.cpDynamicPricingRule[1].max" disabled />个时,</span
-          >
-          <vxe-select
-            v-model="form.cpDynamicPricingRule[1].priceType"
-            :options="[
-              { label: '单价', value: 0, priceType: 0 },
-              { label: '一口价', value: 1, priceType: 1 },
-            ]"
-            placeholder="请选择"
-            clearable
-            transfer
-            style="width: 100px; margin: 0 4px"
-          />
+          <span class="flex items-center">当"穴位/经络/部位" &gt; <a-input placeholder="请输入" class="w-20 ml-2 mr-2"
+              v-model:value="form.cpDynamicPricingRule[1].max" disabled />个时,</span>
+          <vxe-select v-model="form.cpDynamicPricingRule[1].priceType" :options="[
+            { label: '单价', value: 0, priceType: 0 },
+            { label: '一口价', value: 1, priceType: 1 },
+          ]" placeholder="请选择" clearable transfer style="width: 100px; margin: 0 4px" />
           <span>=</span>
-          <a-input v-model:value="form.cpDynamicPricingRule[1].price" style="width: 80px; margin: 0 4px" placeholder="请输入" />
+          <a-input v-model:value="form.cpDynamicPricingRule[1].price" style="width: 80px; margin: 0 4px"
+            placeholder="请输入" />
           <span>元</span>
         </div>
       </div>
+      <a-form-item label="建议频率:" v-if="form.addType === 'itemsList' || form.addType === 'system'" required>
+        <div class="suggested-frequency-row">
+          <span>每</span>
+          <a-input v-model:value="form.frequencyType" placeholder="请输入" style="width: 100px; margin: 0 8px" />
+          <span>天</span>
+          <a-input v-model:value="form.frequencyMeasure" placeholder="请输入" style="width: 100px; margin: 0 8px" />
+          <vxe-select v-model="suggestedFrequencyUnit" :options="unitOptions" placeholder="请选择"
+            :clearable="form.pricingType !== '0'" transfer style="width: 100px; margin-left: 8px" />
+        </div>
+      </a-form-item>
+      <a-form-item label="服务所需时间:"
+      name="offlineDuration"
+        v-if="(form.addType === 'itemsList' || form.addType === 'system') && showServiceRequiredTime" required>
+        <a-input v-model:value="form.offlineDuration" placeholder="请输入" style="width: 200px" />
+        <span style="margin-left: 8px">分钟</span>
+      </a-form-item>
+
 
       <a-form-item label="组成:">
         <div class="herb-list">
           <template v-for="(herb, idx) in form.cpMedicines" :key="herb.id">
             <div class="herb-item">
-              <button class="herb-remove" v-if="form?.cpMedicines?.length > 1" @click="removeHerb(idx)" type="button">×</button>
+              <button class="herb-remove" v-if="form?.cpMedicines?.length > 1" @click="removeHerb(idx)"
+                type="button">×</button>
               <!-- <RemoteSelect :load="cpMedicinesMethod" key-prop="name" v-model:value="herb.name" /> -->
-              <SearchableSelect
-                v-model="herb.name"
-                :options="herbList"
-                placeholder="请选择"
-                value-key="name"
-                label-key="name"
-                key-key="id"
-                :fetch-options="cpMedicinesMethod"
-                style="width: 150px"
-                :allow-custom-input="true"
-              />
+              <SearchableSelect v-model="herb.name" :options="herbList" placeholder="请选择" value-key="name"
+                label-key="name" key-key="id" :fetch-options="cpMedicinesMethod" style="width: 150px"
+                :allow-custom-input="true" />
               <a-input v-model:value="herb.dosage" class="herb-dosage" placeholder="剂量" />
               <span>g</span>
             </div>
@@ -1012,12 +1021,8 @@ function handleDerivation() {
         </div>
       </a-form-item>
       <a-form-item label="特色:" v-if="form.addType === 'itemsList'">
-        <textarea
-          v-model="form.attrFirst"
-          placeholder="请输入"
-          rows="3"
-          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit"
-        />
+        <textarea v-model="form.attrFirst" placeholder="请输入" rows="3"
+          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit" />
       </a-form-item>
       <a-form-item label="功效:">
         <a-input v-model:value="form.effect" placeholder="请输入" />
@@ -1029,17 +1034,14 @@ function handleDerivation() {
         <a-input v-model:value="form.attrFourth" placeholder="请输入" />
       </a-form-item>
       <a-form-item label="操作方法:" v-if="form.addType === 'itemsList'">
-        <textarea
-          v-model="form.attrFifth"
-          placeholder="请输入"
-          rows="3"
-          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit"
-        />
+        <textarea v-model="form.attrFifth" placeholder="请输入" rows="3"
+          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit" />
       </a-form-item>
 
       <div class="image-row">
         <a-form-item label="操作图片:" class="image-form-item" v-if="form.addType === 'itemsList'">
-          <a-upload :showUploadList="uploadProps" v-model:file-list="optionsList" list-type="picture-card" @preview="handlePreview" :maxCount="1" :customRequest="customUpload">
+          <a-upload :showUploadList="uploadProps" v-model:file-list="optionsList" list-type="picture-card"
+            @preview="handlePreview" :maxCount="1" :customRequest="customUpload">
             <div v-if="optionsList.length < 1">
               <PlusOutlined />
               <div style="margin-top: 8px">上传</div>
@@ -1047,7 +1049,8 @@ function handleDerivation() {
           </a-upload>
         </a-form-item>
         <a-form-item label="商品图片:" class="image-form-item" name="photo" required>
-          <a-upload :showUploadList="uploadProps" v-model:file-list="fileList" list-type="picture-card" @preview="handlePreview" :maxCount="1" :customRequest="customUpload">
+          <a-upload :showUploadList="uploadProps" v-model:file-list="fileList" list-type="picture-card"
+            @preview="handlePreview" :maxCount="1" :customRequest="customUpload">
             <div v-if="fileList.length < 1">
               <PlusOutlined />
               <div style="margin-top: 8px">上传</div>
@@ -1058,16 +1061,9 @@ function handleDerivation() {
       <a-form-item label="操作视频:" class="image-form-item" v-if="form.addType === 'itemsList'">
         <div class="video-upload-wrapper">
           <div class="video-upload-container">
-            <a-upload
-              :showUploadList="false"
-              :custom-request="customVideoRequest"
-              :max-count="1"
-              :multiple="false"
-              :before-upload="beforeVideoUpload"
-              @change="handleVideoChange"
-              :file-list="videoFileList"
-              accept="video/mp4,video/avi,video/mov,video/webm"
-            >
+            <a-upload :showUploadList="false" :custom-request="customVideoRequest" :max-count="1" :multiple="false"
+              :before-upload="beforeVideoUpload" @change="handleVideoChange" :file-list="videoFileList"
+              accept="video/mp4,video/avi,video/mov,video/webm">
               <div v-if="!form.itemVideoFirst && !uploading" class="video-upload-btn">
                 <PlusOutlined />
                 <div style="margin-top: 8px">上传视频</div>
@@ -1087,17 +1083,13 @@ function handleDerivation() {
                 <div class="video-thumbnail">
                   <video :src="form.itemVideoFirst" preload="metadata" class="video-preview" />
                   <div class="video-overlay">
-                    <a-button
-                      type="primary"
-                      size="small"
-                      @click.stop="
-                        handleVideoPreview({
-                          uid: 'video-preview',
-                          name: '操作视频',
-                          url: form.itemVideoFirst,
-                        })
-                      "
-                    >
+                    <a-button type="primary" size="small" @click.stop="
+                      handleVideoPreview({
+                        uid: 'video-preview',
+                        name: '操作视频',
+                        url: form.itemVideoFirst,
+                      })
+                      ">
                       预览
                     </a-button>
                     <a-button danger size="small" @click.stop="removeVideo"> 删除 </a-button>
@@ -1113,20 +1105,12 @@ function handleDerivation() {
         </div>
       </a-form-item>
       <a-form-item label="疗程说明:" v-if="form.addType === 'itemsList'">
-        <textarea
-          v-model="form.attrSixth"
-          placeholder="请输入"
-          rows="3"
-          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit"
-        />
+        <textarea v-model="form.attrSixth" placeholder="请输入" rows="3"
+          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit" />
       </a-form-item>
       <a-form-item label="使用注意:" v-if="form.addType === 'itemsList'">
-        <textarea
-          v-model="form.attrSeventh"
-          placeholder="请输入"
-          rows="3"
-          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit"
-        />
+        <textarea v-model="form.attrSeventh" placeholder="请输入" rows="3"
+          style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 4px; resize: vertical; font-family: inherit" />
       </a-form-item>
       <a-form-item label="推导逻辑:" v-if="form.addType === 'itemsList'" :required="checkedList.includes('2')">
         <button @click="handleDerivation" v-if="!hasDerivationLogic || isDerivationEmpty">编辑</button>
@@ -1140,23 +1124,37 @@ function handleDerivation() {
               <span class="derivation-label">年龄限制:</span>
               <span class="derivation-content">{{ derivationData.cpPatientMatchRule.age }}</span>
             </div>
-            <div v-if="derivationData.cpPatientMatchRule.diagnoseDiseaseNames && derivationData.cpPatientMatchRule.diagnoseDiseaseNames.length > 0" class="derivation-item">
+            <div
+              v-if="derivationData.cpPatientMatchRule.diagnoseDiseaseNames && derivationData.cpPatientMatchRule.diagnoseDiseaseNames.length > 0"
+              class="derivation-item">
               <span class="derivation-label">专病:</span>
-              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.diagnoseDiseaseNames.join('、') }}</span>
+              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.diagnoseDiseaseNames.join('、')
+                }}</span>
             </div>
-            <div v-if="derivationData.cpPatientMatchRule.diagnoseSyndromeNames && derivationData.cpPatientMatchRule.diagnoseSyndromeNames.length > 0" class="derivation-item">
+            <div
+              v-if="derivationData.cpPatientMatchRule.diagnoseSyndromeNames && derivationData.cpPatientMatchRule.diagnoseSyndromeNames.length > 0"
+              class="derivation-item">
               <span class="derivation-label">证型:</span>
-              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.diagnoseSyndromeNames.join('、') }}</span>
+              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.diagnoseSyndromeNames.join('、')
+                }}</span>
             </div>
-            <div v-if="derivationData.cpPatientMatchRule.constitutionGroupNames && derivationData.cpPatientMatchRule.constitutionGroupNames.length > 0" class="derivation-item">
+            <div
+              v-if="derivationData.cpPatientMatchRule.constitutionGroupNames && derivationData.cpPatientMatchRule.constitutionGroupNames.length > 0"
+              class="derivation-item">
               <span class="derivation-label">体质:</span>
-              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.constitutionGroupNames.join('、') }}</span>
+              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.constitutionGroupNames.join('、')
+                }}</span>
             </div>
-            <div v-if="derivationData.cpPatientMatchRule.willillStateNames && derivationData.cpPatientMatchRule.willillStateNames.length > 0" class="derivation-item">
+            <div
+              v-if="derivationData.cpPatientMatchRule.willillStateNames && derivationData.cpPatientMatchRule.willillStateNames.length > 0"
+              class="derivation-item">
               <span class="derivation-label">欲病状态:</span>
-              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.willillStateNames.join('、') }}</span>
+              <span class="derivation-content">{{ derivationData.cpPatientMatchRule.willillStateNames.join('、')
+                }}</span>
             </div>
-            <div v-if="derivationData.cpPatientMatchRule.tabooCrowds && derivationData.cpPatientMatchRule.tabooCrowds.length > 0" class="derivation-item">
+            <div
+              v-if="derivationData.cpPatientMatchRule.tabooCrowds && derivationData.cpPatientMatchRule.tabooCrowds.length > 0"
+              class="derivation-item">
               <span class="derivation-label">禁忌人群:</span>
               <span class="derivation-content">{{ derivationData.cpPatientMatchRule.tabooCrowds.join('、') }}</span>
             </div>
@@ -1165,22 +1163,17 @@ function handleDerivation() {
       </a-form-item>
       <!-- 跳转类型 -->
       <a-form-item label="跳转类型:" v-if="form.addType === 'itemsList'" style="width: 100%">
-        <Vxe-select v-model="form.attrNinth" :options="jumpTypeOptions" placeholder="请选择" clearable transfer style="width: 100%" />
+        <Vxe-select v-model="form.attrNinth" :options="jumpTypeOptions" placeholder="请选择" clearable transfer
+          style="width: 100%" />
       </a-form-item>
       <!-- 购买链接 -->
       <a-form-item label="购买链接:" v-if="form.addType === 'itemsList' && showBuyLink">
         <a-input v-model:value="form.attrEighth" placeholder="请输入" />
       </a-form-item>
-      <a-form-item label="小程序码:" class="image-form-item" 
-      v-if="form.addType === 'itemsList' && showMiniProgramCode" :required="isMiniProgramCodeType">
-        <a-upload
-          :showUploadList="uploadProps"
-          v-model:file-list="miniProgramCodeList"
-          list-type="picture-card"
-          @preview="handlePreview"
-          :maxCount="1"
-          :customRequest="customUpload"
-        >
+      <a-form-item label="小程序码:" class="image-form-item" v-if="form.addType === 'itemsList' && showMiniProgramCode"
+        :required="isMiniProgramCodeType">
+        <a-upload :showUploadList="uploadProps" v-model:file-list="miniProgramCodeList" list-type="picture-card"
+          @preview="handlePreview" :maxCount="1" :customRequest="customUpload">
           <div v-if="miniProgramCodeList.length < 1">
             <PlusOutlined />
             <div style="margin-top: 8px">上传</div>
@@ -1189,41 +1182,27 @@ function handleDerivation() {
       </a-form-item>
       <!-- 机构名称 -->
       <a-form-item label="机构名称:" v-if="form?.addType === 'itemsList'" required name="institutionId">
-        <a-tree-select
-          :disabled="form.addType === 'itemsList' && !!form.sourceId"
-          v-model:value="form.institutionId"
-          style="width: 100%"
-          :dropdownStyle="{ maxHeight: '400px', overflow: 'auto', zIndex: 4000 }"
-          :dropdownMatchSelectWidth="false"
-          :getPopupContainer="getSafePopupContainer"
-          placeholder="请选择"
-          allow-clear
-          tree-default-expand-all
-          :tree-data="branch"
-          :loading="branchLoading"
-          @change="onInstitutionChange"
-        ></a-tree-select>
+        <a-tree-select :disabled="form.addType === 'itemsList' && !!form.sourceId" v-model:value="form.institutionId"
+          style="width: 100%" :dropdownStyle="{ maxHeight: '400px', overflow: 'auto', zIndex: 4000 }"
+          :dropdownMatchSelectWidth="false" :getPopupContainer="getSafePopupContainer" placeholder="请选择" allow-clear
+          tree-default-expand-all :tree-data="branch" :loading="branchLoading"
+          @change="onInstitutionChange"></a-tree-select>
       </a-form-item>
       <a-form-item label="供应商:" name="conditioningProgramSupplierId" :rules="supplierRules" style="width: 100%">
-        <a-select
-          v-model:value="form.conditioningProgramSupplierId"
-          :options="supplierOptions"
-          placeholder="请选择"
-          allow-clear
-          show-search
-          :getPopupContainer="getSafePopupContainer"
-          :dropdownMatchSelectWidth="false"
-          :dropdownStyle="{ zIndex: 5000 }"
-          style="width: 100%"
-          @change="getConditioningProgramSupplier"
-          not-found-content="暂无数据"
-        />
+        <a-select v-model:value="form.conditioningProgramSupplierId" :options="supplierOptions" placeholder="请选择"
+          allow-clear show-search :getPopupContainer="getSafePopupContainer" :dropdownMatchSelectWidth="false"
+          :dropdownStyle="{ zIndex: 5000 }" style="width: 100%" @change="getConditioningProgramSupplier"
+          not-found-content="暂无数据" />
       </a-form-item>
-      <a-form-item label="配送:" name="isDelivery" v-if="showDelivery" :required="form.sellType === '1'">
-        <a-checkbox-group v-model:value="deliverArr" @change="deliveryChange">
-          <a-checkbox value="Y">支持</a-checkbox>
-          <a-checkbox value="N">不支持</a-checkbox>
-        </a-checkbox-group>
+      <a-form-item  name="isDelivery" v-if="showDelivery">
+      
+         
+          <span> <span v-if="isDeliveryRequired" style="color: #ff4d4f">* </span>配送:</span>
+      
+        <a-radio-group :value="form.isDelivery">
+          <a-radio value="Y" @click="handleDeliveryClick('Y')">支持</a-radio>
+          <a-radio value="N" @click="handleDeliveryClick('N')">不支持</a-radio>
+        </a-radio-group>
       </a-form-item>
       <a-form-item label="启用状态:" name="status" v-if="form.addType === 'itemsList'" required>
         <a-radio-group v-model:value="form.status">
@@ -1231,16 +1210,12 @@ function handleDerivation() {
           <a-radio value="1">停用</a-radio>
         </a-radio-group>
       </a-form-item>
-      <a-image
-        :width="200"
-        :style="{ display: 'none' }"
-        :preview="{
-          visible,
-          onVisibleChange: setVisible,
-        }"
-        :src="previewImg"
-      />
-      <video :src="videoUrl" controls v-if="videoUrl" style="width: 100%; max-width: 500px; border-radius: 6px" preload="metadata" />
+      <a-image :width="200" :style="{ display: 'none' }" :preview="{
+        visible,
+        onVisibleChange: setVisible,
+      }" :src="previewImg" />
+      <video :src="videoUrl" controls v-if="videoUrl" style="width: 100%; max-width: 500px; border-radius: 6px"
+        preload="metadata" />
       <div class="form-actions-center">
         <a-button @click="cancel">取消</a-button>
         <a-button type="primary" style="background: #faad14; border: none" @click="doSubmit">确定</a-button>
@@ -1258,28 +1233,34 @@ function handleDerivation() {
   flex-direction: column;
   /* max-height: 80vh; */
 }
+
 .per-rule {
   margin-bottom: 16px;
 }
+
 .price-row {
   display: flex;
   align-items: center;
   margin-bottom: 15px;
 }
+
 .suggested-frequency-row {
   display: flex;
   align-items: center;
 }
+
 .label {
   font-weight: 500;
   color: #222;
   margin-right: 8px;
 }
+
 .herb-list {
   display: flex;
   align-items: center;
   flex-wrap: wrap;
 }
+
 .herb-item {
   position: relative;
   display: flex;
@@ -1288,11 +1269,13 @@ function handleDerivation() {
   margin-bottom: 8px;
   padding-left: 16px;
 }
+
 .herb-dosage {
   width: 70px;
   font-size: 14px;
   margin-right: 10px;
 }
+
 .herb-remove {
   position: absolute;
   left: 0;
@@ -1310,6 +1293,7 @@ function handleDerivation() {
   align-items: center;
   justify-content: center;
 }
+
 .form-actions-center {
   display: flex;
   justify-content: center;
@@ -1325,9 +1309,11 @@ function handleDerivation() {
   flex-direction: column;
   overflow: auto;
 }
+
 .slider-section {
   margin-bottom: 16px;
 }
+
 .slider-labels {
   display: flex;
   justify-content: space-between;
@@ -1337,10 +1323,12 @@ function handleDerivation() {
   font-weight: 500;
   color: #222;
 }
+
 .slider-row {
   display: flex;
   align-items: center;
 }
+
 .slider-desc {
   color: #222;
   white-space: nowrap;
@@ -1349,11 +1337,13 @@ function handleDerivation() {
   align-items: center;
   justify-content: center;
 }
+
 .slider-value {
   margin-left: 16px;
   color: #faad14;
   font-weight: bold;
 }
+
 .upload-btn {
   display: flex;
   flex-direction: column;
@@ -1367,51 +1357,61 @@ function handleDerivation() {
   transition: border-color 0.3s;
   width: 100px;
 }
+
 .upload-btn:hover {
   border-color: #1890ff;
   background: #f0f7ff;
 }
+
 .upload-btn .anticon {
   font-size: 16px;
   color: #1890ff;
 }
+
 .upload-btn .upload-text {
   margin-top: 8px;
   color: #666;
   font-size: 15px;
 }
+
 .video-preview {
   border-radius: 6px;
   background: #000;
   width: 120px !important;
   height: 100px !important;
 }
+
 .video-preview video {
   border-radius: 6px;
   background: #000;
   width: 120px;
   height: 100px;
 }
+
 .video-preview .ant-btn-link {
   padding: 0;
   font-size: 14px;
   color: #ff4d4f;
 }
+
 .image-row {
   display: flex;
   gap: 32px;
   align-items: flex-start;
   margin-bottom: 16px;
 }
+
 .image-form-item {
   flex: 1;
   margin-bottom: 0 !important;
 }
+
 .image-form-item .ant-upload-picture-card-wrapper {
   display: flex;
   flex-direction: column;
   align-items: flex-start;
 }
+
 .derivation-item {
   display: flex;
   align-items: center;
@@ -1422,6 +1422,7 @@ function handleDerivation() {
   border-radius: 4px;
   border: 1px solid #e8e8e8;
 }
+
 .derivation-label {
   font-weight: 500;
   color: #222;
@@ -1429,18 +1430,21 @@ function handleDerivation() {
   white-space: nowrap;
   font-size: 13px;
 }
+
 .derivation-content {
   color: #555;
   font-size: 13px;
   flex: 1;
   word-break: break-all;
 }
+
 .derivation-container {
   display: flex;
   flex-wrap: wrap;
   gap: 16px;
   align-items: flex-start;
 }
+
 .video-upload-wrapper {
   display: flex;
   align-items: flex-start;

+ 1 - 1
src/service/IntroduceProjectList.vue

@@ -83,7 +83,7 @@ function addProject(row: any) {
       id: 'add-items-modal',
       title: '新增项目',
       width: 1000,
-      height: 700,
+      height: 1000,
       escClosable: true,
       destroyOnClose: true,
       slots: {

+ 11 - 16
src/service/OrderDetail.vue

@@ -80,7 +80,7 @@ const deliveryInfo = computed(() => {
 // 点击项目名称查看商品详情
 function handleItemNameClick(row: any) {
   VxeUI.modal.open({
-    title: '项目商品详情',
+    title: '商品详情',
     fullscreen: true,
     escClosable: true,
     destroyOnClose: true,
@@ -292,7 +292,7 @@ watch(
       </div>
     </div>
     <!-- 用户信息 -->
-    <div class="info-section">
+    <div class="info-section" v-if="tableData?.patientName || (isShowDelivery && tableData.provinceName)">
       <h3 class="info-title">用户信息</h3>
       <div class="info-content-wrapper">
         <div class="info-content">
@@ -304,29 +304,21 @@ watch(
             <span class="info-label">收货信息:</span>
             <span class="info-value">{{ deliveryInfo }}</span>
           </div>
-          <!-- <div class="info-item" v-if="tableData?.contactPerson">
-          <span class="info-label">联系人:</span>
-          <span class="info-value">{{ tableData?.contactPerson }}</span>
-        </div>
-        <div class="info-item" v-if="tableData?.contactPhone || tableData?.phone">
-          <span class="info-label">联系电话:</span>
-          <span class="info-value">{{ tableData?.contactPhone || tableData?.phone }}</span>
-        </div> -->
         </div>
       </div>
     </div>
 
     <!-- 分账信息 -->
     <div class="info-section"
-      v-if="tableData?.profitSharing  || tableData?.profitSharingAmount ">
+      v-if="tableData?.profitSharing !== null || tableData?.profitSharingAmount !== null">
       <h3 class="info-title">分账信息</h3>
       <div class="info-content-wrapper">
-        <div class="info-content">
-          <div class="info-item" v-if="tableData?.profitSharing">
+        <div class="info-content-profit-sharing">
+          <div class="info-item" v-if="tableData?.profitSharing!==null">
             <span class="info-label">分账比例:</span>
-            <span class="info-value">{{ tableData?.profitSharing }}%</span>
+            <span class="info-value">{{ tableData?.profitSharing }}</span>
           </div>
-          <div class="info-item" v-if="tableData?.profitSharingAmount">
+          <div class="info-item" v-if="tableData?.profitSharingAmount !== null">
             <span class="info-label">分账金额:</span>
             <span class="info-value">
               {{ tableData?.profitSharingAmount }}元
@@ -342,7 +334,10 @@ watch(
 .table-wrapper {
   position: relative;
 }
-
+.info-content-profit-sharing{
+  display: flex;
+  gap: 50px;
+}
 .voided-stamp {
   position: absolute;
   left: 50%;

+ 2 - 5
src/service/ServiceItemsList.vue

@@ -247,11 +247,8 @@ function editConfirmed(model?: SystemItemModel, index?: number) {
   } else {
     VxeUI.modal.open({
       title: model?.id ? `编辑项目` : `新增项目`,
-      height: 800,
-      width: 850,
-      // position: {
-      //   top: Math.min(100, window.innerHeight * 0.1),
-      // },
+      height: 1000,
+      width: 900,
       escClosable: true,
       destroyOnClose: true,
       id: `add-items-modal`,

+ 2 - 2
src/service/ServiceItemsSystem.vue

@@ -246,8 +246,8 @@ function editItems(model?: SystemItemModel, index?: number) {
   } else {
     VxeUI.modal.open({
       title: model?.id ? `编辑项目` : `新增项目`,
-      height: 700,
-      width: 850,
+      height: 1000,
+      width: 900,
       position: {
         top: Math.min(100, window.innerHeight * 0.1),
       },

+ 116 - 31
src/service/SingleItemDetail.vue

@@ -25,9 +25,9 @@ const mockLogisticsData = computed(() => {
   console.log('props.data===', props.data);
   return {
     trackingNumber: `${expressTypeText[props.data.expressType || '']} ${props.data.expressNo || ''}`,
-    recipientName: props.data.liaison || '',
-    recipientPhone: props.data.phone || '',
-    recipientAddress: props.data.provinceName + ' ' + props.data.cityName + ' ' + props.data.areaName + ' ' + props.data.detailAddress
+    recipientName: props.data.liaison ? props.data.liaison + ', ' : '',
+    recipientPhone: props.data.phone ? props.data.phone + ', ' : '',
+    recipientAddress: (props.data.provinceName !== null ? props.data.provinceName + ' ' : '') + (props.data.cityName !== null ? props.data.cityName + ' ' : '') + (props.data.areaName !== null ? props.data.areaName + ' ' : '') + (props.data.detailAddress !== null ? props.data.detailAddress : '')
   };
 });
 const expressTypeText: Record<string, string> = {
@@ -42,7 +42,9 @@ const expressTypeText: Record<string, string> = {
 };
 // 包裹数据
 const mockPackageItems = computed(() => {
-  return (props.data as any).sameExpress
+  console.log('props.data===', props.data);
+
+  return (props.data as any).sameExpress ?? []
 });
 // 商品状态映射 - 收货状态(实体商品使用)
 const receiptStatusText: Record<string, string> = {
@@ -96,25 +98,25 @@ const sellTypeText: Record<string, string> = {
         <div class="info-content">
           <div class="info-row">
             <div class="info-item">
-              <span class="info-label">商品类型</span>
+              <span class="info-label">商品类型:</span>
               <span class="info-value">{{ sellTypeText[data.sellType] || '-' }}</span>
             </div>
             <div class="info-item">
-              <span class="info-label">方案类型</span>
+              <span class="info-label">方案类型:</span>
               <span class="info-value">{{ data.conditioningProgramType || '-' }}</span>
             </div>
           </div>
           <div class="info-row">
             <div class="info-item">
-              <span class="info-label">数量</span>
+              <span class="info-label">数量:</span>
               <span class="info-value">{{ data?.totalMeasure || '-' }}</span>
             </div>
             <div class="info-item">
-              <span class="info-label">商品总价</span>
+              <span class="info-label">商品总价:</span>
               <span class="info-value">¥{{ data?.totalPrice || '-' }}</span>
             </div>
             <div class="info-item" v-if="data?.sellType === '1' ? data?.receiptStatus : data?.progress">
-              <span class="info-label">商品状态</span>
+              <span class="info-label">商品状态:</span>
               <span class="info-value">
                 {{ data?.sellType === '1'
                   ? (data?.receiptStatus ? receiptStatusText[data.receiptStatus] : '-')
@@ -128,17 +130,31 @@ const sellTypeText: Record<string, string> = {
     <!-- 分账信息 -->
     <div class="info-section">
       <h3 class="info-title">分账信息</h3>
-      <vxe-table :data="mockSplitAccountList" border>
+      <vxe-table
+        class="split-account-table"
+        :data="mockSplitAccountList"
+        border
+      >
         <vxe-column field="profitSharingTime" title="分账时间" align="center" />
         <vxe-column field="conditioningProgramSupplierName" title="供应商" align="center" />
+        <vxe-column field="profitSharingStatus" title="分账状态" align="center" >
+          <template #default="{ row }">
+            {{ row.profitSharingStatus === '1' ? '未分账' : row.profitSharingStatus === '2' ? '已分账' : row.profitSharingStatus === '3' ? '分账异常' : '' }}
+          </template>
+        </vxe-column>
         <vxe-column field="profitSharing" title="分账比例" align="center">
           <template #default="{ row }">
             {{ row.profitSharing || '-' }}%
           </template>
         </vxe-column>
-        <vxe-column field="realAmount" title="分账金额" align="center">
+        <vxe-column field="profitSharingAmount" title="预计分账金额" align="center" >
+          <template #default="{ row }">
+            {{ row.profitSharingAmount ? row.profitSharingAmount + '元' : '' }}
+          </template>
+        </vxe-column>
+        <vxe-column field="realAmount" title="到账金额" align="center">
           <template #default="{ row }">
-            {{ row.realAmount }}元
+            {{ row.realAmount ? row.realAmount + '元' : '' }}
           </template>
         </vxe-column>
       </vxe-table>
@@ -146,22 +162,32 @@ const sellTypeText: Record<string, string> = {
     <!-- 物流信息和包裹内商品 实体商品才显示 sellType==='1'-->
     <div class="info-section" v-if="data.sellType === '1'">
       <!-- receiptType	收货方式 0-快递 1-线下取货 -->
-      <h3 class="info-title">{{ data.receiptType === '0' ? '物流信息' : data.receiptType === '1' ? '线下取货' : '' }}</h3>
-      <div class="info-content-wrapper">
+      <!-- <h3 class="info-title">{{ data.receiptType === '0' ? '物流信息' : data.receiptType === '1' ? '线下取货' : '' }}</h3> -->
+      <h3 class="info-title">物流信息</h3>
+    
+      <div class="info-content-wrapper" v-if="data.receiptType">
         <!-- 物流信息 -->
         <div class="logistics-content" v-if="data.receiptType === '0'">
-          <div class="logistics-tracking">
-            <span class="tracking-number">{{ mockLogisticsData.trackingNumber }}</span>
-            <a @click="handleCopyTracking" class="copy-link">复制</a>
-          </div>
-          <div class="logistics-recipient">
-            <span class="receive-icon">收</span>
-            <span class="recipient-info">
-              {{ mockLogisticsData.recipientName  
-              || '' }}, {{ mockLogisticsData.recipientPhone  || '' }}, {{
-                mockLogisticsData.recipientAddress || '' }}
-            </span>
-          </div>
+          <template v-if="mockPackageItems.length > 0">
+            <div class="logistics-tracking">
+              <span class="tracking-number">{{ mockLogisticsData.trackingNumber }}</span>
+              <a @click="handleCopyTracking" class="copy-link">复制</a>
+            </div>
+            <div class="logistics-recipient">
+              <span class="receive-icon">收</span>
+              <span class="recipient-info">
+                {{ mockLogisticsData.recipientName
+                  || '' }} {{ mockLogisticsData.recipientPhone || '' }} {{
+                  mockLogisticsData.recipientAddress || '' }}
+              </span>
+            </div>
+          </template>
+          <template v-else>
+            暂无
+          </template>
+        </div>
+        <div class="logistics-content" v-if="data.receiptType === '1'">
+          线下取货
         </div>
         <!-- 包裹内商品 -->
         <div class="package-items-wrapper" v-if="mockPackageItems.length > 0">
@@ -170,11 +196,15 @@ const sellTypeText: Record<string, string> = {
             包裹内商品
           </h3>
           <div class="package-items">
+          
             <div v-for="(item, index) in mockPackageItems" :key="index" class="package-item">
-              <div class="package-item-image">
+              <div class="package-item-image" v-if="item.conditioningProgramPhoto">
                 <a-image v-if="item.conditioningProgramPhoto" :width="60" :height="60" style="border-radius: 4px;"
                   :src="item.conditioningProgramPhoto" class="item-img" />
               </div>
+              <div class="package-item-placeholder" v-else>
+                <text class="placeholder-icon">📦</text>
+              </div>
               <div class="package-item-details">
                 <div class="package-item-name">{{ item.conditioningProgramName }}</div>
                 <div class="package-item-spec">{{ item.convertDose }} {{ item.convertUnit }}</div>
@@ -187,6 +217,9 @@ const sellTypeText: Record<string, string> = {
           </div>
         </div>
       </div>
+      <div class="info-content-wrapper" v-else>
+        暂无
+      </div>
     </div>
 
     <!-- 服务记录 线下服务才显示  1-实体商品 2-线下服务 3-线上权益-->
@@ -212,8 +245,16 @@ const sellTypeText: Record<string, string> = {
         <vxe-column field="operateTime" title="核销时间" align="center" />
         <vxe-column field="startTime" title="操作开始时间" align="center" />
         <vxe-column field="endTime" title="操作结束时间" align="center" />
-        <vxe-column field="operateDuration" title="治疗时长" align="center" />
-        <vxe-column field="arrangeDuration" title="预定服务时长" align="center" />
+        <vxe-column field="operateDuration" title="治疗时长" align="center">
+          <template #default="{ row }">
+            {{ row.operateDuration ? row.operateDuration + '分钟' : '' }}
+          </template>
+        </vxe-column>
+        <vxe-column field="arrangeDuration" title="预定服务时长" align="center">
+          <template #default="{ row }">
+            {{ row.arrangeDuration ? row.arrangeDuration + '分钟' : '' }}
+          </template>
+        </vxe-column>
         <vxe-column field="operateBy" title="操作人" align="center" />
         <vxe-column field="feedback" title="治疗备注" align="center" />
       </vxe-table>
@@ -233,6 +274,24 @@ const sellTypeText: Record<string, string> = {
     background: #fff;
   }
 
+  .service-package-placeholder {
+    width: 120rpx;
+    height: 120rpx;
+    border-radius: 8rpx;
+    margin-right: 20rpx;
+    background-color: #f5f5f5;
+    border: 1px solid #e8e8e8;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-shrink: 0;
+  }
+
+  .placeholder-icon {
+    font-size: 40rpx;
+    opacity: 0.3;
+  }
+
   .info-title {
     font-size: 16px;
     font-weight: 600;
@@ -256,7 +315,22 @@ const sellTypeText: Record<string, string> = {
   .info-content-wrapper {
     border: 1px solid #e8e8e8;
     border-radius: 4px;
-    padding: 20px;
+    padding: 10px 20px;
+  }
+
+  /* 分账表格仅按内容自适应高度,避免一行数据时出现多余空白 */
+  .split-account-table {
+    :deep(.vxe-table--wrapper),
+    :deep(.vxe-table) {
+      height: auto !important;
+    }
+
+    :deep(.vxe-table--body-wrapper) {
+      height: auto !important;
+      min-height: 0 !important;
+      max-height: none !important;
+      flex: unset !important;
+    }
   }
 
   .product-info {
@@ -414,7 +488,7 @@ const sellTypeText: Record<string, string> = {
 
   .star-icon {
     color: #ffc107;
-    font-size: 16px;
+    font-size: 20px;
     margin-right: 0;
   }
 
@@ -435,6 +509,17 @@ const sellTypeText: Record<string, string> = {
     flex-shrink: 0;
   }
 
+  .package-item-placeholder {
+    flex-shrink: 0;
+    width: 60px;
+    height: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: #f5f5f5;
+    border-radius: 4px;
+  }
+
   .item-img {
     border-radius: 4px;
     object-fit: cover;