| | |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | const data = JSON.parse(res.data); |
| | | console.log(data,'文件'); |
| | | |
| | | if (data.code === 200) { |
| | | resolve({ |
| | | ...data, |
| | |
| | | file.status = 'uploading'; |
| | | const res = await uploadFile(file.file, 'base'); |
| | | Object.assign(file, { |
| | | url: res.fileName, |
| | | fileName: res.fileName, |
| | | url: res.url, |
| | | fileName: res.name, |
| | | newFileName: res.newFileName, |
| | | originalFilename: res.originalFilename, |
| | | status: 'success' |
| | | status: 'success', |
| | | size:res.size |
| | | }); |
| | | emit("upload-base", file); |
| | | } catch (error) { |
| | |
| | | "path": "pages/case/index", |
| | | "style": { |
| | | "navigationBarTitleText": "我的上报" |
| | | |
| | | } |
| | | }, |
| | | { |
| | |
| | | "path": "pages/case/transfer", |
| | | "style": { |
| | | "navigationBarTitleText": "转运登记" |
| | | |
| | | } |
| | | }, |
| | | { |
| | |
| | | // 处理基础附件上传 |
| | | const handleBaseUpload = (file) => { |
| | | console.log("基础附件上传成功:", file); |
| | | attachments.value.push(file); |
| | | }; |
| | | |
| | | // 处理其他附件上传 |
| | | const handleFilesUpdate = (files) => { |
| | | console.log(files, "files"); |
| | | |
| | | attachments.value = files.map((file) => ({ |
| | | ...file, |
| | | // 确保只存储半路径 |
| | | url: file.url.startsWith("http") |
| | | ? file.url.replace(baseUrlHt, "") |
| | | : file.url, |
| | | // url: file.url.startsWith("http") |
| | | // ? file.url.replace(baseUrlHt, "") |
| | | // : file.url, |
| | | })); |
| | | }; |
| | | |
| | |
| | | } |
| | | |
| | | try { |
| | | console.log(attachments.value); |
| | | |
| | | uni.showLoading({ title: isEditMode.value ? "修改中..." : "提交中..." }); |
| | | // 准备提交数据 |
| | | const submitData = { |
| | | ...form.value, |
| | | age: parseInt(form.value.age) || 0, |
| | | annexfilesList: attachments.value.map((file) => ({ |
| | | url: file.url, |
| | | name: file.name, |
| | | path: file.url, |
| | | fileName: file.name, |
| | | type: file.type, |
| | | })), |
| | | phone: form.value.infophone, |
| | |
| | | }; |
| | | |
| | | const loadCaseData = async (id) => { |
| | | // 模拟加载编辑数据 |
| | | // form.value = { |
| | | // caseNo: "DON20241216001", |
| | | // treatmenthospitalname: "青岛镜湖医院", |
| | | // treatmentdeptname: "神经外科", |
| | | // name: "张三", |
| | | // nation: "汉族", |
| | | // nationality: "中国", |
| | | // idcardtype: "居民身份证", |
| | | // idcardno: "370203198510123456", |
| | | // sex: "1", |
| | | // birthday: "1985-10-12", |
| | | // age: "38", |
| | | // inpatientno: "ZY20241216001", |
| | | // diagnosisname: "脑外伤导致脑死亡", |
| | | // bloodType: "A", |
| | | // rhYin: "positive", |
| | | // infoname: "李医生", |
| | | // infophone: "13800138000", |
| | | // reporterno: "张医生", |
| | | // reporttime: currentTime.value, |
| | | // }; |
| | | try { |
| | | loading.value = true; |
| | | const res = await uni.$uapi.get( |
| | | `/project/donatebaseinforeport/getInfo/${id}`, |
| | | ); |
| | | |
| | | if (res) { |
| | | console.log(res, "res"); |
| | | |
| | | form.value = res; |
| | | console.log(1, "res"); |
| | | if (res.code) { |
| | | form.value = res.data; |
| | | |
| | | // 处理选择器索引 |
| | | // updatePickerIndexes(); |
| | | |
| | | console.log(2, "res"); |
| | | // 处理附件 |
| | | if (res.annexfilesList) { |
| | | attachments.value = res.annexfilesList; |
| | | if (res.data.annexfilesList) { |
| | | attachments.value = res.data.annexfilesList; |
| | | attachments.value.forEach((item) => { |
| | | item.url = item.path; |
| | | item.name = item.fileName; |
| | | }); |
| | | } |
| | | console.log(3, "res"); |
| | | |
| | |
| | | display: flex; |
| | | gap: 20rpx; |
| | | margin-top: 40rpx; |
| | | // background: #fff; |
| | | // padding: 20rpx 0; |
| | | // position: fixed; |
| | | // bottom: 0; |
| | | // left: 0; |
| | | // right: 0; |
| | | // padding: 20rpx 30rpx; |
| | | // padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); |
| | | // box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08); |
| | | // z-index: 9; |
| | | } |
| | | |
| | | .btn { |
| | |
| | | border-radius: 12rpx !important; |
| | | padding: 20rpx 24rpx !important; |
| | | background: #fff !important; |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | margin-top: 40rpx; |
| | | } |
| | | |
| | | .btn { |
| | |
| | | try { |
| | | const res = await uni.$uapi.get(`/project/donatebaseinforeport/getInfo/${id}`) |
| | | |
| | | if (res) { |
| | | caseDetail.value = res |
| | | if (res.data) { |
| | | caseDetail.value = res.data |
| | | |
| | | // 如果状态是"已上报"(1),自动更新为"已阅读"(2) |
| | | if (caseDetail.value.reportStatus === '1') { |
| | |
| | | </view> |
| | | <text |
| | | class="status" |
| | | :class="caseItem.reportStatus.replace('ed', '')" |
| | | :class="getStatusClass(caseItem.reportStatus)" |
| | | >{{ caseItem.statusText }}</text |
| | | > |
| | | </view> |
| | |
| | | 创建转运单 |
| | | </button> |
| | | |
| | | <!-- 需转运但未同意 --> |
| | | <!-- <button |
| | | v-if="caseItem.isTransport == '2' && !hasTransport(caseItem)" |
| | | class="transport-btn disabled" |
| | | disabled |
| | | > |
| | | 需先同意案例 |
| | | </button> --> |
| | | |
| | | <!-- 有转运单可查看 --> |
| | | <button |
| | | v-if="hasTransport(caseItem)" |
| | |
| | | <view class="actions"> |
| | | <button |
| | | class="action-btn" |
| | | v-if="caseItem.reportStatus != '1'" |
| | | v-if="caseItem.reportStatus == '1' || caseItem.reportStatus == '2'" |
| | | @tap.stop="withdrawCase(caseItem)" |
| | | > |
| | | 撤回 |
| | |
| | | </button> |
| | | <button |
| | | class="action-btn secondary" |
| | | v-if="caseItem.reportStatus != '1'" |
| | | v-if="caseItem.reportStatus == '1' || caseItem.reportStatus == '2'" |
| | | @tap.stop="editCase(caseItem)" |
| | | > |
| | | 编辑 |
| | |
| | | </view> |
| | | |
| | | <!-- 加载更多 --> |
| | | <view class="load-more" v-if="hasMore"> |
| | | <text>加载中...</text> |
| | | <view class="load-more" v-if="loadingMore"> |
| | | <u-loading-icon text="加载中..."></u-loading-icon> |
| | | </view> |
| | | |
| | | <!-- 无更多数据 --> |
| | | <view class="no-more" v-if="!hasMore && filteredCases.length > 0"> |
| | | <text>已加载全部数据</text> |
| | | </view> |
| | | |
| | | <!-- 空状态 --> |
| | | <view class="empty-state" v-if="!loading && filteredCases.length == 0"> |
| | | <image src="/static/empty/no-case.png" mode="aspectFit" /> |
| | | <text>暂无器官捐献案例记录</text> |
| | | </view> |
| | | |
| | | <!-- 加载状态 --> |
| | | <view class="empty-state" v-if="loading"> |
| | | <text>数据加载中...</text> |
| | | </view> |
| | | </scroll-view> |
| | | |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 加载状态 --> |
| | | <u-loading-page v-if="initLoading" :loading="true" text="数据加载中..." /> |
| | | </view> |
| | | <view class="float-button" @tap="createNewCase"> |
| | | <u-icon name="plus" color="#fff" size="24"></u-icon> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onLoad, onShow } from "@dcloudio/uni-app"; |
| | | import { onLoad, onShow, onReachBottom } from "@dcloudio/uni-app"; |
| | | import { useDict } from "@/utils/dict"; |
| | | |
| | | // 响应式数据 |
| | | const dict = ref({}); |
| | | const loading = ref(false); |
| | | const initLoading = ref(false); |
| | | const loadingMore = ref(false); |
| | | const cases = ref([]); |
| | | const total = ref(0); |
| | | const pageNum = ref(1); |
| | |
| | | }; |
| | | |
| | | const statusInfo = statusMap[apiData.reportStatus] || statusMap["1"]; |
| | | |
| | | // 转运状态映射 |
| | | const getTransportStatusClass = (caseData) => { |
| | | if (!hasTransport(caseData)) { |
| | | return "not-transported"; |
| | | } |
| | | const status = getTransportStatus(caseData); |
| | | const map = { |
| | | 1: "pending", |
| | | 2: "transporting", |
| | | 3: "completed", |
| | | 4: "cancelled", |
| | | 5: "draft", |
| | | }; |
| | | return map[status] || "pending"; |
| | | }; |
| | | |
| | | return { |
| | | id: apiData.id || apiData.donatebaseinfoReportId, |
| | |
| | | }; |
| | | // 转运判断方法 - 与PC端保持一致 |
| | | const hasTransport = (caseData) => { |
| | | console.log(caseData.serviceTransport); |
| | | return caseData.serviceTransport && caseData.serviceTransport.length > 0; |
| | | }; |
| | | |
| | | const createNewCase = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/case/CaseDetails' |
| | | }); |
| | | }; |
| | | const canCreateTransport = (caseData) => { |
| | | return ( |
| | | (caseData.isTransport != "4" || caseData.isTransport == 2) && // 需要转运 |
| | | (caseData.isTransport == "2" || caseData.isTransport == 2) && // 需要转运 |
| | | !hasTransport(caseData) |
| | | ); // 无转运单 |
| | | }; |
| | |
| | | url: `/pages/case/transferinfo?caseId=${caseItem.id}&caseNo=${caseItem.donorNo}`, |
| | | }); |
| | | }; |
| | | |
| | | const getStatusClass = (status) => { |
| | | const map = { |
| | | '1': 'reported', // 已上报 |
| | | '2': 'read', // 已阅读 |
| | | '3': 'agreed', // 已同意 |
| | | '4': 'rejected' // 已驳回 |
| | | }; |
| | | return map[status] || 'reported'; |
| | | } |
| | | const viewTransportDetail = (caseItem) => { |
| | | if (hasTransport(caseItem)) { |
| | | const transport = caseItem.serviceTransport[0]; |
| | |
| | | |
| | | // 筛选相关 |
| | | const caseTypes = [ |
| | | { label: "全部", value: "" }, |
| | | { label: "全部", value: "all" }, |
| | | { label: "已上报", value: "1" }, |
| | | { label: "已阅读", value: "2" }, |
| | | { label: "已同意", value: "3" }, |
| | |
| | | const currentType = ref("all"); |
| | | const startDate = ref(""); |
| | | const endDate = ref(""); |
| | | |
| | | // 转运状态映射 |
| | | const transportStatusMap = { |
| | | not_transported: { |
| | | text: "未转运", |
| | | color: "warning", |
| | | class: "not-transported", |
| | | }, |
| | | transporting: { text: "转运中", color: "primary", class: "transporting" }, |
| | | completed: { text: "已完成", color: "success", class: "completed" }, |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onLoad(async (options) => { |
| | |
| | | await loadInitialData(); |
| | | }); |
| | | |
| | | onReachBottom(() => { |
| | | if (hasMore.value && !loading.value && !loadingMore.value) { |
| | | onLoadMore(); |
| | | } |
| | | }); |
| | | |
| | | onShow(() => { |
| | | const transportUpdate = uni.getStorageSync("transportStatusUpdate"); |
| | | if (transportUpdate) { |
| | |
| | | |
| | | // 数据加载函数 |
| | | const loadInitialData = async () => { |
| | | loading.value = true; |
| | | initLoading.value = true; |
| | | try { |
| | | await Promise.all([donatebaseinforeportList(), donateTotal()]); |
| | | await Promise.all([donatebaseinforeportList(true), donateTotal()]); |
| | | } catch (error) { |
| | | console.error("初始化数据失败:", error); |
| | | } finally { |
| | | loading.value = false; |
| | | initLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 获取案例列表 |
| | | const donatebaseinforeportList = async () => { |
| | | const donatebaseinforeportList = async (reset = false) => { |
| | | if (reset) { |
| | | pageNum.value = 1; |
| | | hasMore.value = true; |
| | | } |
| | | |
| | | if (loading.value || loadingMore.value || !hasMore.value) return; |
| | | |
| | | if (reset) { |
| | | loading.value = true; |
| | | } else { |
| | | loadingMore.value = true; |
| | | } |
| | | |
| | | try { |
| | | const params = { |
| | | pageNum: pageNum.value, |
| | | pageSize: pageSize.value, |
| | | }; |
| | | |
| | | |
| | | // 筛选条件 - 如果选择了"全部",不传reportStatus参数 |
| | | if (currentType.value !== "all") { |
| | | params.reportStatus = currentType.value; |
| | | } |
| | | |
| | | // 时间筛选条件 |
| | | if (startDate.value && endDate.value) { |
| | | // 将时间格式转换为接口需要的格式 |
| | | params.startDate = startDate.value; |
| | | params.endDate = endDate.value; |
| | | } |
| | | |
| | | console.log("请求参数:", params); |
| | | |
| | | const res = await uni.$uapi.post( |
| | | "/project/donatebaseinforeport/list", |
| | | params, |
| | | ); |
| | | console.log(res, "res"); |
| | | |
| | | const mappedData = res.map((item) => mapApiDataToCaseItem(item)); |
| | | |
| | | if (pageNum.value == 1) { |
| | | cases.value = mappedData; |
| | | |
| | | console.log("接口返回数据:", res); |
| | | |
| | | if (res.code == 200) { |
| | | const data =res.data || []; |
| | | const mappedData = data.map((item) => mapApiDataToCaseItem(item)); |
| | | |
| | | if (reset || pageNum.value == 1) { |
| | | console.log(mappedData,'mappedData'); |
| | | |
| | | cases.value = mappedData; |
| | | } else { |
| | | cases.value = [...cases.value, ...mappedData]; |
| | | } |
| | | |
| | | // 判断是否还有更多数据 |
| | | hasMore.value = data.length >= pageSize.value; |
| | | total.value = res.total || 0; |
| | | |
| | | // 如果有数据加载成功,增加页码 |
| | | if (data.length > 0) { |
| | | pageNum.value++; |
| | | } |
| | | } else { |
| | | cases.value = [...cases.value, ...mappedData]; |
| | | throw new Error(res.msg || "加载失败"); |
| | | } |
| | | |
| | | hasMore.value = pageNum.value * pageSize.value < res.total; |
| | | total.value = res.total; |
| | | } catch (error) { |
| | | console.error("获取案例列表失败:", error); |
| | | uni.showToast({ |
| | | title: "网络请求失败", |
| | | icon: "none", |
| | | }); |
| | | } finally { |
| | | loading.value = false; |
| | | loadingMore.value = false; |
| | | } |
| | | }; |
| | | |
| | |
| | | const donateTotal = async () => { |
| | | try { |
| | | const res = await uni.$uapi.post("/project/donatebaseinforeport/getTotal"); |
| | | stats.value = { |
| | | totalCases: res["all"] || 0, |
| | | readCases: res["2"] || 0, |
| | | agreedCases: res["3"] || 0, |
| | | }; |
| | | if (res.code === 200) { |
| | | stats.value = { |
| | | totalCases: res.data["all"] || 0, |
| | | readCases: res.data["2"] || 0, |
| | | agreedCases: res.data["3"] || 0, |
| | | }; |
| | | } else { |
| | | throw new Error(res.msg || "获取统计失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取统计数据失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 计算属性:筛选记录 |
| | | // 计算属性:筛选记录 - 现在只做前端状态筛选 |
| | | const filteredCases = computed(() => { |
| | | let result = cases.value; |
| | | |
| | | if (currentType.value !== "all") { |
| | | result = result.filter( |
| | | (caseItem) => caseItem.reportStatus == currentType.value, |
| | | ); |
| | | } |
| | | |
| | | if (startDate.value && endDate.value) { |
| | | result = result.filter((caseItem) => { |
| | | const caseDate = caseItem.reportTime.split(" ")[0]; |
| | | return caseDate >= startDate.value && caseDate <= endDate.value; |
| | | }); |
| | | } |
| | | |
| | | |
| | | // 只做前端状态筛选,时间筛选已经通过接口实现 |
| | | // if (currentType.value !== "all") { |
| | | // result = result.filter( |
| | | // (caseItem) => caseItem.reportStatus == currentType.value, |
| | | // ); |
| | | // } |
| | | |
| | | return result; |
| | | }); |
| | | |
| | | // 事件处理函数 |
| | | const selectType = (type) => { |
| | | currentType.value = type; |
| | | // 重置分页,重新加载数据 |
| | | pageNum.value = 1; |
| | | donatebaseinforeportList(true); |
| | | }; |
| | | |
| | | const onStartDateChange = (e) => { |
| | | startDate.value = e.detail.value; |
| | | // 如果结束日期已选择,立即触发筛选 |
| | | if (endDate.value) { |
| | | pageNum.value = 1; |
| | | donatebaseinforeportList(true); |
| | | } |
| | | }; |
| | | |
| | | const onEndDateChange = (e) => { |
| | | endDate.value = e.detail.value; |
| | | // 如果开始日期已选择,立即触发筛选 |
| | | if (startDate.value) { |
| | | pageNum.value = 1; |
| | | donatebaseinforeportList(true); |
| | | } |
| | | }; |
| | | |
| | | // 刷新和加载更多 |
| | | const onRefresh = async () => { |
| | | refreshing.value = true; |
| | | pageNum.value = 1; |
| | | await donatebaseinforeportList(); |
| | | await donatebaseinforeportList(true); |
| | | await donateTotal(); |
| | | setTimeout(() => { |
| | | refreshing.value = false; |
| | | }, 1000); |
| | | }; |
| | | |
| | | const onLoadMore = async () => { |
| | | if (!hasMore.value || loading.value) return; |
| | | pageNum.value += 1; |
| | | if (!hasMore.value || loading.value || loadingMore.value) return; |
| | | await donatebaseinforeportList(); |
| | | }; |
| | | |
| | |
| | | showWithdrawModal.value = true; |
| | | }; |
| | | |
| | | const confirmWithdraw = () => { |
| | | const index = cases.value.findIndex( |
| | | (item) => item.id == currentCase.value.id, |
| | | ); |
| | | if (index !== -1) { |
| | | cases.value.splice(index, 1); |
| | | stats.value.totalCases -= 1; |
| | | const confirmWithdraw = async () => { |
| | | try { |
| | | const res = await uni.$uapi.delete(`/project/donatebaseinforeport/${currentCase.value.id}`); |
| | | |
| | | if (res.code === 200) { |
| | | // 从列表中移除 |
| | | const index = cases.value.findIndex( |
| | | (item) => item.id == currentCase.value.id |
| | | ); |
| | | if (index !== -1) { |
| | | cases.value.splice(index, 1); |
| | | stats.value.totalCases -= 1; |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: "撤回成功", |
| | | icon: "success", |
| | | }); |
| | | } else { |
| | | throw new Error(res.msg || "撤回失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("撤回失败:", error); |
| | | uni.showToast({ |
| | | title: "撤回成功", |
| | | icon: "success", |
| | | title: "撤回失败,请重试", |
| | | icon: "none", |
| | | }); |
| | | } finally { |
| | | showWithdrawModal.value = false; |
| | | } |
| | | showWithdrawModal.value = false; |
| | | }; |
| | | |
| | | const cancelWithdraw = () => { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .load-more, .no-more { |
| | | text-align: center; |
| | | padding: 40rpx 0; |
| | | color: $text-secondary; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | padding: 120rpx 0; |
| | | text-align: center; |
| | |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | /* 浮动按钮样式 */ |
| | | .float-button { |
| | | position: fixed; |
| | | bottom: 120rpx; |
| | | right: 40rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background: $primary-color; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 8rpx 20rpx rgba($primary-color, 0.3); |
| | | z-index: 100; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | transform: scale(0.95); |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <text |
| | | v-for="(type, index) in transportTypes" |
| | | :key="index" |
| | | :class="{ active: currentType === type.value }" |
| | | :class="{ active: currentType == type.value }" |
| | | @tap="selectType(type.value)" |
| | | >{{ type.label }}</text |
| | | > |
| | |
| | | </button> |
| | | <button |
| | | class="action-btn secondary" |
| | | v-if="item.transitStatus === 1 || item.transitStatus === 5" |
| | | v-if="item.transitStatus != 3" |
| | | @tap.stop="editTransport(item)" |
| | | > |
| | | 编辑 |
| | |
| | | </view> |
| | | |
| | | <!-- 加载更多 --> |
| | | <view class="load-more" v-if="loading"> |
| | | <view class="load-more" v-if="loadingMore"> |
| | | <u-loading-icon text="加载中..."></u-loading-icon> |
| | | </view> |
| | | |
| | | <!-- 无更多数据 --> |
| | | <view class="no-more" v-if="!hasMore && filteredTransports.length > 0"> |
| | | <text>已加载全部数据</text> |
| | | </view> |
| | | |
| | | <!-- 空状态 --> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 加载状态 --> |
| | | <u-loading-page v-if="initLoading" :loading="true" text="数据加载中..." /> |
| | | |
| | | <!-- 浮动按钮 --> |
| | | <!-- <view class="float-button" @tap="createTransport"> |
| | | <u-icon name="plus" color="#fff" size="24"></u-icon> |
| | | </view> --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onLoad, onShow } from "@dcloudio/uni-app"; |
| | | import { onLoad, onShow, onReachBottom } from "@dcloudio/uni-app"; |
| | | import { useDict } from "@/utils/dict"; |
| | | |
| | | // 字典数据 |
| | |
| | | // 转运单数据 |
| | | const transports = ref([]); |
| | | const loading = ref(false); |
| | | const initLoading = ref(false); |
| | | const loadingMore = ref(false); |
| | | const refreshing = ref(false); |
| | | const hasMore = ref(true); |
| | | const pageNum = ref(1); |
| | |
| | | // 获取字典数据 |
| | | dict.value = await useDict("sys_user_sex"); |
| | | // 初始化数据 |
| | | loadTransports(); |
| | | // 加载统计数据 |
| | | loadStats(); |
| | | await loadInitialData(); |
| | | }); |
| | | |
| | | onReachBottom(() => { |
| | | if (hasMore.value && !loading.value && !loadingMore.value) { |
| | | onLoadMore(); |
| | | } |
| | | }); |
| | | |
| | | onShow(() => { |
| | |
| | | }; |
| | | }; |
| | | |
| | | // 筛选记录 |
| | | // 计算属性:筛选记录 |
| | | const filteredTransports = computed(() => { |
| | | let result = transports.value; |
| | | |
| | | // 状态筛选 |
| | | |
| | | // 前端状态筛选 |
| | | if (currentType.value !== "") { |
| | | result = result.filter((item) => item.transitStatus === currentType.value); |
| | | } |
| | | |
| | | // 日期筛选 |
| | | if (startDate.value && endDate.value) { |
| | | result = result.filter((item) => { |
| | | const transportDate = item.createTime |
| | | ? item.createTime.split(" ")[0] |
| | | : ""; |
| | | return transportDate >= startDate.value && transportDate <= endDate.value; |
| | | }); |
| | | } |
| | | |
| | | |
| | | return result; |
| | | }); |
| | | |
| | |
| | | // 选择类型 |
| | | const selectType = (type) => { |
| | | currentType.value = type; |
| | | // 重置分页,重新加载数据 |
| | | pageNum.value = 1; |
| | | loadTransports(true); |
| | | }; |
| | | |
| | | // 日期选择 |
| | | const onStartDateChange = (e) => { |
| | | startDate.value = e.detail.value; |
| | | // 如果结束日期已选择,立即触发筛选 |
| | | if (endDate.value) { |
| | | pageNum.value = 1; |
| | | loadTransports(true); |
| | | } |
| | | }; |
| | | |
| | | const onEndDateChange = (e) => { |
| | | endDate.value = e.detail.value; |
| | | // 如果开始日期已选择,立即触发筛选 |
| | | if (startDate.value) { |
| | | pageNum.value = 1; |
| | | loadTransports(true); |
| | | } |
| | | }; |
| | | |
| | | // 查看详情 |
| | |
| | | // 查看详情 |
| | | const viewDetails = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/transport/detail?id=${item.id}`, |
| | | url: `/pages/case/transferinfo?id=${item.id}`, |
| | | }); |
| | | }; |
| | | |
| | | // 编辑转运单 |
| | | const editTransport = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/transport/detail?id=${item.id}&edit=true`, |
| | | url: `/pages/case/transferinfo?id=${item.id}&edit=true`, |
| | | }); |
| | | }; |
| | | |
| | | // 创建转运单 |
| | | const createTransport = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/transport/create' |
| | | }); |
| | | }; |
| | | |
| | |
| | | showActionModal.value = false; |
| | | }; |
| | | |
| | | // 数据加载函数 |
| | | const loadInitialData = async () => { |
| | | initLoading.value = true; |
| | | try { |
| | | await Promise.all([loadTransports(true), loadStats()]); |
| | | } catch (error) { |
| | | console.error("初始化数据失败:", error); |
| | | } finally { |
| | | initLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 下拉刷新 |
| | | const onRefresh = () => { |
| | | const onRefresh = async () => { |
| | | refreshing.value = true; |
| | | pageNum.value = 1; |
| | | loadTransports(); |
| | | loadStats(); |
| | | await loadTransports(true); |
| | | await loadStats(); |
| | | setTimeout(() => { |
| | | refreshing.value = false; |
| | | }, 1000); |
| | | }; |
| | | |
| | | // 加载更多 |
| | | const onLoadMore = () => { |
| | | if (!hasMore.value || loading.value) return; |
| | | pageNum.value++; |
| | | loadTransports(); |
| | | const onLoadMore = async () => { |
| | | if (!hasMore.value || loading.value || loadingMore.value) return; |
| | | await loadTransports(); |
| | | }; |
| | | |
| | | // 加载记录 |
| | | const loadTransports = async () => { |
| | | if (loading.value) return; |
| | | |
| | | loading.value = true; |
| | | // 加载转运单列表 |
| | | const loadTransports = async (reset = false) => { |
| | | if (reset) { |
| | | pageNum.value = 1; |
| | | hasMore.value = true; |
| | | } |
| | | |
| | | if (loading.value || loadingMore.value || !hasMore.value) return; |
| | | |
| | | if (reset) { |
| | | loading.value = true; |
| | | } else { |
| | | loadingMore.value = true; |
| | | } |
| | | |
| | | try { |
| | | const params = { |
| | | pageNum: pageNum.value, |
| | | pageSize: pageSize.value, |
| | | }; |
| | | |
| | | if (currentType.value) { |
| | | |
| | | // 筛选条件 |
| | | if (currentType.value !== "") { |
| | | params.transitStatus = currentType.value; |
| | | } |
| | | |
| | | |
| | | // 时间筛选条件 |
| | | if (startDate.value && endDate.value) { |
| | | params.startDate = startDate.value; |
| | | params.endDate = endDate.value; |
| | | } |
| | | |
| | | |
| | | console.log("请求参数:", params); |
| | | |
| | | const res = await uni.$uapi.post("/project/transport/list", params); |
| | | |
| | | if (res) { |
| | | const data = res || []; |
| | | |
| | | console.log("接口返回数据:", res); |
| | | |
| | | if (res && res.code === 200) { |
| | | // 处理不同的数据结构 |
| | | let data = []; |
| | | |
| | | if (res.rows && Array.isArray(res.rows)) { |
| | | data = res.rows; |
| | | } else if (res.data && Array.isArray(res.data)) { |
| | | data = res.data; |
| | | } else if (Array.isArray(res)) { |
| | | data = res; |
| | | } else { |
| | | console.error("接口返回格式不正确:", res); |
| | | throw new Error("接口返回格式不正确"); |
| | | } |
| | | |
| | | const mappedData = data.map((item) => mapApiDataToTransportItem(item)); |
| | | |
| | | if (pageNum.value === 1) { |
| | | |
| | | if (reset || pageNum.value === 1) { |
| | | transports.value = mappedData; |
| | | } else { |
| | | transports.value = [...transports.value, ...mappedData]; |
| | | } |
| | | |
| | | hasMore.value = (res.rows || []).length >= pageSize.value; |
| | | |
| | | // 判断是否还有更多数据 |
| | | hasMore.value = data.length >= pageSize.value; |
| | | |
| | | // 如果有数据加载成功,增加页码 |
| | | if (data.length > 0) { |
| | | pageNum.value++; |
| | | } |
| | | } else { |
| | | throw new Error(res.msg || "加载失败"); |
| | | throw new Error(res?.msg || "加载失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("加载转运单列表失败:", error); |
| | |
| | | }); |
| | | } finally { |
| | | loading.value = false; |
| | | loadingMore.value = false; |
| | | } |
| | | }; |
| | | |
| | |
| | | const loadStats = async () => { |
| | | try { |
| | | const res = await uni.$uapi.post("/api/totalServiceTransportState"); |
| | | if (res) { |
| | | if (res.data) { |
| | | stats.value = { |
| | | totalTransports: res.reduce((sum, item) => sum + item.count, 0) || 0, |
| | | pendingTransports: res[0].count || 0, |
| | | completedTransports: res[3].count || 0, |
| | | totalTransports: |
| | | res.data.reduce((sum, item) => sum + item.count, 0) || 0, |
| | | pendingTransports: res.data[0].count || 0, |
| | | completedTransports: res.data[3].count || 0, |
| | | }; |
| | | } |
| | | } catch (error) { |
| | |
| | | id: currentTransport.value.id, |
| | | transitStatus: newStatus, |
| | | }; |
| | | |
| | | |
| | | const res = await uni.$uapi.post("/project/transport/edit", updateData); |
| | | |
| | | if (res.code === 200) { |
| | | |
| | | if (res && res.code === 200) { |
| | | // 更新本地数据 |
| | | const index = transports.value.findIndex( |
| | | (item) => item.id === currentTransport.value.id, |
| | |
| | | if (index !== -1) { |
| | | transports.value[index].transitStatus = newStatus; |
| | | } |
| | | |
| | | |
| | | // 更新统计数据 |
| | | await loadStats(); |
| | | |
| | | |
| | | // 存储状态更新 |
| | | uni.setStorageSync("transportStatusUpdate", { |
| | | orderId: currentTransport.value.id, |
| | | status: newStatus, |
| | | }); |
| | | |
| | | |
| | | return true; |
| | | } else { |
| | | throw new Error(res.msg || `${actionName}失败`); |
| | | throw new Error(res?.msg || `${actionName}失败`); |
| | | } |
| | | } catch (error) { |
| | | console.error(`${actionName}失败:`, error); |
| | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | /* 保持原有样式完全不变 */ |
| | | .transport-record { |
| | | min-height: 100vh; |
| | | background: linear-gradient(135deg, #fafdff 0%, #e3f0ff 100%); |
| | | padding-bottom: 100rpx; |
| | | |
| | | .stats-card { |
| | | margin: 20rpx; |
| | |
| | | } |
| | | } |
| | | |
| | | .load-more, .no-more { |
| | | text-align: center; |
| | | padding: 40rpx 0; |
| | | color: #86868b; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | padding: 120rpx 0; |
| | | text-align: center; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 浮动按钮 */ |
| | | .float-button { |
| | | position: fixed; |
| | | bottom: 100rpx; |
| | | right: 40rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background: #0071e3; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 8rpx 20rpx rgba(0, 113, 227, 0.3); |
| | | z-index: 100; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | transform: scale(0.95); |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <scroll-view scroll-y class="form-scroll" :show-scrollbar="false"> |
| | | <view class="form-content"> |
| | | <!-- 案例信息卡片 --> |
| | | <view class="form-section" v-if="transportData.caseNo"> |
| | | <view class="form-section" v-if="transportData.reportId"> |
| | | <view class="section-header"> |
| | | <view class="section-icon">📋</view> |
| | | <text class="section-title">关联案例信息</text> |
| | |
| | | </view> |
| | | |
| | | <view class="form-grid"> |
| | | <view class="form-item"> |
| | | <!-- <view class="form-item"> |
| | | <text class="item-label">转运单号</text> |
| | | <u-input |
| | | v-model="transportData.reportId" |
| | |
| | | :disabledColor="disabledColor" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | </view> --> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label required">出发地点</text> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件信息 --> |
| | | <view |
| | | class="form-section" |
| | | v-if=" |
| | | transportData.annexfilesList && |
| | | transportData.annexfilesList.length > 0 |
| | | " |
| | | > |
| | | <view class="section-header"> |
| | | <view class="section-icon">📎</view> |
| | | <text class="section-title" |
| | | >附件信息 ({{ transportData.annexfilesList.length }})</text |
| | | > |
| | | </view> |
| | | |
| | | <view class="attachment-list"> |
| | | <view |
| | | v-for="(file, index) in transportData.annexfilesList" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewFile(file)" |
| | | > |
| | | <view class="file-info"> |
| | | <u-icon |
| | | name="file-text" |
| | | color="#409EFF" |
| | | size="20" |
| | | class="file-icon" |
| | | ></u-icon> |
| | | <view class="file-details"> |
| | | <text class="file-name">{{ file.fileName }}</text> |
| | | <text class="file-time">{{ |
| | | formatTime(file.createTime) |
| | | }}</text> |
| | | </view> |
| | | </view> |
| | | <u-icon name="arrow-right" color="#999" size="16"></u-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 备注信息 --> |
| | | <view class="form-section"> |
| | | <view class="section-header"> |
| | |
| | | > |
| | | 完成转运 |
| | | </u-button> |
| | | <u-button v-if="canDelete" class="btn error" @click="handleDelete"> |
| | | <!-- <u-button v-if="canDelete" class="btn error" @click="handleDelete"> |
| | | 删除 |
| | | </u-button> |
| | | </u-button> --> |
| | | </template> |
| | | |
| | | <!-- 编辑模式 --> |
| | |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | |
| | | <attachment-upload |
| | | ref="attachment" |
| | | :files="attachments" |
| | | :readonly="isReadonly" |
| | | :maxCount="5" |
| | | @update:files="handleFilesUpdate" |
| | | @upload-base="handleBaseUpload" |
| | | @preview="handlePreview" |
| | | /> |
| | | <!-- 时间选择器 --> |
| | | <u-datetime-picker |
| | | :show="showTimePicker" |
| | |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import attachmentUpload from "@/components/attachment"; |
| | | import { useUserStore } from "@/stores/user"; |
| | | |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | |
| | | // 字典数据 |
| | | const dict = ref({}); |
| | | |
| | | // 数据 |
| | | const transportData = ref({}); |
| | | const transportData = ref({ |
| | | annexfilesList: [], // 附件文件地址集合 |
| | | }); |
| | | const loading = ref(false); |
| | | const saving = ref(false); |
| | | const isEdit = ref(false); |
| | |
| | | const showTimePicker = ref(false); |
| | | const departureTimeValue = ref(0); |
| | | const statusSelectVisible = ref(false); |
| | | const isReadonly = ref(false); |
| | | const attachments = ref([]); |
| | | |
| | | |
| | | // 字典选项 |
| | | const statusOptions = ref([ |
| | |
| | | const loadTransportData = async (id) => { |
| | | loading.value = true; |
| | | try { |
| | | const res = await uni.$uapi.post(`/project/transport/list`, { id: id }); |
| | | const res = await uni.$uapi.get(`/project/transport/getInfo/${id}`); |
| | | |
| | | if (res) { |
| | | transportData.value = res[0]; |
| | | if (res.data) { |
| | | transportData.value = res.data; |
| | | |
| | | if (res.data.annexfilesList) { |
| | | attachments.value = res.data.annexfilesList; |
| | | attachments.value.forEach(item=>{ |
| | | item.url=item.opath, |
| | | item.name=item.fileName |
| | | }) |
| | | } |
| | | } else { |
| | | throw new Error(res.msg || "数据加载失败"); |
| | | } |
| | |
| | | try { |
| | | // 如果有案例ID,加载案例信息 |
| | | if (caseId) { |
| | | const res = await uni.$uapi.post( |
| | | `/project/transport/list`, |
| | | { id: caseId }, |
| | | const res = await uni.$uapi.get( |
| | | `/project/donatebaseinforeport/getInfo/${caseId}`, |
| | | ); |
| | | if (res) { |
| | | const caseData = res[0]; |
| | | if (res.data) { |
| | | const caseData = res.data; |
| | | console.log(caseData); |
| | | |
| | | transportData.value = { |
| | | ...transportData.value, |
| | | caseNo: caseNo || caseData.caseNo, |
| | | patName: caseData.patName, |
| | | sex: caseData.sex, |
| | | age: caseData.age, |
| | | diagnosisname: caseData.diagnosisname, |
| | | treatmentHospitalName: caseData.treatmenthospitalname, |
| | | transitStatus: 1, // 待转运 |
| | | createTime: new Date() |
| | | .toISOString() |
| | | .replace("T", " ") |
| | | .substring(0, 19), |
| | | caseNo: caseData.caseNo || "", |
| | | patName: caseData.name || "", |
| | | sex: caseData.sex || "", |
| | | age: caseData.age || "", |
| | | reportId: caseData.id || null, |
| | | diagnosisname: caseData.diagnosisname || "", |
| | | treatmentHospitalName: caseData.treatmenthospitalname || "", |
| | | treatmentDeptName: caseData.treatmentdeptname || "", |
| | | // 可以设置一些默认值 |
| | | transportStartPlace: caseData.treatmenthospitalname || "", |
| | | contactPerson: caseData.coordinatorName || "", |
| | | }; |
| | | console.log(transportData.value, "transportData.value"); |
| | | } |
| | | } else { |
| | | transportData.value = { |
| | |
| | | // 时间确认 |
| | | const onTimeConfirm = (e) => { |
| | | const date = new Date(e.value); |
| | | // 添加秒的部分 |
| | | transportData.value.transportStartTime = `${date.getFullYear()}-${( |
| | | date.getMonth() + 1 |
| | | ) |
| | |
| | | .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")} ${date |
| | | .getHours() |
| | | .toString() |
| | | .padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`; |
| | | .padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date |
| | | .getSeconds() |
| | | .toString() |
| | | .padStart(2, "0")}`; // 添加秒 |
| | | |
| | | showTimePicker.value = false; |
| | | }; |
| | | |
| | |
| | | saving.value = true; |
| | | |
| | | try { |
| | | // const submitData = { |
| | | // ...transportData.value, |
| | | |
| | | // }; |
| | | const submitData = { |
| | | ...transportData.value, |
| | | annexfilesList: attachments.value.map((file) => ({ |
| | | path: file.url, |
| | | fileName: file.name, |
| | | type: file.type, |
| | | })), |
| | | }; |
| | | |
| | | let res; |
| | | if (transportId.value) { |
| | | // 修改 |
| | |
| | | const updateData = { |
| | | id: transportId.value, |
| | | transitStatus: newStatus, |
| | | ...transportData.value, |
| | | annexfilesList: attachments.value.map((file) => ({ |
| | | path: file.url, |
| | | fileName: file.name, |
| | | type: file.type, |
| | | })), |
| | | }; |
| | | |
| | | const res = await uni.$uapi.post("/system/transport/edit", updateData); |
| | | const res = await uni.$uapi.post("/project/transport/edit", updateData); |
| | | |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: `${actionName}成功`, icon: "success" }); |
| | |
| | | } |
| | | }; |
| | | |
| | | // 删除转运单 |
| | | const handleDelete = () => { |
| | | uni.showModal({ |
| | | title: "确认删除", |
| | | content: `确定要删除转运单 ${transportData.value.reportId} 吗?`, |
| | | success: async (res) => { |
| | | if (res.confirm) { |
| | | try { |
| | | const result = await uni.$uapi.delete( |
| | | `/system/transport/${transportId.value}`, |
| | | ); |
| | | if (result.code === 200) { |
| | | uni.showToast({ title: "删除成功", icon: "success" }); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | uni.showToast({ title: result.msg || "删除失败", icon: "none" }); |
| | | } |
| | | } catch (error) { |
| | | console.error("删除失败:", error); |
| | | uni.showToast({ title: "删除失败", icon: "none" }); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | // 处理基础附件上传 |
| | | const handleBaseUpload = (file) => { |
| | | console.log("基础附件上传成功:", file); |
| | | attachments.value.push(file); |
| | | |
| | | }; |
| | | |
| | | // 处理其他附件上传 |
| | | const handleFilesUpdate = (files) => { |
| | | console.log(files,'files'); |
| | | |
| | | attachments.value = files.map((file) => ({ |
| | | ...file, |
| | | // 确保只存储半路径 |
| | | // url: file.url.startsWith("http") |
| | | // ? file.url.replace(baseUrlHt, "") |
| | | // : file.url, |
| | | })); |
| | | console.log(attachments.value); |
| | | |
| | | }; |
| | | |
| | | // 预览文件 - 修改为使用完整URL |
| | | const handlePreview = (file) => { |
| | | const fullUrl = file.url.startsWith("http") |
| | | ? file.url |
| | | : baseUrlHt + (file.url.startsWith("/") ? "" : "/") + file.url; |
| | | |
| | | if (file.type.includes("image")) { |
| | | uni.previewImage({ |
| | | urls: attachments.value |
| | | .filter((f) => f.type.includes("image")) |
| | | .map((f) => |
| | | f.url.startsWith("http") |
| | | ? f.url |
| | | : baseUrlHt + (f.url.startsWith("/") ? "" : "/") + f.url, |
| | | ), |
| | | current: fullUrl, |
| | | }); |
| | | } else if (file.type.includes("pdf")) { |
| | | uni.downloadFile({ |
| | | url: fullUrl, |
| | | success: (res) => { |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | fileType: "pdf", |
| | | showMenu: true, |
| | | }); |
| | | }, |
| | | fail: (err) => { |
| | | console.error("打开文档失败:", err); |
| | | uni.showToast({ title: "打开文件失败", icon: "none" }); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.showToast({ title: "暂不支持此文件类型预览", icon: "none" }); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | |
| | | padding: 20rpx 30rpx; |
| | | padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); |
| | | box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08); |
| | | z-index: 100; |
| | | z-index: 9; |
| | | } |
| | | |
| | | .btn { |
| | |
| | | } else { |
| | | try { |
| | | const resp = await uni.$uapi.get("/system/dict/data/type/" + dictType); |
| | | res.value[dictType] = resp.map(p => ({ |
| | | res.value[dictType] = resp.data.map(p => ({ |
| | | label: p.dictLabel, |
| | | value: p.dictValue, |
| | | elTagType: p.listClass, |
| | |
| | | */ |
| | | const showToast = (message) => { |
| | | if (uni.$u?.toast) { |
| | | uni.$u.toast(message) |
| | | uni.$u.toast(message); |
| | | } else { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | icon: "none", |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 基础配置 - 从环境变量中获取baseURL |
| | | const config = { |
| | | baseURL: '/api', // 使用环境变量中的配置 |
| | | baseURL: "/api", // 使用环境变量中的配置 |
| | | timeout: 60000, |
| | | header: { |
| | | 'Content-Type': 'application/json', |
| | | 'X-Business-System': 'medical-system', |
| | | |
| | | "Content-Type": "application/json", |
| | | "X-Business-System": "medical-system", |
| | | }, |
| | | // 新增配置项 |
| | | loginPage: '/pages/login/Login', // 登录页路径 |
| | | loginPage: "/pages/login/Login", // 登录页路径 |
| | | tokenExpiredCode: 401, // 后端返回的Token过期状态码 |
| | | noPermissionCode: 403, // 无权限状态码 |
| | | // 添加白名单配置(不需要校验token的接口) |
| | | whiteList: [ |
| | | '/login', // 登录接口 |
| | | "/login", // 登录接口 |
| | | // '/dingtalk/auth/login', // 钉钉授权登录接口 |
| | | // '/dingtalk/auth/bind' // 钉钉授权登录绑定接口 |
| | | ] |
| | | } |
| | | ], |
| | | }; |
| | | |
| | | /** |
| | | * 检查请求是否在白名单中 |
| | |
| | | */ |
| | | const isInWhiteList = (url) => { |
| | | // 提取相对路径(移除baseURL)并忽略查询参数 |
| | | const relativeUrl = url.replace(config.baseURL, '').split('?')[0]; |
| | | return config.whiteList.some(path => { |
| | | const relativeUrl = url.replace(config.baseURL, "").split("?")[0]; |
| | | return config.whiteList.some((path) => { |
| | | // 处理通配符情况 |
| | | if (path.endsWith('/')) { |
| | | return relativeUrl.startsWith(path) |
| | | if (path.endsWith("/")) { |
| | | return relativeUrl.startsWith(path); |
| | | } |
| | | // 精确匹配路径 |
| | | return relativeUrl === path |
| | | }) |
| | | } |
| | | return relativeUrl === path; |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 跳转到登录页 |
| | | */ |
| | | const navigateToLogin = () => { |
| | | uni.redirectTo({ |
| | | url: config.loginPage + '?redirect=' + encodeURIComponent(getCurrentPagePath()) |
| | | }) |
| | | } |
| | | url: |
| | | config.loginPage + |
| | | "?redirect=" + |
| | | encodeURIComponent(getCurrentPagePath()), |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 获取当前页面路径(用于登录后跳回) |
| | | */ |
| | | const getCurrentPagePath = () => { |
| | | const pages = getCurrentPages() |
| | | return pages[pages.length - 1]?.route || '' |
| | | } |
| | | const pages = getCurrentPages(); |
| | | return pages[pages.length - 1]?.route || ""; |
| | | }; |
| | | |
| | | /** |
| | | * 检查Token是否过期 |
| | |
| | | */ |
| | | const checkTokenExpired = (statusCode) => { |
| | | if (statusCode === config.tokenExpiredCode) { |
| | | showToast('登录已过期,请重新登录') |
| | | navigateToLogin() |
| | | return true |
| | | showToast("登录已过期,请重新登录"); |
| | | navigateToLogin(); |
| | | return true; |
| | | } |
| | | return false |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | /** |
| | | * 请求拦截器 |
| | | */ |
| | | const requestInterceptor = (options) => { |
| | | const token = uni.getStorageSync('token') |
| | | |
| | | const token = uni.getStorageSync("token"); |
| | | |
| | | // 如果请求在白名单中,直接放行 |
| | | if (isInWhiteList(options.url)) { |
| | | return options |
| | | return options; |
| | | } |
| | | |
| | | // 如果未登录且不是白名单接口,跳转登录 |
| | | // console.log(token,'token'); |
| | | |
| | | |
| | | if (!token) { |
| | | navigateToLogin() |
| | | throw new Error('未登录') |
| | | navigateToLogin(); |
| | | throw new Error("未登录"); |
| | | } |
| | | |
| | | // 添加Token到Header |
| | | options.header = { |
| | | ...options.header, |
| | | 'Authorization': `Bearer ${token}` |
| | | } |
| | | |
| | | return options |
| | | } |
| | | Authorization: `Bearer ${token}`, |
| | | }; |
| | | |
| | | return options; |
| | | }; |
| | | |
| | | /** |
| | | * 响应拦截器 |
| | | */ |
| | | const responseInterceptor = (response) => { |
| | | const { statusCode, data } = response |
| | | |
| | | const { statusCode, data } = response; |
| | | |
| | | // Token过期处理 |
| | | if (checkTokenExpired(statusCode)) { |
| | | return Promise.reject(data) |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 无权限处理 |
| | | if (statusCode === config.noPermissionCode) { |
| | | showToast('无权限访问') |
| | | return Promise.reject(data) |
| | | showToast("无权限访问"); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 其他错误状态码 |
| | | if (statusCode !== 200) { |
| | | showToast(`请求失败: ${statusCode}`) |
| | | return Promise.reject(data) |
| | | showToast(`请求失败: ${statusCode}`); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 接口自定义错误码处理(假设data.code为0表示成功) |
| | | if (data?.code !== 200) { |
| | | showToast(data?.msg || '操作失败') |
| | | return Promise.reject(data) |
| | | showToast(data?.msg || "操作失败"); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | return data.data || data // 返回实际业务数据 |
| | | } |
| | | return data; // 返回实际业务数据 |
| | | }; |
| | | |
| | | /** |
| | | * 核心请求方法 |
| | |
| | | ...config, |
| | | ...options, |
| | | url: config.baseURL + options.url, |
| | | header: { ...config.header, ...options.header } |
| | | } |
| | | header: { ...config.header, ...options.header }, |
| | | }; |
| | | |
| | | // 请求拦截 |
| | | options = await requestInterceptor(options) |
| | | |
| | | options = await requestInterceptor(options); |
| | | |
| | | // 发起请求 |
| | | return new Promise((resolve, reject) => { |
| | | uni.request({ |
| | | ...options, |
| | | success: (res) => { |
| | | resolve(responseInterceptor(res)) |
| | | resolve(responseInterceptor(res)); |
| | | }, |
| | | fail: (err) => { |
| | | showToast('网络错误,请重试') |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | showToast("网络错误,请重试"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | }); |
| | | } catch (err) { |
| | | return Promise.reject(err) |
| | | return Promise.reject(err); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 快捷方法封装 |
| | | const http = { |
| | | get(url, data = {}, options = {}) { |
| | | return request({ url, data, method: 'GET', ...options }) |
| | | return request({ url, data, method: "GET", ...options }); |
| | | }, |
| | | post(url, data = {}, options = {}) { |
| | | return request({ url, data, method: 'POST', ...options }) |
| | | return request({ url, data, method: "POST", ...options }); |
| | | }, |
| | | put(url, data = {}, options = {}) { |
| | | return request({ url, data, method: 'PUT', ...options }) |
| | | return request({ url, data, method: "PUT", ...options }); |
| | | }, |
| | | delete(url, data = {}, options = {}) { |
| | | return request({ url, data, method: 'DELETE', ...options }) |
| | | return request({ url, data, method: "DELETE", ...options }); |
| | | }, |
| | | // 新增:上传文件方法 |
| | | upload(url, filePath, name = 'file', formData = {}) { |
| | | upload(url, filePath, name = "file", formData = {}) { |
| | | return request({ |
| | | url, |
| | | method: 'POST', |
| | | method: "POST", |
| | | filePath, |
| | | name, |
| | | formData, |
| | | header: { 'Content-Type': 'multipart/form-data' } |
| | | }) |
| | | } |
| | | } |
| | | header: { "Content-Type": "multipart/form-data" }, |
| | | }); |
| | | }, |
| | | }; |
| | | |
| | | // 挂载到全局 |
| | | uni.$uapi = http |
| | | uni.$uapi = http; |
| | | |
| | | export default http |
| | | export default http; |