feat: 个人信息认证

This commit is contained in:
wangdongbo 2026-01-21 15:27:18 +08:00
parent f6b504a2bd
commit 9b545b442d
21 changed files with 81 additions and 2731 deletions

View File

@ -1,98 +0,0 @@
// 患者管理模块 API 封装
import api from '../utils/http.js'
// API 基础路径
const BASE_PATH = '/order'
/**
* 初始化咨询订单
*/
export function initConsultOrder({ doctorCode, accountId, openId, memberId }) {
return api.post(`${BASE_PATH}/consult-orders/init`, { doctorCode, accountId, openId, memberId })
}
/**
* 绑定订单的患者信息
*/
export function bindOrderPatient({ accountId, memberId, orderId }) {
return api.post(`${BASE_PATH}/consult-orders/bind-member`, { accountId, memberId, orderId })
}
/**
* 补充病情描述
*/
export function submitOrderDescription({ accountId, orderId, description, diseases, images, hasVisitedHospital }) {
return api.post(`${BASE_PATH}/consult-orders/supplement-description`, { accountId, orderId, description, diseases, images, hasVisitedHospital })
}
/**
* 获取订单信息
*/
export function getOrderInfo({ orderId }) {
return api.post(`${BASE_PATH}/consult-orders/get`, { orderId })
}
/**
* 获取订单列表
*/
export function getOrderList({ accountId, page, pageSize: limit, orderStatus }) {
return api.post(`${BASE_PATH}/consult-orders/list`, { accountId, page, limit, orderStatus })
}
/**
* 获取最新订单
*/
export function getLatestOrder({ accountId, chatGroupId }) {
return api.post(`${BASE_PATH}/consult-orders/latest`, { accountId, chatGroupId })
}
/**
* 获取订单支付信息
*/
export function getOrderTradeNo({ orderId, accountId }) {
return api.post(`${BASE_PATH}/consult-orders/get-trade-no`, { accountId, orderId })
}
/**
* 取消订单
*/
export function cancelOrder({ orderId, accountId }) {
return api.post(`${BASE_PATH}/consult-orders/cancel`, { orderId, accountId })
}
/**
* 获取账号统计信息
*/
export function getAccountStats(accountId, statusList) {
return api.post(`${BASE_PATH}/consult-orders/account-stats`, { accountId, statusList })
}
export function getConsultInfo({doctorCode, memberId, accountId}) {
return api.post(`${BASE_PATH}/consult-orders/get-consult-info`, { doctorCode, memberId, accountId })
}
/**
* 更新订单最后一条消息ID
*/
export function updateLastMessageId({ orderId, lastMessageId }) {
return api.post(`${BASE_PATH}/consult-orders/update-last-message`, { orderId, lastMessageId })
}
/**
* 发送消息到群组
* @param {Object} params
* @param {string} params.groupId - 群组ID
* @param {string} params.desc - 消息描述/类型'WAIT_DOCTOR_ACCEPT'
* @param {string} params.message - 消息内容
* @param {string} params.ext - 扩展信息JSON字符串
* @param {string} params.fromAccount - 发送者账号可选
*/
export function sendMessageToGroup({ groupId, desc, message, ext, fromAccount }) {
return api.post(`${BASE_PATH}/consult-orders/send-message-to-group`, {
groupId,
desc,
message,
ext,
fromAccount
})
}

View File

@ -1,13 +0,0 @@
import api from "../../utils/http.js";
const BASE_PATH = "/corp";
/**
* 获取科室列表 corpId
* @param {Object} params
* @param {string} params.corpId
*/
export function getDeptList(params = {}) {
return api.post(`${BASE_PATH}`, { type: "getDeptList", ...params });
}

View File

@ -1,48 +0,0 @@
import api from '../../utils/http.js'
// API 基础路径
const BASE_PATH = '/corp'
/**
* 获取用户签名
* @param {string} userId - 用户ID
* @returns {Promise} 返回包含 userSig 信息的 Promise
*/
export const getUserSig = (userId) => {
return api.post(`${BASE_PATH}/tencent-im/user-sig`, { userId })
}
export async function getChatStatus(chatGroupId) {
return api.post(`${BASE_PATH}/tencent-im/get-chat-status`, { chatGroupId, role: 'patient' })
}
export const sendSystemMessage = (groupId, data, Desc = '', Ext = '') => {
return api.post(`${BASE_PATH}/tencent-im/send-group-message`, {
groupId,
msgBody: [
{
MsgType: "TIMCustomElem",
MsgContent: {
Data: data,
Desc,
Ext
}
}
]
})
}
/**
* 获取群组聊天记录POST请求
* @param {string} groupId - 群组ID
* @param {number} limit - 每页数量默认20最大100
* @param {number} skip - 跳过数量默认0
* @returns {Promise} 返回包含聊天记录的 Promise
*/
export const getChatRecordsByGroupId = (groupId, limit = 20, skip = 0) => {
return api.post(`${BASE_PATH}/tencent-im/chat-records`, {
GroupId: groupId,
limit,
skip
})
}

View File

@ -1,15 +0,0 @@
import api from '../../utils/http.js'
// API 基础路径
const BASE_PATH = '/corp/rate-records'
export const getRate = (id) => {
return api.post(`${BASE_PATH}/get-by-id`, { id })
}
export function submitRate({ id, rate, words }) {
return api.post(`${BASE_PATH}/submit`, { id, rate, words })
}
export const getRateList = ({ page, pageSize, doctorId }) => {
return api.post(`${BASE_PATH}/displayable-list`, { page, limit: pageSize, userId: doctorId })
}

View File

@ -1,75 +0,0 @@
import api from '../../utils/http.js'
const BASE_PATH = "/corp/doctors";
/**
* 获取医生
* @param {string} id - 医生ID
* @returns {Promise<Object>} 医生数据
*/
export function getDoctorInfo(id) {
return api.post(`${BASE_PATH}/get-by-id`, { id })
}
/**
* 获取邀请医生
* @param {string} doctorId - 医生ID
* @returns {Promise<Object>} 邀请医生数据
*/
export function getDoctorByDoctorId(doctorId) {
return api.get(`${BASE_PATH}/doctor/${doctorId}`)
}
/**
* 获取医生
* @param {string} accountId - 账户ID
* @returns
*/
export function getDoctorInfoByAccountId(accountId) {
return api.post(`${BASE_PATH}/get-by-account-id`, { accountId })
}
/**
* 获取医生
* @param {string} doctorId - 医生ID
* @returns
*/
export function getDoctorInfoByDoctorId(doctorId) {
return api.post(`${BASE_PATH}/get-by-doctor-id`, { doctorId })
}
/**
* 创建医生
* @param {Object} data - 医生数据
* @param {string} data.accountId - 账户ID
* @param {string} data.avatar - 医生头像
* @param {string} data.name - 医生姓名
* @param {string} data.phone - 医生手机号
* @param {string} data.hospitalId - 医院ID
* @param {string} data.hospitalName - 医院名称
* @param {string} data.departmentId - 科室ID
* @param {string} data.title - 医生职称
* @param {string} data.specialty - 医生擅长
* @param {string} data.intro - 个人简介
* @param {string} data.titleCertificate - 职称证书
* @param {string} data.practiceLicenseCode - 执业证编号
* @param {string} data.practiceLicenseFront - 执业证正面
* @param {string} data.practiceLicenseBack - 执业证背面
* @param {string} data.medicalLicenseFront - 资格证正面
* @param {string} data.medicalLicenseBack - 资格证背面
* @param {string} data.medicalLicenseCode - 资格证编号
* @param {string} data.idCardFront - 身份证正面
* @param {string} data.idCardBack - 身份证背面
* @param {string} data.workCard - 工作证
* @returns {Promise<Object>} 创建结果
*/
export function createDoctorInfo(data) {
return api.post(`${BASE_PATH}/create`, data)
}
/**
* 更新医生
* @param {Object} data - 医生数据
* @returns {Promise<Object>} 更新结果
*/
export function updateDoctorInfo(data) {
return api.post(`${BASE_PATH}/update`, data)
}

View File

@ -1,50 +0,0 @@
// 患者管理模块 API 封装
import api from '../utils/http.js'
// API 基础路径
const BASE_PATH = '/medicine'
export function getDiagnosisList({ type, keyword, page, limit }) {
return api.post(`${BASE_PATH}/diagnoses/list`, { type, keyword, page, limit })
}
export function getAccountPrescriptions({ accountId: patientAccountId, doctorId, patientId, statusList, page, limit }) {
return api.post(`${BASE_PATH}/prescriptions/get-by-patient-account`, { patientAccountId, doctorId, statusList, page, limit, patientId })
}
export function getPrescriptionStats(accountId, statusList) {
return api.post(`${BASE_PATH}/prescriptions/account-stats`, { accountId, statusList })
}
export function getPrescriptionDetail(id) {
return api.post(`${BASE_PATH}/prescriptions/get-by-id`, { id })
}
export function getPrescriptionDetailWithRps(id) {
return api.post(`${BASE_PATH}/prescriptions/get-by-id-with-rps`, { id })
}
export function bindSharePrescription({ id, accountId, openId, patientId }) {
return api.post(`${BASE_PATH}/prescriptions/bind-member`, { id, accountId, openId, patientId })
}
export function getPrescriptionTradeNo({ id, openId, accountId, logistics }) {
return api.post(`${BASE_PATH}/prescriptions/get-trade-no`, { id, openId, accountId, logistics })
}
export function cancelPrescription({ accountId, id }) {
return api.post(`${BASE_PATH}/prescriptions/cancel`, { id, accountId })
}
export function applyMedicines({ id, patientAccountId, openId }) {
return api.post(`${BASE_PATH}/prescriptions/apply-represcription`, { id, patientAccountId, openId })
}
export function getCartMedicines({ accountId, ids }) {
return api.post(`${BASE_PATH}/prescriptions/get-cart-medicines`, { accountId, ids })
}
export function getMergeTradeNo({ ids, openId, accountId, logistics }) {
return api.post(`${BASE_PATH}/prescriptions/get-merge-trade-no`, { ids, openId, accountId, logistics })
}

View File

@ -1,5 +1,5 @@
<template>
<picker mode="selector" :range="range" :disabled="disableChange" @change="change($event)">
<picker mode="selector" :range="displayRange" :disabled="disableChange" @change="change($event)">
<common-cell :name="name" :required="required">
<view class="form-content__wrapper">
<view class="flex-main-content truncate" :class="value ? '' : 'form__placeholder'">
@ -42,12 +42,30 @@ const props = defineProps({
})
const placeholder = computed(() => `请输入${props.name || ''}`)
const value = computed(() => props.form && props.form && props.form[props.title] ? props.form[props.title] : '')
const displayRange = computed(() => {
// rangelabel
if (Array.isArray(props.range) && props.range.length > 0 && typeof props.range[0] === 'object') {
return props.range.map(item => item.label);
}
return props.range;
})
const value = computed(() => {
if (!props.form || !props.form[props.title]) return '';
const currentValue = props.form[props.title];
// rangelabel
if (Array.isArray(props.range) && props.range.length > 0 && typeof props.range[0] === 'object') {
const option = props.range.find(item => item.value === currentValue);
return option ? option.label : currentValue;
}
return currentValue;
})
function change(e) {
const selectedValue = props.range[e.detail.value];
emits('change', {
title: props.title,
value: props.range[e.detail.value]
value: selectedValue
})
}
</script>

View File

@ -1,417 +0,0 @@
<template>
<uni-popup ref="symptomPopup" type="bottom" :mask-click="false">
<view class="symptom-popup">
<view class="popup-header">
<text class="popup-title">请描述您的病情</text>
</view>
<view class="popup-content">
<textarea class="symptom-textarea" v-model="symptomDescription" placeholder="请描述您的病情..." :maxlength="200"
show-confirm-bar="false"></textarea>
<view class="section">
<view class="checkbox-row">
<text class="section-title">是否到线下医院就诊过</text>
<uni-data-checkbox class="checkbox-group" mode="tag" v-model="hasVisitedHospital" :localdata="yesNoOptions"
:multiple="false"></uni-data-checkbox>
</view>
</view>
<!-- 诊断选择 - 只有选择"是"才显示 -->
<view class="section" v-if="hasVisitedHospital === 'yes'">
<text class="section-title">诊断*</text>
<view class="diagnosis-container">
<!-- 已选择的疾病标签 -->
<view class="selected-diseases" v-if="selectedDiseases.length > 0">
<view v-for="(disease, index) in selectedDiseases" :key="`${index}-common`" class="tag-item tag-item-active"
@click="removeDiseaseTag(index)">
{{ disease }}
</view>
<!-- <uni-tag v-for="(disease, index) in selectedDiseases" :key="index" :text="disease" type="primary"
size="small" :circle="true" @click="removeDiseaseTag(index)"></uni-tag> -->
</view>
<!-- 添加疾病按钮 -->
<view class="add-disease-btn" @click="goToSelectDisease">
<view class="tag-item">+ 添加</view>
</view>
</view>
</view>
<view class="section">
<view class="upload-header">
<text class="section-title">上传资料</text>
<text class="upload-subtitle">病历处方检查检验报告等图片</text>
</view>
<view class="upload-actions">
<view class="upload-btn" @click="add()">
<uni-icons type="camera" size="24" color="#666"></uni-icons>
<text class="upload-text">添加照片</text>
</view>
</view>
<!-- 已选择的图片预览 -->
<view class="image-preview" v-if="images.length > 0">
<view v-for="(image, index) in images" :key="image" class="preview-item">
<image :src="image" class="preview-image" mode="aspectFill"></image>
<view class="remove-btn" @click="remove(index)">
<uni-icons type="close" size="16" color="white"></uni-icons>
</view>
</view>
</view>
</view>
</view>
<view class="popup-footer">
<button class="popup-btn cancel-btn" @click="closeSymptomPopup">取消</button>
<button class="popup-btn submit-btn" @click="submitSymptomDescription">提交</button>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, watch } from 'vue'
import { submitOrderDescription } from '@/api/consult-order';
import { registerOnceEvent } from '@/hooks/useOnceEvent'
import useImageUpload from '@/hooks/useImageUpload'
import { set } from '@/utils/cache'
import { toast } from '@/utils/widget'
const emits = defineEmits(['close', 'submit'])
// props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
orderId: {
type: String,
default: ''
},
accountId: {
type: String,
default: ''
},
isIMMode: {
type: Boolean,
default: false
}
})
// emit
//
const symptomPopup = ref(null)
const symptomDescription = ref('')
const loading = ref(false)
const { images, add, remove, uploadImages } = useImageUpload('order')
// 线
const hasVisitedHospital = ref('')
const yesNoOptions = ref([
{ value: 'yes', text: '是' },
{ value: 'no', text: '否' }
])
//
const selectedDiseases = ref([])
//
const closeSymptomPopup = () => {
emits('close')
}
//
const goToSelectDisease = () => {
const eventName = registerOnceEvent(changeDisease)
set('diseaseSelected', selectedDiseases.value)
//
uni.navigateTo({
url: `/pages-home/consultation/disease?eventName=${eventName}`
})
}
//
const removeDiseaseTag = (index) => {
selectedDiseases.value.splice(index, 1)
}
//
const submitSymptomDescription = async () => {
if (loading.value) return;
loading.value = true;
if (!symptomDescription.value.trim() && images.value.length === 0) {
toast('请输入病情描述或上传图片')
loading.value = false;
return
}
if (images.value.length) {
const res = await uploadImages()
if (!res) {
loading.value = false;
return;
}
}
//
const descriptionData = {
description: symptomDescription.value.trim(),
hasVisitedHospital: hasVisitedHospital.value === 'yes',
diseases: selectedDiseases.value,
images: images.value
}
const res = await submitOrderDescription({
accountId: props.accountId,
orderId: props.orderId,
...descriptionData
})
if (res && res.success) {
toast(res.message);
closeSymptomPopup()
emits('submit', res.data)
}
loading.value = false
}
function changeDisease(diseases) {
selectedDiseases.value = diseases
}
watch(() => props.visible, (n) => {
if (n) {
symptomPopup.value && symptomPopup.value.open()
} else {
symptomPopup.value && symptomPopup.value.close()
}
})
</script>
<style scoped lang="scss">
/* 病情描述弹窗样式 */
.symptom-popup {
background-color: white;
border-top-left-radius: 16rpx;
border-top-right-radius: 16rpx;
max-height: 75vh;
display: flex;
flex-direction: column;
z-index: 999; // ConsultationBar
}
.popup-header {
padding: 20rpx 20rpx 0;
}
.popup-title {
font-size: $font-size-text;
font-weight: 600;
color: #333;
}
.popup-content {
flex: 1;
padding: 10rpx 20rpx 20rpx 20rpx;
overflow-y: auto;
}
.symptom-textarea {
width: 100%;
min-height: 120rpx;
background-color: #f8f9fa;
border: 1rpx solid #e9ecef;
border-radius: 16rpx;
padding: 12rpx;
font-size: 28rpx;
line-height: 1.5;
box-sizing: border-box;
resize: none;
}
.section {
margin-top: 20rpx;
}
.section-title {
font-size: $font-size-text;
font-weight: 600;
color: #333;
margin-bottom: 0;
flex: 1;
}
.upload-subtitle {
font-size: $font-size-tip;
color: #666;
display: block;
margin-bottom: 16rpx;
}
.upload-header {
display: flex;
align-items: center;
margin-bottom: 16rpx;
gap: 12rpx;
}
.upload-header .section-title {
margin-bottom: 0;
flex: none;
}
.upload-header .upload-subtitle {
margin-bottom: 0;
flex: 1;
text-align: left;
}
.upload-actions {
display: flex;
gap: 20rpx;
}
.upload-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 120rpx;
height: 120rpx;
background-color: #f8f9fa;
border: 1rpx solid #e9ecef;
border-radius: 16rpx;
gap: 8rpx;
transition: all 0.3s;
}
.upload-btn:active {
background-color: #e9ecef;
transform: scale(0.95);
}
.upload-text {
font-size: $font-size-tip;
color: #666;
}
.image-preview {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
margin-top: 16rpx;
}
.preview-item {
position: relative;
width: 80rpx;
height: 80rpx;
}
.preview-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.remove-btn {
position: absolute;
top: -8rpx;
right: -8rpx;
width: 24rpx;
height: 24rpx;
background-color: #ff4757;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.popup-footer {
display: flex;
padding: 20rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #f0f0f0;
gap: 16rpx;
}
.popup-btn {
flex: 1;
height: 88rpx;
border-radius: 12rpx;
font-size: $font-size-text;
font-weight: 500;
border: none;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
}
.cancel-btn {
background-color: #f8f9fa;
color: #666;
}
.cancel-btn:active {
background-color: #e9ecef;
}
.submit-btn {
background-color: $primary-color;
color: white;
}
.popup-btn::after {
border: none;
}
.checkbox-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
}
.checkbox-row .uni-data-checklist {
flex: none;
}
.checkbox-group {
flex-shrink: 0;
}
/* 诊断容器样式 */
.diagnosis-container {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8rpx;
}
.selected-diseases {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
}
.add-disease-btn {
cursor: pointer;
}
.tag-item {
border: 1px solid #eee;
padding: 8rpx 30rpx;
border-radius: 40rpx;
font-size: 28rpx;
color: #666;
margin-bottom: 12rpx;
}
.tag-item-active {
background-color: #0074ff;
border-color: #0074ff;
color: #fff;
}
</style>

View File

@ -1,85 +0,0 @@
<template>
<view v-if="data" class="symptom-detail">
<view class="symptom-row">
<text class="label-text">患者</text>
<text class="content-text">{{ data.patient }}</text>
</view>
<view class="symptom-row">
<text class="label-text">病情描述</text>
<text class="content-text">{{ data.description }}</text>
</view>
<view v-if="data.diseases" class="symptom-row">
<text class="label-text">线下确诊疾病</text>
<text class="content-text">{{ data.diseases }}</text>
</view>
<view v-if="data.images && data.images.length" class="symptom-row" @click="previewImage()">
<text class="label-text">附件</text>
<text class="preview-btn">{{ data.images.length }} 点击查看</text>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
payload: {
type: Object,
default: () => ({})
}
});
const data = computed(() => {
try {
const extension = JSON.parse(props.payload.extension);
return {
patient: typeof extension.patient === 'string' ? extension.patient : '',
description: typeof extension.description === 'string' ? extension.description : '',
hasVisitedHospital: typeof extension.hasVisitedHospital === 'boolean' ? extension.hasVisitedHospital : false,
diseases: typeof extension.diseases === 'string' ? extension.diseases : '',
images: Array.isArray(extension.images) ? extension.images : [],
}
} catch (e) { }
return null
})
function previewImage() {
console.log('预览图片')
uni.previewImage({
urls: data.value.images
})
}
</script>
<style scpoed>
/* 病情描述样式 */
.symptom-detail {
min-width: 360rpx;
max-width: 600rpx;
}
.symptom-row {
padding: 4rpx 0;
line-height: 42rpx;
}
.label-text {
color: #999;
font-size: 28rpx;
}
.content-text {
color: #333;
font-size: 28rpx;
}
.preview-btn {
display: inline-block;
border: 1px solid;
color: #0074ff;
line-height: 36rpx;
font-size: 24rpx;
padding: 0 12rpx;
border-radius: 22rpx;
}
</style>

View File

@ -1,140 +0,0 @@
<template>
<view v-if="data" class="card-detail">
<view class="card-row">
<text class="label-text">患者</text>
<text class="content-text">{{ data.patient }}</text>
</view>
<view class="card-row">
<text class="label-text">诊断</text>
<text class="content-text">{{ data.diseases }}</text>
</view>
<view v-if="data.medicines.length && data.type === 'western'" class="card-row card-row--flex">
<view class="row-label label-text">RP</view>
<view class="row-content">
<view v-for="(i, idx) in data.medicines" :key="idx">
<view class="content-text"> {{ i.info }}</view>
<view class="label-text"> {{ i.useInfo }}</view>
</view>
</view>
</view>
<view v-else-if="data.type === 'chinese'" class="card-row card-row--flex">
<view class="row-label label-text">RP </view>
<view class="row-content">
<view class="content-text">中药处方</view>
<view class="label-text">
{{ data.doseNum }}, 每日{{ data.dailyDoseNum }},1剂分{{ data.timesPerDosage }}次服用, {{ data.administrationTime
}},
{{ data.instructions || '' }}
</view>
</view>
</view>
<view class="buy-btn" @click="toBuy()">立即购买</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
import { getPrescriptionDetail } from '@/api/medicine';
import { toast } from '@/utils/widget';
const props = defineProps({
payload: {
type: Object,
default: () => ({})
}
});
const data = computed(() => {
try {
const extension = JSON.parse(props.payload.extension);
return {
...extension,
id: extension.id,
patient: typeof extension.patient === 'string' ? extension.patient : '',
diseases: typeof extension.diseases === 'string' ? extension.diseases : '',
medicines: Array.isArray(extension.medicines) ? formatWesternMedicine(extension.medicines) : [],
type: typeof extension.type === 'string' ? extension.type : '',
}
} catch (e) { }
return null
})
function formatWesternMedicine(medicines) {
return medicines.map(med => {
const info = `${med.name} ${med.specification || ''} x${med.quantity || ''}${med.unit || ''}`;
const useInfo = `${med.administrationTime || ''} ${med.usageName || ''}, ${med.frequencyName}, 每次${med.dosage || ''} ${med.dosageUnit || ''} ${med.medicationCycle ? `用药${med.medicationCycle}`:''} `
return { info, useInfo }
});
}
async function toBuy() {
const res = await getPrescriptionDetail(data.value.id);
if (res.data && res.data.status === 'init') {
uni.navigateTo({
url: `/pages-cart/payment?ids=${data.value.id}`
})
} else if (res.data && res.data.status == 'abolished') {
toast('处方已作废')
} else if (res.data) {
toast('处方已失效')
} else {
toast('查询处方信息失败')
}
};
</script>
<style scpoed>
/* 病情描述样式 */
.card-detail {
min-width: 360rpx;
max-width: 600rpx;
}
.card-row {
padding: 4rpx 0;
line-height: 42rpx;
}
.card-row--flex {
display: flex;
}
.card-row--flex .row-label {
flex-shrink: 0;
}
.card-row--flex .row-content {
width: 0;
flex-grow: 1;
}
.label-text {
color: #999;
font-size: 28rpx;
}
.content-text {
color: #333;
font-size: 28rpx;
}
.preview-btn {
display: inline-block;
border: 1px solid;
color: #0074ff;
line-height: 36rpx;
font-size: 24rpx;
padding: 0 12rpx;
border-radius: 22rpx;
}
.buy-btn {
background-color: #0074ff;
color: #fff;
text-align: center;
line-height: 60rpx;
font-size: 28rpx;
border-radius: 30rpx;
margin-top: 20rpx;
}
</style>

View File

@ -1,574 +0,0 @@
<template>
<view class="message-card" :class="cardTypeClass">
<view class="card-header">
<text class="card-title">{{ title }}</text>
<view v-if="showDetail" class="detail-btn" @click.stop="handleDetail">
<text class="detail-text">详情</text>
<uni-icons type="right" size="14" color="#fff"></uni-icons>
</view>
</view>
<view class="card-content">
<!-- 病情描述类型 -->
<fill-description-card v-if="payload.description === 'PATIENT_FILL_CONSULTATION'" :payload="payload" />
<medicines-card v-else-if="payload.description === 'DOCTOR_WRITE_PRESCRIPTION'" :payload="payload" />
<refill-medicine-card v-else-if="payload.description === 'USER_REFILL_PRESCRIPTION'" :payload="payload" />
<reject-refill-medicine-card v-else-if="payload.description === 'REJECT_REFILL_PRESCRIPTION'"
:payload="payload" />
<!-- 续方申请类型 -->
<template v-if="messageData.messageType === 'refill'">
<view class="refill-detail">
<!-- 诊断信息 -->
<view class="refill-diagnosis">
<text class="label-text">诊断</text>
<text class="content-text">{{ messageData.diagnosis }}</text>
</view>
<!-- 药品信息 -->
<view class="refill-medicines">
<text class="section-title">药品</text>
<!-- 中药处方 -->
<view v-if="messageData.prescriptionType === '中药处方'" class="chinese-medicine-refill">
<text class="medicine-type-title">{{
messageData.prescriptionType
}}</text>
<text class="medicine-description">{{
messageData.prescriptionDesc
}}</text>
</view>
<!-- 西药处方 -->
<view v-else class="western-medicine-refill">
<view v-for="(medicine, index) in messageData.medicines" :key="index" class="medicine-item-refill">
<view class="medicine-info-refill">
<text class="medicine-name-refill">{{ medicine.name }} {{ medicine.spec }}</text>
<text class="medicine-count-refill">{{
medicine.count
}}</text>
</view>
<text class="medicine-usage-refill">{{ medicine.usage }}</text>
</view>
</view>
</view>
</view>
</template>
<!-- 问卷调查类型 -->
<template v-if="messageData.messageType === 'survey'">
<view class="survey-detail">
<!-- 问卷标题和描述 -->
<view class="survey-header">
<view class="survey-info">
<text class="survey-title">{{ messageData.surveyTitle }}</text>
<text class="survey-subtitle">{{
messageData.surveyDescription
}}</text>
</view>
</view>
<!-- 问卷详情 -->
<view class="survey-content">
<view class="survey-item">
<text class="survey-label">问卷名称</text>
<text class="survey-value">{{ messageData.surveyName }}</text>
</view>
<view class="survey-item">
<text class="survey-label">预计用时</text>
<text class="survey-value">{{ messageData.estimatedTime }}</text>
</view>
<view class="survey-item" v-if="messageData.reward">
<text class="survey-label">完成奖励</text>
<text class="survey-value reward-text">{{
messageData.reward
}}</text>
</view>
</view>
<!-- 问卷说明 -->
<view class="survey-note" v-if="messageData.note">
<text class="note-text">{{ messageData.note }}</text>
</view>
</view>
</template>
<!-- 立即购买按钮 -->
<view class="card-footer" @click="handlePurchase" v-if="messageData.messageType === 'prescription'">
<text class="view-all-text">立即购药</text>
</view>
<!-- 续方申请按钮 -->
<view class="card-footer" @click="handleBuy" v-if="messageData.messageType === 'refill'">
<text class="view-all-text">申请再次购买</text>
</view>
<!-- 问卷调查按钮 -->
<view class="card-footer" @click="handleBuy" v-if="messageData.messageType === 'survey'">
<text class="view-all-text">去填写</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from "vue";
import { sendSystemMessage } from "@/api/corp/im.js";
import fillDescriptionCard from "./fill-description-card.vue";
import medicinesCard from "./medicines-card.vue";
import refillMedicineCard from "./refill-medicine-card.vue";
import rejectRefillMedicineCard from "./reject-refill-medicine-card.vue";
const props = defineProps({
payload: {
type: Object,
required: true,
},
messageData: {
type: Object,
required: true,
},
flow: {
type: String,
default: 'out'
},
});
const emit = defineEmits(["viewDetail"]);
const CardTitle = {
PATIENT_FILL_CONSULTATION: "患者病情描述",
USER_REFILL_PRESCRIPTION: '续方申请',
DOCTOR_WRITE_PRESCRIPTION: "医嘱",
REJECT_REFILL_PRESCRIPTION: '续方失败',
symptom: "病情描述",
prescription: "处方单",
refill: "续方申请",
survey: "问卷调查",
};
// const showDetailBtn = computed(() => {
// return ['DOCTOR_WRITE_PRESCRIPTION'].includes(props.payload.description)
// });
//
const title = computed(() => {
return CardTitle[props.payload.description] || CardTitle[props.messageData.messageType] || "消息卡片";
});
const extension = computed(() => {
try {
return JSON.parse(props.payload.extension);
} catch (e) { }
return {}
})
const showDetail = computed(() => {
if (props.payload.description === 'DOCTOR_WRITE_PRESCRIPTION' && extension.value.id) {
return true
}
return false
})
function handleDetail() {
if (props.payload.description === 'DOCTOR_WRITE_PRESCRIPTION' && extension.value.id) {
uni.navigateTo({
url: `/pages-home/prescription/detail?id=${extension.value.id}`
})
}
}
//
const hasAttachment = computed(() => {
return props.messageData.images && props.messageData.images.length > 0;
});
//
const number = computed(() => {
return `${props.messageData.images.length}`;
});
//
const cardTypeClass = computed(() => {
return `card-${props.messageData.messageType}`;
});
//
const handleBuy = () => {
emit("viewDetail", props.messageData);
};
//
const handlePurchase = () => {
console.log('购买成功', props.messageData);
const groupID = props.messageData.conversationID.replace('GROUP', '');
sendSystemMessage(groupID, 'purchased');
};
</script>
<style lang="scss">
.message-card {
border-radius: 4rpx;
overflow: hidden;
}
.card-symptom,
.card-prescription,
.card-refill,
.card-survey {
background-color: $primary-color;
}
.card-header {
padding: 12rpx 12rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
color: white;
font-size: 28rpx;
font-weight: 600;
}
.detail-btn {
display: flex;
align-items: center;
gap: 4rpx;
padding: 4rpx 8rpx;
border-radius: 4rpx;
transition: all 0.3s;
}
.detail-text {
color: white;
font-size: 22rpx;
}
.detail-arrow {
color: white;
font-size: 22rpx;
font-weight: 600;
}
.card-content {
margin: 10rpx;
padding: 12rpx 16rpx 16rpx;
background-color: white;
border-radius: 4rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.patient-info {
margin-bottom: 12rpx;
}
.label-text {
color: #666;
font-size: 24rpx;
}
.content-text {
color: #333;
font-size: 24rpx;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
/* 医院就诊信息样式 */
.hospital-visit-info {
margin-top: 8rpx;
}
.visit-text {
color: #666;
font-size: 13rpx;
}
/* 处方单样式 */
.prescription-detail {
margin-bottom: 12rpx;
}
.prescription-section {
width: 420rpx;
margin-bottom: 8rpx;
margin-top: 4rpx;
}
.section-title {
color: #666;
font-size: 24rpx;
display: block;
margin-bottom: 8rpx;
}
.medicine-list {
display: flex;
flex-direction: column;
gap: 6rpx;
}
.medicine-item-wrapper {
background-color: #f8f9fa;
border-radius: 6rpx;
padding: 8rpx 12rpx;
}
.medicine-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.medicine-usage {
margin-top: 4rpx;
padding-top: 4rpx;
border-top: 1px solid #e9ecef;
}
.medicine-name {
color: #333;
font-size: 24rpx;
font-weight: 500;
flex: 1;
margin-right: 8rpx;
}
.medicine-spec {
color: #666;
font-size: 24rpx;
flex: 1;
text-align: center;
}
.medicine-count {
color: #333;
font-size: 24rpx;
font-weight: 600;
min-width: 30rpx;
text-align: right;
}
.usage-text {
color: #666;
font-size: 24rpx;
line-height: 1.4;
}
/* 中药处方样式 */
.tcm-prescription {
background-color: #f8f9fa;
border-radius: 6rpx;
padding: 12rpx;
}
.tcm-label {
color: #333;
font-size: 24rpx;
font-weight: 600;
display: block;
margin-bottom: 8rpx;
}
.card-footer {
background-color: $primary-color;
border-radius: 4rpx;
padding: 8rpx 16rpx;
text-align: center;
margin: 0 -4rpx;
transition: all 0.3s;
}
.card-footer:active {
background-color: $primary-color;
transform: scale(0.98);
}
.view-all-text {
color: white;
font-size: 24rpx;
font-weight: 500;
}
/* 续方申请样式 */
.refill-detail {
margin-bottom: 12rpx;
width: 420rpx;
max-width: 95vw;
}
.refill-patient-info {
margin-bottom: 12rpx;
padding-bottom: 8rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.refill-diagnosis {
margin-bottom: 12rpx;
display: flex;
align-items: flex-start;
}
.refill-diagnosis .label-text {
min-width: 50rpx;
margin-right: 8rpx;
}
.refill-medicines {
margin-bottom: 12rpx;
}
.refill-medicines .section-title {
margin-bottom: 8rpx;
}
/* 中药续方样式 */
.chinese-medicine-refill {
background-color: #f8f9fa;
border-radius: 6rpx;
padding: 12rpx;
}
.medicine-type-title {
color: #333;
font-size: 24rpx;
font-weight: 600;
display: block;
margin-bottom: 8rpx;
}
.medicine-description {
color: #666;
font-size: 24rpx;
line-height: 1.4;
}
/* 西药续方样式 */
.western-medicine-refill {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.medicine-item-refill {
background-color: #f8f9fa;
border-radius: 6rpx;
padding: 10rpx 12rpx;
}
.medicine-info-refill {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4rpx;
}
.medicine-name-refill {
color: #333;
font-size: 24rpx;
font-weight: 500;
flex: 1;
margin-right: 8rpx;
}
.medicine-count-refill {
color: #333;
font-size: 24rpx;
font-weight: 600;
min-width: 30rpx;
text-align: right;
}
.medicine-usage-refill {
color: #666;
font-size: 22rpx;
line-height: 1.3;
}
/* 问卷调查样式 */
.survey-detail {
margin-bottom: 12rpx;
width: 420rpx;
max-width: 90vw;
}
.survey-header {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
padding-bottom: 12rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.survey-info {
flex: 1;
}
.survey-title {
color: #333;
font-size: 26rpx;
font-weight: 600;
display: block;
margin-bottom: 4rpx;
line-height: 1.3;
}
.survey-subtitle {
color: #666;
font-size: 24rpx;
line-height: 1.4;
}
.survey-content {
margin-bottom: 12rpx;
}
.survey-item {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.survey-item:last-child {
margin-bottom: 0;
}
.survey-label {
color: #666;
font-size: 24rpx;
min-width: 80rpx;
margin-right: 8rpx;
}
.survey-value {
color: #333;
font-size: 24rpx;
flex: 1;
}
.reward-text {
color: #ff6b35;
font-weight: 600;
}
.survey-note {
background-color: #f8f9fa;
border-radius: 6rpx;
padding: 10rpx 12rpx;
margin-top: 12rpx;
}
.note-text {
color: #666;
font-size: 23rpx;
line-height: 1.4;
}
</style>

View File

@ -1,102 +0,0 @@
<template>
<view v-if="data" class="card-detail">
<view class="card-row">
<text class="label-text">患者</text>
<text class="content-text">{{ data.patient }}</text>
</view>
<view class="card-row">
<text class="label-text">诊断</text>
<text class="content-text">{{ data.diseases }}</text>
</view>
<view v-if="data.medicines.length && data.type === 'western'" class="card-row card-row--flex">
<view class="row-label label-text">RP</view>
<view class="row-content">
<view v-for="i in data.medicines" :key="i._id">
<view class="content-text"> {{ i.info }}</view>
<view class="label-text"> {{ i.useInfo }}</view>
</view>
</view>
</view>
<view v-else-if="data.type === 'chinese'" class="card-row card-row--flex">
<view class="row-label label-text">RP </view>
<view class="row-content">
<view class="content-text">中药处方</view>
<view class="label-text">
{{ data.doseNum }}, 每日{{ data.dailyDoseNum }},1剂分{{ data.timesPerDosage }}服用, {{ data.administrationTime
}},
{{ data.instructions || '' }}
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
payload: {
type: Object,
default: () => ({})
}
});
const data = computed(() => {
try {
const extension = JSON.parse(props.payload.extension);
return {
...extension,
patient: typeof extension.patient === 'string' ? extension.patient : '',
diseases: typeof extension.diseases === 'string' ? extension.diseases : '',
medicines: Array.isArray(extension.medicines) ? formatWesternMedicine(extension.medicines) : [],
type: typeof extension.type === 'string' ? extension.type : '',
}
} catch (e) { }
return null
})
function formatWesternMedicine(medicines) {
return medicines.map(med => {
const info = `${med.name} ${med.specification || ''} x${med.quantity || ''}${med.unit || ''}`;
const useInfo = `${med.administrationTime || ''} ${med.usageName || ''}, ${med.frequencyName}, 每次${med.dosage || ''} ${med.dosageUnit || ''} `
return { info, useInfo }
});
}
</script>
<style scpoed>
/* 病情描述样式 */
.card-detail {
min-width: 360rpx;
max-width: 600rpx;
}
.card-row {
padding: 4rpx 0;
line-height: 42rpx;
}
.card-row--flex {
display: flex;
}
.card-row--flex .row-label {
flex-shrink: 0;
}
.card-row--flex .row-content {
width: 0;
flex-grow: 1;
}
.label-text {
color: #999;
font-size: 28rpx;
}
.content-text {
color: #333;
font-size: 28rpx;
}
</style>

View File

@ -1,50 +0,0 @@
<template>
<view v-if="data" class="card-detail">
<view class="card-row">
<text class="label-text">理由</text>
<text class="content-text">{{ data.reason || '' }}</text>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
payload: {
type: Object,
default: () => ({})
}
});
const data = computed(() => {
try {
const extension = JSON.parse(props.payload.extension);
return extension
} catch (e) { }
return {}
})
</script>
<style scpoed>
/* 病情描述样式 */
.card-detail {
min-width: 360rpx;
max-width: 600rpx;
}
.card-row {
padding: 4rpx 0;
line-height: 42rpx;
}
.label-text {
color: #999;
font-size: 28rpx;
}
.content-text {
color: #333;
font-size: 28rpx;
}
</style>

View File

@ -1,30 +0,0 @@
import { computed, onUnmounted, ref, watch } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import dayjs from 'dayjs';
import { getChatOrder, orderStatus } from "@/utils/order.js";
export default function useChatOrder(orderId) {
const currentOrder = ref(null);// 当前聊天室的最新咨询订单
const countdown = ref(''); // 倒计时
const chatRoomStatus = computed(() => {
const order = currentOrder.value || {};
const isWaiting = order.orderStatus === orderStatus.PAID;
const isPending = order.orderStatus === orderStatus.CONSULTING;
return { isWaiting, isPending };
})
async function getCurrentOrder() {
if (orderId.value) {
currentOrder.value = await getChatOrder(orderId.value)
} else {
currentOrder.value = null
}
}
watch(orderId, n=>{
getCurrentOrder()
})
return { currentOrder, chatRoomStatus, countdown, getCurrentOrder }
}

View File

@ -1,146 +0,0 @@
import { ref, computed } from 'vue';
import { onShow, onUnload } from "@dcloudio/uni-app";
import { getChatStatus } from "@/api/corp/im.js";
import { getDoctorByDoctorId } from '@/api/doctor/doctor';
import { toast } from '@/utils/widget';
import dayjs from 'dayjs';
import useChatOrder from './chat-order-hook';
/**
* 获取聊天室当前进行中业务hook
* @param {string聊天群组ID} groupID
*/
export default function useChatBusiness(groupID) {
const groupInfo = ref({});
const assistant = computed(() => {
if (groupInfo.value.assistant?.memberAccount) {
return { assistantId: groupInfo.value.assistant.memberAccount, name: groupInfo.value.assistant.name }
}
return null;
})
const doctor = computed(() => {
// 优先返回从API获取的详细医生信息
if (doctorInfo.value) {
return doctorInfo.value;
}
// 如果详细信息还未加载,返回基本信息
if (groupInfo.value.doctor?.memberAccount) {
return {
doctorId: groupInfo.value.doctor.memberAccount,
name: groupInfo.value.doctor.name,
avatar: groupInfo.value.doctor.avatar
}
}
return null;
})
const patient = computed(() => {
if (groupInfo.value.patient?.memberAccount) {
return { openId: groupInfo.value.patient.memberAccount, name: groupInfo.value.patient.name, patientId: groupInfo.value.patient.patientId }
}
return null;
})
const chatMember = computed(() => {
const res = {
assistant: { name: '医生助理', avatar: '/static/assistant.png' }
};
if (assistant.value) {
res[assistant.value.assistantId] = { name: `医生助理${assistant.value.name}`, avatar: '/static/assistant.png' };
}
if (doctor.value) {
res[doctor.value.doctorId] = { name: `医生${doctor.value.name || ''}`, avatar: doctor.value.avatar || '/static/home/doctor.svg' };
}
if (patient.value) {
res[patient.value.openId] = { name: '我' || `患者${patient.value.name}`, avatar: '/static/center/user-avatar.png' };
}
return res;
})
const chatRoomBusiness = computed(() => groupInfo.value.groupBusiness || {});
const isPending = computed(() => groupInfo.value.status === 'active');
const isClosed = computed(() => !isPending.value);
const doctorInfo = ref(null) // 医生信息
const orderId = computed(() => {
return chatRoomBusiness.value.businessType === 'consultation' ? chatRoomBusiness.value.businessId : ''
})
const { currentOrder, chatRoomStatus, countdown, getCurrentOrder } = useChatOrder(orderId)
const chatRoomCountDown = ref('') // 会话倒计时
const showCountdown = computed(() => {
const isPendingOrder = currentOrder.value && chatRoomStatus.value.isPending && orderId.value == currentOrder.value.orderId;
const isRefill = chatRoomBusiness.value.businessType === 'refill_prescription'
return chatRoomCountDown.value && (isPendingOrder || isRefill)
})
async function getChatBusiness() {
if (!groupID.value) return;
const res = await getChatStatus(groupID.value);
if (res && res.success) {
groupInfo.value = res.data;
setCountdown()
getDoctor()
} else {
await toast('获取聊天室业务失败')
uni.navigateBack()
}
}
function setCountdown() {
if (isPending.value && groupInfo.value.expireTime && dayjs(groupInfo.value.expireTime).isAfter(dayjs())) {
if (getApp().chatRoomCountDownTimer) clearInterval(getApp().chatRoomCountDownTimer);
getApp().chatRoomCountDownTimer = setInterval(() => {
const now = dayjs();
const endTime = dayjs(groupInfo.value.expireTime);
const diff = endTime.diff(now, 'second');
if (diff <= 0) {
clearCountdown();
getChatBusiness()
return;
}
// 计算剩余时分秒
const hours = Math.floor(diff / 3600);
const minutes = Math.floor((diff % 3600) / 60);
const seconds = diff % 60;
const pad = n => n.toString().padStart(2, '0');
chatRoomCountDown.value = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`.replace(/^00\:/, '');
}, 1000)
} else {
clearCountdown()
}
}
function clearCountdown() {
if (getApp().chatRoomCountDownTimer) {
clearInterval(getApp().chatRoomCountDownTimer);
getApp().chatRoomCountDownTimer = null;
}
chatRoomCountDown.value = ''
}
async function getDoctor() {
const doctorId = groupInfo.value.doctor?.memberAccount;
if (doctorId && (!doctorInfo.value || doctorInfo.value.doctorId !== doctorId)) {
const res = await getDoctorByDoctorId(doctorId)
doctorInfo.value = res && res.data ? res.data : null;
}
}
onUnload(() => {
clearCountdown()
})
onShow(() => {
getChatBusiness()
})
return { chatRoomBusiness, getChatBusiness, currentOrder, chatRoomStatus, countdown, getCurrentOrder, chatRoomCountDown, showCountdown, doctorInfo, isClosed, chatMember }
}

View File

@ -7,7 +7,7 @@
name="姓名"
:required="true"
:form="formData"
title="name"
title="anotherName"
@change="handleFieldChange"
/>
<!-- 头像 -->
@ -67,7 +67,6 @@
</view>
</common-cell>
<!-- 个人介绍 -->
<form-textarea
name="个人介绍"
@ -98,9 +97,10 @@ import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue
import api from "@/utils/api.js";
const { account, doctorInfo } = storeToRefs(useAccountStore());
const { useLoad } = useGuard();
const { getDoctorInfo } = useAccountStore();
//
const formData = ref({
name: "",
anotherName: "",
avatar: "",
gender: "",
mobile: "",
@ -113,50 +113,38 @@ const formData = ref({
});
//
const genderOptions = ["男", "女"];
const genderOptions = [
{ label: "男", value: "0" },
{ label: "女", value: "1" },
];
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
//
const handleFieldChange = (e) => {
formData.value[e.title] = e.value;
if (e.title === "gender") {
formData.value[e.title] = e.value.value;
} else {
formData.value[e.title] = e.value;
}
};
//
const chooseAvatar = async () => {
const uploadRes = await chooseAndUploadImage({
businessType: "other",
accessLevel: "public",
loadingTitle: "上传中...",
});
if (uploadRes?.previewUrl) {
formData.value.avatar = uploadRes.previewUrl;
const url = await chooseAndUploadImage();
if (url) {
formData.value.avatar = url;
}
};
const handleCancel = () => {
uni.navigateBack();
};
//
const handleSave = async () => {
//
if (!formData.value.name) {
uni.showToast({
title: "请输入姓名",
icon: "none",
});
return;
}
uni.showLoading({
title: "保存中...",
});
createDoctorInfo();
};
useLoad(() => {
debugger
if (doctorInfo.value) {
formData.value = doctorInfo.value;
formData.value = { ...doctorInfo.value };
} else {
formData.value.mobile = account.value.mobile;
}
@ -164,12 +152,22 @@ useLoad(() => {
//
const createDoctorInfo = async () => {
if (!formData.value.anotherName) {
uni.showToast({
title: "请输入姓名",
icon: "none",
});
return;
}
let params = {
anotherName: formData.value.name,
anotherName: formData.value.anotherName,
avatar: formData.value.avatar,
gender: formData.value.gender,
mobile: formData.value.mobile,
weChatOpenId: account.value.openid,
deptIds: [],
loginTypes: ["wxApp"],
corpId: account.value.corpId,
};
const res = await api("addCorpMember", {
params,
@ -179,6 +177,8 @@ const createDoctorInfo = async () => {
title: "创建成功",
icon: "success",
});
await getDoctorInfo();
uni.navigateBack();
} else {
uni.showToast({
title: "创建失败",
@ -187,6 +187,25 @@ const createDoctorInfo = async () => {
console.error("创建医生信息失败:", res);
}
};
const updateDoctorInfo = async () => {
let params = {
anotherName: formData.value.anotherName,
avatar: formData.value.avatar,
gender: formData.value.gender,
};
const res = await api("updateCorpMember", {
params,
});
if (res.success && res.data) {
uni.showToast({
title: "更新成功",
icon: "success",
});
}
await getDoctorInfo();
uni.navigateBack();
};
//
const openDepartmentSelect = () => {
uni.navigateTo({

View File

@ -14,10 +14,7 @@ export default defineStore("accountStore", () => {
const openid = ref("");
// 医生信息
const doctorInfo = ref(null);
async function login(phoneCode = '') {
debugger
if (loading.value) return;
loading.value = true;
try {
@ -26,7 +23,6 @@ export default defineStore("accountStore", () => {
provider: "weixin",
scope: "snsapi_base",
});
console.log('logincode: ', code)
if (code) {
const res = await api('wxAppLogin', {
phoneCode,
@ -35,19 +31,12 @@ export default defineStore("accountStore", () => {
loading.value = false;
if (res.success && res.data) {
if (!res.data.mobile) {
const pages = getCurrentPages();
const current = pages[pages.length - 1];
const params = current && current.options
? Object.keys(current.options).map(key => `${key}=${current.options[key]}`).join('&')
: '';
const redirectUrl = current && current.route ? `/${current.route}${params ? `?${params}` : ''}` : '';
const target = redirectUrl ? `/pages/login/login?redirect=${encodeURIComponent(redirectUrl)}` : '/pages/login/login';
const target = '/pages/login/login';
uni.redirectTo({ url: target });
return;
}
account.value = res.data;
openid.value = res.data.openid;
debugger;
await getDoctorInfo(openid.value);
return res.data
}
@ -58,11 +47,10 @@ export default defineStore("accountStore", () => {
}
loading.value = false
}
async function getDoctorInfo(weChatOpenId) {
async function getDoctorInfo() {
try {
const res = await api('getCorpMemberData', {
weChatOpenId,
weChatOpenId: account.value.openid,
});
if (res.success && res.data) {
doctorInfo.value = res.data;
@ -72,5 +60,5 @@ export default defineStore("accountStore", () => {
}
}
return { account, openid, doctorInfo, login }
return { account, openid, doctorInfo, login, getDoctorInfo }
})

View File

@ -10,7 +10,8 @@ const urlsConfig = {
getDeptList: 'getRealDeptList',
getHospitalList: 'getRealHospital',
addCorpMember: 'addCorpMember',
getCorpMemberData: 'getCorpMemberData'
getCorpMemberData: 'getCorpMemberData',
updateCorpMember: 'updateCorpMember'
},
knowledgeBase: {

View File

@ -1,798 +0,0 @@
/**
* 聊天相关工具函数
*/
// 通用消息提示
export const showMessage = (title, icon = 'none') => {
uni.showToast({
title,
icon,
});
};
// 检查问诊状态
export const checkConsultationStatus = (waitingForDoctor, consultationEnded) => {
if (waitingForDoctor) {
showMessage("等待医生接诊中,无法发送消息");
return false;
}
if (consultationEnded) {
showMessage("问诊已结束,无法发送消息");
return false;
}
return true;
};
// 检查IM连接状态
export const checkIMConnection = (timChatManager) => {
if (!timChatManager.tim || !timChatManager.isLoggedIn) {
// showMessage("IM连接异常请重新进入");
return false;
}
return true;
};
// 发送消息前的通用验证
export const validateBeforeSend = (waitingForDoctor, consultationEnded, timChatManager) => {
if (!checkConsultationStatus(waitingForDoctor, consultationEnded)) {
return false;
}
if (!checkIMConnection(timChatManager)) {
return false;
}
return true;
};
// 获取语音文件URL
export const getVoiceUrl = (message) => {
let voiceUrl = '';
if (message.payload && message.payload.url) {
voiceUrl = message.payload.url;
} else if (message.payload && message.payload.file) {
voiceUrl = message.payload.file;
} else if (message.payload && message.payload.tempFilePath) {
voiceUrl = message.payload.tempFilePath;
} else if (message.payload && message.payload.filePath) {
voiceUrl = message.payload.filePath;
}
return voiceUrl;
};
// 验证语音URL格式
export const validateVoiceUrl = (voiceUrl) => {
if (!voiceUrl) {
console.error('语音文件URL不存在');
showMessage('语音文件不存在');
return false;
}
if (!voiceUrl.startsWith('http') && !voiceUrl.startsWith('wxfile://') && !voiceUrl.startsWith('/')) {
console.error('语音文件URL格式不正确:', voiceUrl);
showMessage('语音文件格式错误');
return false;
}
return true;
};
// 创建音频上下文
export const createAudioContext = (voiceUrl) => {
const audioContext = uni.createInnerAudioContext();
audioContext.src = voiceUrl;
audioContext.onPlay(() => {
console.log('语音开始播放');
});
audioContext.onEnded(() => {
console.log('语音播放结束');
});
audioContext.onError((err) => {
console.error('语音播放失败:', err);
console.error('错误详情:', {
errMsg: err.errMsg,
errno: err.errno,
src: voiceUrl
});
showMessage('语音播放失败');
});
return audioContext;
};
// ==================== 时间相关工具方法 ====================
/**
* 验证时间戳格式
* @param {number|string} timestamp - 时间戳
* @returns {boolean} 是否为有效时间戳
*/
export const validateTimestamp = (timestamp) => {
if (!timestamp) return false;
const num = Number(timestamp);
if (isNaN(num)) return false;
// 检查是否为有效的时间戳范围1970年到2100年
const minTimestamp = 0;
const maxTimestamp = 4102444800000; // 2100年1月1日
return num >= minTimestamp && num <= maxTimestamp;
};
/**
* 格式化时间 - 今天/昨天显示文字其他显示日期 + 空格 + 24小时制时间
* @param {number|string} timestamp - 时间戳
* @returns {string} 格式化后的时间字符串
*/
export const formatTime = (timestamp) => {
// 验证时间戳
if (!validateTimestamp(timestamp)) {
return "未知时间";
}
// 确保时间戳是毫秒级
let timeInMs = timestamp;
if (timestamp < 1000000000000) {
// 如果时间戳小于这个值,可能是秒级时间戳
timeInMs = timestamp * 1000;
}
const date = new Date(timeInMs);
const now = new Date();
// 验证日期是否有效
if (isNaN(date.getTime())) {
return "未知时间";
}
// 格式化时间HH:MM (24小时制)
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const timeStr = `${hours}:${minutes}`;
// 检查是否是今天
if (date.toDateString() === now.toDateString()) {
return `${timeStr}`;
}
// 检查是否是昨天
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
if (date.toDateString() === yesterday.toDateString()) {
return `昨天 ${timeStr}`;
}
// 其他日期显示完整日期
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const dateStr = `${month}/${day}`;
return `${dateStr} ${timeStr}`;
};
/**
* 计算时间差
* @param {number|string} startTime - 开始时间戳
* @param {number|string} endTime - 结束时间戳
* @returns {object} 包含天小时分钟秒的时间差对象
*/
export const calculateTimeDiff = (startTime, endTime) => {
if (!validateTimestamp(startTime) || !validateTimestamp(endTime)) {
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
}
let startMs = startTime;
let endMs = endTime;
if (startTime < 1000000000000) startMs = startTime * 1000;
if (endTime < 1000000000000) endMs = endTime * 1000;
const diffMs = Math.abs(endMs - startMs);
const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const hours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diffMs % (1000 * 60)) / 1000);
return { days, hours, minutes, seconds };
};
/**
* 格式化倒计时
* @param {number|string} endTime - 结束时间戳
* @param {number|string} currentTime - 当前时间戳可选默认使用当前时间
* @returns {string} 格式化后的倒计时字符串
*/
export const formatCountdown = (endTime, currentTime = Date.now()) => {
const diff = calculateTimeDiff(currentTime, endTime);
if (diff.days > 0) {
return `${diff.days}${diff.hours}${diff.minutes}`;
} else if (diff.hours > 0) {
return `${diff.hours}${diff.minutes}${diff.seconds}`;
} else if (diff.minutes > 0) {
return `${diff.minutes}${diff.seconds}`;
} else {
return `${diff.seconds}`;
}
};
// ==================== 媒体选择相关工具方法 ====================
/**
* 检查并请求相册权限
* @returns {Promise<boolean>} 是否有权限
*/
const checkAlbumPermission = () => {
return new Promise((resolve) => {
uni.getSetting({
success: (res) => {
const authStatus = res.authSetting['scope.album'];
if (authStatus === undefined) {
// 未授权过,会自动弹出授权窗口
resolve(true);
} else if (authStatus === false) {
// 已拒绝授权,需要引导用户手动开启
uni.showModal({
title: '需要相册权限',
content: '请在设置中开启相册权限,以便选择图片',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.album']) {
resolve(true);
} else {
resolve(false);
}
},
fail: () => {
resolve(false);
}
});
} else {
resolve(false);
}
},
fail: () => {
resolve(false);
}
});
} else {
// 已授权
resolve(true);
}
},
fail: () => {
// 获取设置失败,尝试直接调用
resolve(true);
}
});
});
};
/**
* 选择媒体文件
* @param {object} options - 选择选项
* @param {function} onSuccess - 成功回调
* @param {function} onFail - 失败回调
*/
export const chooseMedia = async (options, onSuccess, onFail) => {
// 如果需要从相册选择,先检查权限
const sourceType = options.sourceType || ['album', 'camera'];
if (sourceType.includes('album')) {
const hasPermission = await checkAlbumPermission();
if (!hasPermission) {
console.log('用户未授予相册权限');
if (onFail) {
onFail({ errMsg: '未授权相册权限' });
}
return;
}
}
uni.chooseMedia({
count: options.count || 1,
mediaType: options.mediaType || ['image'],
sizeType: options.sizeType || ['original', 'compressed'],
sourceType: sourceType,
success: function (res) {
console.log('选择媒体成功:', res);
if (onSuccess) onSuccess(res);
},
fail: function (err) {
// 用户取消选择
if (err.errMsg.includes('cancel')) {
console.log('用户取消选择');
return;
}
// 权限相关错误
if (err.errMsg.includes('permission') || err.errMsg.includes('auth') || err.errMsg.includes('拒绝')) {
console.error('相册权限被拒绝:', err);
uni.showModal({
title: '需要相册权限',
content: '请在设置中开启相册权限后重试',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting();
}
}
});
if (onFail) {
onFail(err);
}
return;
}
// 其他错误
console.error('选择媒体失败:', err);
if (onFail) {
onFail(err);
} else {
showMessage('选择图片失败,请重试');
}
}
});
};
/**
* 选择图片
* @param {function} onSuccess - 成功回调
* @param {function} onFail - 失败回调
*/
export const chooseImage = (onSuccess, onFail) => {
chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera']
}, onSuccess, onFail);
};
/**
* 拍照
* @param {function} onSuccess - 成功回调
* @param {function} onFail - 失败回调
*/
export const takePhoto = (onSuccess, onFail) => {
chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['original', 'compressed'],
sourceType: ['camera']
}, onSuccess, onFail);
};
// ==================== 录音相关工具方法 ====================
/**
* 初始化录音管理器
* @param {object} options - 录音选项
* @param {function} onStop - 录音结束回调
* @param {function} onError - 录音错误回调
* @returns {object} 录音管理器实例
*/
export const initRecorderManager = (options = {}, onStop, onError) => {
const recorderManager = wx.getRecorderManager();
// 监听录音结束事件
recorderManager.onStop((res) => {
console.log('录音成功,结果:', res);
if (onStop) onStop(res);
});
// 监听录音错误事件
recorderManager.onError((err) => {
console.error('录音失败:', err);
if (onError) {
onError(err);
} else {
showMessage("录音失败");
}
});
return recorderManager;
};
/**
* 开始录音
* @param {object} recorderManager - 录音管理器
* @param {object} options - 录音参数
*/
export const startRecord = (recorderManager, options = {}) => {
if (!recorderManager) {
console.error('录音管理器未初始化');
return;
}
const recordOptions = {
duration: 60000, // 录音的时长,单位 ms最大值 60000010 分钟)
sampleRate: 44100, // 采样率
numberOfChannels: 1, // 录音通道数
encodeBitRate: 192000, // 编码码率
format: 'aac', // 音频格式
...options
};
recorderManager.start(recordOptions);
};
/**
* 停止录音
* @param {object} recorderManager - 录音管理器
*/
export const stopRecord = (recorderManager) => {
if (!recorderManager) {
console.error('录音管理器未初始化');
return;
}
recorderManager.stop();
};
// ==================== 消息发送相关工具方法 ====================
/**
* 创建自定义消息
* @param {string} messageType - 消息类型
* @param {object} data - 消息数据
* @param {function} formatTime - 时间格式化函数
* @returns {object} 自定义消息对象
*/
export const createCustomMessage = (messageType, data, formatTime) => {
return {
messageType,
time: formatTime(Date.now()),
...data
};
};
/**
* 发送自定义消息的通用方法
* @param {object} messageData - 消息数据
* @param {object} timChatManager - IM管理器
* @param {function} validateBeforeSend - 发送前验证函数
* @param {function} onSuccess - 成功回调
*/
export const sendCustomMessage = async (messageData, timChatManager, validateBeforeSend, onSuccess) => {
if (!validateBeforeSend()) {
return;
}
const result = await timChatManager.sendCustomMessage(messageData);
if (result && result.success) {
if (onSuccess) onSuccess();
} else {
console.error('发送自定义消息失败:', result?.error);
}
};
/**
* 发送消息的通用方法
* @param {string} messageType - 消息类型
* @param {any} data - 消息数据
* @param {object} timChatManager - IM管理器
* @param {function} validateBeforeSend - 发送前验证函数
* @param {function} onSuccess - 成功回调
*/
export const sendMessage = async (messageType, data, timChatManager, validateBeforeSend, onSuccess, cloudCustomData) => {
if (!validateBeforeSend()) {
return;
}
let result;
switch (messageType) {
case 'text':
result = await timChatManager.sendTextMessage(data, cloudCustomData);
break;
case 'image':
result = await timChatManager.sendImageMessage(data, cloudCustomData);
break;
case 'voice':
result = await timChatManager.sendVoiceMessage(data.file, data.duration,cloudCustomData);
break;
default:
console.error('未知的消息类型:', messageType);
return;
}
if (result && result.success) {
if (onSuccess) onSuccess();
} else {
console.error('发送消息失败:', result?.error);
showMessage('发送失败,请重试');
}
};
// ==================== 状态检查相关工具方法 ====================
/**
* 检查IM连接状态
* @param {object} timChatManager - IM管理器
* @param {function} onError - 错误回调
* @returns {boolean} 连接状态
*/
export const checkIMConnectionStatus = (timChatManager, onError) => {
if (!timChatManager.tim || !timChatManager.isLoggedIn) {
const errorMsg = "IM连接异常请重新进入";
if (onError) {
onError(errorMsg);
} else {
showMessage(errorMsg);
}
return false;
}
return true;
};
/**
* 检查是否显示时间分割线
* @param {object} message - 当前消息
* @param {number} index - 消息索引
* @param {Array} messageList - 消息列表
* @returns {boolean} 是否显示时间分割线
*/
export const shouldShowTime = (message, index, messageList) => {
if (index === 0) return true;
const prevMessage = messageList[index - 1];
// 使用工具函数验证时间戳
if (!validateTimestamp(message.lastTime) || !validateTimestamp(prevMessage.lastTime)) {
return false;
}
const timeDiff = message.lastTime - prevMessage.lastTime;
return timeDiff > 5 * 60 * 1000; // 5分钟显示一次时间
};
/**
* 预览图片
* @param {string} url - 图片URL
*/
export const previewImage = (url) => {
uni.previewImage({
urls: [url],
current: url,
});
};
// ==================== 录音相关工具方法 ====================
/**
* 检查录音时长并处理
* @param {object} res - 录音结果
* @param {Function} onTimeTooShort - 时间太短的回调
* @returns {boolean} 录音时长是否有效
*/
export const checkRecordingDuration = (res, onTimeTooShort = null) => {
const duration = Math.floor(res.duration / 1000);
if (duration < 1) {
console.log('录音时间太短,取消发送');
if (onTimeTooShort) {
onTimeTooShort();
} else {
showMessage('说话时间太短');
}
return false;
}
return true;
};
// ==================== 防抖和节流工具 ====================
/**
* 防抖函数
* @param {Function} func - 要防抖的函数
* @param {number} wait - 等待时间毫秒
* @returns {Function} 防抖后的函数
*/
export const debounce = (func, wait = 300) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
/**
* 节流函数
* @param {Function} func - 要节流的函数
* @param {number} limit - 限制时间毫秒
* @returns {Function} 节流后的函数
*/
export const throttle = (func, limit = 300) => {
let inThrottle;
return function executedFunction(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
};
// ==================== 自定义消息解析相关工具方法 ====================
// 自定义消息解析缓存
const customMessageCache = new Map();
/**
* 解析自定义消息带缓存
* @param {object} message - 消息对象
* @param {function} formatTime - 时间格式化函数
* @returns {object} 解析后的消息对象
*/
export const parseCustomMessage = (message, formatTime) => {
// 使用消息ID作为缓存键
const cacheKey = message.ID;
// 检查缓存
if (customMessageCache.has(cacheKey)) {
return customMessageCache.get(cacheKey);
}
try {
const customData = JSON.parse(message.payload.data);
const parsedMessage = {
messageType: customData.messageType,
content: customData.content,
symptomContent: customData.symptomContent,
hasVisitedHospital: customData.hasVisitedHospital,
selectedDiseases: customData.selectedDiseases,
images: customData.images,
medicines: customData.medicines,
diagnosis: customData.diagnosis,
prescriptionType: customData.prescriptionType,
prescriptionDesc: customData.prescriptionDesc,
tcmPrescription: customData.tcmPrescription, // 新增中药处方字段
patientName: customData.patientName,
gender: customData.gender,
age: customData.age,
surveyTitle: customData.surveyTitle,
surveyDescription: customData.surveyDescription,
surveyName: customData.surveyName,
estimatedTime: customData.estimatedTime,
reward: customData.reward,
note: customData.note,
orderId: customData.orderId, // 新增订单ID字段
timestamp: customData.timestamp, // 新增时间戳字段
conversationID: message.conversationID, // 保留conversationID
time: formatTime(message.lastTime),
};
// 缓存解析结果
customMessageCache.set(cacheKey, parsedMessage);
return parsedMessage;
} catch (error) {
const fallbackMessage = {
messageType: "unknown",
content: "未知消息类型",
};
// 缓存错误结果,避免重复解析
customMessageCache.set(cacheKey, fallbackMessage);
return fallbackMessage;
}
};
/**
* 清理消息缓存
*/
export const clearMessageCache = () => {
customMessageCache.clear();
};
/**
* 获取解析后的自定义消息带缓存
* @param {object} message - 消息对象
* @param {function} formatTime - 时间格式化函数
* @returns {object} 解析后的消息对象
*/
export const getParsedCustomMessage = (message, formatTime) => {
return parseCustomMessage(message, formatTime);
};
/**
* 处理查看详情
* @param {object} message - 解析后的消息对象
* @param {object} patientInfo - 患者信息
*/
export const handleViewDetail = (message, patientInfo) => {
if (message.messageType === "symptom") {
uni.showModal({
title: "完整病情描述",
content: message.symptomContent,
showCancel: false,
confirmText: "知道了",
});
} else if (message.messageType === "prescription") {
// 处理处方单详情查看
let content = `患者:${patientInfo.name}\n诊断:${message.diagnosis || '无'}\n\n`;
if (message.prescriptionType === '中药处方' && message.tcmPrescription) {
content += `处方类型:中药处方\n处方详情:${message.tcmPrescription.description}\n`;
if (message.tcmPrescription.usage) {
content += `用法用量:${message.tcmPrescription.usage}\n`;
}
} else if (message.prescriptionType === '西药处方' && message.medicines) {
content += `处方类型:西药处方\n药品清单:\n`;
const medicineDetails = message.medicines
.map((med) => `${med.name} ${med.spec} ×${med.count}`)
.join("\n");
content += medicineDetails + "\n";
// 添加用法用量
const usageDetails = message.medicines
.filter(med => med.usage)
.map(med => `${med.name}${med.usage}`)
.join("\n");
if (usageDetails) {
content += `\n用法用量:\n${usageDetails}\n`;
}
}
content += `\n开方时间:${message.time}`;
uni.showModal({
title: "处方详情",
content: content,
showCancel: false,
confirmText: "知道了",
});
} else if (message.messageType === "refill") {
// 处理续方申请详情查看
let content = `患者:${message.patientName} ${message.gender} ${message.age}\n诊断:${message.diagnosis}\n\n`;
if (message.prescriptionType === "中药处方") {
content += `处方类型:${message.prescriptionType}\n处方详情:${message.prescriptionDesc}`;
} else {
const medicineDetails = message.medicines
.map((med) => `${med.name} ${med.spec} ${med.count}\n${med.usage}`)
.join("\n\n");
content += `药品清单:\n${medicineDetails}`;
}
uni.showModal({
title: "续方申请详情",
content: content,
showCancel: false,
confirmText: "知道了",
});
} else if (message.messageType === "survey") {
// 处理问卷调查详情查看或跳转
uni.showModal({
title: "问卷调查",
content: `${message.surveyTitle}\n\n${message.surveyDescription
}\n\n问卷名称${message.surveyName}\n预计用时${message.estimatedTime}${message.reward ? "\n完成奖励" + message.reward : ""
}${message.note ? "\n\n说明" + message.note : ""}`,
confirmText: "去填写",
cancelText: "稍后再说",
success: (res) => {
if (res.confirm) {
// 这里可以跳转到问卷页面
uni.showToast({
title: "正在跳转到问卷页面",
icon: "none",
});
}
},
});
}
};

View File

@ -1,21 +1,20 @@
const env = __VITE_ENV__;
export async function uploadFile(tempFilePath, businessType, accessLevel = 'public') {
export async function uploadFile(tempFilePath) {
try {
const res = await new Promise((resolve, reject) => {
uni.uploadFile({
url: `${env.MP_API_BASE_URL}/upload`,
filePath: tempFilePath,
name: 'file',
formData: { businessType, accessLevel },
success: (resp) => resolve(resp),
fail: (err) => reject(err),
});
});
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
if (data && data.success) {
return data.data;
if (data && data.success && data.filePath) {
return `${env.MP_API_BASE_URL}${data.filePath}`;
}
} catch (e) {
console.log('upload file error:', e);
@ -32,12 +31,7 @@ export async function chooseAndUploadImage(options = {}) {
const {
count = 1,
sizeType = ['compressed'],
sourceType = ['album', 'camera'],
businessType = 'other',
accessLevel = 'public',
loadingTitle = '上传中...',
successToast = '上传成功',
failToast = '上传失败',
sourceType = ['album', 'camera']
} = options;
const imageResult = await new Promise((resolve) => {
@ -54,20 +48,13 @@ export async function chooseAndUploadImage(options = {}) {
if (!tempFilePath) {
return null;
}
uni.showLoading({ title: loadingTitle });
try {
const uploadRes = await uploadFile(tempFilePath, businessType, accessLevel);
uni.hideLoading();
const uploadRes = await uploadFile(tempFilePath);
if (uploadRes) {
uni.showToast({ title: successToast, icon: 'success' });
return uploadRes;
}
uni.showToast({ title: failToast, icon: 'none' });
return null;
} catch (e) {
uni.hideLoading();
uni.showToast({ title: failToast, icon: 'none' });
return null;
}
}

View File

@ -1,22 +0,0 @@
import { getOrderInfo } from "@/api/consult-order.js";
// 聊天页依赖的最小订单状态枚举(与后端 consult-order 一致)
export const orderStatus = {
INIT: "INIT",
PAID: "PAID",
CONSULTING: "CONSULTING",
COMPLETED: "COMPLETED",
CANCELLED: "CANCELLED",
};
/**
* `pages/message/hooks/chat-order-hook.js` 使用
* 根据 orderId 获取订单信息
*/
export async function getChatOrder(orderId) {
const res = await getOrderInfo({ orderId });
if (res && res.success) return res.data;
return Promise.reject(res?.message || "获取订单失败");
}