feat: 会员管理、在线复诊优化、新增配送仓库
This commit is contained in:
@@ -38,6 +38,23 @@ function basenameFromPath(filePath) {
|
|||||||
return parts[parts.length - 1] || '';
|
return parts[parts.length - 1] || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件路径/文件名推断 Content-Type
|
||||||
|
* 小程序本地临时路径通常没有 MIME,必须按扩展名推断,否则 OSS 会落成 octet-stream
|
||||||
|
* 与后端 Policy 的 starts-with $Content-Type 配套:表单必须带该字段
|
||||||
|
*/
|
||||||
|
function resolveUploadContentType(filePath, fileName) {
|
||||||
|
const name = String(fileName || filePath || '').toLowerCase();
|
||||||
|
if (name.endsWith('.pdf')) return 'application/pdf';
|
||||||
|
if (name.endsWith('.png')) return 'image/png';
|
||||||
|
if (name.endsWith('.jpg') || name.endsWith('.jpeg')) return 'image/jpeg';
|
||||||
|
if (name.endsWith('.gif')) return 'image/gif';
|
||||||
|
if (name.endsWith('.webp')) return 'image/webp';
|
||||||
|
if (name.endsWith('.bmp')) return 'image/bmp';
|
||||||
|
// 医生端直传以图片为主,缺省按 jpeg,避免浏览器当附件下载
|
||||||
|
return 'image/jpeg';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Promise<{ accessKeyId, policy, signature, host, bucket, key, expire }>}
|
* @returns {Promise<{ accessKeyId, policy, signature, host, bucket, key, expire }>}
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +82,8 @@ export async function uploadToOss(options) {
|
|||||||
const signature = await getOssSignatureFromBackend();
|
const signature = await getOssSignatureFromBackend();
|
||||||
const objectName = generateObjectName(filePath);
|
const objectName = generateObjectName(filePath);
|
||||||
|
|
||||||
|
// Content-Type / Content-Disposition 必须与后端 PostPolicy 条件一致,否则 OSS 报 AccessDenied
|
||||||
|
// (starts-with 空前缀要求字段存在;inline 便于浏览器预览而非附件下载)
|
||||||
const formData = {
|
const formData = {
|
||||||
key: objectName,
|
key: objectName,
|
||||||
policy: signature.policy,
|
policy: signature.policy,
|
||||||
@@ -72,6 +91,8 @@ export async function uploadToOss(options) {
|
|||||||
signature: signature.signature,
|
signature: signature.signature,
|
||||||
success_action_status: '200',
|
success_action_status: '200',
|
||||||
'x-oss-object-acl': 'public-read',
|
'x-oss-object-acl': 'public-read',
|
||||||
|
'Content-Type': resolveUploadContentType(filePath, fileName),
|
||||||
|
'Content-Disposition': 'inline',
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- 药师「我的」主页:审方 Tab 壳下的个人中心,对齐 my-pharmacist 能力 -->
|
||||||
<view class="safe-area-inset-bottom">
|
<view class="safe-area-inset-bottom">
|
||||||
<u-navbar title="我的" :is-back="false" title-size="34" title-color="#fff" :background="{
|
<u-navbar title="我的" :is-back="false" title-size="34" title-color="#fff" :background="{
|
||||||
background:'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)'}">
|
background:'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)'}">
|
||||||
@@ -17,12 +18,15 @@
|
|||||||
职业机构
|
职业机构
|
||||||
</view>
|
</view>
|
||||||
<view class="store">
|
<view class="store">
|
||||||
{{list.store.name || ' '}}
|
{{(list.store && list.store.name) || ' '}}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 菜单卡:绑定微信 / 个人资料 / 设置 / 切换账号(对齐医生与 my-pharmacist) -->
|
||||||
<view class="card">
|
<view class="card">
|
||||||
|
<view class="wx-bind-wrap">
|
||||||
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
|
</view>
|
||||||
<view class="setup" @click="toInfo">
|
<view class="setup" @click="toInfo">
|
||||||
个人资料
|
个人资料
|
||||||
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
|
||||||
@@ -31,8 +35,17 @@
|
|||||||
设置
|
设置
|
||||||
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="account-switch-wrap">
|
||||||
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 独立绿色退出按钮:设置页已上提,必须挂在本页否则药师无退出入口 -->
|
||||||
|
<view class="logout-wrap">
|
||||||
|
<u-button :throttle-time="0" shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
@click="logout">退出登录</u-button>
|
||||||
|
</view>
|
||||||
|
|
||||||
<u-tabbar v-model="current" :list="listed" active-color="#6ACDBB" @change="changed"></u-tabbar>
|
<u-tabbar v-model="current" :list="listed" active-color="#6ACDBB" @change="changed"></u-tabbar>
|
||||||
<d-loading-page :loading="loading"></d-loading-page>
|
<d-loading-page :loading="loading"></d-loading-page>
|
||||||
@@ -40,10 +53,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
/**
|
||||||
my
|
* 药师端「我的」页(审方 Tab 实际入口)
|
||||||
} from "@/api/all.js";
|
* 补齐绑定微信 / 切换账号 / 退出登录,与 pages/my/my-pharmacist.vue 能力对齐
|
||||||
|
*/
|
||||||
|
import { my, logout } from '@/api/all.js'
|
||||||
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
export default {
|
export default {
|
||||||
|
components: { WxBindEntry, AccountSwitchEntry },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
keyword: "",
|
keyword: "",
|
||||||
@@ -54,36 +72,28 @@
|
|||||||
statusBarHeight: this.$statusBarHeight,
|
statusBarHeight: this.$statusBarHeight,
|
||||||
loading: true,
|
loading: true,
|
||||||
userInfo: uni.getStorageSync("userInfo") || {},
|
userInfo: uni.getStorageSync("userInfo") || {},
|
||||||
list: [],
|
list: {},
|
||||||
waitList: [],
|
waitList: [],
|
||||||
is_req: false,
|
is_req: false,
|
||||||
current: 1,
|
current: 1,
|
||||||
listed: '',
|
listed: '',
|
||||||
};
|
};
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
|
// 从设置/资料返回或切换账号后,刷新微信绑定展示状态
|
||||||
|
this.refreshWxBind()
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.listed = [{
|
this.listed = [{
|
||||||
iconPath: require("../../static/image/w-sf.png"),
|
iconPath: require("../../static/image/w-sf.png"),
|
||||||
selectedIconPath: require("../../static/image/sf.png"),
|
selectedIconPath: require("../../static/image/sf.png"),
|
||||||
text: '审方',
|
text: '审方',
|
||||||
// pagePath: '/pages/pharmacist/index',
|
|
||||||
current: 0
|
current: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
iconPath: require("../../static/image/w-wd.png"),
|
iconPath: require("../../static/image/w-wd.png"),
|
||||||
selectedIconPath: require("../../static/image/wd.png"),
|
selectedIconPath: require("../../static/image/wd.png"),
|
||||||
text: '我的',
|
text: '我的',
|
||||||
// pagePath: '/pages/pharmacist/pharmacist-my',
|
|
||||||
current: 1
|
current: 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -91,23 +101,20 @@
|
|||||||
methods: {
|
methods: {
|
||||||
changed(index) {
|
changed(index) {
|
||||||
this.current = index;
|
this.current = index;
|
||||||
// console.log(this.current);
|
|
||||||
|
|
||||||
if (this.current == 0) {
|
if (this.current == 0) {
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: '/pages/pharmacist/index',
|
url: '/pages/pharmacist/index',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** 拉取药师资料(头像、姓名、执业机构) */
|
||||||
async getList() {
|
async getList() {
|
||||||
let res = {};
|
let res = {};
|
||||||
res = await my({
|
res = await my({
|
||||||
store_id: uni.getStorageSync('store_id') || '11001',
|
store_id: uni.getStorageSync('store_id') || '11001',
|
||||||
})
|
})
|
||||||
this.list = [];
|
this.list = {};
|
||||||
if (res.errcode == 0) {
|
if (res.errcode == 0) {
|
||||||
// console.log(res, 'wd');
|
|
||||||
this.list = res.data;
|
this.list = res.data;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@@ -117,16 +124,34 @@
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** 刷新绑定微信入口状态(组件内部按手机号/账号判断) */
|
||||||
|
refreshWxBind() {
|
||||||
|
if (this.$refs.wxBindEntry) {
|
||||||
|
this.$refs.wxBindEntry.refresh()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 退出登录:清本地会话并回登录页 */
|
||||||
|
logout() {
|
||||||
|
logout({ store_id: uni.getStorageSync('store_id') || 11001 }).then((res) => {
|
||||||
|
if (res.errcode != -1) {
|
||||||
|
uni.clearStorage()
|
||||||
|
uni.clearStorageSync()
|
||||||
|
this.$go('/pages/login/index', 2)
|
||||||
|
} else {
|
||||||
|
this.$toast(res.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
toInfo() {
|
toInfo() {
|
||||||
this.$go('../../subPackages/sub_pharmacist/pharmacist_info')
|
this.$go('../../subPackages/sub_pharmacist/pharmacist_info')
|
||||||
},
|
},
|
||||||
|
|
||||||
toSetup() {
|
toSetup() {
|
||||||
this.$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')
|
this.$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getList()
|
this.getList()
|
||||||
|
this.refreshWxBind()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -136,19 +161,6 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.bg {
|
|
||||||
position: fixed;
|
|
||||||
width: 750rpx;
|
|
||||||
height: 400rpx;
|
|
||||||
// background: linear-gradient(
|
|
||||||
// 180deg,
|
|
||||||
// #00BFA6 0%,
|
|
||||||
// #6ACDBB 0%,
|
|
||||||
// rgba(41, 121, 255, 0) 100%
|
|
||||||
// );
|
|
||||||
z-index: 981;
|
|
||||||
}
|
|
||||||
|
|
||||||
.head {
|
.head {
|
||||||
font-family: PingFang SC-Bold, PingFang SC;
|
font-family: PingFang SC-Bold, PingFang SC;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -188,7 +200,6 @@
|
|||||||
|
|
||||||
.card {
|
.card {
|
||||||
width: 710rpx;
|
width: 710rpx;
|
||||||
height: 176rpx;
|
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
margin: 30rpx auto;
|
margin: 30rpx auto;
|
||||||
font-family: PingFang SC-Bold, PingFang SC;
|
font-family: PingFang SC-Bold, PingFang SC;
|
||||||
@@ -202,6 +213,21 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 30rpx;
|
padding: 0 30rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wx-bind-wrap {
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-switch-wrap {
|
||||||
|
padding: 8rpx 30rpx 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-wrap {
|
||||||
|
width: 710rpx;
|
||||||
|
margin: 0 auto 32rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -131,6 +131,18 @@
|
|||||||
<text class="block-label">主诉</text>
|
<text class="block-label">主诉</text>
|
||||||
<text class="block-content">{{ parsedObj.chief_complaint }}</text>
|
<text class="block-content">{{ parsedObj.chief_complaint }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="parsedObj.illnessInfo" class="text-block">
|
||||||
|
<text class="block-label">适用症/症状</text>
|
||||||
|
<text class="block-content">{{ parsedObj.illnessInfo }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="parsedObj.illnessInfo" class="info-row">
|
||||||
|
<text class="label">是否就诊过</text>
|
||||||
|
<text class="value">{{ parsedObj.has_visited === 1 || parsedObj.has_visited === '1' ? '是' : '否' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="parsedObj.illnessInfo" class="info-row">
|
||||||
|
<text class="label">是否使用过药品</text>
|
||||||
|
<text class="value">{{ parsedObj.has_used_drug === 1 || parsedObj.has_used_drug === '1' ? '是' : '否' }}</text>
|
||||||
|
</view>
|
||||||
<view v-if="registerCardDrugRows.length" class="drug-list-block">
|
<view v-if="registerCardDrugRows.length" class="drug-list-block">
|
||||||
<text class="block-label">所选药品</text>
|
<text class="block-label">所选药品</text>
|
||||||
<view v-for="row in registerCardDrugRows" :key="row._wxKey" class="info-row drug-spec-row">
|
<view v-for="row in registerCardDrugRows" :key="row._wxKey" class="info-row drug-spec-row">
|
||||||
@@ -185,6 +197,21 @@
|
|||||||
<text class="label">患者选择</text>
|
<text class="label">患者选择</text>
|
||||||
<text class="value">{{ followUpAnsweredLabel }}</text>
|
<text class="value">{{ followUpAnsweredLabel }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="parsedObj.has_visited === 1 || parsedObj.has_visited === '1' || parsedObj.has_visited === 0 || parsedObj.has_visited === '0'"
|
||||||
|
class="info-row"
|
||||||
|
>
|
||||||
|
<text class="label">是否就诊过</text>
|
||||||
|
<text class="value">{{ parsedObj.has_visited === 1 || parsedObj.has_visited === '1' ? '是' : '否' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="parsedObj.illness_info" class="text-block">
|
||||||
|
<text class="block-label">适用症/症状</text>
|
||||||
|
<text class="block-content">{{ parsedObj.illness_info }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="parsedObj.supplement" class="text-block">
|
||||||
|
<text class="block-label">补充信息</text>
|
||||||
|
<text class="block-content">{{ parsedObj.supplement }}</text>
|
||||||
|
</view>
|
||||||
<view v-if="parsedObj.answer_status === 'pending'" class="follow-up-pending-readonly">
|
<view v-if="parsedObj.answer_status === 'pending'" class="follow-up-pending-readonly">
|
||||||
<text class="follow-up-pending-tip">请在挂号后的用药确认页完成选择,此处不可更改</text>
|
<text class="follow-up-pending-tip">请在挂号后的用药确认页完成选择,此处不可更改</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -294,7 +321,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<text class="desc-text">{{ parsedObj.reason || '本次服务已完成' }}</text>
|
<text class="desc-text">{{ parsedObj.end_reason || parsedObj.reason || parsedObj.message || '本次服务已完成' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
|
|||||||
@@ -2615,6 +2615,11 @@ export default {
|
|||||||
const doctorId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id');
|
const doctorId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id');
|
||||||
if (!doctorId) return;
|
if (!doctorId) return;
|
||||||
const senderUserId = String(doctorId).startsWith('doctor-') ? String(doctorId) : `doctor-${doctorId}`;
|
const senderUserId = String(doctorId).startsWith('doctor-') ? String(doctorId) : `doctor-${doctorId}`;
|
||||||
|
// 在线复诊开方后:电子处方卡 + 购药指引 + 用药温馨提示
|
||||||
|
const tipTexts = [
|
||||||
|
'根据您的病情描述,已为您开具电子处方,请点击上方的‘立即支付’,进行购药;如有商品价格和订单等问题请咨询平台客服。',
|
||||||
|
'温馨提示:用药前请详细阅读药品说明书,并严格按照线下医嘱用药。如用药过程中出现病情变化或其它不适症状,请立即停药并及时就医。(请您注意,如果您尚未在医院就诊或未曾使用过本次申请的药品,请暂时不要支付订单。我们建议您在医生的指导下使用药品,以确保用药安全。)',
|
||||||
|
];
|
||||||
try {
|
try {
|
||||||
await sendToUserHttpApi({
|
await sendToUserHttpApi({
|
||||||
room_id: roomId,
|
room_id: roomId,
|
||||||
@@ -2624,6 +2629,17 @@ export default {
|
|||||||
message_content: JSON.stringify(inner),
|
message_content: JSON.stringify(inner),
|
||||||
duration: 0
|
duration: 0
|
||||||
});
|
});
|
||||||
|
for (let i = 0; i < tipTexts.length; i++) {
|
||||||
|
// 温馨提示由小助手发送,处方卡仍由医生发送
|
||||||
|
await sendToUserHttpApi({
|
||||||
|
room_id: roomId,
|
||||||
|
sender_user_id: 'doctor_assistant',
|
||||||
|
receiver_user_id: receiverUserId,
|
||||||
|
message_type: 0,
|
||||||
|
message_content: tipTexts[i],
|
||||||
|
duration: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('trySendPrescriptionChatMessage', e);
|
console.warn('trySendPrescriptionChatMessage', e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user