Просмотр исходного кода

Merge branch 'release/昆明医科大学第一附属医院' of https://code.dashoo.cn/dashoo/labsop_h5 into release/昆明医科大学第一附属医院

LYK 2 недель назад
Родитель
Сommit
90b8d82326

+ 9 - 9
.env.development

@@ -10,19 +10,19 @@
 # 本地环境
 ENV = development
 # 本地环境接口地址
-VITE_API_URL = http://192.168.0.216:9957/
+VITE_API_URL = http://192.168.0.216:9960/
 VITE_API_WECHAT = /wechat/
 VITE_TENANT = default
 
 # 微服务地址
-VITE_ADMIN = dashoo.labsop.admin-50000
-VITE_WORKFLOW = dashoo.labsop.workflow-50000
-VITE_FINANCE = dashoo.labsop.finance-50000
-VITE_SCI = dashoo.labsop.scientific-50000
-VITE_INSTR_ADMIN = dashoo.labsop.apparatus-50000
-VITE_LEARNING = dashoo.labsop.learning-50000
-VITE_PLATFORM_API = dashoo.labsop.platform-50000
-VITE_LABORATORY: dashoo.labsop.laboratory-50000
+VITE_ADMIN = dashoo.labsop.admin-53000
+VITE_WORKFLOW = dashoo.labsop.workflow-53000
+VITE_FINANCE = dashoo.labsop.finance-53000
+VITE_SCI = dashoo.labsop.scientific-53000
+VITE_INSTR_ADMIN = dashoo.labsop.apparatus-53000
+VITE_LEARNING = dashoo.labsop.learning-53000
+VITE_PLATFORM_API = dashoo.labsop.platform-53000
+VITE_LABORATORY: dashoo.labsop.laboratory-53000
 
 #公共配置
 VITE_UPLOAD = http://192.168.0.218:9933/weedfs/upload

+ 2 - 1
components.d.ts

@@ -3,7 +3,7 @@
 // @ts-nocheck
 // Generated by unplugin-vue-components
 // Read more: https://github.com/vuejs/core/pull/3399
-export {}
+export { }
 
 declare module 'vue' {
   export interface GlobalComponents {
@@ -28,6 +28,7 @@ declare module 'vue' {
     VanNotify: typeof import('vant/es')['Notify']
     VanPicker: typeof import('vant/es')['Picker']
     VanPickerGroup: typeof import('vant/es')['PickerGroup']
+    VanPickerGroup: typeof import('vant/es')['PickerGroup']
     VanPopup: typeof import('vant/es')['Popup']
     VanRadio: typeof import('vant/es')['Radio']
     VanRadioGroup: typeof import('vant/es')['RadioGroup']

+ 2 - 2
src/constants/pageConstants.ts

@@ -213,7 +213,7 @@ export const SUPPORT_FILE_UPLOAD_TYPE_MAX = ".jpg,.jpeg,.png,.doc,.docx,.xls,.xl
 
 // 扫码跳转微信方法
 
-const APPID = 'wx979e7a671a498586'
+const APPID = 'wxa227b3d53da8cd12'
 
 const PATH = 'pages/appointList/user'
 export const scanCodeWxUrl = (terminal: string, type: InstSwitchType) => {
@@ -223,7 +223,7 @@ export const scanCodeWxUrl = (terminal: string, type: InstSwitchType) => {
 }
 
 // 用户 头像大小 / M
-export const userImgSize = 3
+export const userImgSize = 4
 
 // 开关设备类型
 export enum InstSwitchType {

+ 1 - 1
src/utils/micro_request.ts

@@ -166,7 +166,7 @@ function processResponse(res) {
     return Promise.reject(new Error(message));
   } else if (code !== 200) {
     showNotify({ message: message, type: 'danger' });
-    return Promise.reject("error");
+    return Promise.reject(res.data);
   } else {
     // if (res.data.msg) {
     //   Message({

+ 7 - 7
src/view/entry/add.vue

@@ -46,13 +46,13 @@
           <template #extra>个月</template>
         </van-field>
         <template v-if="item.platformType == '10'">
-          <van-field
-            v-model="item.cellType"
-            label="拟培养细胞种类"
-            placeholder="拟培养细胞种类"
-            :rules="[{ required: item.isChecked, message: '请输入拟培养细胞种类' }]"
-            :disabled="!item.isChecked"
-          />
+<!--          <van-field-->
+<!--            v-model="item.cellType"-->
+<!--            label="拟培养细胞种类"-->
+<!--            placeholder="拟培养细胞种类"-->
+<!--            :rules="[{ required: item.isChecked, message: '请输入拟培养细胞种类' }]"-->
+<!--            :disabled="!item.isChecked"-->
+<!--          />-->
           <van-field
             v-model="item.cellType"
             label="细胞房预约需求"

+ 4 - 4
src/view/entry/detail.vue

@@ -101,10 +101,10 @@
             <div class="label">费用:</div>
             <div class="value">{{ state.form.platPlatformAppointCellRes[0].price }}元</div>
           </li>
-          <li>
-            <div class="label">拟培养细胞种类:</div>
-            <div class="value">{{ state.form.platPlatformAppointCellRes[0].cellType }}</div>
-          </li>
+<!--          <li>-->
+<!--            <div class="label">拟培养细胞种类:</div>-->
+<!--            <div class="value">{{ state.form.platPlatformAppointCellRes[0].cellType }}</div>-->
+<!--          </li>-->
           <li>
             <div class="label">细胞房预约需求:</div>
             <div class="value">{{ state.form.cellSourceType == '10' ? '普通' : '层流' }}</div>

+ 8 - 2
src/view/home/index.vue

@@ -103,7 +103,7 @@
       <ul class="approval">
         <li v-for="item in trainingList" :key="item.id" @click="onRouterPush('/training/enroll', { state: item.id })">
           <div class="flex justify-between">
-            <van-text-ellipsis :content="`${item.title}`" />
+            <div class="item-title">{{ item.title }}</div>
             <van-tag v-if="item.status === '40'">已结束</van-tag>
             <van-tag v-if="item.status !== '40' && item.applyStatus === '10'" type="warning">未报名</van-tag>
             <van-tag v-else-if="item.status !== '40' && item.applyStatus === '20'" type="primary">已报名</van-tag>
@@ -329,9 +329,15 @@ onMounted(() => {
         margin-top: 8px;
         border-bottom: 1px solid #ecf5fa;
 
-        .van-text-ellipsis {
+        .item-title {
           font-weight: bold;
           font-size: 16px;
+          flex: 1;
+          width: 0;
+          margin-right: 8px;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
         }
 
         footer {

+ 405 - 458
src/view/instr/appoint.vue

@@ -14,35 +14,17 @@
       </van-cell-group>
       <h4>预约时间</h4>
       <van-cell-group class="mt10">
-        <van-field
-          v-model="state.form.startTime"
-          is-link
-          readonly
-          label="开始时间"
-          placeholder="开始时间"
+        <van-field v-model="state.form.startTime" is-link readonly label="开始时间" placeholder="开始时间"
           @click="onRouterPush('/instr-calendar', { id: state.form.instId })"
-          :rules="[{ required: true, message: '开始时间不能为空' }]"
-        />
-        <van-field
-          v-model="state.form.endTime"
-          is-link
-          readonly
-          label="结束时间"
-          placeholder="结束时间"
+          :rules="[{ required: true, message: '开始时间不能为空' }]" />
+        <van-field v-model="state.form.endTime" is-link readonly label="结束时间" placeholder="结束时间"
           @click="onRouterPush('/instr-calendar', { id: state.form.instId })"
-          :rules="[{ required: true, message: '结束时间不能为空' }]"
-        />
+          :rules="[{ required: true, message: '结束时间不能为空' }]" />
       </van-cell-group>
       <h4>费用预估</h4>
       <van-cell-group class="mt10">
-        <van-field
-          label="预估费用"
-          v-model="state.estimateFee"
-          placeholder="请选择预约时间和相关信息后自动计算"
-          readonly
-          is-link
-          @click="showCostDetails"
-        />
+        <van-field label="预估费用" v-model="state.estimateFee" placeholder="请选择预约时间和相关信息后自动计算" readonly is-link
+          @click="showCostDetails" />
       </van-cell-group>
       <h4>申请明细</h4>
       <template v-if="state.appointId == 0">
@@ -54,50 +36,22 @@
                 <van-radio name="service">服务</van-radio>
               </van-radio-group>
             </template>
-          </van-field> -->
-          <van-field
-            v-if="state.form.projectType == 'project'"
-            label="课题组"
-            placeholder="课题组"
-            @click="state.showProject = true"
-            v-model="state.form.projectName"
-            :rules="[{ required: true, message: '课题不能为空' }]"
-          >
+</van-field> -->
+          <van-field v-if="state.form.projectType == 'project'" label="课题组" placeholder="课题组"
+            @click="state.showProject = true" v-model="state.form.projectName"
+            :rules="[{ required: true, message: '课题不能为空' }]">
           </van-field>
-          <van-field
-            v-if="state.form.projectType == 'service'"
-            label="服务"
-            placeholder="服务"
-            @click="state.shwoService = true"
-            v-model="state.form.serviceName"
-            :rules="[{ required: true, message: '服务不能为空' }]"
-          >
+          <van-field v-if="state.form.projectType == 'service'" label="服务" placeholder="服务"
+            @click="state.shwoService = true" v-model="state.form.serviceName"
+            :rules="[{ required: true, message: '服务不能为空' }]">
           </van-field>
-          <van-field
-            v-if="state.form.projectType == 'project'"
-            label="经费卡"
-            placeholder="经费卡"
-            is-link
-            readonly
-            @click="state.showExpenseCard = true"
-            v-model="state.form.expenseCardName"
-          ></van-field>
-          <van-field
-            label="预约人"
-            placeholder="预约人"
-            is-link
-            readonly
-            @click="openSelectUser"
-            v-model="state.form.nickName"
-            :rules="[{ required: true, message: '预约人不能为空' }]"
-          ></van-field>
-          <van-field
-            label="联系电话"
-            placeholder="联系电话"
-            v-model="state.form.userContact"
-            :rules="[{ required: true, message: '联系电话不能为空' }]"
-          ></van-field>
-          <van-field name="assistEnable" label="辅助上机" >
+          <van-field v-if="state.form.projectType == 'project'" label="经费卡" placeholder="经费卡" is-link readonly
+            @click="state.showExpenseCard = true" v-model="state.form.expenseCardName"></van-field>
+          <van-field label="预约人" placeholder="预约人" is-link readonly @click="openSelectUser"
+            v-model="state.form.nickName" :rules="[{ required: true, message: '预约人不能为空' }]"></van-field>
+          <van-field label="联系电话" placeholder="联系电话" v-model="state.form.userContact"
+            :rules="[{ required: true, message: '联系电话不能为空' }]"></van-field>
+          <van-field name="assistEnable" label="辅助上机">
             <template #input>
               <van-radio-group v-model="state.form.assistEnable" direction="horizontal">
                 <van-radio style="margin-right: 20px" :name="false">否</van-radio>
@@ -105,7 +59,8 @@
               </van-radio-group>
             </template>
           </van-field>
-          <van-field label="备注" placeholder="备注" v-model="state.form.remark" rows="2" autosize type="textarea" maxlength="300" show-word-limit></van-field>
+          <van-field label="备注" placeholder="备注" v-model="state.form.remark" rows="2" autosize type="textarea"
+            maxlength="300" show-word-limit></van-field>
         </van-cell-group>
       </template>
       <CustomForm ref="customFormRef" :formData="state.form.createForm"></CustomForm>
@@ -116,439 +71,431 @@
   </van-action-bar>
   <!-- 选择服务 -->
   <van-popup v-model:show="state.shwoService" position="bottom">
-    <van-picker :columns="serviceList" :columns-field-names="{ text: 'name', value: 'id' }" @confirm="pickService" @cancel="state.shwoService = false" />
+    <van-picker :columns="serviceList" :columns-field-names="{ text: 'name', value: 'id' }" @confirm="pickService"
+      @cancel="state.shwoService = false" />
   </van-popup>
   <!-- 选择课题 -->
   <van-popup v-model:show="state.showProject" position="bottom">
-    <van-picker
-      :columns="projectList"
-      :columns-field-names="{ text: 'projectName', value: 'projectId' }"
-      @confirm="pickProject"
-      @cancel="state.showProject = false"
-    />
+    <van-picker :columns="projectList" :columns-field-names="{ text: 'projectName', value: 'projectId' }"
+      @confirm="pickProject" @cancel="state.showProject = false" />
   </van-popup>
   <!-- 选择经费卡 -->
   <van-popup v-model:show="state.showExpenseCard" position="bottom">
-    <van-picker
-      :columns="fundsList"
-      :columns-field-names="{ text: 'finAccount', value: 'id' }"
-      @confirm="pickExpenseCard"
-      @cancel="state.showExpenseCard = false"
-    />
+    <van-picker :columns="fundsList" :columns-field-names="{ text: 'finAccount', value: 'id' }"
+      @confirm="pickExpenseCard" @cancel="state.showExpenseCard = false" />
   </van-popup>
   <!-- 选择预约人 -->
   <van-popup v-model:show="state.showAppointUser" position="bottom">
-    <van-picker
-      :columns="userList"
-      :columns-field-names="{ text: 'nickName', value: 'id' }"
-      @confirm="pickAppointUser"
-      @cancel="state.showAppointUser = false"
-    />
+    <van-picker :columns="userList" :columns-field-names="{ text: 'nickName', value: 'id' }" @confirm="pickAppointUser"
+      @cancel="state.showAppointUser = false" />
   </van-popup>
   <AppointDialog ref="appointDialogRef" />
 </template>
 
 <script lang="ts" setup>
-  import to from 'await-to-js'
-  import { useRoute, useRouter } from 'vue-router'
-  import { useInstrApi } from '/@/api/instr'
-  import { useInstDocApi } from '/@/api/instr/document'
-  import { defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
-  import { formatDate } from '/@/utils/formatTime'
-  import { showNotify, showDialog } from 'vant'
-  import download from 'downloadjs'
-  import { useNoticeApi } from '/@/api/instr/notice'
-  import { useProApi } from '/@/api/project'
-  import technicalApi from '/@/api/technical/index'
-  import { Local } from '/@/utils/storage'
-  import { useUserApi } from '/@/api/system/user'
-  import { useUserInfo } from '/@/stores/userInfo'
-  import { storeToRefs } from 'pinia'
-  import instAppoint from '/@/api/instr/instAppoint'
-  import { useConfigApi } from '/@/api/system/config'
-  const CustomForm = defineAsyncComponent(() => import('/@/components/CustomForm.vue'))
-  const storesUseUserInfo = useUserInfo()
-  const { userInfos } = storeToRefs(storesUseUserInfo)
-  const route = useRoute()
-  const router = useRouter()
-  const projApi = useProApi()
-  const instApi = useInstrApi()
-  const userApi = useUserApi()
-  const configApi = useConfigApi()
-  const serviceList = ref([])
-  const projectList = ref([])
-  const fundsList = ref([])
-  const userList = ref([])
-  const appointDialogRef = ref()
-  const formRef = ref()
-  const customFormRef = ref()
-  const state = reactive({
-    loading: false,
-    appointId: 0,
-    isActiveService: false,
-    showProject: false,
-    shwoService: false,
-    showExpenseCard: false,
-    showAppointUser: false,
-    isInstrHead: false,
-    instDetail: {} as any,
-    estimateFee: '', // 预估费用
-    costDetails: [] as any[], // 费用明细
-    form: {
-      instId: 0,
-      instName: '',
-      startTime: '',
-      endTime: null,
-      projectName: null,
-      projectId: null,
-      serviceId: null,
-      serviceName: null,
-      expenseCardId: 0,
-      expenseCardName: '',
-      userContact: '',
-      userId: 0,
-      nickName: '',
-      projectType: '',
-      assistEnable: false, // 是否辅助上机,默认为false(否)
-      createForm: [],
-      remark: ''
-    }
-  })
+import to from 'await-to-js'
+import { useRoute, useRouter } from 'vue-router'
+import { useInstrApi } from '/@/api/instr'
+import { useInstDocApi } from '/@/api/instr/document'
+import { defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
+import { formatDate } from '/@/utils/formatTime'
+import { showNotify, showDialog } from 'vant'
+import download from 'downloadjs'
+import { useNoticeApi } from '/@/api/instr/notice'
+import { useProApi } from '/@/api/project'
+import technicalApi from '/@/api/technical/index'
+import { Local } from '/@/utils/storage'
+import { useUserApi } from '/@/api/system/user'
+import { useUserInfo } from '/@/stores/userInfo'
+import { storeToRefs } from 'pinia'
+import instAppoint from '/@/api/instr/instAppoint'
+import { useConfigApi } from '/@/api/system/config'
+const CustomForm = defineAsyncComponent(() => import('/@/components/CustomForm.vue'))
+const storesUseUserInfo = useUserInfo()
+const { userInfos } = storeToRefs(storesUseUserInfo)
+const route = useRoute()
+const router = useRouter()
+const projApi = useProApi()
+const instApi = useInstrApi()
+const userApi = useUserApi()
+const configApi = useConfigApi()
+const serviceList = ref([])
+const projectList = ref([])
+const fundsList = ref([])
+const userList = ref([])
+const appointDialogRef = ref()
+const formRef = ref()
+const customFormRef = ref()
+const state = reactive({
+  loading: false,
+  appointId: 0,
+  isActiveService: false,
+  showProject: false,
+  shwoService: false,
+  showExpenseCard: false,
+  showAppointUser: false,
+  isInstrHead: false,
+  instDetail: {} as any,
+  estimateFee: '', // 预估费用
+  costDetails: [] as any[], // 费用明细
+  form: {
+    instId: 0,
+    instName: '',
+    startTime: '',
+    endTime: null,
+    projectName: null,
+    projectId: null,
+    serviceId: null,
+    serviceName: null,
+    expenseCardId: 0,
+    expenseCardName: '',
+    userContact: '',
+    userId: 0,
+    nickName: '',
+    projectType: '',
+    assistEnable: false, // 是否辅助上机,默认为false(否)
+    createForm: [],
+    remark: ''
+  }
+})
 
-  // 自定义验证函数:验证是否辅助上机
-  const validateAssistEnable = (value) => {
-    // 对于布尔值,只要不是undefined就是有效值
-    return value !== undefined
+// 自定义验证函数:验证是否辅助上机
+const validateAssistEnable = (value) => {
+  // 对于布尔值,只要不是undefined就是有效值
+  return value !== undefined
+}
+
+// 获取预估费用
+const getEstimateFee = async () => {
+  // 必须有开始时间、结束时间和仪器ID才能计算预估费用
+  if (!state.form.instId || !state.form.startTime || !state.form.endTime) {
+    state.estimateFee = ''
+    return
   }
 
-  // 获取预估费用
-  const getEstimateFee = async () => {
-    // 必须有开始时间、结束时间和仪器ID才能计算预估费用
-    if (!state.form.instId || !state.form.startTime || !state.form.endTime) {
-      state.estimateFee = ''
-      return
-    }
-    
-    // 使用原始格式,不进行时间格式转换
-    const params = {
-      instId: state.form.instId,
-      startTime: state.form.startTime,
-      endTime: state.form.endTime,
-      projectId: state.form.projectId || 0,
-      expenseCardId: state.form.expenseCardId || 0,
-      assistEnable: state.form.assistEnable
-    }
-    
-    const [err, res]: ToResponse = await to(instApi.getEstimateFee(params))
-    if (err) {
-      state.estimateFee = '计算失败'
-      return
-    }
-    
-    if (res?.code === 200 && res.data) {
-      // 根据返回格式获取预估费用
-      const cost = res.data.estimatedCost || res.data
-      state.estimateFee = `¥${cost}`
-      
-      // 保存费用明细数据
-      if (res.data.costDetails && res.data.costDetails.length > 0) {
-        state.costDetails = res.data.costDetails
-      } else {
-        state.costDetails = []
-      }
+  // 使用原始格式,不进行时间格式转换
+  const params = {
+    instId: state.form.instId,
+    startTime: state.form.startTime,
+    endTime: state.form.endTime,
+    projectId: state.form.projectId || 0,
+    expenseCardId: state.form.expenseCardId || 0,
+    assistEnable: state.form.assistEnable
+  }
+
+  const [err, res]: ToResponse = await to(instApi.getEstimateFee(params))
+  if (err) {
+    state.estimateFee = '计算失败'
+    return
+  }
+
+  if (res?.code === 200 && res.data) {
+    // 根据返回格式获取预估费用
+    const cost = res.data.estimatedCost === 0 ? 0 : res.data.estimatedCost || res.data
+    state.estimateFee = `¥${cost}`
+
+    // 保存费用明细数据
+    if (res.data.costDetails && res.data.costDetails.length > 0) {
+      state.costDetails = res.data.costDetails
     } else {
-      state.estimateFee = '无法计算'
+      state.costDetails = []
     }
+  } else {
+    state.estimateFee = '无法计算'
   }
+}
 
-  // 选择课题还是服务
-  const changeProjectType = () => {
-    state.form.serviceId = 0
-    state.form.serviceName = ''
-    state.form.projectId = 0
-    state.form.projectName = ''
-    state.form.expenseCardId = 0
-    state.form.expenseCardName = ''
-    fundsList.value = []
-    getEstimateFee() // 重新计算预估费用
-  }
-  const pickProject = ({ selectedOptions }) => {
-    state.form.projectId = selectedOptions[0].projectId
-    state.form.projectName = selectedOptions[0].projectName
-    state.showProject = false
-    getFundsData()
-    getEstimateFee() // 重新计算预估费用
+// 选择课题还是服务
+const changeProjectType = () => {
+  state.form.serviceId = 0
+  state.form.serviceName = ''
+  state.form.projectId = 0
+  state.form.projectName = ''
+  state.form.expenseCardId = 0
+  state.form.expenseCardName = ''
+  fundsList.value = []
+  getEstimateFee() // 重新计算预估费用
+}
+const pickProject = ({ selectedOptions }) => {
+  state.form.projectId = selectedOptions[0].projectId
+  state.form.projectName = selectedOptions[0].projectName
+  state.showProject = false
+  getFundsData()
+  getEstimateFee() // 重新计算预估费用
+}
+// 选择服务
+const pickService = ({ selectedOptions }) => {
+  state.form.serviceId = selectedOptions[0].id
+  state.form.serviceName = selectedOptions[0].name
+  state.shwoService = false
+  getEstimateFee() // 重新计算预估费用
+}
+// 经费卡选择
+const pickExpenseCard = ({ selectedOptions }) => {
+  state.form.expenseCardId = selectedOptions[0].id
+  state.form.expenseCardName = selectedOptions[0].finAccount
+  state.showExpenseCard = false
+  getEstimateFee() // 重新计算预估费用
+}
+const getFundsData = async () => {
+  const [err, res]: ToResponse = await to(projApi.getFinanceAccountList({ projId: state.form.projectId }))
+  if (err) return
+  fundsList.value = res?.data.list ? [res?.data.list] : []
+  if (fundsList.value && fundsList.value.length > 0 && fundsList.value[0].length > 0) {
+    state.form.expenseCardId = fundsList.value[0][0].id
+    state.form.expenseCardName = fundsList.value[0][0].finAccount
   }
-  // 选择服务
-  const pickService = ({ selectedOptions }) => {
-    state.form.serviceId = selectedOptions[0].id
-    state.form.serviceName = selectedOptions[0].name
-    state.shwoService = false
-    getEstimateFee() // 重新计算预估费用
+  getEstimateFee() // 重新计算预估费用
+}
+// 预约人选择
+const pickAppointUser = ({ selectedOptions }) => {
+  state.form.nickName = selectedOptions[0].nickName
+  state.form.userId = selectedOptions[0].id
+  state.form.userContact = selectedOptions[0].phone
+  state.showAppointUser = false
+  state.form.serviceId = 0
+  state.form.serviceName = ''
+  state.form.projectId = 0
+  state.form.projectName = ''
+  state.form.expenseCardId = 0
+  state.form.expenseCardName = ''
+  fundsList.value = []
+  getMyProjectInfo(state.form.userId)
+  getEstimateFee() // 重新计算预估费用
+}
+// 选择预约人
+const openSelectUser = () => {
+  if (!state.isInstrHead) return
+  state.showAppointUser = true
+}
+const init = async () => {
+  //延长预约会传一个预约id 有预约id 获取预约详情
+  const [err, res]: ToResponse = await to(configApi.getEntityMapByKey({ configKey: 'instr_is_activate_service' }))
+  if (err) return
+  state.isActiveService = res.data?.configValue == '10' ? true : false
+  state.form.projectType = 'project'
+  state.form.userId = userInfos.value.id || 0
+  state.form.nickName = userInfos.value.nickName || ''
+  state.form.userContact = userInfos.value.phone || ''
+  getMyProjectInfo()
+  getUserService()
+  getUserList()
+  getInstrDetails()
+  getAppointConfig()
+}
+const getInstrDetails = async () => {
+  const [err, res]: ToResponse = await to(instApi.getDetail({ id: state.form.instId }))
+  if (err) return
+  if (res?.code === 200) {
+    state.instDetail = res.data
+    state.form.instName = state.instDetail.instName
+    const userInfo = storesUseUserInfo.userInfos
+    state.isInstrHead = userInfo.id ? res.data.instHeadId.split(',').includes('' + userInfo?.id) : false
   }
-  // 经费卡选择
-  const pickExpenseCard = ({ selectedOptions }) => {
-    state.form.expenseCardId = selectedOptions[0].id
-    state.form.expenseCardName = selectedOptions[0].finAccount
-    state.showExpenseCard = false
-    getEstimateFee() // 重新计算预估费用
+}
+// 获取用户下的服务
+const getUserService = async () => {
+  const [err, res]: ToResponse = await to(technicalApi.getList({ noPage: true }))
+  if (err) return
+  serviceList.value = [res?.data.list]
+  if (state.form.projectType == 'service') {
+    state.form.serviceId = res?.data.list[0].id || 0
+    state.form.serviceName = res?.data.list[0].name || ''
   }
-  const getFundsData = async () => {
-    const [err, res]: ToResponse = await to(projApi.getFinanceAccountList({ projId: state.form.projectId }))
-    if (err) return
-    fundsList.value = res?.data.list ? [res?.data.list] : []
-    if (fundsList.value && fundsList.value.length > 0 && fundsList.value[0].length > 0) {
-      state.form.expenseCardId = fundsList.value[0][0].id
-      state.form.expenseCardName = fundsList.value[0][0].finAccount
-    }
-    getEstimateFee() // 重新计算预估费用
+}
+// 获取用户相关的课题组
+const getMyProjectInfo = async (id?: number) => {
+  let params = {}
+  if (id) {
+    params = { id }
+  } else {
+    params = {}
   }
-  // 预约人选择
-  const pickAppointUser = ({ selectedOptions }) => {
-    state.form.nickName = selectedOptions[0].nickName
-    state.form.userId = selectedOptions[0].id
-    state.form.userContact = selectedOptions[0].phone
-    state.showAppointUser = false
-    state.form.serviceId = 0
-    state.form.serviceName = ''
-    state.form.projectId = 0
-    state.form.projectName = ''
-    state.form.expenseCardId = 0
-    state.form.expenseCardName = ''
-    fundsList.value = []
-    getMyProjectInfo(state.form.userId)
+  const [err, res]: ToResponse = await to(projApi.getMySelfProjectGroup(params))
+  if (err) return
+  // state.form.projectName = res?.data.pgName || ''
+  // state.form.projectId = res?.data.id || null
+  projectList.value = [{ projectName: res?.data.pgName || '', projectId: res?.data.id || null }]
+  if (state.form.projectType == 'project') {
+    state.form.projectId = res?.data.id || null
+    state.form.projectName = res?.data.pgName || ''
+    getFundsData()
+  } else {
     getEstimateFee() // 重新计算预估费用
   }
-  // 选择预约人
-  const openSelectUser = () => {
-    if (!state.isInstrHead) return
-    state.showAppointUser = true
+}
+const getUserList = async () => {
+  const [err, res]: ToResponse = await to(userApi.getUserList({ noPage: true }))
+  if (err) return
+  userList.value = [res?.data.list]
+}
+// 预约配置信息
+const getAppointConfig = async () => {
+  const params = {
+    instId: state.form.instId,
+    code: 'InstCfgAppoint'
   }
-  const init = async () => {
-    //延长预约会传一个预约id 有预约id 获取预约详情
-    const [err, res]: ToResponse = await to(configApi.getEntityMapByKey({ configKey: 'instr_is_activate_service' }))
-    if (err) return
-    state.isActiveService = res.data?.configValue == '10' ? true : false
-    state.form.projectType = 'project'
-    state.form.userId = userInfos.value.id || 0
-    state.form.nickName = userInfos.value.nickName || ''
-    state.form.userContact = userInfos.value.phone || ''
-    getMyProjectInfo()
-    getUserService()
-    getUserList()
-    getInstrDetails()
-    getAppointConfig()
+  const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
+  if (err) return
+  state.form.createForm = res?.data?.config.createForm ? JSON.parse(res.data.config.createForm) : []
+}
+const onRouterPush = (val: string, params?: any) => {
+  router.push({
+    path: val,
+    query: { ...params }
+  })
+  // 如果是跳转到日历页面,返回后重新计算预估费用
+  if (val === '/instr-calendar') {
+    // 使用setTimeout确保从日历页面返回后再计算
+    setTimeout(() => {
+      if (state.form.startTime && state.form.endTime) {
+        getEstimateFee()
+      }
+    }, 1000)
   }
-  const getInstrDetails = async () => {
-    const [err, res]: ToResponse = await to(instApi.getDetail({ id: state.form.instId }))
-    if (err) return
-    if (res?.code === 200) {
-      state.instDetail = res.data
-      state.form.instName = state.instDetail.instName
-      const userInfo = storesUseUserInfo.userInfos
-      state.isInstrHead = userInfo.id ? res.data.instHeadId.split(',').includes('' + userInfo?.id) : false
-    }
+}
+// 展示费用明细
+const showCostDetails = () => {
+  if (state.costDetails.length === 0) {
+    showNotify({
+      type: 'warning',
+      message: '暂无费用明细'
+    })
+    return
   }
-  // 获取用户下的服务
-  const getUserService = async () => {
-    const [err, res]: ToResponse = await to(technicalApi.getList({ noPage: true }))
-    if (err) return
-    serviceList.value = [res?.data.list]
-    if (state.form.projectType == 'service') {
-      state.form.serviceId = res?.data.list[0].id || 0
-      state.form.serviceName = res?.data.list[0].name || ''
-    }
+
+  let detailsHtml = '<div class="cost-details-content">'
+  state.costDetails.forEach((item, index) => {
+    detailsHtml += `<div class="cost-item">`
+    detailsHtml += `<div class="cost-item-name">${item.itemName || ''}</div>`
+    detailsHtml += `<div class="cost-item-amount">¥${item.amount || 0}</div>`
+    detailsHtml += `</div>`
+  })
+  detailsHtml += '</div>'
+
+  showDialog({
+    title: '费用明细',
+    message: detailsHtml,
+    className: 'cost-details-dialog',
+    allowHtml: true,
+    showConfirmButton: true,
+    confirmButtonText: '确定'
+  })
+}
+
+// 防抖计时器
+const debounceTimer = ref(0)
+
+const onClickButton = async () => {
+  // 防抖处理:如果已经有计时器在运行,则清除并重新设置
+  if (debounceTimer.value) {
+    clearTimeout(debounceTimer.value)
   }
-  // 获取用户相关的课题组
-  const getMyProjectInfo = async (id?: number) => {
-    let params = {}
-    if (id) {
-      params = { id }
-    } else {
-      params = {}
-    }
-    const [err, res]: ToResponse = await to(projApi.getMySelfProjectGroup(params))
-    if (err) return
-    // state.form.projectName = res?.data.pgName || ''
-    // state.form.projectId = res?.data.id || null
-    projectList.value = [{ projectName: res?.data.pgName || '', projectId: res?.data.id || null }]
-    if (state.form.projectType == 'project') {
-      state.form.projectId = res?.data.id || null
-      state.form.projectName = res?.data.pgName || ''
-      getFundsData()
-    } else {
-      getEstimateFee() // 重新计算预估费用
+
+  // 设置新的防抖计时器
+  debounceTimer.value = setTimeout(async () => {
+    state.loading = true
+    const [errValid] = await to(formRef.value.validate())
+    const customForm = customFormRef.value.getFormData()
+    if (errValid || (state.form.createForm.length && !customForm)) {
+      state.loading = false
+      debounceTimer.value = 0
+      return
     }
-  }
-  const getUserList = async () => {
-    const [err, res]: ToResponse = await to(userApi.getUserList({ noPage: true }))
-    if (err) return
-    userList.value = [res?.data.list]
-  }
-  // 预约配置信息
-  const getAppointConfig = async () => {
-    const params = {
-      instId: state.form.instId,
-      code: 'InstCfgAppoint'
+    const params = JSON.parse(JSON.stringify(state.form))
+    params.userName = params.nickName
+    params.sampleForm = JSON.stringify(customForm)
+    delete params.createForm
+    const [err]: ToResponse = await to(instAppoint.add(params))
+    if (err) {
+      state.loading = false
+      debounceTimer.value = 0
+      return
     }
-    const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
-    if (err) return
-    state.form.createForm = res?.data?.config.createForm ? JSON.parse(res.data.config.createForm) : []
-  }
-  const onRouterPush = (val: string, params?: any) => {
-    router.push({
-      path: val,
-      query: { ...params }
+    showNotify({
+      type: 'success',
+      message: '预约成功'
     })
-    // 如果是跳转到日历页面,返回后重新计算预估费用
-    if (val === '/instr-calendar') {
-      // 使用setTimeout确保从日历页面返回后再计算
-      setTimeout(() => {
-        if (state.form.startTime && state.form.endTime) {
-          getEstimateFee()
-        }
-      }, 1000)
-    }
-  }
-  // 展示费用明细
-    const showCostDetails = () => {
-      if (state.costDetails.length === 0) {
-        showNotify({
-          type: 'warning',
-          message: '暂无费用明细'
-        })
-        return
-      }
-      
-      let detailsHtml = '<div class="cost-details-content">'
-      state.costDetails.forEach((item, index) => {
-        detailsHtml += `<div class="cost-item">`
-        detailsHtml += `<div class="cost-item-name">${item.itemName || ''}</div>`
-        detailsHtml += `<div class="cost-item-amount">¥${item.amount || 0}</div>`
-        detailsHtml += `</div>`
-      })
-      detailsHtml += '</div>'
-      
-      showDialog({
-        title: '费用明细',
-        message: detailsHtml,
-        className: 'cost-details-dialog',
-        allowHtml: true,
-        showConfirmButton: true,
-        confirmButtonText: '确定'
-      })
-    }
 
-  // 防抖计时器
-  const debounceTimer = ref(0)
-  
-  const onClickButton = async () => {
-    // 防抖处理:如果已经有计时器在运行,则清除并重新设置
-    if (debounceTimer.value) {
-      clearTimeout(debounceTimer.value)
-    }
-    
-    // 设置新的防抖计时器
-    debounceTimer.value = setTimeout(async () => {
-      state.loading = true
-      const [errValid] = await to(formRef.value.validate())
-      const customForm = customFormRef.value.getFormData()
-      if (errValid || (state.form.createForm.length && !customForm)) {
-        state.loading = false
-        debounceTimer.value = 0
-        return
-      }
-      const params = JSON.parse(JSON.stringify(state.form))
-      params.userName = params.nickName
-      params.sampleForm = JSON.stringify(customForm)
-      delete params.createForm
-      const [err]: ToResponse = await to(instAppoint.add(params))
-      if (err) {
-        state.loading = false
-        debounceTimer.value = 0
-        return
-      }
-      showNotify({
-        type: 'success',
-        message: '预约成功'
+    // 接口成功后继续保持加载状态2秒,提供更好的用户体验
+    setTimeout(() => {
+      router.push({
+        path: '/instr-detail',
+        query: {
+          id: params.instId
+        }
       })
-      
-      // 接口成功后继续保持加载状态2秒,提供更好的用户体验
-      setTimeout(() => {
-        router.push({
-          path: '/instr-detail',
-          query: {
-            id: params.instId
-          }
-        })
-        debounceTimer.value = 0
-        state.loading = false // 5秒后重置加载状态
-      }, 5000)
-    }, 500) // 500ms防抖延迟
+      debounceTimer.value = 0
+      state.loading = false // 5秒后重置加载状态
+    }, 5000)
+  }, 500) // 500ms防抖延迟
+}
+onMounted(() => {
+  const id = route.query.id ? +route.query.id : 0
+  const startTime = route.query.startTime ? formatDate(new Date(+route.query.startTime), 'YYYY-mm-dd HH:MM') : ''
+  const endTime = route.query.endTime ? formatDate(new Date(+route.query.endTime), 'YYYY-mm-dd HH:MM') : ''
+  state.form.instId = id
+  state.form.startTime = startTime
+  state.form.endTime = endTime
+  init()
+  // 如果有开始时间和结束时间,初始化后计算预估费用
+  if (startTime && endTime) {
+    setTimeout(() => {
+      getEstimateFee()
+    }, 500) // 延迟执行,确保其他初始化完成
   }
-  onMounted(() => {
-    const id = route.query.id ? +route.query.id : 0
-    const startTime = route.query.startTime ? formatDate(new Date(+route.query.startTime), 'YYYY-mm-dd HH:MM') : ''
-    const endTime = route.query.endTime ? formatDate(new Date(+route.query.endTime), 'YYYY-mm-dd HH:MM') : ''
-    state.form.instId = id
-    state.form.startTime = startTime
-    state.form.endTime = endTime
-    init()
-    // 如果有开始时间和结束时间,初始化后计算预估费用
-    if (startTime && endTime) {
-      setTimeout(() => {
-        getEstimateFee()
-      }, 500) // 延迟执行,确保其他初始化完成
-    }
-  })
+})
 
-  // 监听开始时间和结束时间变化,重新计算预估费用
-  watch(
-    () => [state.form.startTime, state.form.endTime],
-    () => {
-      if (state.form.startTime && state.form.endTime) {
-        getEstimateFee()
-      }
+// 监听开始时间和结束时间变化,重新计算预估费用
+watch(
+  () => [state.form.startTime, state.form.endTime],
+  () => {
+    if (state.form.startTime && state.form.endTime) {
+      getEstimateFee()
     }
-  )
+  }
+)
 
-  // 监听辅助上机选项变化,重新计算预估费用
-  watch(
-    () => state.form.assistEnable,
-    () => {
-      if (state.form.startTime && state.form.endTime) {
-        getEstimateFee()
-      }
+// 监听辅助上机选项变化,重新计算预估费用
+watch(
+  () => state.form.assistEnable,
+  () => {
+    if (state.form.startTime && state.form.endTime) {
+      getEstimateFee()
     }
-  )
+  }
+)
 </script>
 
 <style lang="scss" scoped>
-  .container {
-    flex: 1;
-    padding: 10px;
-    background-color: #f9f9f9;
-    overflow-y: auto;
-    h4 {
+.container {
+  flex: 1;
+  padding: 10px;
+  background-color: #f9f9f9;
+  overflow-y: auto;
+
+  h4 {
+    height: 18px;
+    line-height: 18px;
+    display: flex;
+    margin: 10px 0;
+
+    span {
+      font-weight: normal;
+      margin-left: auto;
+    }
+
+    &::before {
+      display: inline-block;
+      content: '';
+      width: 3px;
       height: 18px;
-      line-height: 18px;
-      display: flex;
-      margin: 10px 0;
-      span {
-        font-weight: normal;
-        margin-left: auto;
-      }
-      &::before {
-        display: inline-block;
-        content: '';
-        width: 3px;
-        height: 18px;
-        background-color: #1c9bfd;
-        margin-right: 4px;
-        vertical-align: middle;
-      }
+      background-color: #1c9bfd;
+      margin-right: 4px;
+      vertical-align: middle;
     }
   }
+}
 </style>
 
 <style lang="scss">
@@ -558,26 +505,26 @@
     max-height: 60vh;
     overflow-y: auto;
   }
-  
+
   .cost-details-content {
     padding: 16px;
-    
+
     .cost-item {
       display: flex;
       justify-content: space-between;
       align-items: center;
       padding: 12px 0;
       border-bottom: 1px solid #eee;
-      
+
       &:last-child {
         border-bottom: none;
       }
-      
+
       .cost-item-name {
         font-size: 14px;
         color: #333;
       }
-      
+
       .cost-item-amount {
         font-size: 16px;
         font-weight: bold;

+ 20 - 0
src/view/instr/calendar.vue

@@ -85,8 +85,20 @@
     selected: [],
     calendar: [],
   })
+  const instRoomType = ref(10)
+  const getInstDetails = async () => {
+    try {
+      const [err, res]: ToResponse = await to(instApi.getDetail({ id: instId.value }));
+      if (!err && res?.data) {
+        instRoomType.value = res.data.instRoomType || 10;
+      }
+    } catch (error) {
+      console.error('获取仪器详情失败:', error);
+    }
+  }
   // 获取系统设置时间间隔
   const getTimeSplit = async () => {
+    await getInstDetails()
     const [err, res]: ToResponse = await to(
       instApi.getSettingDetail({
         instId: Number(instId.value),
@@ -108,6 +120,13 @@
       instId: instId.value,
       date: currentDate,
       dateType: 'week',
+      currentUser: false,
+    }
+    if (instRoomType.value == 20) {
+        // 这里需要获取当前用户ID,您需要根据实际情况获取当前用户ID
+        params.currentUser = true;
+        console.log('房间类型,只查看自己的预约记录');
+
     }
     await Promise.all([
       instApi.getAppointInfo({ ...params }),
@@ -275,6 +294,7 @@
   }
   onMounted(() => {
     instId.value = route.query.id ? +route.query.id : 0
+
     getTimeSplit()
   })
 </script>

+ 33 - 467
src/view/instr/sampleAppoint.vue

@@ -5,443 +5,7 @@
  * @LastEditTime: 2025-03-28 11:44:38
  * @FilePath: \labsop_h5\src\view\instr\detail.vue
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
--->
-<template>
-  <div class="container">
-    <van-form ref="formRef" required="auto">
-      <van-cell-group>
-        <van-field
-          v-model="state.form.deliverTime"
-          is-link
-          readonly
-          label="送样时间"
-          placeholder="请选择计划送样时间"
-          @click="state.showDeliverTime = true"
-          :rules="[{ required: true, message: '送样时间不能为空' }]"
-        />
-        <van-field
-          v-model="state.form.testTime"
-          is-link
-          readonly
-          label="检测时间"
-          placeholder="请选择检测时间"
-          @click="state.showTestTime = true"
-          :rules="[{ required: false }]"
-        />
-        <van-field
-          label="课题"
-          placeholder="课题"
-          readonly
-          v-model="state.form.projectName"
-          :rules="[{ required: false }]"
-        />
-        <van-field
-          v-if="state.InstCfgCharge"
-          label="经费卡"
-          placeholder="请选择经费卡"
-          is-link
-          readonly
-          @click="state.showExpenseCard = true"
-          v-model="state.form.expenseCardName"
-          :rules="[{ required: true, message: '经费卡不能为空' }]"
-        ></van-field>
-        <van-field
-          label="样品说明"
-          placeholder="输入样品说明"
-          v-model="state.form.sampleDesc"
-          :rules="[{ required: false }]"
-        ></van-field>
-        <van-field
-          label="联系电话"
-          placeholder="输入联系电话"
-          v-model="state.form.userContact"
-          :rules="[{ required: false }]"
-        ></van-field>
-      </van-cell-group>
-
-      <div class="mt10 card-wrap">
-        <h4>检测信息</h4>
-        <div
-          class="card-item"
-          v-for="(v, i) in state.form.sampleItem"
-          :key="i"
-        >
-          <div class="flex flex-between mb10">
-            <span class="label">检测项:</span>
-            <span class="value bold">{{ v.name }}</span>
-          </div>
-          <div class="flex flex-between mb10 flex-center">
-            <span class="label">样本数量:</span>
-            <van-stepper
-              v-model="v.count"
-              min="0"
-              integer
-            />
-          </div>
-          <div class="flex flex-between mb10">
-            <span class="label">单价:</span>
-            <span class="value price">¥{{ v.price }}</span>
-          </div>
-          <div class="flex flex-between">
-            <span class="label">总金额:</span>
-            <span class="value price bold">¥{{ (v.count * v.price).toFixed(2) }}</span>
-          </div>
-        </div>
-      </div>
-
-      <CustomForm
-        ref="customFormRef"
-        :formData="state.form.sampleForm"
-      ></CustomForm>
-    </van-form>
-  </div>
-  <van-action-bar placeholder>
-    <van-action-bar-button
-      class="w100"
-      type="primary"
-      text="保存"
-      @click="onClickButton"
-      :loading="state.loading"
-    />
-  </van-action-bar>
-
-  <!-- 送样时间 -->
-  <van-popup
-    v-model:show="state.showDeliverTime"
-    position="bottom"
-  >
-    <van-date-picker
-      v-model="state.currentDate"
-      title="选择送样时间"
-      @confirm="onConfirmDeliverDate"
-      @cancel="state.showDeliverTime = false"
-    />
-  </van-popup>
-  <van-popup
-    v-model:show="state.showDeliverTimeTime"
-    position="bottom"
-  >
-    <van-time-picker
-      v-model="state.currentTime"
-      title="选择送样时间"
-      @confirm="onConfirmDeliverTime"
-      @cancel="state.showDeliverTimeTime = false"
-    />
-  </van-popup>
-
-  <!-- 检测时间 -->
-  <van-popup
-    v-model:show="state.showTestTime"
-    position="bottom"
-  >
-    <van-date-picker
-      v-model="state.currentDateTest"
-      title="选择检测时间"
-      @confirm="onConfirmTestDate"
-      @cancel="state.showTestTime = false"
-    />
-  </van-popup>
-    <van-popup
-    v-model:show="state.showTestTimeTime"
-    position="bottom"
-  >
-    <van-time-picker
-      v-model="state.currentTimeTest"
-      title="选择检测时间"
-      @confirm="onConfirmTestTime"
-      @cancel="state.showTestTimeTime = false"
-    />
-  </van-popup>
-
-  <!-- 选择经费卡 -->
-  <van-popup
-    v-model:show="state.showExpenseCard"
-    position="bottom"
-  >
-    <van-picker
-      :columns="fundsList"
-      :columns-field-names="{ text: 'finAccount', value: 'id' }"
-      @confirm="pickExpenseCard"
-      @cancel="state.showExpenseCard = false"
-    />
-  </van-popup>
-</template>
-
-<script lang="ts" setup>
-  import to from 'await-to-js'
-  import { useRoute, useRouter } from 'vue-router'
-  import { useInstrApi } from '/@/api/instr'
-  import { defineAsyncComponent, onMounted, reactive, ref } from 'vue'
-  import { formatDate } from '/@/utils/formatTime'
-  import { showNotify, showToast } from 'vant'
-  import { useProApi } from '/@/api/project'
-  import { useSampleApi } from '/@/api/instr/sample'
-
-  const CustomForm = defineAsyncComponent(() => import('/@/components/CustomForm.vue'))
-  const route = useRoute()
-  const router = useRouter()
-  const projApi = useProApi()
-  const instApi = useInstrApi()
-  const sampleApi = useSampleApi()
-
-  const fundsList = ref([])
-  const formRef = ref()
-  const customFormRef = ref()
-
-  const state = reactive({
-    loading: false,
-    InstCfgCharge: false,
-    showDeliverTime: false,
-    showDeliverTimeTime: false,
-    currentDate: [],
-    currentTime: [],
-    showTestTime: false,
-    showTestTimeTime: false,
-    currentDateTest: [],
-    currentTimeTest: [],
-    showExpenseCard: false,
-    form: {
-      instId: 0,
-      deliverTime: '',
-      testTime: '',
-      projectName: '',
-      projectId: null,
-      expenseCardId: 0,
-      expenseCardName: '',
-      sampleDesc: '',
-      sampleItem: [] as any[],
-      userContact: '',
-      sampleForm: [] as any[],
-    },
-  })
-
-  // Determine Default Time
-  const now = new Date()
-  const dateStr = formatDate(now, 'YYYY-mm-dd')
-  const timeStr = formatDate(now, 'HH:MM')
-  state.currentDate = dateStr.split('-')
-  state.currentTime = timeStr.split(':')
-  state.currentDateTest = dateStr.split('-')
-  state.currentTimeTest = timeStr.split(':')
-  state.form.deliverTime = `${dateStr} ${timeStr}`
-  state.form.testTime = `${dateStr} ${timeStr}`
-
-
-  const onConfirmDeliverDate = ({ selectedValues }) => {
-      state.currentDate = selectedValues
-      state.showDeliverTime = false
-      state.showDeliverTimeTime = true
-  }
-  const onConfirmDeliverTime = ({ selectedValues }) => {
-      state.currentTime = selectedValues
-      state.form.deliverTime = `${state.currentDate.join('-')} ${state.currentTime.join(':')}`
-      state.showDeliverTimeTime = false
-  }
-
-    const onConfirmTestDate = ({ selectedValues }) => {
-      state.currentDateTest = selectedValues
-      state.showTestTime = false
-      state.showTestTimeTime = true
-  }
-  const onConfirmTestTime = ({ selectedValues }) => {
-      state.currentTimeTest = selectedValues
-      state.form.testTime = `${state.currentDateTest.join('-')} ${state.currentTimeTest.join(':')}`
-      state.showTestTimeTime = false
-  }
-
-
-  const init = async () => {
-    getSampleConfig()
-    getChargeConfig()
-  }
-
-  // 送样配置信息
-  const getSampleConfig = async () => {
-    const params = {
-      instId: state.form.instId,
-      code: 'InstCfgSample',
-    }
-    const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
-    if (err) return
-    state.form.sampleForm = res?.data?.config.sampleForm ? JSON.parse(res.data.config.sampleForm) : []
-  }
-
-  // 计费配置信息
-  const getChargeConfig = async () => {
-    const params = {
-      instId: state.form.instId,
-      code: 'InstCfgCharge',
-    }
-    const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
-    if (err) return
-    state.InstCfgCharge = res?.data.config.enable && !res?.data.config.sampleFreeEnable
-    state.form.sampleItem = res?.data?.config?.sampleItemPrice || []
-    state.form.sampleItem = state.form.sampleItem.map((item) => {
-      return {
-        ...item,
-        count: 1,
-      }
-    })
-    await getMyProjectInfo()
-  }
-
-  const getMyProjectInfo = async () => {
-    const [err, res]: ToResponse = await to(projApi.getMySelfProjectGroup({}))
-    if (err) return
-    state.form.projectName = res?.data.pgName || ''
-    state.form.projectId = res?.data.id || null
-    if (state.form.projectId) {
-      getFundsData()
-      getTestList()
-    }
-  }
-
-  const getFundsData = async () => {
-    const [err, res]: ToResponse = await to(projApi.getFinanceAccountList({ projId: state.form.projectId }))
-    if (err) return
-    fundsList.value = res?.data.list ? [res?.data.list] : []
-    if (fundsList.value && fundsList.value.length > 0 && fundsList.value[0].length > 0) {
-      state.form.expenseCardId = fundsList.value[0][0].id
-      state.form.expenseCardName = fundsList.value[0][0].finAccount
-    }
-  }
-
-  const getTestList = async () => {
-    const [err, res]: ToResponse = await to(
-      sampleApi.getSampleTestOption({
-        instId: state.form.instId,
-        projectId: state.form.projectId,
-      }),
-    )
-    if (err) return
-    if (res.data.length > 0) {
-      state.form.sampleItem.forEach((item) => {
-        item.price = res.data.find((price) => price.name === item.name)?.price || 0
-      })
-    }
-  }
-
-  // 经费卡选择
-  const pickExpenseCard = ({ selectedOptions }) => {
-    state.form.expenseCardId = selectedOptions[0].id
-    state.form.expenseCardName = selectedOptions[0].finAccount
-    state.showExpenseCard = false
-  }
-
-  const onClickButton = async () => {
-    state.loading = true
-    const [errValid] = await to(formRef.value.validate())
-    if (errValid) {
-       state.loading = false
-       return
-    }
-
-    if (state.InstCfgCharge && !state.form.expenseCardId) {
-      showNotify({ type: 'warning', message: '请选择经费卡' })
-      state.loading = false
-      return
-    }
-
-    const customForm = customFormRef.value.getFormData()
-    if (state.form.sampleForm.length > 0 && !customForm) {
-      state.loading = false
-      return
-    }
-
-    const sampleItem = state.form.sampleItem.map((item) => {
-      return {
-        name: item.name,
-        count: Number(item.count),
-      }
-    })
-
-    let params: any = Object.assign({}, state.form)
-    params.sampleItem = sampleItem
-    params.sampleForm = JSON.stringify(customForm || [])
-
-    const [err, res]: ToResponse = await to(sampleApi.add(params))
-    state.loading = false
-    if (err) return
-    if (res && res.code == 200) {
-       showToast({
-          type: 'success',
-          message: '提交成功'
-       })
-       router.back()
-    }
-  }
-
-  onMounted(() => {
-    state.form.instId = route.query.id ? +route.query.id : 0
-    init()
-  })
-</script>
-
-<style lang="scss" scoped>
-  .container {
-    flex: 1;
-    padding: 10px;
-    background-color: #f7f8fa;
-    overflow-y: auto;
-
-    h4 {
-      height: 18px;
-      line-height: 18px;
-      display: flex;
-      margin: 10px 0;
-      span {
-        font-weight: normal;
-        margin-left: auto;
-      }
-      &::before {
-        display: inline-block;
-        content: '';
-        width: 3px;
-        height: 18px;
-        background-color: #1c9bfd;
-        margin-right: 4px;
-        vertical-align: middle;
-      }
-    }
-  }
-  .card-wrap {
-    padding-bottom: 20px;
-  }
-  .card-item {
-    background: #fff;
-    padding: 15px;
-    border-radius: 8px;
-    margin-bottom: 10px;
-    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
-
-    .label {
-      color: #646566;
-      font-size: 14px;
-    }
-    .value {
-      color: #323233;
-      font-size: 14px;
-      &.bold {
-        font-weight: bold;
-      }
-      &.price {
-        color: #ee0a24;
-      }
-    }
-  }
-  .flex {
-    display: flex;
-  }
-  .flex-between {
-    justify-content: space-between;
-  }
-  .flex-center {
-    align-items: center;
-  }
-  .mb10 {
-    margin-bottom: 10px;
-  }
-</style><!--
+--><!--
  * @Author: wanglj wanglijie@dashoo.cn
  * @Date: 2025-03-24 09:17:15
  * @LastEditors: wanglj wanglijie@dashoo.cn
@@ -492,36 +56,38 @@
 
       <CustomForm ref="customFormRef" :formData="state.form.sampleForm"></CustomForm>
     </van-form>
+
+    <van-action-bar placeholder>
+      <van-action-bar-button class="w100" type="primary" text="保存" @click="onClickButton" :loading="state.loading" />
+    </van-action-bar>
+
+    <!-- 送样时间 -->
+    <van-popup v-model:show="state.showDeliverTime" position="bottom">
+      <van-date-picker v-model="state.currentDate" title="选择送样时间" @confirm="onConfirmDeliverDate"
+        @cancel="state.showDeliverTime = false" />
+    </van-popup>
+    <van-popup v-model:show="state.showDeliverTimeTime" position="bottom">
+      <van-time-picker v-model="state.currentTime" title="选择送样时间" @confirm="onConfirmDeliverTime"
+        @cancel="state.showDeliverTimeTime = false" />
+    </van-popup>
+
+    <!-- 检测时间 -->
+    <van-popup v-model:show="state.showTestTime" position="bottom">
+      <van-date-picker v-model="state.currentDateTest" title="选择检测时间" @confirm="onConfirmTestDate"
+        @cancel="state.showTestTime = false" />
+    </van-popup>
+    <van-popup v-model:show="state.showTestTimeTime" position="bottom">
+      <van-time-picker v-model="state.currentTimeTest" title="选择检测时间" @confirm="onConfirmTestTime"
+        @cancel="state.showTestTimeTime = false" />
+    </van-popup>
+
+    <!-- 选择经费卡 -->
+    <van-popup v-model:show="state.showExpenseCard" position="bottom">
+      <van-picker :columns="fundsList" :columns-field-names="{ text: 'finAccount', value: 'id' }"
+        @confirm="pickExpenseCard" @cancel="state.showExpenseCard = false" />
+    </van-popup>
   </div>
-  <van-action-bar placeholder>
-    <van-action-bar-button class="w100" type="primary" text="保存" @click="onClickButton" :loading="state.loading" />
-  </van-action-bar>
-
-  <!-- 送样时间 -->
-  <van-popup v-model:show="state.showDeliverTime" position="bottom">
-    <van-date-picker v-model="state.currentDate" title="选择送样时间" @confirm="onConfirmDeliverDate"
-      @cancel="state.showDeliverTime = false" />
-  </van-popup>
-  <van-popup v-model:show="state.showDeliverTimeTime" position="bottom">
-    <van-time-picker v-model="state.currentTime" title="选择送样时间" @confirm="onConfirmDeliverTime"
-      @cancel="state.showDeliverTimeTime = false" />
-  </van-popup>
-
-  <!-- 检测时间 -->
-  <van-popup v-model:show="state.showTestTime" position="bottom">
-    <van-date-picker v-model="state.currentDateTest" title="选择检测时间" @confirm="onConfirmTestDate"
-      @cancel="state.showTestTime = false" />
-  </van-popup>
-  <van-popup v-model:show="state.showTestTimeTime" position="bottom">
-    <van-time-picker v-model="state.currentTimeTest" title="选择检测时间" @confirm="onConfirmTestTime"
-      @cancel="state.showTestTimeTime = false" />
-  </van-popup>
-
-  <!-- 选择经费卡 -->
-  <van-popup v-model:show="state.showExpenseCard" position="bottom">
-    <van-picker :columns="fundsList" :columns-field-names="{ text: 'finAccount', value: 'id' }"
-      @confirm="pickExpenseCard" @cancel="state.showExpenseCard = false" />
-  </van-popup>
+
 </template>
 
 <script lang="ts" setup>
@@ -633,7 +199,7 @@ const getChargeConfig = async () => {
   const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
   if (err) return
   state.InstCfgCharge = res?.data.config.enable && !res?.data.config.sampleFreeEnable
-  if (res?.data?.config?.sampleCountEnable) {
+  if (res?.data?.config?.sampleCountEnable && res?.data?.config?.enable) {
     state.form.sampleItem = res?.data?.config?.sampleItemPrice || []
     state.form.sampleItem = state.form.sampleItem.map((item) => {
       return {

+ 13 - 1
src/view/login/index.vue

@@ -22,7 +22,7 @@
         </template>
       </van-field>
 
-      <div class="mt10 input-box code">
+      <div class="mt10 input-box code" v-if="state.isCaptcha === '10'">
         <van-field v-model="state.form.idValueC" placeholder="验证码" />
         <div class="login-code">
           <img class="captcha-image" width="120" height="40" :src="state.captchaImage" @click="getCaptchaImage" />
@@ -69,6 +69,8 @@
 import { onMounted, reactive, ref } from 'vue'
 import to from 'await-to-js'
 import { useLoginApi } from '/@/api/login/index'
+import { useConfigApi } from '/@/api/system/config'
+
 import { Local } from '/@/utils/storage'
 import crypto from 'sm-crypto'
 import { useRouter, useRoute } from 'vue-router'
@@ -82,6 +84,7 @@ const router = useRouter()
 const route = useRoute()
 const sm3 = crypto.sm3
 const loginApi = useLoginApi()
+const configApi = useConfigApi()
 const storesUseUserInfo = useUserInfo()
 const { userInfos, openId, unionId } = storeToRefs(storesUseUserInfo)
 const showPassword = ref(false)
@@ -127,8 +130,16 @@ const state = reactive({
     idKeyC: '',
     saltValue: '',
   },
+  isCaptcha: '20',
 })
 
+// 获取基础配置 是否需要验证码
+const getBaseConfig = async () => {
+  const [err, res]: ToResponse = await to(configApi.getEntityMapByKeys({ configKeys: ['isCaptcha'] }));
+  if (err) return
+  state.isCaptcha = res.data.isCaptcha
+}
+
 const getCaptchaImage = async () => {
   const [err, res]: ToResponse = await to(loginApi.getCaptchaImg())
   if (err) return
@@ -220,6 +231,7 @@ const blueTooth = async () => {
   }
 }
 onMounted(async () => {
+  getBaseConfig()
   const code: string = route.query.code ? route.query.code.toString() : ''
   //  if (!code) {
   //       const appid = "wxdd446f7e55c80c16";

+ 114 - 8
src/view/register/index.vue

@@ -26,14 +26,25 @@
         <van-cell-group inset>
           <van-field v-model="state.form.userName" label="登录账号" placeholder="登录账号" :rules="[{ required: true, message: '请填写登录账号' },
           { validator: checkUserNameExists, message: '账号不可用' }]" />
-          <van-field v-model="state.form.password" type="password" label="密码" placeholder="密码"
+          <van-field v-model="state.form.password" :type="state.showPassword ? 'text' : 'password'" label="密码"
+            placeholder="密码" :right-icon="state.showPassword ? 'eye-o' : 'closed-eye'"
+            @click-right-icon="state.showPassword = !state.showPassword"
             :rules="[{ required: true, message: '密码不能为空' }, { validator: checkPassword, message: '密码不合法' }]" />
-          <van-field v-model="state.form.confirmPassword" type="password" label="确认密码" placeholder="确认密码"
+          <van-field v-model="state.form.confirmPassword" :type="state.showConfirmPassword ? 'text' : 'password'"
+            label="确认密码" placeholder="确认密码" :right-icon="state.showConfirmPassword ? 'eye-o' : 'closed-eye'"
+            @click-right-icon="state.showConfirmPassword = !state.showConfirmPassword"
             :rules="[{ required: true, validator: confirmPasswordSame, message: '两次输入的密码不一致' }]" />
         </van-cell-group>
       </van-form>
       <van-form ref="personInfoRef" v-show="state.active == 1" required="auto">
         <van-cell-group inset>
+          <van-field name="avatar" label="头像" :rules="[{ required: true, message: '请上传头像' }]">
+            <template #input>
+              <van-uploader v-model="state.form.fileList" multiple :max-count="1" :before-read="beforeRead"
+                :after-read="afterRead" />
+            </template>
+          </van-field>
+
           <van-field v-model="state.form.nickName" label="姓名" placeholder="姓名"
             :rules="[{ required: true, message: '请填写姓名' }]" />
           <van-field label="性别" :rules="[{ required: false }]">
@@ -45,7 +56,7 @@
               </van-radio-group>
             </template>
           </van-field>
-          <van-field>
+          <van-field v-if="false">
             <template #label>
               <span>用户类型</span>
               <el-tooltip class="box-item" effect="dark" placement="top" append-to="body">
@@ -73,8 +84,7 @@
             :rules="[{ required: true, message: '请填写部门描述' }]" />
           <van-field v-model="state.form.phone" type="tel" label="手机号" placeholder="手机号"
             @blur="checkUserNamePhoneExists('phone')" :rules="[{ required: true, message: '请填写手机号' }]" />
-          <van-field v-model="state.form.email" label="邮箱" placeholder="邮箱"
-            :rules="[{ required: true, message: '请填写邮箱' }]" />
+          <van-field v-model="state.form.email" label="邮箱" placeholder="邮箱" />
           <van-field label="证件类型" :rules="[{ required: true, message: '请选择证件类型' }]">
             <template #input>
               <van-radio-group disabled v-model="state.form.idType" label="证件类型" placeholder="证件类型"
@@ -224,6 +234,7 @@
   import { UserTypeTooltip } from '/@/constants/pageConstants'
   import { isPasswordValid } from '/@/utils/stringUtils'
 
+  const uploadUrl = import.meta.env.VITE_UPLOAD;
   const sm3 = crypto.sm3
   const loginApi = useLoginApi()
   const router = useRouter()
@@ -258,6 +269,8 @@
   const pjtTypeList = ref(<any[]>[])
   const state = reactive({
     active: 0,
+    showPassword: false,
+    showConfirmPassword: false,
     loading: {
       signIn: false,
     },
@@ -275,6 +288,7 @@
       confirmPassword: '',
       status: '10', // 用户状态
       describe: '', // 用户描述
+      fileList: [],
       avatar: '',
       idType: '', //证件类型
       idCode: '', // 证件号
@@ -457,8 +471,8 @@
       .then(res => {
         return true
       })
-      .catch(() => {
-        return "密码不合法"
+      .catch((err) => {
+        return err.message || "密码不合法"
       })
   }
 
@@ -593,11 +607,103 @@
     }
   }
 
+  const beforeRead = (file: any) => {
+    if (
+      file.type !== 'image/jpeg' &&
+      file.type !== 'image/jpg' &&
+      file.type !== 'image/png' &&
+      file.type !== 'image/bmp' &&
+      file.type !== 'image/gif'
+    ) {
+      showNotify({
+        type: 'danger',
+        message: '上传图片必须是JPG/PNG/BMP/GIF类型!'
+      });
+      return false;
+    } else if (file.size / 1024 / 1024 > 4) {
+      showNotify({
+        type: 'danger',
+        message: '图片大小不能超过4MB!'
+      });
+      return false;
+    }
+    return true;
+  };
+
+  // 上传头像方法
+  const afterRead = async (file: any) => {
+    // 创建FormData对象
+    const formData = new FormData();
+    formData.append('file', file.file);
+
+    try {
+      // 使用fetch上传文件
+      const response = await fetch(uploadUrl, {
+        method: 'POST',
+        body: formData
+      });
+
+      if (response.ok) {
+        const result = await response.json();
+        console.log(result);
+        // 假设服务器返回的格式为 { code: 200, data: { url: "文件URL" } }
+        if (result.Code === 200 && result.Data) {
+          // 清空之前的头像,只保留最新上传的
+          state.form.avatar = result.Data;
+          state.form.fileList = [{
+            url: result.Data,
+            isImage: true
+          }]
+          // 触发表单验证,更新头像字段的验证状态
+          personInfoRef.value?.validate('avatar').catch(() => { });
+          showNotify({
+            type: 'success',
+            message: '头像上传成功'
+          });
+        } else {
+          // 上传失败,清空头像
+          state.form.avatar = '';
+          state.form.fileList = [];
+          showNotify({
+            type: 'danger',
+            message: '头像上传失败:' + (result.message || '未知错误')
+          });
+        }
+      } else {
+        // 上传失败,清空头像
+        state.form.avatar = '';
+        state.form.fileList = [];
+        showNotify({
+          type: 'danger',
+          message: '头像上传失败:服务器错误'
+        });
+      }
+    } catch (error) {
+      console.error('头像上传错误:', error);
+      // 上传失败,清空头像
+      state.form.avatar = '';
+      state.form.fileList = [];
+      showNotify({
+        type: 'danger',
+        message: '头像上传失败:网络错误'
+      });
+    }
+  }
+
   const onRegister = async () => {
     const form = state.form.registerType == '10' ? personInfoRef.value : personInfoRef.value
     const [error] = await to(form.validate())
     if (error) return
 
+    // 验证头像是否已上传
+    if (!state.form.avatar) {
+      showNotify({
+        type: 'danger',
+        message: '请上传头像'
+      });
+      return
+    }
+
     // 如果是课题组负责人,需要校验项目信息
     if (state.form.registerType === '20') {
       if (!validateProjectList()) {
@@ -803,4 +909,4 @@
     }
   }
 }
-</style>
+</style>

+ 13 - 2
src/view/user/edit.vue

@@ -16,7 +16,7 @@
     </header> -->
     <van-form ref="formRef" @submit="onSubmit" class="mt10" required="auto">
       <van-cell-group>
-        <van-cell class="flex" is-link size="normal" title="头像" @click="editAvatar">
+        <van-cell class="flex" is-link size="normal" title="头像" @click="editAvatar" required>
           <template #value>
             <!-- <img class="avatar" width="30" height="30" :src="userInfos.avatar" alt="" /> -->
             <van-image width="30px" height="30px" :src="userInfos.avatar" />
@@ -228,6 +228,7 @@ const uploadBaseFunc = (file) => {
       if (res) {
         if (res.data.Code == 200) {
           // 图片上传成功,直接修改
+          state.form.avatar = res?.data.Data || '' // 更新表单中的头像字段
           const [err]: ToResponse = await to(userApi.setAvatar({ fileUrl: res?.data.Data || '' }))
           if (err) return
           showNotify({
@@ -265,6 +266,16 @@ const initForm = async () => {
 const onSubmit = async () => {
   const [errValid] = await to(formRef.value.validate())
   if (errValid) return
+  
+  // 验证头像是否存在
+  if (!state.form.avatar && !userInfos.value.avatar) {
+    showNotify({
+      type: 'danger',
+      message: '请上传头像'
+    })
+    return
+  }
+  
   state.form.userId = state.form.id
   const params = JSON.parse(JSON.stringify(state.form))
   const [err]: ToResponse = await to(userApi.updateProfile(params))
@@ -337,4 +348,4 @@ onMounted(() => {
     width: 500px;
   }
 }
-</style>
+</style>

+ 14 - 5
src/view/user/password.vue

@@ -4,23 +4,29 @@
       <van-cell-group>
         <van-field
           v-model="state.form.oldPassword"
-          type="password"
+          :type="state.showOldPassword ? 'text' : 'password'"
           label="旧密码"
           placeholder="旧密码"
+          :right-icon="state.showOldPassword ? 'eye-o' : 'closed-eye'"
+          @click-right-icon="state.showOldPassword = !state.showOldPassword"
           :rules="[{ required: true, message: '请输入旧密码' }]"
         />
         <van-field
           v-model="state.form.newPassword"
-          type="password"
+          :type="state.showNewPassword ? 'text' : 'password'"
           label="新密码"
           placeholder="新密码"
+          :right-icon="state.showNewPassword ? 'eye-o' : 'closed-eye'"
+          @click-right-icon="state.showNewPassword = !state.showNewPassword"
           :rules="[{ required: true, message: '请输入新密码' }, {validator: checkPassword, message: '密码不合法'}]"
         />
         <van-field
           v-model="state.form.newPassword2"
-          type="password"
+          :type="state.showNewPassword2 ? 'text' : 'password'"
           label="确认密码"
           placeholder="确认密码"
+          :right-icon="state.showNewPassword2 ? 'eye-o' : 'closed-eye'"
+          @click-right-icon="state.showNewPassword2 = !state.showNewPassword2"
           :rules="[{ required: true, message: '请输入确认密码', validator: checkConfirmPassword }]"
         />
       </van-cell-group>
@@ -56,6 +62,9 @@
   const userSexList = ref(<RowDicDataType[]>[])
   const loginApi = useLoginApi()
   const state = reactive({
+    showOldPassword: false,
+    showNewPassword: false,
+    showNewPassword2: false,
     form: {
       oldPassword: '',
       newPassword: '',
@@ -82,8 +91,8 @@
       .then(res => {
         return true
       })
-      .catch(() => {
-        return "密码不合法"
+      .catch((err) => {
+        return err.message || "密码不合法"
       })
   }
   const checkConfirmPassword = (value: string) => {