Przeglądaj źródła

修改流程

master
leiyun 2 tygodni temu
rodzic
commit
64ab8ee392
3 zmienionych plików z 153 dodań i 28 usunięć
  1. +20
    -3
      pages/message/detail.vue
  2. +7
    -0
      pages/message/message.vue
  3. +126
    -25
      pages/sample-info/sample-info.vue

+ 20
- 3
pages/message/detail.vue Wyświetl plik

@@ -20,6 +20,8 @@
<text class="body-text" v-if="msg.type === 1">{{ auditApprovedText }}</text>
<text class="body-text" v-else-if="msg.type === 2">您于 {{ formatDate(msg.create_time) }} 提交的个人资料经审核未通过。</text>
<text class="body-text" v-else-if="msg.type === 3">您的样品已经寄回,请注意查收。</text>
<text class="body-text" v-else-if="msg.type === 4 && !msg.reason">您的送检信息修改申请已审核通过,请前往送检信息页面重新提交。</text>
<text class="body-text" v-else-if="msg.type === 4">您的送检信息修改申请未通过。</text>
<text class="body-text" v-if="msg.type === 2">请根据以下原因修改后重新提交。</text>
</view>

@@ -41,8 +43,8 @@
</view>

<!-- 驳回原因 -->
<view v-if="msg.type === 2 && msg.reason" class="reason-box">
<text class="reason-label">驳回原因:</text>
<view v-if="(msg.type === 2 || msg.type === 4) && msg.reason" class="reason-box">
<text class="reason-label">{{ msg.type === 4 ? '审核原因:' : '驳回原因:' }}</text>
<text class="reason-text">{{ msg.reason }}</text>
</view>

@@ -61,6 +63,9 @@
<view v-if="msg.type === 3" class="btn-area">
<u-button text="复制单号" @click="copyReturnTrackingNo" color="#0E63E3" size="large" />
</view>
<view v-if="msg.type === 4" class="btn-area">
<u-button text="查看送检信息" @click="goSampleInfo" color="#0E63E3" size="large" />
</view>
</view>
</view>
</template>
@@ -79,19 +84,27 @@ const tagText = computed(() => {
if (!msg.value) return ''
if (msg.value.type === 1) return '已通过'
if (msg.value.type === 3) return '已寄回'
if (msg.value.type === 4) return msg.value.reason ? '未通过' : '已通过'
return '已驳回'
})
const headerTitle = computed(() => (msg.value && msg.value.type === 3) ? '回寄信息' : '审核通知')
const headerTitle = computed(() => {
if (!msg.value) return ''
if (msg.value.type === 3) return '回寄信息'
if (msg.value.type === 4) return '送检信息通知'
return '审核通知'
})
const tagClass = computed(() => {
if (!msg.value) return ''
if (msg.value.type === 1) return 'tag-success'
if (msg.value.type === 3) return 'tag-return'
if (msg.value.type === 4) return msg.value.reason ? 'tag-fail' : 'tag-success'
return 'tag-fail'
})
const mainTitle = computed(() => {
if (!msg.value) return ''
if (msg.value.type === 1) return '资料已审核通过'
if (msg.value.type === 3) return '样品已经寄回'
if (msg.value.type === 4) return msg.value.reason ? '送检修改申请未通过' : '送检修改申请已通过'
return '您提交的资料未通过审核'
})

@@ -115,6 +128,10 @@ const goMyInfo = () => {
uni.navigateTo({ url: '/pages/myinfo/myinfo' })
}

const goSampleInfo = () => {
uni.navigateTo({ url: '/pages/sample-info/sample-info' })
}

const copySampleReceiverInfo = () => {
const info = sampleReceiverInfo.value
const text = [


+ 7
- 0
pages/message/message.vue Wyświetl plik

@@ -76,18 +76,21 @@ const loadMessages = async () => {
const getMsgClass = (item) => {
if (item.type === 1) return 'success'
if (item.type === 3) return 'return'
if (item.type === 4) return item.reason ? 'fail' : 'sample'
return 'fail'
}

const getMsgIcon = (item) => {
if (item.type === 1) return 'checkmark-circle-fill'
if (item.type === 3) return 'order'
if (item.type === 4) return item.reason ? 'close-circle-fill' : 'checkmark-circle-fill'
return 'close-circle-fill'
}

const getMsgColor = (item) => {
if (item.type === 1) return '#52c41a'
if (item.type === 3) return '#0e63e3'
if (item.type === 4) return item.reason ? '#f5222d' : '#52c41a'
return '#f5222d'
}

@@ -137,6 +140,10 @@ const goDetail = (id) => {
&.return {
background: #f0f5ff;
}

&.sample {
background: #f6ffed;
}
}

.msg-body {


+ 126
- 25
pages/sample-info/sample-info.vue Wyświetl plik

@@ -61,6 +61,10 @@
<text v-if="returnTime" class="return-time">回寄时间:{{ returnTime }}</text>
<text class="copy-return-no" @tap="copyReturnTrackingNo">复制单号</text>
</view>
<view v-if="sampleEditRejectReason" class="reject-info">
<text class="reject-title">修改申请未通过</text>
<text class="reject-text">原因:{{ sampleEditRejectReason }}</text>
</view>
<view class="form-group">
<text class="form-label">送检样本类型{{ sampleRequired ? '' : '(选填)' }}</text>
<u-checkbox-group v-model="form.sample_types" placement="row" :wrap="true" @change="onSampleTypesChange">
@@ -131,9 +135,24 @@
<text class="agree-text">请阅读并同意</text>
<text class="agree-link" @tap.stop="openNotice">《患者告知书》</text>
</view>
<u-button :text="submitButtonText" :loading="submitting" @click="handleSubmit" color="#0E63E3" size="large" />
<u-button v-if="canApplySampleEdit" text="申请修改送检信息" :loading="applyEditSubmitting" @click="showApplyEditDialog"
color="#0E63E3" plain size="large" />
<u-button v-if="!canApplySampleEdit" :text="submitButtonText" :loading="submitting" @click="handleSubmit" color="#0E63E3" size="large" />
</view>

<!-- 修改申请弹窗 -->
<u-popup :show="applyEditVisible" mode="center" round="12" :safeAreaInsetBottom="false" @close="applyEditVisible = false">
<view class="apply-popup">
<view class="apply-title">申请修改送检信息</view>
<view class="apply-desc">请填写需要修改送检信息的原因,平台审核通过后即可重新编辑。</view>
<textarea v-model="applyEditReason" class="apply-textarea" maxlength="500" placeholder="请输入申请原因" />
<view class="apply-actions">
<u-button text="取消" @click="applyEditVisible = false" plain color="#909399" />
<u-button text="提交申请" :loading="applyEditSubmitting" @click="submitApplyEdit" color="#0E63E3" />
</view>
</view>
</u-popup>

<!-- 地区选择器 -->
<u-picker v-if="regionColumns[0].length" :show="showRegionPicker" :columns="regionColumns" @confirm="onRegionConfirm"
@cancel="showRegionPicker = false" @change="onRegionChange" :defaultIndex="regionDefaultIndex" />
@@ -163,19 +182,33 @@ const submitting = ref(false)
const agreed = ref(false)
const sampleTypeList = ref([])
const sampleRequired = ref(false)
const sampleReceiverInfo = ref({ address: '', receiver: '', phone: '' })
const sampleReceiverInfo = ref({ address: '', receiver: '', phone: '', contact_phone: '' })
const sampleInfoStatus = ref(0)
const returnTrackingNo = ref('')
const returnTime = ref('')
const sampleEditReason = ref('')
const sampleEditRejectReason = ref('')
const sampleEditApplyTime = ref('')
const applyEditVisible = ref(false)
const applyEditReason = ref('')
const applyEditSubmitting = ref(false)
const sampleEditAuditTmplId = ref('')
const isSampleReturned = computed(() => sampleInfoStatus.value === 2)
const isSampleInfoLocked = computed(() => sampleInfoStatus.value === 1 || sampleInfoStatus.value === 2)
const isSampleEditApplying = computed(() => sampleInfoStatus.value === 3)
const isSampleInfoLocked = computed(() => sampleInfoStatus.value === 1 || sampleInfoStatus.value === 2 || sampleInfoStatus.value === 3)
const canApplySampleEdit = computed(() => sampleInfoStatus.value === 1)
const sampleInfoStatusText = computed(() => {
if (sampleInfoStatus.value === 3) return '申请审核中'
if (sampleInfoStatus.value === 2) return '已寄回'
if (sampleInfoStatus.value === 1) return '已生效'
return ''
})
const sampleInfoStatusClass = computed(() => sampleInfoStatus.value === 2 ? 'returned' : 'effective')
const sampleInfoStatusClass = computed(() => {
if (sampleInfoStatus.value === 3) return 'applying'
return sampleInfoStatus.value === 2 ? 'returned' : 'effective'
})
const submitButtonText = computed(() => {
if (sampleInfoStatus.value === 3) return '修改申请审核中'
if (sampleInfoStatus.value === 2) return '样品已寄回'
if (sampleInfoStatus.value === 1) return '送检信息已生效'
return '提交送检信息'
@@ -280,8 +313,34 @@ onLoad(async () => {
await loadRegions()
await loadPatientInfo()
await loadSampleTypes()
await loadSubscribeConfig()
})

const loadSubscribeConfig = async () => {
try {
const res = await get('/api/mp/subscribeConfig')
if (res.data && res.data.sample_edit_audit) {
sampleEditAuditTmplId.value = res.data.sample_edit_audit
}
} catch (e) {}
}

const requestSampleEditSubscribe = () => {
return new Promise((resolve) => {
if (!sampleEditAuditTmplId.value) return resolve(false)
// #ifdef MP-WEIXIN
wx.requestSubscribeMessage({
tmplIds: [sampleEditAuditTmplId.value],
success: () => resolve(true),
fail: () => resolve(false)
})
// #endif
// #ifndef MP-WEIXIN
resolve(false)
// #endif
})
}

const loadPatientInfo = async () => {
try {
const res = await get('/api/mp/sampleInfo')
@@ -302,8 +361,11 @@ const loadPatientInfo = async () => {
sampleInfoStatus.value = Number(res.data.sample_info_status) || 0
returnTrackingNo.value = res.data.return_tracking_no || ''
returnTime.value = res.data.return_time || ''
sampleReceiverInfo.value = res.data.sample_receiver_info || { address: '', receiver: '', phone: '' }
if (sampleInfoStatus.value === 1) {
sampleEditReason.value = res.data.sample_edit_reason || ''
sampleEditRejectReason.value = res.data.sample_edit_reject_reason || ''
sampleEditApplyTime.value = res.data.sample_edit_apply_time || ''
sampleReceiverInfo.value = res.data.sample_receiver_info || { address: '', receiver: '', phone: '', contact_phone: '' }
if ((sampleInfoStatus.value === 1 && !sampleEditRejectReason.value) || sampleInfoStatus.value === 3) {
setTimeout(() => showLockedTip(), 300)
}
}
@@ -406,9 +468,18 @@ const contactPlatform = () => {
}

const showLockedTip = () => {
if (isSampleEditApplying.value) {
uni.showModal({
title: '温馨提示',
content: '送检信息修改申请正在审核中,请等待平台处理。',
showCancel: false,
confirmText: '知道了'
})
return
}
if (isSampleReturned.value) {
uni.showModal({
title: '提示',
title: '温馨提示',
content: `样品已经寄回,回寄物流单号:${returnTrackingNo.value || '—'}。`,
cancelText: '知道了',
confirmText: '复制单号',
@@ -419,26 +490,46 @@ const showLockedTip = () => {
return
}
const phone = getContactPhone()
if (!phone) {
uni.showModal({
title: '提示',
content: '送检信息已生效,如需修改请联系平台重置修改权限。',
showCancel: false,
confirmText: '知道了'
})
return
}
let content = '送检信息已生效,如需修改请点击页面的【申请修改送检信息】按钮申请,通过后可重新提交送检信息。'
if (phone) content += `详情咨询${phone}。`
uni.showModal({
title: '提示',
content: `送检信息已生效,如需修改请联系平台【${phone}】重置修改权限。`,
cancelText: '知道了',
confirmText: '联系平台',
success: (res) => {
if (res.confirm) contactPlatform()
}
title: '温馨提示',
content,
showCancel: false,
confirmText: '知道了'
})
}

const showApplyEditDialog = () => {
if (!canApplySampleEdit.value) return showLockedTip()
applyEditReason.value = ''
applyEditVisible.value = true
}

const submitApplyEdit = async () => {
if (!applyEditReason.value.trim()) {
return uni.showToast({ title: '请填写申请原因', icon: 'none' })
}
applyEditSubmitting.value = true
try {
await requestSampleEditSubscribe()
const params = { reason: applyEditReason.value.trim() }
// #ifdef MP-WEIXIN
params.mp_env_version = uni.getAccountInfoSync().miniProgram.envVersion || 'release'
// #endif
await post('/api/mp/applySampleInfoEdit', params)
sampleInfoStatus.value = 3
sampleEditReason.value = applyEditReason.value.trim()
sampleEditApplyTime.value = ''
applyEditVisible.value = false
uni.showToast({ title: '申请已提交', icon: 'success' })
} catch (e) {
if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
} finally {
applyEditSubmitting.value = false
}
}

const copyReturnTrackingNo = () => {
if (!returnTrackingNo.value) return uni.showToast({ title: '暂无单号', icon: 'none' })
uni.setClipboardData({
@@ -494,10 +585,12 @@ const handleSubmit = async () => {
uni.showToast({ title: '提交成功', icon: 'success' })
setTimeout(() => uni.navigateBack(), 1500)
} catch (e) {
if (e && e.data && [1, 2].includes(Number(e.data.sample_info_status))) {
if (e && e.data && [1, 2, 3].includes(Number(e.data.sample_info_status))) {
sampleInfoStatus.value = Number(e.data.sample_info_status)
returnTrackingNo.value = e.data.return_tracking_no || returnTrackingNo.value
returnTime.value = e.data.return_time || returnTime.value
sampleEditReason.value = e.data.sample_edit_reason || sampleEditReason.value
sampleEditApplyTime.value = e.data.sample_edit_apply_time || sampleEditApplyTime.value
if (e.data.sample_receiver_info) sampleReceiverInfo.value = e.data.sample_receiver_info
return showLockedTip()
}
@@ -512,7 +605,7 @@ const handleSubmit = async () => {
.page { min-height: 100vh; background: #f4f4f5; padding: 24rpx; padding-bottom: 260rpx; }
.section { background: #fff; border-radius: 10rpx; padding: 32rpx; margin-bottom: 24rpx; border: 1rpx solid #ebeef5; }
.section-title { display: flex; align-items: center; gap: 12rpx; font-size: 32rpx; font-weight: 600; color: #333; margin-bottom: 28rpx; }
.status-pill { margin-left: auto; font-size: 22rpx; font-weight: 500; padding: 4rpx 14rpx; border-radius: 6rpx; &.effective { color: #52c41a; background: #f6ffed; border: 1rpx solid #b7eb8f; } &.returned { color: #0e63e3; background: #f0f5ff; border: 1rpx solid #adc6ff; } }
.status-pill { margin-left: auto; font-size: 22rpx; font-weight: 500; padding: 4rpx 14rpx; border-radius: 6rpx; &.effective { color: #52c41a; background: #f6ffed; border: 1rpx solid #b7eb8f; } &.returned { color: #0e63e3; background: #f0f5ff; border: 1rpx solid #adc6ff; } &.applying { color: #d46b08; background: #fff7e6; border: 1rpx solid #ffd591; } }
.info-compact { padding-bottom: 8rpx; }
.info-compact-row { display: flex; gap: 32rpx; margin-bottom: 12rpx; &:last-child { margin-bottom: 0; } }
.info-compact-item { font-size: 26rpx; color: #666; }
@@ -521,6 +614,9 @@ const handleSubmit = async () => {
.return-text { display: block; font-size: 28rpx; color: #0e63e3; font-weight: 600; line-height: 1.5; }
.return-time { display: block; font-size: 24rpx; color: #909399; margin-top: 8rpx; }
.copy-return-no { display: block; text-align: right; font-size: 26rpx; color: #0e63e3; margin-top: 12rpx; }
.reject-info { background: #fff2f0; border-left: 6rpx solid #ff4d4f; border-radius: 8rpx; padding: 24rpx; margin-bottom: 20rpx; }
.reject-title { display: block; font-size: 28rpx; font-weight: 600; color: #a8071a; margin-bottom: 10rpx; }
.reject-text { display: block; font-size: 26rpx; color: #606266; line-height: 1.6; }
.receiver-info { padding-bottom: 8rpx; }
.receiver-row { display: flex; gap: 20rpx; margin-bottom: 14rpx; &:last-child { margin-bottom: 0; } }
.receiver-label { width: 120rpx; flex-shrink: 0; font-size: 26rpx; color: #909399; }
@@ -543,4 +639,9 @@ const handleSubmit = async () => {
.agree-row { display: flex; align-items: center; margin-bottom: 16rpx; padding-left: 4rpx; }
.agree-text { font-size: 24rpx; color: #666; margin-left: 8rpx; }
.agree-link { font-size: 24rpx; color: #0e63e3; }
.apply-popup { width: 620rpx; background: #fff; border-radius: 16rpx; padding: 32rpx; }
.apply-title { font-size: 32rpx; font-weight: 600; color: #303133; margin-bottom: 12rpx; }
.apply-desc { font-size: 26rpx; color: #606266; line-height: 1.6; margin-bottom: 20rpx; }
.apply-textarea { width: 100%; height: 180rpx; box-sizing: border-box; border: 1rpx solid #dcdfe6; border-radius: 8rpx; padding: 20rpx; font-size: 28rpx; color: #303133; background: #fff; }
.apply-actions { display: flex; gap: 20rpx; margin-top: 24rpx; }
</style>

Ładowanie…
Anuluj
Zapisz