Merge remote-tracking branch 'origin/dev-wdb' into dev-hjf
@ -1,4 +1,5 @@
|
|||||||
MP_API_BASE_URL=http://192.168.137.1:8080
|
MP_API_BASE_URL=http://localhost:8080
|
||||||
|
MP_IMAGE_URL=https://patient.youcan365.com
|
||||||
MP_CACHE_PREFIX=development
|
MP_CACHE_PREFIX=development
|
||||||
MP_WX_APP_ID=wx93af55767423938e
|
MP_WX_APP_ID=wx93af55767423938e
|
||||||
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
MP_API_BASE_URL=http://localhost:8080
|
MP_API_BASE_URL=http://localhost:8080
|
||||||
MP_CACHE_PREFIX=development
|
MP_CACHE_PREFIX=development
|
||||||
MP_WX_APP_ID=wx93af55767423938e
|
MP_WX_APP_ID=wx93af55767423938e
|
||||||
|
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
||||||
MP_TIM_SDK_APP_ID=1600072268
|
MP_TIM_SDK_APP_ID=1600072268
|
||||||
|
|||||||
3
App.vue
@ -48,6 +48,9 @@ page {
|
|||||||
.relative {
|
.relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
.absolute{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="showCancel || showConfirm" class="relative px-15 py-12 bg-white text-center"
|
<view v-if="showCancel || showConfirm" class="relative flex px-15 py-12 bg-white text-center"
|
||||||
:class="hidedenShadow ? '' : 'shadow-up'">
|
:class="hidedenShadow ? '' : 'shadow-up'">
|
||||||
<view v-if="showCancel" class="flex-grow py-10 text-base border-primary rounded text-primary rounded"
|
<view v-if="showCancel" class="flex-grow py-10 text-base border-primary rounded text-primary rounded"
|
||||||
@click="cancel()">
|
@click="cancel()">
|
||||||
|
|||||||
@ -49,6 +49,7 @@ const displayRange = computed(() => {
|
|||||||
}
|
}
|
||||||
return props.range;
|
return props.range;
|
||||||
})
|
})
|
||||||
|
|
||||||
const value = computed(() => {
|
const value = computed(() => {
|
||||||
if (!props.form) return '';
|
if (!props.form) return '';
|
||||||
const currentValue = props.form[props.title];
|
const currentValue = props.form[props.title];
|
||||||
@ -65,7 +66,7 @@ function change(e) {
|
|||||||
const selectedValue = props.range[e.detail.value];
|
const selectedValue = props.range[e.detail.value];
|
||||||
emits('change', {
|
emits('change', {
|
||||||
title: props.title,
|
title: props.title,
|
||||||
value: selectedValue
|
value: typeof selectedValue === 'object' ? selectedValue.value : selectedValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -5,14 +5,15 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="mt-10">
|
<view class="mt-10">
|
||||||
<textarea
|
<textarea
|
||||||
|
:auto-height="autoHeight"
|
||||||
:disabled="disableChange"
|
:disabled="disableChange"
|
||||||
:value="value"
|
:value="value"
|
||||||
class="form-textarea"
|
class="form-textarea"
|
||||||
|
:class="border ? 'form-textarea--border' : ''"
|
||||||
:style="textareaStyle"
|
:style="textareaStyle"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
placeholder-class="form__placeholder"
|
placeholder-class="form__placeholder"
|
||||||
:maxlength="wordLimit"
|
:maxlength="wordLimit"
|
||||||
:auto-height="autoHeight"
|
|
||||||
@input="change($event)" />
|
@input="change($event)" />
|
||||||
<view v-if="wordLimit > 0" class="form-textarea__count">
|
<view v-if="wordLimit > 0" class="form-textarea__count">
|
||||||
{{ value && value.length ? value.length : 0 }} / {{ wordLimit }}
|
{{ value && value.length ? value.length : 0 }} / {{ wordLimit }}
|
||||||
@ -25,6 +26,10 @@ import { computed } from 'vue';
|
|||||||
|
|
||||||
const emits = defineEmits(['change']);
|
const emits = defineEmits(['change']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
border: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
@ -98,12 +103,15 @@ function change(e) {
|
|||||||
.form-textarea {
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
border: 1px solid #eee;
|
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-textarea--border {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
.form-textarea__count {
|
.form-textarea__count {
|
||||||
padding-top: 20rpx;
|
padding-top: 20rpx;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="full-page" :style="pageStyle">
|
<view class="full-page" :class="pageClass" :style="pageStyle">
|
||||||
<view v-if="hasHeader" class="page-header">
|
<view v-if="hasHeader" class="page-header">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</view>
|
</view>
|
||||||
<view class="page-main" :style="mainStyle">
|
<view class="page-main" :class="mainClass" :style="mainStyle">
|
||||||
<view v-if="customScroll" class="page-scroll">
|
<view v-if="customScroll" class="page-scroll">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</view>
|
</view>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</view>
|
</view>
|
||||||
<!-- #ifdef MP-->
|
<!-- #ifdef MP-->
|
||||||
<view class="safeareaBottom"></view>
|
<view v-if="showSafeArea" class="safeareaBottom"></view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -27,8 +27,11 @@ import useDebounce from '@/utils/useDebounce';
|
|||||||
const emits = defineEmits(['reachBottom']);
|
const emits = defineEmits(['reachBottom']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
customScroll: { type: Boolean, default: false },
|
customScroll: { type: Boolean, default: false },
|
||||||
|
mainClass: { type: String, default: '' },
|
||||||
mainStyle: { default: '' },
|
mainStyle: { default: '' },
|
||||||
pageStyle: { default: '' }
|
pageClass: { type: String, default: '' },
|
||||||
|
pageStyle: { default: '' },
|
||||||
|
showSafeArea: { type: Boolean, default: true }
|
||||||
});
|
});
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const hasHeader = computed(() => !!slots.header);
|
const hasHeader = computed(() => !!slots.header);
|
||||||
|
|||||||
@ -39,8 +39,10 @@ export default function useGuard() {
|
|||||||
|
|
||||||
async function triggleShowEvents() {
|
async function triggleShowEvents() {
|
||||||
await promise;
|
await promise;
|
||||||
|
if (account.value && account.value.openid) {
|
||||||
onShowEvents.value.forEach(fn => fn(onShowOptions.value))
|
onShowEvents.value.forEach(fn => fn(onShowOptions.value))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function useShow(fn) {
|
function useShow(fn) {
|
||||||
onShowEvents.value.push(fn)
|
onShowEvents.value.push(fn)
|
||||||
@ -53,8 +55,8 @@ export default function useGuard() {
|
|||||||
const route = routes.find(i => page && i.path === page.route);
|
const route = routes.find(i => page && i.path === page.route);
|
||||||
const requireLogin = route && route.meta && route.meta.login;
|
const requireLogin = route && route.meta && route.meta.login;
|
||||||
if (requireLogin && !account.value) {
|
if (requireLogin && !account.value) {
|
||||||
const res = await login()
|
await login()
|
||||||
if (res) {
|
if (account.value) {
|
||||||
resolve()
|
resolve()
|
||||||
} else {
|
} else {
|
||||||
return toLoginPage(opts, page.route);
|
return toLoginPage(opts, page.route);
|
||||||
@ -64,7 +66,6 @@ export default function useGuard() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onShow(opts => {
|
onShow(opts => {
|
||||||
console.log('onShow')
|
|
||||||
onShowOptions.value = { ...opts };
|
onShowOptions.value = { ...opts };
|
||||||
triggleShowEvents()
|
triggleShowEvents()
|
||||||
})
|
})
|
||||||
|
|||||||
40
pages.json
@ -27,9 +27,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/case/case",
|
"path": "pages/message/common-phrases",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "病例"
|
"navigationBarTitleText": "常用语"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/message/article-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "宣教文章"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/message/survey-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "问卷列表"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -158,6 +170,12 @@
|
|||||||
"navigationBarTitleText": "工作台"
|
"navigationBarTitleText": "工作台"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/case/case",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "病例"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/work/profile",
|
"path": "pages/work/profile",
|
||||||
"style": {
|
"style": {
|
||||||
@ -169,6 +187,24 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "选择科室"
|
"navigationBarTitleText": "选择科室"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/work/verify/assistant",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "上传证照"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/work/verify/doctor",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "上传证照"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/login/login",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "授权登录"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@ -1,27 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="team" class="pt-lg px-15 flex flex-col items-center text-center">
|
<view class="pt-lg px-15 flex flex-col items-center text-center">
|
||||||
<group-avatar :avatarList="team.avatars" />
|
|
||||||
<view class="mt-15 text-base font-semibold text-dark">{{
|
|
||||||
team.teamName
|
|
||||||
}}</view>
|
|
||||||
<view class="mt-12 text-sm text-gray">{{ team.corpName }}</view>
|
|
||||||
<view class="mt-15 text-lg text-dark font-semibold"
|
|
||||||
>为您提供团队个性化专属服务</view
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
<view v-else class="pt-lg px-15 flex flex-col items-center text-center">
|
|
||||||
<image src="/static/logo-plain.png" class="logo"></image>
|
<image src="/static/logo-plain.png" class="logo"></image>
|
||||||
<view class="mt-15 text-xl font-semibold text-dark">柚健康</view>
|
<view class="mt-15 text-xl font-semibold text-dark">柚健康</view>
|
||||||
<view class="mt-12 text-base text-dark">生命全周期健康管理伙伴</view>
|
<view class="mt-12 text-base text-dark">生命全周期健康管理伙伴</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="login-btn-wrap">
|
<view class="login-btn-wrap">
|
||||||
<button
|
<button v-if="checked" class="login-btn" type="primary" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
||||||
v-if="checked"
|
|
||||||
class="login-btn"
|
|
||||||
type="primary"
|
|
||||||
open-type="getPhoneNumber"
|
|
||||||
@getphonenumber="getPhoneNumber"
|
|
||||||
>
|
|
||||||
手机号快捷登录
|
手机号快捷登录
|
||||||
</button>
|
</button>
|
||||||
<!-- <button v-if="checked" class="login-btn" type="primary" @click="getPhoneNumber()">
|
<!-- <button v-if="checked" class="login-btn" type="primary" @click="getPhoneNumber()">
|
||||||
@ -31,10 +15,7 @@
|
|||||||
手机号快捷登录
|
手机号快捷登录
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view class="flex items-center justify-center mt-12 px-15" @click="checked = !checked">
|
||||||
class="flex items-center justify-center mt-12 px-15"
|
|
||||||
@click="checked = !checked"
|
|
||||||
>
|
|
||||||
<checkbox :checked="checked" style="transform: scale(0.7)" />
|
<checkbox :checked="checked" style="transform: scale(0.7)" />
|
||||||
<view class="text-sm text-gray">我已阅读并同意</view>
|
<view class="text-sm text-gray">我已阅读并同意</view>
|
||||||
<view class="text-sm text-primary">《用户协议》、</view>
|
<view class="text-sm text-primary">《用户协议》、</view>
|
||||||
@ -44,16 +25,16 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import useAccountStore from "@/store/account";
|
import useAccountStore from "@/store/account";
|
||||||
import { get } from "@/utils/cache";
|
import { get } from "@/utils/cache";
|
||||||
import { toast } from "@/utils/widget";
|
import { toast } from "@/utils/widget";
|
||||||
|
|
||||||
import groupAvatar from "@/components/group-avatar.vue";
|
|
||||||
|
|
||||||
const team = ref(true);
|
const team = ref(true);
|
||||||
const checked = ref(false);
|
const checked = ref(false);
|
||||||
const redirectUrl = ref("");
|
const redirectUrl = ref("");
|
||||||
|
const { doctorInfo } = storeToRefs(useAccountStore());
|
||||||
const { login } = useAccountStore();
|
const { login } = useAccountStore();
|
||||||
|
|
||||||
function attempRedirect(url) {
|
function attempRedirect(url) {
|
||||||
@ -82,7 +63,7 @@ function remind() {
|
|||||||
|
|
||||||
function toHome() {
|
function toHome() {
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: "/pages/message/message",
|
url: "/pages/work/work",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +73,10 @@ async function getPhoneNumber(e) {
|
|||||||
const res = await login(phoneCode);
|
const res = await login(phoneCode);
|
||||||
if (res && redirectUrl.value) {
|
if (res && redirectUrl.value) {
|
||||||
await attempToPage(redirectUrl.value);
|
await attempToPage(redirectUrl.value);
|
||||||
|
} else if (res && !(doctorInfo.value && doctorInfo.value.anotherName)) {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/work/profile'
|
||||||
|
})
|
||||||
} else if (res) {
|
} else if (res) {
|
||||||
toHome();
|
toHome();
|
||||||
}
|
}
|
||||||
@ -109,7 +94,6 @@ onLoad((opts) => {
|
|||||||
if (opts.source === "teamInvite") {
|
if (opts.source === "teamInvite") {
|
||||||
team.value = get("invite-team-info");
|
team.value = get("invite-team-info");
|
||||||
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
||||||
console.log("redirectUrl", redirectUrl.value);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (opts.redirect) {
|
if (opts.redirect) {
|
||||||
|
|||||||
503
pages/message/article-list.vue
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
<template>
|
||||||
|
<view class="article-page">
|
||||||
|
<view class="header">
|
||||||
|
<view class="search-bar">
|
||||||
|
<uni-icons type="search" size="18" color="#999" />
|
||||||
|
<input
|
||||||
|
class="search-input"
|
||||||
|
v-model="searchTitle"
|
||||||
|
placeholder="输入内容名称搜索"
|
||||||
|
@input="handleSearch"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="content">
|
||||||
|
<view class="category-sidebar">
|
||||||
|
<scroll-view scroll-y class="category-scroll">
|
||||||
|
<view
|
||||||
|
v-for="cate in categoryList"
|
||||||
|
:key="cate._id || 'all'"
|
||||||
|
class="category-item"
|
||||||
|
:class="{ active: currentCateId === (cate._id || 'all') }"
|
||||||
|
@click="selectCategory(cate)"
|
||||||
|
>
|
||||||
|
{{ cate.name }}
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="article-list">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="article-scroll"
|
||||||
|
@scrolltolower="loadMore"
|
||||||
|
lower-threshold="50"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-if="loading && articleList.length === 0"
|
||||||
|
class="loading-container"
|
||||||
|
>
|
||||||
|
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="articleList.length === 0" class="empty-container">
|
||||||
|
<empty-data title="暂无文章" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else>
|
||||||
|
<view
|
||||||
|
v-for="article in articleList"
|
||||||
|
:key="article._id"
|
||||||
|
class="article-item"
|
||||||
|
>
|
||||||
|
<view class="article-content" @click="previewArticle(article)">
|
||||||
|
<text class="article-title">{{ article.title }}</text>
|
||||||
|
<text class="article-date">创建时间:{{ article.date }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="article-action">
|
||||||
|
<button
|
||||||
|
class="send-btn"
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="sendArticle(article)"
|
||||||
|
>
|
||||||
|
发送
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="loading && articleList.length > 0" class="loading-more">
|
||||||
|
<uni-icons type="spinner-cycle" size="20" color="#999" />
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-if="!loading && articleList.length >= total"
|
||||||
|
class="no-more"
|
||||||
|
>
|
||||||
|
没有更多了
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 文章预览弹窗 -->
|
||||||
|
<uni-popup ref="previewPopup" type="bottom" :safe-area="false">
|
||||||
|
<view class="preview-container">
|
||||||
|
<view class="preview-header">
|
||||||
|
<text class="preview-title">{{ previewArticleData.title }}</text>
|
||||||
|
<view class="preview-close" @click="closePreview">
|
||||||
|
<uni-icons type="closeempty" size="24" color="#333" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="preview-content">
|
||||||
|
<rich-text :nodes="previewArticleData.content"></rich-text>
|
||||||
|
</scroll-view>
|
||||||
|
<view class="preview-footer">
|
||||||
|
<button class="preview-close-btn" @click="closePreview">关闭</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { getArticleCateList, getArticleList, getArticle } from "@/utils/api.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import EmptyData from "@/components/empty-data.vue";
|
||||||
|
|
||||||
|
const env = __VITE_ENV__;
|
||||||
|
const accountStore = useAccountStore();
|
||||||
|
const corpId = env.MP_CORP_ID;
|
||||||
|
const userId = ref("");
|
||||||
|
|
||||||
|
// 搜索关键词
|
||||||
|
const searchTitle = ref("");
|
||||||
|
let searchTimer = null;
|
||||||
|
|
||||||
|
// 分类列表
|
||||||
|
const categoryList = ref([{ _id: "", name: "全部" }]);
|
||||||
|
const currentCateId = ref("");
|
||||||
|
|
||||||
|
// 文章列表
|
||||||
|
const articleList = ref([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const page = ref(1);
|
||||||
|
const pageSize = 30;
|
||||||
|
const total = ref(0);
|
||||||
|
|
||||||
|
// 预览文章数据
|
||||||
|
const previewArticleData = ref({
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
const previewPopup = ref(null);
|
||||||
|
|
||||||
|
// 获取分类列表
|
||||||
|
const getCategoryList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getArticleCateList({ corpId: corpId });
|
||||||
|
if (res.success && res.list) {
|
||||||
|
const cates = res.list || [];
|
||||||
|
categoryList.value = [{ _id: "", name: "全部" }, ...cates];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取分类列表失败:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择分类
|
||||||
|
const selectCategory = (cate) => {
|
||||||
|
currentCateId.value = cate._id || "";
|
||||||
|
page.value = 1;
|
||||||
|
articleList.value = [];
|
||||||
|
loadArticleList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索处理
|
||||||
|
const handleSearch = () => {
|
||||||
|
if (searchTimer) {
|
||||||
|
clearTimeout(searchTimer);
|
||||||
|
}
|
||||||
|
searchTimer = setTimeout(() => {
|
||||||
|
page.value = 1;
|
||||||
|
articleList.value = [];
|
||||||
|
loadArticleList();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载文章列表
|
||||||
|
const loadArticleList = async () => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
corpId: corpId,
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize,
|
||||||
|
enable: true,
|
||||||
|
title: searchTitle.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果选择了分类,添加分类ID
|
||||||
|
if (currentCateId.value) {
|
||||||
|
params.cateIds = [currentCateId.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await getArticleList(params);
|
||||||
|
if (res.success && res.list) {
|
||||||
|
const { list = [], total: count = 0 } = res;
|
||||||
|
const formattedList = list.map((item) => {
|
||||||
|
// 格式化日期
|
||||||
|
let date = "";
|
||||||
|
if (item.createTime) {
|
||||||
|
const d = new Date(item.createTime);
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(d.getDate()).padStart(2, "0");
|
||||||
|
date = `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
date,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (page.value === 1) {
|
||||||
|
articleList.value = formattedList;
|
||||||
|
} else {
|
||||||
|
articleList.value = [...articleList.value, ...formattedList];
|
||||||
|
}
|
||||||
|
total.value = count;
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.message || "获取文章列表失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载文章列表失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "加载失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载更多
|
||||||
|
const loadMore = () => {
|
||||||
|
if (loading.value || articleList.value.length >= total.value) return;
|
||||||
|
page.value += 1;
|
||||||
|
loadArticleList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览文章
|
||||||
|
const previewArticle = async (article) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({ title: "加载中..." });
|
||||||
|
const res = await getArticle({ id: article._id, corpId: corpId });
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (res.success && res.data ) {
|
||||||
|
previewArticleData.value = {
|
||||||
|
title: res.data.title || article.title,
|
||||||
|
content: res.data.content || "",
|
||||||
|
};
|
||||||
|
previewPopup.value?.open();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.message || "预览文章失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("预览文章失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "预览失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭预览
|
||||||
|
const closePreview = () => {
|
||||||
|
previewPopup.value?.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送文章
|
||||||
|
const sendArticle = (article) => {
|
||||||
|
// 获取当前页面栈
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const prevPage = pages[pages.length - 2];
|
||||||
|
|
||||||
|
if (prevPage) {
|
||||||
|
// 获取医生信息
|
||||||
|
const doctorInfo = accountStore.doctorInfo;
|
||||||
|
userId.value = doctorInfo?.userid || accountStore.openid;
|
||||||
|
|
||||||
|
// 通过事件总线传递数据
|
||||||
|
uni.$emit("sendArticle", {
|
||||||
|
article: article,
|
||||||
|
corpId: corpId,
|
||||||
|
userId: userId.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "已选择文章",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟返回,让用户看到提示
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCategoryList();
|
||||||
|
loadArticleList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.article-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-sidebar {
|
||||||
|
width: 200rpx;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-right: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-scroll {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item.active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
border-left: 4rpx solid #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-list {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-scroll {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container,
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-break: break-all;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-date {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn {
|
||||||
|
padding: 8rpx 32rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-more,
|
||||||
|
.no-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预览弹窗样式 */
|
||||||
|
.preview-container {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 20rpx 20rpx 0 0;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 30rpx;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-close {
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 30rpx;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-footer {
|
||||||
|
padding: 20rpx;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-close-btn {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1249,3 +1249,111 @@ $primary-color: #0877F1;
|
|||||||
0%, 100% { transform: translateY(0); }
|
0%, 100% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-10rpx); }
|
50% { transform: translateY(-10rpx); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 文章卡片样式 */
|
||||||
|
.article-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
max-width: 500rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-image {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文章卡片在不同消息流中的样式 */
|
||||||
|
.message-right .article-card {
|
||||||
|
background-color: #e8f4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-left .article-card {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 问卷卡片样式 */
|
||||||
|
.survey-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
max-width: 500rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-image {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 问卷卡片在不同消息流中的样式 */
|
||||||
|
.message-right .survey-card {
|
||||||
|
background-color: #e8f4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-left .survey-card {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|||||||
1058
pages/message/common-phrases.vue
Normal file
@ -53,7 +53,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { computed, ref, onMounted, onUnmounted, nextTick } from "vue";
|
||||||
import {
|
import {
|
||||||
chooseImage,
|
chooseImage,
|
||||||
takePhoto as takePhotoUtil,
|
takePhoto as takePhotoUtil,
|
||||||
@ -64,19 +64,19 @@ import {
|
|||||||
sendCustomMessage as sendCustomMessageUtil,
|
sendCustomMessage as sendCustomMessageUtil,
|
||||||
sendMessage as sendMessageUtil,
|
sendMessage as sendMessageUtil,
|
||||||
checkRecordingDuration,
|
checkRecordingDuration,
|
||||||
validateBeforeSend
|
validateBeforeSend,
|
||||||
} from '@/utils/chat-utils.js';
|
} from "@/utils/chat-utils.js";
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
timChatManager: { type: Object, required: true },
|
timChatManager: { type: Object, required: true },
|
||||||
patientInfo: { type: Object, default: () => ({}) },
|
patientInfo: { type: Object, default: () => ({}) },
|
||||||
chatRoomBusiness: { type: Object, default: () => ({}) },
|
chatRoomBusiness: { type: Object, default: () => ({}) },
|
||||||
formatTime: { type: Function, required: true }
|
formatTime: { type: Function, required: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits(['messageSent', 'scrollToBottom']);
|
const emit = defineEmits(["messageSent", "scrollToBottom"]);
|
||||||
|
|
||||||
// 输入相关状态
|
// 输入相关状态
|
||||||
const inputText = ref("");
|
const inputText = ref("");
|
||||||
@ -85,9 +85,12 @@ const showMorePanel = ref(false);
|
|||||||
const isRecording = ref(false);
|
const isRecording = ref(false);
|
||||||
const recordingText = ref("录音中...");
|
const recordingText = ref("录音中...");
|
||||||
const cloudCustomData = computed(() => {
|
const cloudCustomData = computed(() => {
|
||||||
const arr = [props.chatRoomBusiness.businessType, props.chatRoomBusiness.businessId];
|
const arr = [
|
||||||
return arr.filter(Boolean).join('|');
|
props.chatRoomBusiness.businessType,
|
||||||
})
|
props.chatRoomBusiness.businessId,
|
||||||
|
];
|
||||||
|
return arr.filter(Boolean).join("|");
|
||||||
|
});
|
||||||
|
|
||||||
// 录音相关扩展状态(特效 + 取消逻辑)
|
// 录音相关扩展状态(特效 + 取消逻辑)
|
||||||
const recordingDuration = ref(0);
|
const recordingDuration = ref(0);
|
||||||
@ -112,10 +115,12 @@ const initRecorderManager = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查录音时长
|
// 检查录音时长
|
||||||
if (!checkRecordingDuration(res, () => {
|
if (
|
||||||
|
!checkRecordingDuration(res, () => {
|
||||||
isRecording.value = false;
|
isRecording.value = false;
|
||||||
recordingText.value = "录音中...";
|
recordingText.value = "录音中...";
|
||||||
})) {
|
})
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,19 +146,36 @@ const initRecorderManager = () => {
|
|||||||
const sendTextMessage = async () => {
|
const sendTextMessage = async () => {
|
||||||
if (!inputText.value.trim()) return;
|
if (!inputText.value.trim()) return;
|
||||||
|
|
||||||
await sendMessage('text', inputText.value);
|
await sendMessage("text", inputText.value);
|
||||||
inputText.value = "";
|
inputText.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 从常用语发送文本消息
|
||||||
|
const sendTextMessageFromPhrase = async (content) => {
|
||||||
|
if (!content.trim()) return;
|
||||||
|
|
||||||
|
await sendMessage("text", content);
|
||||||
|
|
||||||
|
// 发送成功后滚动到底部
|
||||||
|
nextTick(() => {
|
||||||
|
emit("scrollToBottom");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件调用
|
||||||
|
defineExpose({
|
||||||
|
sendTextMessageFromPhrase
|
||||||
|
});
|
||||||
|
|
||||||
// 发送图片消息
|
// 发送图片消息
|
||||||
const sendImageMessage = async (imageFile) => {
|
const sendImageMessage = async (imageFile) => {
|
||||||
console.log('chat-input sendImageMessage 被调用,参数:', imageFile);
|
console.log("chat-input sendImageMessage 被调用,参数:", imageFile);
|
||||||
await sendMessage('image', imageFile);
|
await sendMessage("image", imageFile);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送语音消息
|
// 发送语音消息
|
||||||
const sendVoiceMessage = async (voiceFile, duration) => {
|
const sendVoiceMessage = async (voiceFile, duration) => {
|
||||||
await sendMessage('voice', { file: voiceFile, duration });
|
await sendMessage("voice", { file: voiceFile, duration });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送消息的通用方法(文本、图片、语音)
|
// 发送消息的通用方法(文本、图片、语音)
|
||||||
@ -166,7 +188,7 @@ const sendMessage = async (messageType, data) => {
|
|||||||
() => {
|
() => {
|
||||||
showMorePanel.value = false;
|
showMorePanel.value = false;
|
||||||
// 发送成功后滚动到底部
|
// 发送成功后滚动到底部
|
||||||
emit('messageSent');
|
emit("messageSent");
|
||||||
},
|
},
|
||||||
cloudCustomData.value
|
cloudCustomData.value
|
||||||
);
|
);
|
||||||
@ -180,7 +202,7 @@ const sendCustomMessage = async (messageData) => {
|
|||||||
() => validateBeforeSend(false, false, props.timChatManager),
|
() => validateBeforeSend(false, false, props.timChatManager),
|
||||||
() => {
|
() => {
|
||||||
showMorePanel.value = false;
|
showMorePanel.value = false;
|
||||||
emit('messageSent');
|
emit("messageSent");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -200,17 +222,22 @@ const toggleMorePanel = () => {
|
|||||||
const showImagePicker = () => {
|
const showImagePicker = () => {
|
||||||
chooseImage(
|
chooseImage(
|
||||||
(file) => {
|
(file) => {
|
||||||
console.log('选择图片成功,文件对象:', file);
|
console.log("选择图片成功,文件对象:", file);
|
||||||
// 直接传递文件对象,不需要额外处理
|
// 直接传递文件对象,不需要额外处理
|
||||||
sendImageMessage(file);
|
sendImageMessage(file);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.error('选择图片失败:', err);
|
console.error("选择图片失败:", err);
|
||||||
if (!err.errMsg?.includes('permission') && !err.errMsg?.includes('auth') && !err.errMsg?.includes('拒绝') && !err.errMsg?.includes('未授权')) {
|
if (
|
||||||
|
!err.errMsg?.includes("permission") &&
|
||||||
|
!err.errMsg?.includes("auth") &&
|
||||||
|
!err.errMsg?.includes("拒绝") &&
|
||||||
|
!err.errMsg?.includes("未授权")
|
||||||
|
) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '选择图片失败,请重试',
|
title: "选择图片失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,17 +247,22 @@ const showImagePicker = () => {
|
|||||||
const takePhoto = () => {
|
const takePhoto = () => {
|
||||||
takePhotoUtil(
|
takePhotoUtil(
|
||||||
(file) => {
|
(file) => {
|
||||||
console.log('拍照成功,文件对象:', file);
|
console.log("拍照成功,文件对象:", file);
|
||||||
// 直接传递文件对象,不需要额外处理
|
// 直接传递文件对象,不需要额外处理
|
||||||
sendImageMessage(file);
|
sendImageMessage(file);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.error('拍照失败:', err);
|
console.error("拍照失败:", err);
|
||||||
if (!err.errMsg?.includes('permission') && !err.errMsg?.includes('auth') && !err.errMsg?.includes('拒绝') && !err.errMsg?.includes('未授权')) {
|
if (
|
||||||
|
!err.errMsg?.includes("permission") &&
|
||||||
|
!err.errMsg?.includes("auth") &&
|
||||||
|
!err.errMsg?.includes("拒绝") &&
|
||||||
|
!err.errMsg?.includes("未授权")
|
||||||
|
) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '拍照失败,请重试',
|
title: "拍照失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,12 +335,11 @@ const cancelRecord = () => {
|
|||||||
stopRecordUtil(recorderManager);
|
stopRecordUtil(recorderManager);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 发送问卷调查消息
|
// 发送问卷调查消息
|
||||||
const sendSurveyMessage = async () => {
|
const sendSurveyMessage = async () => {
|
||||||
const surveyMessage = createCustomMessage("survey", {
|
const surveyMessage = createCustomMessage(
|
||||||
|
"survey",
|
||||||
|
{
|
||||||
content: "医生发送了问卷调查",
|
content: "医生发送了问卷调查",
|
||||||
surveyTitle: "治疗效果评估",
|
surveyTitle: "治疗效果评估",
|
||||||
surveyDescription: "您好,为了帮助了解您的病情变化,请您如实填写问卷。",
|
surveyDescription: "您好,为了帮助了解您的病情变化,请您如实填写问卷。",
|
||||||
@ -316,26 +347,68 @@ const sendSurveyMessage = async () => {
|
|||||||
estimatedTime: "约3-5分钟",
|
estimatedTime: "约3-5分钟",
|
||||||
reward: "积分奖励10分",
|
reward: "积分奖励10分",
|
||||||
note: "问卷内容涉及您的症状变化、用药情况等,请根据实际情况填写。",
|
note: "问卷内容涉及您的症状变化、用药情况等,请根据实际情况填写。",
|
||||||
}, props.formatTime);
|
},
|
||||||
|
props.formatTime
|
||||||
|
);
|
||||||
|
|
||||||
await sendCustomMessage(surveyMessage);
|
await sendCustomMessage(surveyMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更多面板按钮配置
|
// 跳转到常用语页面
|
||||||
|
const goToCommonPhrases = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/message/common-phrases'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到宣教文章页面
|
||||||
|
const goToArticleList = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/message/article-list'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到问卷列表页面
|
||||||
|
const goToSurveyList = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/message/survey-list'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const morePanelButtons = [
|
const morePanelButtons = [
|
||||||
{ text: '照片', icon: '/static/home/photo.png', action: showImagePicker },
|
{ text: "照片", icon: "/static/icon/zhaopian.png", action: showImagePicker },
|
||||||
{ text: '拍摄', icon: '/static/home/video.png', action: takePhoto },
|
{
|
||||||
// { text: '病情', icon: '/static/home/avatar.svg', action: sendSymptomMessage },
|
text: "回访任务",
|
||||||
// { text: '处方', icon: '/static/home/avatar.svg', action: sendPrescriptionMessage },
|
icon: "/static/icon/zhaopian.png",
|
||||||
// { text: '续方', icon: '/static/home/avatar.svg', action: sendRefillMessage },
|
action: showImagePicker,
|
||||||
// { text: '问卷', icon: '/static/home/avatar.svg', action: sendSurveyMessage }
|
},
|
||||||
|
{
|
||||||
|
text: "常用语",
|
||||||
|
icon: "/static/icon/changyongyu.png",
|
||||||
|
action: goToCommonPhrases,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "宣教",
|
||||||
|
icon: "/static/icon/xuanjiaowenzhang.png",
|
||||||
|
action: goToArticleList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "问卷",
|
||||||
|
icon: "/static/icon/wenjuan.png",
|
||||||
|
action: goToSurveyList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "结束问诊",
|
||||||
|
icon: "/static/icon/jieshuzixun.png",
|
||||||
|
action: showImagePicker,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function handleInputFocus() {
|
function handleInputFocus() {
|
||||||
console.log('handleInputFocus')
|
console.log("handleInputFocus");
|
||||||
nextTick().then(() => {
|
nextTick().then(() => {
|
||||||
emit('scrollToBottom')
|
emit("scrollToBottom");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -343,14 +416,14 @@ onMounted(() => {
|
|||||||
initRecorderManager();
|
initRecorderManager();
|
||||||
|
|
||||||
// 监听关闭功能栏事件
|
// 监听关闭功能栏事件
|
||||||
uni.$on('closeMorePanel', () => {
|
uni.$on("closeMorePanel", () => {
|
||||||
showMorePanel.value = false;
|
showMorePanel.value = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 移除事件监听
|
// 移除事件监听
|
||||||
uni.$off('closeMorePanel');
|
uni.$off("closeMorePanel");
|
||||||
clearDurationTimer();
|
clearDurationTimer();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
88
pages/message/components/consult-accept.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<view class="consult-accept-container">
|
||||||
|
<view class="accept-card">
|
||||||
|
<view class="accept-content">
|
||||||
|
<text class="accept-text">患者已发起咨询申请,请及时接诊</text>
|
||||||
|
</view>
|
||||||
|
<view class="accept-actions">
|
||||||
|
<button class="btn-cancel" @click="handleReject">暂不接受</button>
|
||||||
|
<button class="btn-confirm" @click="handleAccept">接受咨询</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineEmits } from 'vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['accept', 'reject']);
|
||||||
|
|
||||||
|
const handleAccept = () => {
|
||||||
|
emit('accept');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReject = () => {
|
||||||
|
emit('reject');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.consult-accept-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20rpx 32rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-content {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel,
|
||||||
|
.btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
background-color: #1677ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -3,6 +3,7 @@
|
|||||||
<text v-if="message.type === 'TIMTextElem'" class="message-text">
|
<text v-if="message.type === 'TIMTextElem'" class="message-text">
|
||||||
{{ message.payload.text }}
|
{{ message.payload.text }}
|
||||||
</text>
|
</text>
|
||||||
|
|
||||||
<!-- 图片消息 -->
|
<!-- 图片消息 -->
|
||||||
<image
|
<image
|
||||||
v-else-if="message.type === 'TIMImageElem'"
|
v-else-if="message.type === 'TIMImageElem'"
|
||||||
@ -61,9 +62,47 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 自定义消息卡片 -->
|
||||||
<!-- <template v-else-if="message.type === 'TIMCustomElem'">
|
<template v-else-if="message.type === 'TIMCustomElem'">
|
||||||
|
<!-- 文章消息 -->
|
||||||
<view
|
<view
|
||||||
|
v-if="getCustomMessageType(message) === 'article'"
|
||||||
|
class="article-card"
|
||||||
|
@click="handleArticleClick(message)"
|
||||||
|
>
|
||||||
|
<view class="article-content">
|
||||||
|
<view class="article-title">{{ getArticleData(message).title }}</view>
|
||||||
|
<view class="article-desc">{{ getArticleData(message).desc }}</view>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
v-if="getArticleData(message).imgUrl"
|
||||||
|
class="article-image"
|
||||||
|
:src="getArticleData(message).imgUrl"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 问卷消息 -->
|
||||||
|
<view
|
||||||
|
v-else-if="getCustomMessageType(message) === 'survey'"
|
||||||
|
class="survey-card"
|
||||||
|
@click="handleSurveyClick(message)"
|
||||||
|
>
|
||||||
|
<view class="survey-content">
|
||||||
|
<view class="survey-title">{{ getSurveyData(message).title }}</view>
|
||||||
|
<view class="survey-desc">{{ getSurveyData(message).desc }}</view>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
v-if="getSurveyData(message).imgUrl"
|
||||||
|
class="survey-image"
|
||||||
|
:src="getSurveyData(message).imgUrl"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其他自定义消息 -->
|
||||||
|
<!-- <view
|
||||||
|
v-else
|
||||||
class="card-avatar-row"
|
class="card-avatar-row"
|
||||||
@click="() => console.log('点击头像', message)"
|
@click="() => console.log('点击头像', message)"
|
||||||
>
|
>
|
||||||
@ -73,8 +112,8 @@
|
|||||||
:flow="message.flow"
|
:flow="message.flow"
|
||||||
@viewDetail="$emit('viewDetail', $event)"
|
@viewDetail="$emit('viewDetail', $event)"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view> -->
|
||||||
</template> -->
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -102,6 +141,8 @@ const isPlaying = computed(() => {
|
|||||||
// 计算图片样式
|
// 计算图片样式
|
||||||
const getImageStyle = (imageInfo) => {
|
const getImageStyle = (imageInfo) => {
|
||||||
// 如果没有尺寸信息,使用默认样式
|
// 如果没有尺寸信息,使用默认样式
|
||||||
|
imageInfo.width = imageInfo.width || imageInfo.Width;
|
||||||
|
imageInfo.height = imageInfo.height || imageInfo.Height;
|
||||||
if (!imageInfo || !imageInfo.width || !imageInfo.height) {
|
if (!imageInfo || !imageInfo.width || !imageInfo.height) {
|
||||||
return {
|
return {
|
||||||
width: "400rpx",
|
width: "400rpx",
|
||||||
@ -160,6 +201,90 @@ const getVoiceStyle = (duration) => {
|
|||||||
width: width + "rpx",
|
width: width + "rpx",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取自定义消息类型
|
||||||
|
const getCustomMessageType = (message) => {
|
||||||
|
try {
|
||||||
|
if (message.payload && message.payload.data) {
|
||||||
|
const data = JSON.parse(message.payload.data);
|
||||||
|
return data.type || '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析自定义消息失败:', error);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取文章数据
|
||||||
|
const getArticleData = (message) => {
|
||||||
|
try {
|
||||||
|
if (message.payload && message.payload.data) {
|
||||||
|
const data = JSON.parse(message.payload.data);
|
||||||
|
return {
|
||||||
|
title: data.title || '宣教文章',
|
||||||
|
desc: data.desc || '宣教文章',
|
||||||
|
url: data.url || '',
|
||||||
|
imgUrl: data.imgUrl || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析文章数据失败:', error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: '宣教文章',
|
||||||
|
desc: '宣教文章',
|
||||||
|
url: '',
|
||||||
|
imgUrl: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理文章点击
|
||||||
|
const handleArticleClick = (message) => {
|
||||||
|
const articleData = getArticleData(message);
|
||||||
|
if (articleData.url) {
|
||||||
|
// 跳转到文章详情页或打开外部链接
|
||||||
|
// 这里可以根据实际需求处理
|
||||||
|
console.log('打开文章:', articleData.url);
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: `/pages/article/detail?url=${encodeURIComponent(articleData.url)}`
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取问卷数据
|
||||||
|
const getSurveyData = (message) => {
|
||||||
|
try {
|
||||||
|
if (message.payload && message.payload.data) {
|
||||||
|
const data = JSON.parse(message.payload.data);
|
||||||
|
return {
|
||||||
|
title: data.title || '填写问卷',
|
||||||
|
desc: data.desc || '请填写问卷',
|
||||||
|
url: data.url || '',
|
||||||
|
imgUrl: data.imgUrl || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析问卷数据失败:', error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: '填写问卷',
|
||||||
|
desc: '请填写问卷',
|
||||||
|
url: '',
|
||||||
|
imgUrl: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理问卷点击
|
||||||
|
const handleSurveyClick = (message) => {
|
||||||
|
const surveyData = getSurveyData(message);
|
||||||
|
if (surveyData.url) {
|
||||||
|
// 跳转到问卷填写页面或打开外部链接
|
||||||
|
console.log('打开问卷:', surveyData.url);
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.url)}`
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
297
pages/message/components/reject-reason-modal.vue
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
<template>
|
||||||
|
<view v-if="visible" class="modal-overlay" @click="handleCancel">
|
||||||
|
<view class="modal-container" @click.stop>
|
||||||
|
<view class="modal-header">
|
||||||
|
<text class="modal-title">选择暂不接受咨询原因,供患者知晓</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="modal-content">
|
||||||
|
<!-- 预设原因选项 -->
|
||||||
|
<view class="reason-options">
|
||||||
|
<view
|
||||||
|
v-for="(option, index) in reasonOptions"
|
||||||
|
:key="index"
|
||||||
|
class="reason-option"
|
||||||
|
:class="{ active: selectedReason === option }"
|
||||||
|
@click="selectReason(option)"
|
||||||
|
>
|
||||||
|
<text class="option-text">{{ option }}</text>
|
||||||
|
<view class="option-icon" :class="{ checked: selectedReason === option }">
|
||||||
|
<text v-if="selectedReason === option" class="check-mark">✓</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 自定义输入选项 -->
|
||||||
|
<view
|
||||||
|
class="reason-option custom-option"
|
||||||
|
:class="{ active: isCustomInput }"
|
||||||
|
@click="selectCustomInput"
|
||||||
|
>
|
||||||
|
<text class="option-text">填写拒诊理由</text>
|
||||||
|
<view class="option-icon arrow">
|
||||||
|
<text class="arrow-icon">›</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 自定义输入框 -->
|
||||||
|
<view v-if="isCustomInput" class="custom-input-container">
|
||||||
|
<textarea
|
||||||
|
class="custom-textarea"
|
||||||
|
v-model="customReason"
|
||||||
|
placeholder="请输入理由,供患者知晓"
|
||||||
|
maxlength="200"
|
||||||
|
:auto-height="true"
|
||||||
|
/>
|
||||||
|
<text class="char-count">{{ customReason.length }}/200</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="modal-footer">
|
||||||
|
<button class="btn-cancel" @click="handleCancel">取消</button>
|
||||||
|
<button class="btn-confirm" @click="handleConfirm">确定</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['confirm', 'cancel']);
|
||||||
|
|
||||||
|
// 预设的拒绝原因选项
|
||||||
|
const reasonOptions = ref([
|
||||||
|
'临时有紧急事务',
|
||||||
|
'患者病情复杂,需线下就诊',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selectedReason = ref('');
|
||||||
|
const isCustomInput = ref(false);
|
||||||
|
const customReason = ref('');
|
||||||
|
|
||||||
|
// 选择预设原因
|
||||||
|
const selectReason = (option) => {
|
||||||
|
selectedReason.value = option;
|
||||||
|
isCustomInput.value = false;
|
||||||
|
customReason.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择自定义输入
|
||||||
|
const selectCustomInput = () => {
|
||||||
|
selectedReason.value = '';
|
||||||
|
isCustomInput.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
// 重置状态
|
||||||
|
selectedReason.value = '';
|
||||||
|
isCustomInput.value = false;
|
||||||
|
customReason.value = '';
|
||||||
|
emit('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handleConfirm = () => {
|
||||||
|
let reason = '';
|
||||||
|
|
||||||
|
if (isCustomInput.value) {
|
||||||
|
reason = customReason.value.trim();
|
||||||
|
if (!reason) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入拒绝原因',
|
||||||
|
icon: 'none',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reason = selectedReason.value;
|
||||||
|
if (!reason) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择拒绝原因',
|
||||||
|
icon: 'none',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('confirm', reason);
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
selectedReason.value = '';
|
||||||
|
isCustomInput.value = false;
|
||||||
|
customReason.value = '';
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
width: 600rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 32rpx 32rpx 24rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
max-height: 600rpx;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #e6f4ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #d9d9d9;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.checked {
|
||||||
|
background-color: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.arrow {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-mark {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-icon {
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #999;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-input-container {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-count {
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 24rpx 32rpx 32rpx;
|
||||||
|
border-top: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel,
|
||||||
|
.btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
background-color: #1677ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="notifyText" class="notify-bar">
|
<!-- <view v-if="notifyText" class="notify-bar">
|
||||||
<view class="notify-line"></view>
|
<view class="notify-line"></view>
|
||||||
<view class="notify-text">{{ notifyText }}</view>
|
<view class="notify-text">{{ notifyText }}</view>
|
||||||
<view class="notify-line"></view>
|
<view class="notify-line"></view>
|
||||||
</view>
|
</view> -->
|
||||||
<view class="system-message">
|
<view class="system-message">
|
||||||
<view class="system-text">{{ text }}</view>
|
<view class="system-text">{{ text }}</view>
|
||||||
</view>
|
</view>
|
||||||
@ -21,17 +21,111 @@ const props = defineProps({
|
|||||||
|
|
||||||
const payload = computed(() => props.message?.payload || {});
|
const payload = computed(() => props.message?.payload || {});
|
||||||
|
|
||||||
const extension = computed(() => {
|
// 解析系统消息内容
|
||||||
|
const systemMessageData = computed(() => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(payload.value.extension);
|
// 尝试从 payload.data 解析系统消息
|
||||||
} catch (e) {
|
if (payload.value.data) {
|
||||||
return {};
|
const data = typeof payload.value.data === 'string'
|
||||||
|
? JSON.parse(payload.value.data)
|
||||||
|
: payload.value.data;
|
||||||
|
|
||||||
|
if (data.type === 'system_message') {
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析系统消息失败:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const text = computed(() => extension.value.patient || payload.value.data || '')
|
// 解析扩展信息
|
||||||
|
const extension = computed(() => {
|
||||||
|
try {
|
||||||
|
if (payload.value.extension) {
|
||||||
|
return typeof payload.value.extension === 'string'
|
||||||
|
? JSON.parse(payload.value.extension)
|
||||||
|
: payload.value.extension;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析扩展信息失败:', e);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
const notifyText = computed(() => extension.value.notifyText || '')
|
// 显示的文本内容
|
||||||
|
const text = computed(() => {
|
||||||
|
// 优先从系统消息数据中获取文本
|
||||||
|
if (systemMessageData.value?.text) {
|
||||||
|
return systemMessageData.value.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据消息类型返回默认文本
|
||||||
|
if (systemMessageData.value?.messageType) {
|
||||||
|
const messageType = systemMessageData.value.messageType;
|
||||||
|
switch (messageType) {
|
||||||
|
case 'consult_pending':
|
||||||
|
return '患者已发起咨询申请,请及时接诊';
|
||||||
|
case 'consult_accepted':
|
||||||
|
return '医生已接诊';
|
||||||
|
case 'consult_rejected':
|
||||||
|
return '医生暂时无法接诊';
|
||||||
|
case 'consult_ended':
|
||||||
|
return '问诊已结束';
|
||||||
|
case 'consult_timeout':
|
||||||
|
return '问诊已超时';
|
||||||
|
default:
|
||||||
|
return systemMessageData.value.content || '[系统消息]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧格式:从 extension 中获取
|
||||||
|
if (extension.value.patient) {
|
||||||
|
return extension.value.patient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧格式:直接从 payload.data 获取
|
||||||
|
if (payload.value.data && typeof payload.value.data === 'string') {
|
||||||
|
// 如果 data 不是 JSON 格式,直接显示
|
||||||
|
try {
|
||||||
|
JSON.parse(payload.value.data);
|
||||||
|
} catch {
|
||||||
|
return payload.value.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '[系统消息]';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 通知文本(红色提示)
|
||||||
|
const notifyText = computed(() => {
|
||||||
|
// 从扩展信息中获取通知文本
|
||||||
|
if (extension.value.notifyText) {
|
||||||
|
return extension.value.notifyText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据系统消息类型显示不同的通知
|
||||||
|
if (systemMessageData.value) {
|
||||||
|
const messageType = systemMessageData.value.messageType;
|
||||||
|
switch (messageType) {
|
||||||
|
case 'consult_pending':
|
||||||
|
return '待接诊';
|
||||||
|
case 'consult_rejected':
|
||||||
|
return '已拒绝';
|
||||||
|
case 'consult_timeout':
|
||||||
|
return '已超时';
|
||||||
|
case 'consult_accepted':
|
||||||
|
return '已接诊';
|
||||||
|
case 'consult_ended':
|
||||||
|
return '已结束';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,9 @@
|
|||||||
<image
|
<image
|
||||||
v-if="message.flow === 'in'"
|
v-if="message.flow === 'in'"
|
||||||
class="doctor-msg-avatar"
|
class="doctor-msg-avatar"
|
||||||
:src="chatMember[message.from]?.avatar || '/static/default-avatar.png'"
|
:src="
|
||||||
|
chatMember[message.from]?.avatar || '/static/default-avatar.png'
|
||||||
|
"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -62,7 +64,9 @@
|
|||||||
<image
|
<image
|
||||||
v-if="message.flow === 'out'"
|
v-if="message.flow === 'out'"
|
||||||
class="user-msg-avatar"
|
class="user-msg-avatar"
|
||||||
:src="chatMember[message.from]?.avatar || '/static/home/avatar.svg'"
|
:src="
|
||||||
|
chatMember[message.from]?.avatar || '/static/home/avatar.svg'
|
||||||
|
"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -108,9 +112,24 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 接受问诊组件 -->
|
||||||
|
<ConsultAccept
|
||||||
|
v-if="showConsultAccept"
|
||||||
|
@accept="handleAcceptConsult"
|
||||||
|
@reject="handleRejectConsult"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 拒绝原因对话框 -->
|
||||||
|
<RejectReasonModal
|
||||||
|
:visible="showRejectReasonModal"
|
||||||
|
@confirm="handleRejectReasonConfirm"
|
||||||
|
@cancel="handleRejectReasonCancel"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 聊天输入组件 -->
|
<!-- 聊天输入组件 -->
|
||||||
<ChatInput
|
<ChatInput
|
||||||
v-if="!isEvaluationPopupOpen"
|
v-if="!isEvaluationPopupOpen && !showConsultAccept"
|
||||||
|
ref="chatInputRef"
|
||||||
:timChatManager="timChatManager"
|
:timChatManager="timChatManager"
|
||||||
:formatTime="formatTime"
|
:formatTime="formatTime"
|
||||||
@scrollToBottom="() => scrollToBottom(true)"
|
@scrollToBottom="() => scrollToBottom(true)"
|
||||||
@ -142,10 +161,13 @@ import {
|
|||||||
handleViewDetail,
|
handleViewDetail,
|
||||||
checkIMConnectionStatus,
|
checkIMConnectionStatus,
|
||||||
} from "@/utils/chat-utils.js";
|
} from "@/utils/chat-utils.js";
|
||||||
|
import { sendConsultRejectedMessage } from "@/utils/api.js";
|
||||||
import useGroupChat from "./hooks/use-group-chat";
|
import useGroupChat from "./hooks/use-group-chat";
|
||||||
import MessageTypes from "./components/message-types.vue";
|
import MessageTypes from "./components/message-types.vue";
|
||||||
import ChatInput from "./components/chat-input.vue";
|
import ChatInput from "./components/chat-input.vue";
|
||||||
import SystemMessage from "./components/system-message.vue";
|
import SystemMessage from "./components/system-message.vue";
|
||||||
|
import ConsultAccept from "./components/consult-accept.vue";
|
||||||
|
import RejectReasonModal from "./components/reject-reason-modal.vue";
|
||||||
|
|
||||||
const timChatManager = globalTimChatManager;
|
const timChatManager = globalTimChatManager;
|
||||||
|
|
||||||
@ -153,16 +175,16 @@ const timChatManager = globalTimChatManager;
|
|||||||
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
|
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
|
||||||
const { initIMAfterLogin } = useAccountStore();
|
const { initIMAfterLogin } = useAccountStore();
|
||||||
|
|
||||||
|
// 聊天输入组件引用
|
||||||
|
const chatInputRef = ref(null);
|
||||||
|
|
||||||
const groupId = ref("");
|
const groupId = ref("");
|
||||||
const {
|
const { chatMember, getGroupInfo } = useGroupChat(groupId);
|
||||||
chatMember,
|
|
||||||
getGroupInfo
|
|
||||||
} = useGroupChat(groupId);
|
|
||||||
|
|
||||||
// 动态设置导航栏标题
|
// 动态设置导航栏标题
|
||||||
const updateNavigationTitle = () => {
|
const updateNavigationTitle = () => {
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: "群聊"
|
title: "群聊",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,6 +198,12 @@ const chatInfo = ref({
|
|||||||
// 评价弹窗状态
|
// 评价弹窗状态
|
||||||
const isEvaluationPopupOpen = ref(false);
|
const isEvaluationPopupOpen = ref(false);
|
||||||
|
|
||||||
|
// 接受问诊状态
|
||||||
|
const showConsultAccept = ref(false);
|
||||||
|
|
||||||
|
// 拒绝原因对话框状态
|
||||||
|
const showRejectReasonModal = ref(false);
|
||||||
|
|
||||||
// 消息列表相关状态
|
// 消息列表相关状态
|
||||||
const messageList = ref([]);
|
const messageList = ref([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
@ -188,10 +216,73 @@ const lastFirstMessageId = ref("");
|
|||||||
|
|
||||||
// 判断是否为系统消息
|
// 判断是否为系统消息
|
||||||
function isSystemMessage(message) {
|
function isSystemMessage(message) {
|
||||||
const description = message.payload?.description;
|
if (message.type !== "TIMCustomElem") {
|
||||||
return (
|
return false;
|
||||||
message.type === "TIMCustomElem" && description === "SYSTEM_NOTIFICATION"
|
}
|
||||||
);
|
|
||||||
|
try {
|
||||||
|
// 检查 payload.data 是否包含系统消息标记
|
||||||
|
if (message.payload?.data) {
|
||||||
|
const data = typeof message.payload.data === 'string'
|
||||||
|
? JSON.parse(message.payload.data)
|
||||||
|
: message.payload.data;
|
||||||
|
|
||||||
|
// 检查是否为系统消息类型
|
||||||
|
if (data.type === 'system_message') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 description 是否为系统消息标记
|
||||||
|
if (message.payload?.description === '系统消息标记') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧的系统消息格式
|
||||||
|
if (message.payload?.description === 'SYSTEM_NOTIFICATION') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('判断系统消息失败:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有待接诊的系统消息
|
||||||
|
function checkConsultPendingStatus() {
|
||||||
|
// 查找最新的系统消息
|
||||||
|
for (let i = messageList.value.length - 1; i >= 0; i--) {
|
||||||
|
const message = messageList.value[i];
|
||||||
|
|
||||||
|
if (message.type === "TIMCustomElem" && message.payload?.data) {
|
||||||
|
try {
|
||||||
|
const data = typeof message.payload.data === 'string'
|
||||||
|
? JSON.parse(message.payload.data)
|
||||||
|
: message.payload.data;
|
||||||
|
|
||||||
|
// 如果是 consult_pending 类型的系统消息,显示接受组件
|
||||||
|
if (data.type === 'system_message' && data.messageType === 'consult_pending') {
|
||||||
|
showConsultAccept.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是其他系统消息类型(如已接诊、已结束等),隐藏接受组件
|
||||||
|
if (data.type === 'system_message' &&
|
||||||
|
(data.messageType === 'consult_accepted' ||
|
||||||
|
data.messageType === 'consult_ended' ||
|
||||||
|
data.messageType === 'consult_rejected')) {
|
||||||
|
showConsultAccept.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析系统消息失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到相关系统消息,隐藏接受组件
|
||||||
|
showConsultAccept.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取消息气泡样式类
|
// 获取消息气泡样式类
|
||||||
@ -200,7 +291,6 @@ function getBubbleClass(message) {
|
|||||||
if (message.type === "TIMImageElem") {
|
if (message.type === "TIMImageElem") {
|
||||||
return "image-bubble";
|
return "image-bubble";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === "TIMCustomElem") {
|
if (message.type === "TIMCustomElem") {
|
||||||
return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
|
return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
|
||||||
}
|
}
|
||||||
@ -284,6 +374,10 @@ const initTIMCallbacks = async () => {
|
|||||||
if (!existingMessage) {
|
if (!existingMessage) {
|
||||||
messageList.value.push(message);
|
messageList.value.push(message);
|
||||||
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
|
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
|
||||||
|
|
||||||
|
// 检查是否有待接诊的系统消息
|
||||||
|
checkConsultPendingStatus();
|
||||||
|
|
||||||
// 立即滚动到底部,不使用延迟
|
// 立即滚动到底部,不使用延迟
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scrollToBottom(true);
|
scrollToBottom(true);
|
||||||
@ -342,6 +436,9 @@ const initTIMCallbacks = async () => {
|
|||||||
isCompleted.value = data.isCompleted || false;
|
isCompleted.value = data.isCompleted || false;
|
||||||
isLoadingMore.value = false;
|
isLoadingMore.value = false;
|
||||||
|
|
||||||
|
// 检查是否有待接诊的系统消息
|
||||||
|
checkConsultPendingStatus();
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (data.isRefresh) {
|
if (data.isRefresh) {
|
||||||
console.log("后台刷新完成,保持当前滚动位置");
|
console.log("后台刷新完成,保持当前滚动位置");
|
||||||
@ -484,13 +581,13 @@ const scrollToBottom = (immediate = false) => {
|
|||||||
|
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
// 立即滚动:先清空再设置,触发滚动
|
// 立即滚动:先清空再设置,触发滚动
|
||||||
scrollIntoView.value = '';
|
scrollIntoView.value = "";
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scrollIntoView.value = targetId;
|
scrollIntoView.value = targetId;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 正常滚动,使用短延迟确保DOM更新
|
// 正常滚动,使用短延迟确保DOM更新
|
||||||
scrollIntoView.value = '';
|
scrollIntoView.value = "";
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollIntoView.value = targetId;
|
scrollIntoView.value = targetId;
|
||||||
}, 50);
|
}, 50);
|
||||||
@ -590,6 +687,119 @@ onHide(() => {
|
|||||||
stopIMMonitoring();
|
stopIMMonitoring();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 发送常用语(从常用语页面返回时调用)
|
||||||
|
const sendCommonPhrase = (content) => {
|
||||||
|
if (chatInputRef.value) {
|
||||||
|
chatInputRef.value.sendTextMessageFromPhrase(content);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给常用语页面调用
|
||||||
|
defineExpose({
|
||||||
|
sendCommonPhrase,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理接受问诊
|
||||||
|
const handleAcceptConsult = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '处理中...',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发送接受问诊的系统消息
|
||||||
|
const customMessage = {
|
||||||
|
data: JSON.stringify({
|
||||||
|
type: 'system_message',
|
||||||
|
messageType: 'consult_accepted',
|
||||||
|
content: '医生已接诊',
|
||||||
|
timestamp: Date.now(),
|
||||||
|
}),
|
||||||
|
description: '系统消息标记',
|
||||||
|
extension: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = timChatManager.tim.createCustomMessage({
|
||||||
|
to: chatInfo.value.conversationID.replace('GROUP', ''),
|
||||||
|
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
||||||
|
payload: customMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendResult = await timChatManager.tim.sendMessage(message);
|
||||||
|
|
||||||
|
if (sendResult.code === 0) {
|
||||||
|
showConsultAccept.value = false;
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: '已接受问诊',
|
||||||
|
icon: 'success',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(sendResult.message || '发送失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('接受问诊失败:', error);
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '操作失败',
|
||||||
|
icon: 'none',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理拒绝问诊
|
||||||
|
const handleRejectConsult = () => {
|
||||||
|
// 显示拒绝原因选择对话框
|
||||||
|
showRejectReasonModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理拒绝原因确认
|
||||||
|
const handleRejectReasonConfirm = async (reason) => {
|
||||||
|
try {
|
||||||
|
showRejectReasonModal.value = false;
|
||||||
|
|
||||||
|
uni.showLoading({
|
||||||
|
title: '处理中...',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取医生信息
|
||||||
|
const memberName = account.value?.name || '医生';
|
||||||
|
|
||||||
|
// 获取群组ID
|
||||||
|
const groupId = chatInfo.value.conversationID.replace('GROUP', '');
|
||||||
|
|
||||||
|
// 调用后端接口发送拒绝消息
|
||||||
|
const result = await sendConsultRejectedMessage({
|
||||||
|
groupId,
|
||||||
|
memberName,
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
showConsultAccept.value = false;
|
||||||
|
uni.showToast({
|
||||||
|
title: '已拒绝问诊',
|
||||||
|
icon: 'success',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || '发送失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('拒绝问诊失败:', error);
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '操作失败',
|
||||||
|
icon: 'none',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理拒绝原因取消
|
||||||
|
const handleRejectReasonCancel = () => {
|
||||||
|
showRejectReasonModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
// 页面卸载
|
// 页面卸载
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearMessageCache();
|
clearMessageCache();
|
||||||
@ -599,6 +809,172 @@ onUnmounted(() => {
|
|||||||
timChatManager.setCallback("onMessageReceived", null);
|
timChatManager.setCallback("onMessageReceived", null);
|
||||||
timChatManager.setCallback("onMessageListLoaded", null);
|
timChatManager.setCallback("onMessageListLoaded", null);
|
||||||
timChatManager.setCallback("onError", null);
|
timChatManager.setCallback("onError", null);
|
||||||
|
|
||||||
|
// 移除文章发送监听
|
||||||
|
uni.$off("sendArticle");
|
||||||
|
// 移除问卷发送监听
|
||||||
|
uni.$off("sendSurvey");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听文章发送事件
|
||||||
|
uni.$on("sendArticle", async (data) => {
|
||||||
|
const { article, corpId, userId } = data;
|
||||||
|
|
||||||
|
if (!article || !article._id) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "文章信息不完整",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取环境变量
|
||||||
|
const env = __VITE_ENV__;
|
||||||
|
const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
|
||||||
|
|
||||||
|
// 构建文章链接
|
||||||
|
const articleUrl = `${baseUrl}pages/article/index?id=${article._id}&corpId=${corpId}`;
|
||||||
|
|
||||||
|
// 创建自定义消息
|
||||||
|
const customMessage = {
|
||||||
|
data: JSON.stringify({
|
||||||
|
type: "article",
|
||||||
|
title: article.title || "宣教文章",
|
||||||
|
desc: "宣教文章",
|
||||||
|
url: articleUrl,
|
||||||
|
imgUrl:
|
||||||
|
"https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
|
||||||
|
}),
|
||||||
|
description: "ARTICLE",
|
||||||
|
extension: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送自定义消息
|
||||||
|
const message = timChatManager.tim.createCustomMessage({
|
||||||
|
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
||||||
|
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
||||||
|
payload: customMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendResult = await timChatManager.tim.sendMessage(message);
|
||||||
|
|
||||||
|
if (sendResult.code === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "发送成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 记录发送记录(可选)
|
||||||
|
// await addArticleSendRecord({
|
||||||
|
// corpId,
|
||||||
|
// userId,
|
||||||
|
// articleId: article._id,
|
||||||
|
// customerId: chatInfo.value.userID
|
||||||
|
// });
|
||||||
|
} else {
|
||||||
|
throw new Error(sendResult.message || "发送失败");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("发送文章失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || "发送失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听问卷发送事件
|
||||||
|
uni.$on("sendSurvey", async (data) => {
|
||||||
|
const { survey, corpId, userId, sendSurveyId } = data;
|
||||||
|
|
||||||
|
if (!survey || !survey._id) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "问卷信息不完整",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取环境变量
|
||||||
|
const env = __VITE_ENV__;
|
||||||
|
const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
|
||||||
|
const surveyUrl = env.VITE_SURVEY_URL || "";
|
||||||
|
|
||||||
|
// 获取客户信息
|
||||||
|
const customerId = chatInfo.value.userID || "";
|
||||||
|
const customerName = chatInfo.value.customerName || "";
|
||||||
|
|
||||||
|
// 创建问卷记录
|
||||||
|
const { createSurveyRecord } = await import("@/utils/api.js");
|
||||||
|
const recordRes = await createSurveyRecord({
|
||||||
|
corpId,
|
||||||
|
userId,
|
||||||
|
surveryId: survey._id,
|
||||||
|
memberId: customerId,
|
||||||
|
customer: customerName,
|
||||||
|
sendSurveyId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!recordRes.success) {
|
||||||
|
throw new Error(recordRes.message || "创建问卷记录失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
const answerId = recordRes.data?.id || "";
|
||||||
|
|
||||||
|
// 构建问卷链接
|
||||||
|
let surveyLink = "";
|
||||||
|
if (survey.createBy === "system") {
|
||||||
|
// 系统问卷
|
||||||
|
surveyLink = `${surveyUrl}?corpId=${corpId}&surveyId=${survey.surveyId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`;
|
||||||
|
} else {
|
||||||
|
// 自定义问卷
|
||||||
|
surveyLink = `${baseUrl}pages/survery/fill?corpId=${corpId}&surveryId=${
|
||||||
|
survey._id
|
||||||
|
}&memberId=${customerId}&answerId=${answerId}&name=${encodeURIComponent(
|
||||||
|
customerName
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建自定义消息
|
||||||
|
const customMessage = {
|
||||||
|
data: JSON.stringify({
|
||||||
|
type: "survey",
|
||||||
|
title: survey.name || "填写问卷",
|
||||||
|
desc: "请填写问卷",
|
||||||
|
url: surveyLink,
|
||||||
|
imgUrl:
|
||||||
|
"https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
|
||||||
|
}),
|
||||||
|
description: "SURVEY",
|
||||||
|
extension: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送自定义消息
|
||||||
|
const message = timChatManager.tim.createCustomMessage({
|
||||||
|
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
||||||
|
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
||||||
|
payload: customMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendResult = await timChatManager.tim.sendMessage(message);
|
||||||
|
|
||||||
|
if (sendResult.code === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "发送成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(sendResult.message || "发送失败");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("发送问卷失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || "发送失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
423
pages/message/survey-list.vue
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
<template>
|
||||||
|
<view class="survey-page">
|
||||||
|
<view class="header">
|
||||||
|
<view class="search-bar">
|
||||||
|
<uni-icons type="search" size="18" color="#999" />
|
||||||
|
<input class="search-input" v-model="searchName" placeholder="输入问卷名称搜索" @input="handleSearch" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="content">
|
||||||
|
<view class="category-sidebar">
|
||||||
|
<scroll-view scroll-y class="category-scroll">
|
||||||
|
<view
|
||||||
|
v-for="cate in categoryList"
|
||||||
|
:key="cate._id || 'all'"
|
||||||
|
class="category-item"
|
||||||
|
:class="{ active: currentCateId === (cate._id || 'all') }"
|
||||||
|
@click="selectCategory(cate)"
|
||||||
|
>
|
||||||
|
{{ cate.name }}
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="survey-list">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="survey-scroll"
|
||||||
|
@scrolltolower="loadMore"
|
||||||
|
lower-threshold="50"
|
||||||
|
>
|
||||||
|
<view v-if="loading && surveyList.length === 0" class="loading-container">
|
||||||
|
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="surveyList.length === 0" class="empty-container">
|
||||||
|
<empty-data :title="emptyText || '暂无问卷'" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else>
|
||||||
|
<view
|
||||||
|
v-for="survey in surveyList"
|
||||||
|
:key="survey._id"
|
||||||
|
class="survey-item"
|
||||||
|
>
|
||||||
|
<view class="survey-content" @click="previewSurvey(survey)">
|
||||||
|
<text class="survey-title">{{ survey.name }}</text>
|
||||||
|
<text class="survey-desc">{{ survey.description || '暂无问卷说明' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="survey-action">
|
||||||
|
<button class="send-btn" size="mini" type="primary" @click="sendSurvey(survey)">
|
||||||
|
发送
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="loading && surveyList.length > 0" class="loading-more">
|
||||||
|
<uni-icons type="spinner-cycle" size="20" color="#999" />
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="!loading && surveyList.length >= total" class="no-more">
|
||||||
|
没有更多了
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<button class="cancel-btn" @click="goBack">取消</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { getSurveyCateList, getSurveyList, createSurveyRecord } from '@/utils/api.js';
|
||||||
|
import useAccountStore from '@/store/account.js';
|
||||||
|
import EmptyData from '@/components/empty-data.vue';
|
||||||
|
|
||||||
|
const env = __VITE_ENV__;
|
||||||
|
const accountStore = useAccountStore();
|
||||||
|
const corpId = env.MP_CORP_ID;
|
||||||
|
const userId = ref('');
|
||||||
|
|
||||||
|
// 搜索关键词
|
||||||
|
const searchName = ref('');
|
||||||
|
let searchTimer = null;
|
||||||
|
|
||||||
|
// 分类列表
|
||||||
|
const categoryList = ref([{ _id: '', name: '全部' }]);
|
||||||
|
const currentCateId = ref('');
|
||||||
|
|
||||||
|
// 问卷列表
|
||||||
|
const surveyList = ref([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const page = ref(1);
|
||||||
|
const pageSize = 30;
|
||||||
|
const total = ref(0);
|
||||||
|
const emptyText = ref('');
|
||||||
|
|
||||||
|
// 获取分类列表
|
||||||
|
const getCategoryList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getSurveyCateList({ corpId: corpId });
|
||||||
|
if (res.success && res.data) {
|
||||||
|
const cates = res.data.list || [];
|
||||||
|
categoryList.value = [
|
||||||
|
{ _id: '', name: '全部' },
|
||||||
|
...cates
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取分类列表失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择分类
|
||||||
|
const selectCategory = (cate) => {
|
||||||
|
currentCateId.value = cate._id || '';
|
||||||
|
page.value = 1;
|
||||||
|
surveyList.value = [];
|
||||||
|
loadSurveyList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索处理
|
||||||
|
const handleSearch = () => {
|
||||||
|
if (searchTimer) {
|
||||||
|
clearTimeout(searchTimer);
|
||||||
|
}
|
||||||
|
searchTimer = setTimeout(() => {
|
||||||
|
page.value = 1;
|
||||||
|
surveyList.value = [];
|
||||||
|
loadSurveyList();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载问卷列表
|
||||||
|
const loadSurveyList = async () => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
corpId: corpId,
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize,
|
||||||
|
name: searchName.value.trim(),
|
||||||
|
status: 'enable',
|
||||||
|
showCount: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果选择了分类,添加分类ID
|
||||||
|
if (currentCateId.value) {
|
||||||
|
params.cateIds = [currentCateId.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await getSurveyList(params);
|
||||||
|
if (res.success && res.data) {
|
||||||
|
const { list = [], total: count = 0 } = res.data;
|
||||||
|
|
||||||
|
if (page.value === 1) {
|
||||||
|
surveyList.value = list;
|
||||||
|
} else {
|
||||||
|
surveyList.value = [...surveyList.value, ...list];
|
||||||
|
}
|
||||||
|
total.value = count;
|
||||||
|
emptyText.value = '暂无问卷信息';
|
||||||
|
} else {
|
||||||
|
emptyText.value = res.message || '加载失败';
|
||||||
|
uni.showToast({
|
||||||
|
title: res.message || '获取问卷列表失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载问卷列表失败:', error);
|
||||||
|
emptyText.value = '加载失败';
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载更多
|
||||||
|
const loadMore = () => {
|
||||||
|
if (loading.value || surveyList.value.length >= total.value) return;
|
||||||
|
page.value += 1;
|
||||||
|
loadSurveyList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览问卷
|
||||||
|
const previewSurvey = (survey) => {
|
||||||
|
// 可以跳转到问卷预览页面或显示问卷详情
|
||||||
|
uni.showToast({
|
||||||
|
title: '预览功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成随机字符串
|
||||||
|
const generateRandomString = (length) => {
|
||||||
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送问卷
|
||||||
|
const sendSurvey = async (survey) => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
// 获取医生信息
|
||||||
|
const doctorInfo = accountStore.doctorInfo;
|
||||||
|
userId.value = doctorInfo?.userid || accountStore.openid;
|
||||||
|
|
||||||
|
// 生成发送ID
|
||||||
|
const sendSurveyId = generateRandomString(10);
|
||||||
|
|
||||||
|
// 获取当前聊天的客户信息(从上一页传递)
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const prevPage = pages[pages.length - 2];
|
||||||
|
|
||||||
|
// 这里需要从聊天页面获取客户信息
|
||||||
|
// 暂时使用事件传递方式
|
||||||
|
uni.$emit('sendSurvey', {
|
||||||
|
survey: survey,
|
||||||
|
corpId: corpId,
|
||||||
|
userId: userId.value,
|
||||||
|
sendSurveyId: sendSurveyId
|
||||||
|
});
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '已选择问卷',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟返回
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送问卷失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '发送失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCategoryList();
|
||||||
|
loadSurveyList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.survey-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-sidebar {
|
||||||
|
width: 200rpx;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-right: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-scroll {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item.active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
border-left: 4rpx solid #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-list {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-scroll {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container,
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.4;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.survey-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn {
|
||||||
|
padding: 8rpx 32rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-more,
|
||||||
|
.no-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
124
pages/work/components/cert-popup.vue
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup ref="popup" type="center" :mask-click="false">
|
||||||
|
<view class="bg-white rounded overflow-hidden" style="width: 690rpx;">
|
||||||
|
<template v-if="status === 'unverified'">
|
||||||
|
<view class="px-15 py-12 text-center text-lg font-semibold text-dark">
|
||||||
|
认证须知
|
||||||
|
</view>
|
||||||
|
<view class="text-base text-dark px-15 leading-normal font-semibold mt-10">
|
||||||
|
1、认证通过后,您个人账号病历档案管理数(含所有团队)上限由10个升级至100个;
|
||||||
|
</view>
|
||||||
|
<view class="px-15 leading-normal mt-10 ">
|
||||||
|
<text class="text-base text-dark">2、认证前请仔细核对个人信息,确保准确无误。认证后部分信息不支持修改,包括姓名、岗位等。如需修改以上信息,请联系客服人工处理</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-10 px-15 leading-normal font-semibold pb-50 text-lg text-primary" @click="toService()">点击添加客服
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="status === 'verified'">
|
||||||
|
<view class="px-15 py-12 text-center text-lg font-semibold text-dark">
|
||||||
|
提示
|
||||||
|
</view>
|
||||||
|
<view class="text-base text-dark px-15 leading-normal font-semibold mt-10">
|
||||||
|
您的认证已通过。
|
||||||
|
</view>
|
||||||
|
<view class="px-15 leading-normal mt-10 text-base text-dark">
|
||||||
|
若需要修改姓名、岗位等信息,请联系客服人工处理
|
||||||
|
</view>
|
||||||
|
<view class="mt-10 px-15 leading-normal font-semibold pb-50 text-lg text-primary" @click="toService()">
|
||||||
|
点击添加客服
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="status === 'failed'">
|
||||||
|
<view class="px-15 py-12 text-center text-lg font-semibold text-dark">
|
||||||
|
认证失败原因
|
||||||
|
</view>
|
||||||
|
<view class="px-15 leading-normal mt-10 text-base text-dark pb-50">
|
||||||
|
{{ reason }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<view v-if="btns" class="footer-buttons">
|
||||||
|
<button-footer hideden-shadow :cancelText="btns.cancelText" :confirmText="btns.confirmText"
|
||||||
|
:showCancel="btns.showCancel" :showConfirm="btns.showConfirm" @confirm="confirm()" @cancel="close()" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import api from "@/utils/api.js";
|
||||||
|
import { toast } from '@/utils/widget';
|
||||||
|
|
||||||
|
import ButtonFooter from '@/components/button-footer.vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['close', 'confirm'])
|
||||||
|
const props = defineProps({
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const popup = ref();
|
||||||
|
const status = ref('');
|
||||||
|
const reason = ref('');
|
||||||
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
|
|
||||||
|
const btns = computed(() => {
|
||||||
|
if (status.value === 'unverified') {
|
||||||
|
return { showCancel: true, showConfirm: true, confirmText: '去认证' }
|
||||||
|
}
|
||||||
|
if (status.value === 'verified') {
|
||||||
|
return { showConfirm: false, cancelText: '我知道了' }
|
||||||
|
}
|
||||||
|
if (status.value === 'failed') {
|
||||||
|
return { showCancel: true, showConfirm: true, confirmText: '重新认证' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
emits('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirm() {
|
||||||
|
close()
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/work/profile?type=cert",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toService() {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getStatus() {
|
||||||
|
const res = await api('getMemberVerifyStatus', { corpId: account.value.corpId, weChatOpenId: account.value.openid, id: doctorInfo.value._id })
|
||||||
|
if (res && res.success) {
|
||||||
|
status.value = res.data.verifyStatus;
|
||||||
|
reason.value = res.data.reason || '';
|
||||||
|
popup.value && popup.value.open()
|
||||||
|
} else {
|
||||||
|
toast(res.message);
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.visible, n => {
|
||||||
|
if (n) {
|
||||||
|
getStatus()
|
||||||
|
} else {
|
||||||
|
popup.value && popup.value.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.pb-50 {
|
||||||
|
padding-bottom: 100rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
308
pages/work/profile copy.vue
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
<template>
|
||||||
|
<view class="profile-page">
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<view class="form-section bg-white">
|
||||||
|
<!-- 姓名 -->
|
||||||
|
<form-input
|
||||||
|
name="姓名"
|
||||||
|
:required="true"
|
||||||
|
:form="formData"
|
||||||
|
title="anotherName"
|
||||||
|
@change="handleFieldChange"
|
||||||
|
/>
|
||||||
|
<!-- 头像 -->
|
||||||
|
<common-cell name="头像">
|
||||||
|
<view class="form-content__wrapper" @click="chooseAvatar">
|
||||||
|
<view class="flex-main-content flex items-center">
|
||||||
|
<image
|
||||||
|
v-if="formData.avatar"
|
||||||
|
class="avatar-preview"
|
||||||
|
:src="formData.avatar"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</common-cell>
|
||||||
|
<!-- 性别 -->
|
||||||
|
<form-select
|
||||||
|
name="性别"
|
||||||
|
:form="formData"
|
||||||
|
title="gender"
|
||||||
|
:range="genderOptions"
|
||||||
|
@change="handleFieldChange"
|
||||||
|
/>
|
||||||
|
<!-- 手机号(不可修改) -->
|
||||||
|
<common-cell name="手机号 (不可修改)">
|
||||||
|
<view class="form-content__wrapper">
|
||||||
|
<view class="flex-main-content text-dark">{{ formData.mobile }}</view>
|
||||||
|
</view>
|
||||||
|
</common-cell>
|
||||||
|
<!-- 岗位 -->
|
||||||
|
<form-select
|
||||||
|
name="岗位"
|
||||||
|
:form="formData"
|
||||||
|
title="position"
|
||||||
|
:range="positionOptions"
|
||||||
|
@change="handleFieldChange"
|
||||||
|
/>
|
||||||
|
<!-- 职称 -->
|
||||||
|
<form-select
|
||||||
|
name="职称"
|
||||||
|
:form="formData"
|
||||||
|
title="title"
|
||||||
|
:range="titleOptions"
|
||||||
|
@change="handleFieldChange"
|
||||||
|
/>
|
||||||
|
<!-- 科室 -->
|
||||||
|
<common-cell name="科室">
|
||||||
|
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
||||||
|
<view
|
||||||
|
class="flex-main-content text-right"
|
||||||
|
:class="{ 'text-placeholder': !formData.departmentName }"
|
||||||
|
>
|
||||||
|
{{ formData.departmentName || "请选择" }}
|
||||||
|
</view>
|
||||||
|
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</common-cell>
|
||||||
|
|
||||||
|
<!-- 个人介绍 -->
|
||||||
|
<form-textarea
|
||||||
|
name="个人介绍"
|
||||||
|
:form="formData"
|
||||||
|
title="intro"
|
||||||
|
:word-limit="500"
|
||||||
|
@change="handleFieldChange"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="button-footer">
|
||||||
|
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
||||||
|
<view class="btn btn-save" @click="handleSave">保存</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import { chooseAndUploadImage } from "@/utils/file.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import CommonCell from "@/components/form-template/common-cell.vue";
|
||||||
|
import FormInput from "@/components/form-template/form-cell/form-input.vue";
|
||||||
|
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
|
||||||
|
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({
|
||||||
|
anotherName: "",
|
||||||
|
avatar: "",
|
||||||
|
gender: "",
|
||||||
|
mobile: "",
|
||||||
|
position: "",
|
||||||
|
title: "",
|
||||||
|
department: "",
|
||||||
|
departmentName: "",
|
||||||
|
departmentId: "",
|
||||||
|
intro: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选项数据
|
||||||
|
const genderOptions = [
|
||||||
|
{ label: "男", value: "0" },
|
||||||
|
{ label: "女", value: "1" },
|
||||||
|
];
|
||||||
|
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
||||||
|
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
|
||||||
|
// 字段变更处理
|
||||||
|
const handleFieldChange = (e) => {
|
||||||
|
if (e.title === "gender") {
|
||||||
|
formData.value[e.title] = e.value.value;
|
||||||
|
} else {
|
||||||
|
formData.value[e.title] = e.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择头像
|
||||||
|
const chooseAvatar = async () => {
|
||||||
|
const url = await chooseAndUploadImage();
|
||||||
|
if (url) {
|
||||||
|
formData.value.avatar = url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleCancel = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
// 保存
|
||||||
|
const handleSave = async () => {
|
||||||
|
createDoctorInfo();
|
||||||
|
};
|
||||||
|
useLoad(() => {
|
||||||
|
if (doctorInfo.value) {
|
||||||
|
formData.value = { ...doctorInfo.value };
|
||||||
|
} else {
|
||||||
|
formData.value.mobile = account.value.mobile;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建医生信息
|
||||||
|
const createDoctorInfo = async () => {
|
||||||
|
if (!formData.value.anotherName) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请输入姓名",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
if (res.success && res.data) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "创建成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
await getDoctorInfo();
|
||||||
|
uni.navigateBack();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "创建失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
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({
|
||||||
|
url: "/pages/work/department-select",
|
||||||
|
events: {
|
||||||
|
deptSelected: ({ name, deptId }) => {
|
||||||
|
formData.value.department = name || "";
|
||||||
|
formData.value.departmentName = name || "";
|
||||||
|
formData.value.departmentId = deptId || "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.profile-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-content__wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 28rpx;
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-preview {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-placeholder {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #e5e5e5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.avatar-icon {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
gap: 20rpx;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #007aff;
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-save {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@ -1,215 +1,108 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="profile-page">
|
<full-page>
|
||||||
<!-- 表单区域 -->
|
<view class="p-15">
|
||||||
<view class="form-section bg-white">
|
<view class="bg-white px-10 mb-10 rounded">
|
||||||
<!-- 姓名 -->
|
<form-input :form="formData" :required="rule.anotherName.required" wordLimit="10" title="anotherName"
|
||||||
<form-input
|
:name="rule.anotherName.name" @change="onChange($event)" />
|
||||||
name="姓名"
|
<common-cell title="avatar" name="头像">
|
||||||
:required="true"
|
<view class="flex-grow flex items-center justify-end" @click="chooseAvatar()">
|
||||||
:form="formData"
|
<image v-if="formData.avatar" class="avatar mr-5 rounded-full" :src="formData.avatar" />
|
||||||
title="anotherName"
|
<image v-else class="avatar mr-5 rounded-full" src="/static/default-avatar.png" />
|
||||||
@change="handleFieldChange"
|
<uni-icons color="#999" type="right" size="16" />
|
||||||
/>
|
|
||||||
<!-- 头像 -->
|
|
||||||
<common-cell name="头像">
|
|
||||||
<view class="form-content__wrapper" @click="chooseAvatar">
|
|
||||||
<view class="flex-main-content flex items-center">
|
|
||||||
<image
|
|
||||||
v-if="formData.avatar"
|
|
||||||
class="avatar-preview"
|
|
||||||
:src="formData.avatar"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
|
||||||
</view>
|
</view>
|
||||||
</common-cell>
|
</common-cell>
|
||||||
<!-- 性别 -->
|
<form-select :form="formData" name="性别" title="gender" :range="genderOptions" @change="onChange($event)" />
|
||||||
<form-select
|
<form-input :form="formData" disableChange wordLimit="11" title="mobile" name="手机号 (不可修改)" />
|
||||||
name="性别"
|
|
||||||
:form="formData"
|
|
||||||
title="gender"
|
|
||||||
:range="genderOptions"
|
|
||||||
@change="handleFieldChange"
|
|
||||||
/>
|
|
||||||
<!-- 手机号(不可修改) -->
|
|
||||||
<common-cell name="手机号 (不可修改)">
|
|
||||||
<view class="form-content__wrapper">
|
|
||||||
<view class="flex-main-content text-dark">{{ formData.mobile }}</view>
|
|
||||||
</view>
|
</view>
|
||||||
</common-cell>
|
|
||||||
<!-- 岗位 -->
|
|
||||||
<form-select
|
|
||||||
name="岗位"
|
|
||||||
:form="formData"
|
|
||||||
title="position"
|
|
||||||
:range="positionOptions"
|
|
||||||
@change="handleFieldChange"
|
|
||||||
/>
|
|
||||||
<!-- 职称 -->
|
|
||||||
<form-select
|
|
||||||
name="职称"
|
|
||||||
:form="formData"
|
|
||||||
title="title"
|
|
||||||
:range="titleOptions"
|
|
||||||
@change="handleFieldChange"
|
|
||||||
/>
|
|
||||||
<!-- 科室 -->
|
|
||||||
<common-cell name="科室">
|
|
||||||
<view class="form-content__wrapper" @click="openDepartmentSelect">
|
|
||||||
<view
|
|
||||||
class="flex-main-content text-right"
|
|
||||||
:class="{ 'text-placeholder': !formData.departmentName }"
|
|
||||||
>
|
|
||||||
{{ formData.departmentName || "请选择" }}
|
|
||||||
</view>
|
|
||||||
<uni-icons class="form-arrow" type="arrowright"></uni-icons>
|
|
||||||
</view>
|
|
||||||
</common-cell>
|
|
||||||
|
|
||||||
<!-- 个人介绍 -->
|
<view class="bg-white px-10 mb-10 rounded">
|
||||||
<form-textarea
|
<!-- 填写认证资料的时候岗位必填 -->
|
||||||
name="个人介绍"
|
<common-cell :required="type === 'cert'" title="job" :name="rule.job.name">
|
||||||
:form="formData"
|
<view class="flex-grow flex items-center justify-end" @click="selectJob()">
|
||||||
title="intro"
|
<view v-if="jobStr" class="text-base text-base">{{ jobStr }}</view>
|
||||||
:word-limit="500"
|
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||||
@change="handleFieldChange"
|
<uni-icons color="#999" type="right" size="16" />
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
<!-- 底部按钮 -->
|
</common-cell>
|
||||||
<view class="button-footer">
|
<common-cell title="title" :name="rule.title.name">
|
||||||
<view class="btn btn-cancel" @click="handleCancel">取消</view>
|
<view class="flex-grow flex items-center justify-end">
|
||||||
<view class="btn btn-save" @click="handleSave">保存</view>
|
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||||
|
<uni-icons color="#999" type="right" size="16" />
|
||||||
|
</view>
|
||||||
|
</common-cell>
|
||||||
|
<common-cell title="dept" :name="rule.dept.name">
|
||||||
|
<view class="flex-grow flex items-center justify-end">
|
||||||
|
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
|
||||||
|
<uni-icons color="#999" type="right" size="16" />
|
||||||
|
</view>
|
||||||
|
</common-cell>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="bg-white rounded">
|
||||||
|
<form-textarea autoHeight :border="false" :form="formData" title="intro" name="个人介绍" :wordLimit="300"
|
||||||
|
@change="onChange($event)" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<template #footer>
|
||||||
|
<button-footer :cancelText="cancelText" :confirmText="confirmText" @confirm="save()" @cancel="back()" />
|
||||||
|
</template>
|
||||||
|
</full-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import useGuard from "@/hooks/useGuard.js";
|
|
||||||
import { chooseAndUploadImage } from "@/utils/file.js";
|
|
||||||
import useAccountStore from "@/store/account.js";
|
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import CommonCell from "@/components/form-template/common-cell.vue";
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import api from "@/utils/api.js";
|
||||||
|
import { upload } from "@/utils/http.js";
|
||||||
|
import { toast } from "@/utils/widget";
|
||||||
|
|
||||||
|
import buttonFooter from '@/components/button-footer.vue';
|
||||||
|
import commonCell from "@/components/form-template/common-cell.vue";
|
||||||
import FormInput from "@/components/form-template/form-cell/form-input.vue";
|
import FormInput from "@/components/form-template/form-cell/form-input.vue";
|
||||||
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
|
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
|
||||||
import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
|
||||||
import api from "@/utils/api.js";
|
import fullPage from '@/components/full-page.vue';
|
||||||
|
|
||||||
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
const { useLoad } = useGuard();
|
const { useLoad, useShow } = useGuard();
|
||||||
const { getDoctorInfo } = useAccountStore();
|
const { getDoctorInfo } = useAccountStore();
|
||||||
// 表单数据
|
|
||||||
const formData = ref({
|
const job = { assistant: '医生助理', doctor: '医生' };
|
||||||
anotherName: "",
|
|
||||||
avatar: "",
|
const form = ref({});
|
||||||
gender: "",
|
const type = ref('');
|
||||||
mobile: "",
|
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value, mobile: account.value?.mobile }));
|
||||||
position: "",
|
const cancelText = computed(() => doctorInfo.value ? '取消' : '暂不填写');
|
||||||
title: "",
|
const confirmText = computed(() => type.value === 'cert' ? '下一步' : '保存');
|
||||||
department: "",
|
const jobStr = computed(() => {
|
||||||
departmentName: "",
|
const jobs = formData.value && Array.isArray(formData.value.job) ? formData.value.job.filter(i => i === 'assistant' || i === 'doctor') : [];
|
||||||
departmentId: "",
|
return jobs[0] && job[jobs[0]] ? job[jobs[0]] : '';
|
||||||
intro: "",
|
})
|
||||||
});
|
const rule = computed(() => {
|
||||||
|
if (doctorInfo.value && ['verified', 'verifying'].includes(doctorInfo.value.verifyStatus)) {
|
||||||
|
return {
|
||||||
|
anotherName: { name: '姓名 (不可修改)', required: false, disabled: true },
|
||||||
|
job: { name: '岗位 (不可修改)', disabled: true },
|
||||||
|
title: { name: '职称 (不可修改)', disabled: true },
|
||||||
|
dept: { name: '科室 (不可修改)', disabled: true },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
anotherName: { name: '姓名', required: true, disabled: false },
|
||||||
|
job: { name: '岗位', disabled: false },
|
||||||
|
title: { name: '职称', disabled: false },
|
||||||
|
dept: { name: '科室', disabled: false },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 选项数据
|
// 选项数据
|
||||||
const genderOptions = [
|
const genderOptions = [
|
||||||
{ label: "男", value: "0" },
|
{ label: "男", value: "0" },
|
||||||
{ label: "女", value: "1" },
|
{ label: "女", value: "1" },
|
||||||
];
|
];
|
||||||
const positionOptions = ["医生", "护士", "药师", "技师", "其他"];
|
|
||||||
const titleOptions = ["主任医师", "副主任医师", "主治医师", "医师", "其他"];
|
|
||||||
// 字段变更处理
|
|
||||||
const handleFieldChange = (e) => {
|
|
||||||
if (e.title === "gender") {
|
|
||||||
formData.value[e.title] = e.value.value;
|
|
||||||
} else {
|
|
||||||
formData.value[e.title] = e.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择头像
|
|
||||||
const chooseAvatar = async () => {
|
|
||||||
const url = await chooseAndUploadImage();
|
|
||||||
if (url) {
|
|
||||||
formData.value.avatar = url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleCancel = () => {
|
|
||||||
uni.navigateBack();
|
|
||||||
};
|
|
||||||
// 保存
|
|
||||||
const handleSave = async () => {
|
|
||||||
createDoctorInfo();
|
|
||||||
};
|
|
||||||
useLoad(() => {
|
|
||||||
if (doctorInfo.value) {
|
|
||||||
formData.value = { ...doctorInfo.value };
|
|
||||||
} else {
|
|
||||||
formData.value.mobile = account.value.mobile;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建医生信息
|
|
||||||
const createDoctorInfo = async () => {
|
|
||||||
if (!formData.value.anotherName) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "请输入姓名",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let params = {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
if (res.success && res.data) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "创建成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
await getDoctorInfo();
|
|
||||||
uni.switchTab({
|
|
||||||
url: "/pages/work/work"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: "创建失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
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.switchTab({
|
|
||||||
url: "/pages/work/work"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// 打开科室选择
|
// 打开科室选择
|
||||||
const openDepartmentSelect = () => {
|
const openDepartmentSelect = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
@ -223,89 +116,107 @@ const openDepartmentSelect = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
if (pages.length > 1) {
|
||||||
|
uni.navigateBack();
|
||||||
|
} else {
|
||||||
|
uni.switchTab({
|
||||||
|
url: "/pages/work/work",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseAvatar() {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
success: async (res) => {
|
||||||
|
const [path] = res.tempFilePaths;
|
||||||
|
const url = await upload(path);
|
||||||
|
if (url) {
|
||||||
|
form.value.avatar = url;
|
||||||
|
} else {
|
||||||
|
toast('上传失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange({ title, value }) {
|
||||||
|
form.value[title] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectJob() {
|
||||||
|
if (rule.value.job.disabled) return;
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['医生', '医生助理', '无'],
|
||||||
|
success: ({ tapIndex }) => {
|
||||||
|
const job = ['doctor', 'assistant',][tapIndex];
|
||||||
|
form.value.job = job ? [job] : [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toCert() {
|
||||||
|
if (jobStr.value === '医生') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/work/verify/doctor'
|
||||||
|
})
|
||||||
|
} else if (jobStr.value === '医生助理') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/work/verify/assistant'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toast('请选择岗位信息')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (typeof formData.value.anotherName !== 'string' || !formData.value.anotherName.trim()) {
|
||||||
|
return toast('请输入姓名')
|
||||||
|
}
|
||||||
|
if (type.value === 'cert' && !jobStr.value) {
|
||||||
|
return toast('请选择岗位信息')
|
||||||
|
}
|
||||||
|
const apiName = doctorInfo.value ? 'updateCorpMemberFromWxapp' : 'addCorpMemberFromWxapp';
|
||||||
|
const data = {
|
||||||
|
...form.value,
|
||||||
|
weChatOpenId: account.value.openid,
|
||||||
|
mobile: account.value.mobile,
|
||||||
|
corpId: account.value.corpId,
|
||||||
|
}
|
||||||
|
if (doctorInfo.value) {
|
||||||
|
data.id = doctorInfo.value._id;
|
||||||
|
}
|
||||||
|
const res = await api(apiName, data);
|
||||||
|
if (res && res.success) {
|
||||||
|
await getDoctorInfo()
|
||||||
|
form.value = {};
|
||||||
|
if (type.value === 'cert') {
|
||||||
|
toCert()
|
||||||
|
} else {
|
||||||
|
await toast('保存成功');
|
||||||
|
back()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await toast(res?.message || '保存失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useLoad(opts => {
|
||||||
|
type.value = opts?.type;
|
||||||
|
})
|
||||||
|
|
||||||
|
useShow(() => {
|
||||||
|
getDoctorInfo()
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.profile-page {
|
.avatar {
|
||||||
min-height: 100vh;
|
width: 64rpx;
|
||||||
background: #f5f5f5;
|
height: 64rpx;
|
||||||
padding-bottom: 120rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-section {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-content__wrapper {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 28rpx;
|
|
||||||
|
|
||||||
input {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-placeholder {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-preview {
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #e5e5e5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-placeholder {
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #e5e5e5;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.avatar-icon {
|
|
||||||
font-size: 48rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-footer {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
display: flex;
|
|
||||||
padding: 20rpx 30rpx;
|
|
||||||
background: #fff;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
gap: 20rpx;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
flex: 1;
|
|
||||||
height: 88rpx;
|
|
||||||
line-height: 88rpx;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #007aff;
|
|
||||||
color: #007aff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-save {
|
|
||||||
background: #007aff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
179
pages/work/verify/assistant.vue
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<full-page pageClass="bg-white">
|
||||||
|
<view class="p-15">
|
||||||
|
<view class="mt-15 title-bar relative font-semibold">
|
||||||
|
<text class="mr-5 text-dark text-base">请上传</text>
|
||||||
|
<text class="text-primary text-base">身份证正反面</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-12 flex items-center justify-between">
|
||||||
|
<view class="album border rounded overflow-hidden" @click="uploadIdCard('idCardFront')">
|
||||||
|
<image v-if="form.idCardFront" class="w-full h-full" :src="form.idCardFront" />
|
||||||
|
<view v-else class="relative w-full h-full ">
|
||||||
|
<image class="absolute w-full h-full" src="/static/work/fIDCard.png" />
|
||||||
|
<view class="absolute w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<image class="carmra-icon" src="/static/work/camera.png" />
|
||||||
|
<view class="text-lg font-semibold color-428">上传人像面</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="album border rounded overflow-hidden" @click="uploadIdCard('idCardBack')">
|
||||||
|
<image v-if="form.idCardBack" class="w-full h-full" :src="form.idCardBack" />
|
||||||
|
<view v-else class="relative w-full h-full ">
|
||||||
|
<image class="absolute w-full h-full" src="/static/work/aIDCard.png" />
|
||||||
|
<view class="absolute w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<image class="carmra-icon" src="/static/work/camera.png" />
|
||||||
|
<view class="text-lg font-semibold color-428">上传国徽面</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="exam px-15 rounded-sm leading-normal text-dark text-base">
|
||||||
|
身份证示例
|
||||||
|
</view>
|
||||||
|
<view class="mt-12 flex items-center justify-between">
|
||||||
|
<image class="album" src="/static/work/cardFront.png" />
|
||||||
|
<image class="album" src="/static/work/cardBack.png" />
|
||||||
|
</view>
|
||||||
|
<view class="mt-12 text-dark font-semibold text-base">拍摄须知</view>
|
||||||
|
<view class="mt-10 flex items-center justify-between">
|
||||||
|
<view class="flex flex-col items-center justify-center">
|
||||||
|
<image class="mb-5 exam-icon" src="/static/work/fIDCard.png" />
|
||||||
|
<image class="mb-5 status-icon" src="/static/work/hook.png" />
|
||||||
|
<view class="text-base text-dark">标准</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-col items-center justify-center">
|
||||||
|
<image class="mb-5 exam-icon" src="/static/work/lackCard.png" />
|
||||||
|
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||||
|
<view class="text-base text-dark">缺边</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-col items-center justify-center">
|
||||||
|
<image class="mb-5 exam-icon" src="/static/work/vagueCard.png" />
|
||||||
|
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||||
|
<view class="text-base text-dark">模糊</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-col items-center justify-center">
|
||||||
|
<image class="mb-5 exam-icon" src="/static/work/flashCard.png" />
|
||||||
|
<image class="mb-5 status-icon" src="/static/work/error.png" />
|
||||||
|
<view class="text-base text-dark">闪光</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<template #footer>
|
||||||
|
<button-footer cancelText="上一步" confirmText="提交审核" @confirm="save()" @cancel="back()" />
|
||||||
|
</template>
|
||||||
|
</full-page>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import api from "@/utils/api.js";
|
||||||
|
import { upload } from "@/utils/http.js";
|
||||||
|
import { hideLoading, loading as showLoading, toast } from "@/utils/widget";
|
||||||
|
|
||||||
|
import buttonFooter from '@/components/button-footer.vue';
|
||||||
|
import fullPage from '@/components/full-page.vue';
|
||||||
|
|
||||||
|
useGuard();
|
||||||
|
|
||||||
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
|
const form = ref({})
|
||||||
|
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value }))
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadIdCard(type) {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
success: async (res) => {
|
||||||
|
const [path] = res.tempFilePaths;
|
||||||
|
showLoading('正在上传')
|
||||||
|
const url = await upload(path);
|
||||||
|
hideLoading()
|
||||||
|
if (url) {
|
||||||
|
form.value[type] = url;
|
||||||
|
} else {
|
||||||
|
toast('上传失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (typeof formData.value.idCardFront !== 'string' || formData.value.idCardFront.trim() === '') {
|
||||||
|
return toast('请上传人像面')
|
||||||
|
}
|
||||||
|
if (typeof formData.value.idCardBack !== 'string' || formData.value.idCardBack.trim() === '') {
|
||||||
|
return toast('请上传国徽面')
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
idCardFront: formData.value.idCardFront,
|
||||||
|
idCardBack: formData.value.idCardBack,
|
||||||
|
corpId: account.value.corpId,
|
||||||
|
weChatOpenId: account.value.openid,
|
||||||
|
id: doctorInfo.value._id,
|
||||||
|
}
|
||||||
|
const res = await api('submitCertProfile', data);
|
||||||
|
if (res && res.success) {
|
||||||
|
await toast('提交成功');
|
||||||
|
uni.navigateBack({ delta: 2 })
|
||||||
|
} else {
|
||||||
|
toast(res.message || '提交失败')
|
||||||
|
}
|
||||||
|
console.log('form.value: ', formData.value)
|
||||||
|
|
||||||
|
// uni.showToast({ title: '提交成功', icon: 'none' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.title-bar {
|
||||||
|
padding-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-bar::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
height: 80%;
|
||||||
|
width: 8rpx;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
background: #0074ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album {
|
||||||
|
width: 330rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carmra-icon {
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-428 {
|
||||||
|
color: #428bf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exam {
|
||||||
|
margin-top: 80rpx;
|
||||||
|
background: #dde6f6;
|
||||||
|
padding: 8rpx 30rpx;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exam-icon {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 104rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
154
pages/work/verify/doctor.vue
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<full-page pageClass="bg-white">
|
||||||
|
<view class="p-15">
|
||||||
|
<view class="title-bar relative text-dark text-base font-semibold" @click="log()">请填写执业医院</view>
|
||||||
|
<!-- <view class="mt-12 p-10 flex items-center justify-between border rounded">
|
||||||
|
<view class="w-0 flex-grow truncate text-base mr-10" :class="formData.hospitalName ? 'text-dark' : 'text-gray'">
|
||||||
|
{{ formData.hospitalName || '请填写执业医院' }}
|
||||||
|
</view>
|
||||||
|
<uni-icons class="flex-shrink-0" color="#666" type="right" size="16" />
|
||||||
|
</view> -->
|
||||||
|
<view class="mt-12 p-10 flex items-center justify-between border rounded">
|
||||||
|
<input class="w-0 flex-grow text-base mr-10" :value="formData.hospitalName" @change="inputHospitalName($event)"
|
||||||
|
placeholder="请填写执业医院" />
|
||||||
|
</view>
|
||||||
|
<view class="mt-15 title-bar relative font-semibold">
|
||||||
|
<text class="mr-5 text-dark text-base">请上传</text>
|
||||||
|
<text class="text-primary text-base">医师执业资格证</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-12 flex items-center justify-between">
|
||||||
|
<view class="album border rounded overflow-hidden" @click="uploadLicense('medicalLicenseFront')">
|
||||||
|
<image v-if="formData.medicalLicenseFront" class="w-full h-full" :src="formData.medicalLicenseFront" />
|
||||||
|
<view v-else class="w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<uni-icons color="#666" type="camera" size="36" />
|
||||||
|
<view class="text-dark text-base">上传第一页</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="album border rounded overflow-hidden" @click="uploadLicense('medicalLicenseBack')">
|
||||||
|
<image v-if="formData.medicalLicenseBack" class="w-full h-full" :src="formData.medicalLicenseBack" />
|
||||||
|
<view v-else class="w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<uni-icons color="#666" type="camera" size="36" />
|
||||||
|
<view class="text-dark text-base">上传第二页</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="exam px-15 rounded-sm leading-normal text-dark text-base">
|
||||||
|
证书示例
|
||||||
|
</view>
|
||||||
|
<view class="mt-10 text-sm text-dark leading-normal">1、确保姓名、照片、编号、执业范围、签发机关等清晰可见</view>
|
||||||
|
<view class="mt-10 text-sm text-dark leading-normal">2、需上传证书第一、第二页,图片仅供参考,以实际证书为准</view>
|
||||||
|
<view class="mt-12 flex items-center justify-between">
|
||||||
|
<image class="album" src="/static/work/licenseFront.png" />
|
||||||
|
<image class="album" src="/static/work/licenseBack.png" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<template #footer>
|
||||||
|
<button-footer cancelText="上一步" confirmText="提交审核" @confirm="save()" @cancel="back()" />
|
||||||
|
</template>
|
||||||
|
</full-page>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import api from "@/utils/api.js";
|
||||||
|
import { upload } from "@/utils/http.js";
|
||||||
|
import { hideLoading, loading as showLoading, toast } from "@/utils/widget";
|
||||||
|
|
||||||
|
import buttonFooter from '@/components/button-footer.vue';
|
||||||
|
import fullPage from '@/components/full-page.vue';
|
||||||
|
|
||||||
|
useGuard();
|
||||||
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
|
const form = ref({});
|
||||||
|
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value }))
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputHospitalName(e) {
|
||||||
|
form.value.hospitalName = e.detail.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadLicense(type) {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
success: async (res) => {
|
||||||
|
const [path] = res.tempFilePaths;
|
||||||
|
showLoading('正在上传')
|
||||||
|
const url = await upload(path);
|
||||||
|
hideLoading()
|
||||||
|
if (url) {
|
||||||
|
form.value[type] = url;
|
||||||
|
} else {
|
||||||
|
toast('上传失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (typeof formData.value.hospitalName !== 'string' || formData.value.hospitalName.trim() === '') {
|
||||||
|
return toast('请填写执业医院')
|
||||||
|
}
|
||||||
|
if (typeof formData.value.medicalLicenseFront !== 'string' || formData.value.medicalLicenseFront.trim() === '') {
|
||||||
|
return toast('请上传医师执业资格证第一页')
|
||||||
|
}
|
||||||
|
if (typeof formData.value.medicalLicenseBack !== 'string' || formData.value.medicalLicenseBack.trim() === '') {
|
||||||
|
return toast('请上传医师执业资格证第二页')
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
hospitalName: formData.value.hospitalName,
|
||||||
|
medicalLicenseFront: formData.value.medicalLicenseFront,
|
||||||
|
medicalLicenseBack: formData.value.medicalLicenseBack,
|
||||||
|
corpId: account.value.corpId,
|
||||||
|
weChatOpenId: account.value.openid,
|
||||||
|
id: doctorInfo.value._id,
|
||||||
|
}
|
||||||
|
const res = await api('submitCertProfile', data);
|
||||||
|
if (res && res.success) {
|
||||||
|
await toast('提交成功');
|
||||||
|
uni.navigateBack({ delta: 2 })
|
||||||
|
} else {
|
||||||
|
toast(res.message || '提交失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
// uni.showToast({ title: '提交成功', icon: 'none' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
console.log(doctorInfo.value)
|
||||||
|
console.log(formData.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.title-bar {
|
||||||
|
padding-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-bar::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
height: 80%;
|
||||||
|
width: 8rpx;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
background: #0074ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album {
|
||||||
|
width: 330rpx;
|
||||||
|
height: 240rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exam {
|
||||||
|
margin-top: 80rpx;
|
||||||
|
background: #dde6f6;
|
||||||
|
padding: 8rpx 30rpx;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,30 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="work-page">
|
<full-page :showSafeArea="false" :customScroll="true">
|
||||||
<!-- 顶部用户信息区域 -->
|
<template #header>
|
||||||
<view class="user-header bg-white px-15 py-15">
|
<view class="user-header bg-white px-15 py-15">
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between" @click="editProfile()">
|
||||||
<!-- 左侧:用户头像和信息 -->
|
|
||||||
<view class="flex items-center flex-grow">
|
<view class="flex items-center flex-grow">
|
||||||
<view class="user-avatar mr-10">
|
<view class="relative user-avatar mr-10">
|
||||||
<image
|
<image v-if="doctorInfo && doctorInfo.avatar" class="avatar-img rounded-full overflow-hidden"
|
||||||
class="avatar-img"
|
:src="doctorInfo.avatar" mode="aspectFill" />
|
||||||
src="/static/default-avatar.png"
|
<image v-else class="avatar-img rounded-full overflow-hidden" src="/static/default-avatar.png"
|
||||||
mode="aspectFill"
|
mode="aspectFill" />
|
||||||
/>
|
<view v-if="doctorInfo" class="edit-sub flex items-center justify-center rounded-full bg-primary">
|
||||||
|
<image class="edit-icon" src="/static/work/pen.svg" mode="aspectFill" />
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-col">
|
<view class="flex-col">
|
||||||
<text class="user-name text-black text-lg font-semibold"
|
<text v-if="doctorInfo && doctorInfo.anotherName" class="user-name text-dark text-lg font-semibold">
|
||||||
>请完善信息</text
|
{{ doctorInfo.anotherName }}
|
||||||
>
|
</text>
|
||||||
|
<text v-else class="user-name text-black text-lg font-semibold">请完善信息</text>
|
||||||
<view class="flex items-center mt-5">
|
<view class="flex items-center mt-5">
|
||||||
<view
|
<view v-if="!doctorInfo || !doctorInfo.anotherName" class="status-tag tag-orange mr-10">
|
||||||
class="status-tag tag-orange mr-10"
|
|
||||||
@click="handleCompleteInfo"
|
|
||||||
>
|
|
||||||
<text class="tag-text text-white">信息待完善</text>
|
<text class="tag-text text-white">信息待完善</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="status-tag tag-gray" @click="handleVerify">
|
<view v-if="certStatus" class="px-10 py-3 text-sm rounded-full" :class="certStatus.classnames"
|
||||||
<text class="tag-text text-dark">未认证</text>
|
@click.stop="handleCert()">
|
||||||
|
{{ certStatus.text }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -32,49 +32,64 @@
|
|||||||
|
|
||||||
<!-- 右侧:操作按钮 -->
|
<!-- 右侧:操作按钮 -->
|
||||||
<view class="flex items-center">
|
<view class="flex items-center">
|
||||||
<view
|
<view class="action-btn flex-col items-center mr-10" @click="handleInvitePatient">
|
||||||
class="action-btn flex-col items-center mr-15"
|
<image class="mb-5 qrcode-icon" src="/static/work/qrcode.svg" />
|
||||||
@click="handleInvitePatient"
|
<text class="action-text text-dark text-sm">邀请</text>
|
||||||
>
|
|
||||||
<view class="qrcode-icon">
|
|
||||||
<text class="qrcode-text">▦</text>
|
|
||||||
</view>
|
</view>
|
||||||
<text class="action-text text-black text-sm mt-5">邀请患者</text>
|
<view class="action-btn flex-col items-center" @click="handleMore">
|
||||||
</view>
|
<image class="mb-5 qrcode-icon" src="/static/work/more.svg" />
|
||||||
<view class="more-btn" @click="handleMore">
|
<text class="action-text text-dark text-sm">更多</text>
|
||||||
<text class="more-icon">⋯</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="mt-15 px-15 py-12 text-dark text-lg font-semibold bg-white border-b">
|
||||||
|
待办列表11
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 待办列表区域 -->
|
<scroll-view v-if="list.length" scroll-y="true" class="h-full bg-white">
|
||||||
<view class="todo-section px-15 mt-15">
|
<view class="p-15">
|
||||||
<view class="section-title mb-15">
|
<view v-for="i in 10" class="p-15 bg-primary mb-10"></view>
|
||||||
<text class="text-black text-lg font-semibold">待办列表11</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<view v-else class="flex flex-col items-center justify-center h-full bg-white">
|
||||||
<view class="empty-state">
|
|
||||||
<empty-data text="暂无记录" />
|
<empty-data text="暂无记录" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
<template #footer>
|
||||||
</view>
|
<view class="border-b"></view>
|
||||||
|
</template>
|
||||||
|
</full-page>
|
||||||
|
<cert-popup :visible="visible" @close="visible = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
import useGuard from "@/hooks/useGuard.js";
|
import useGuard from "@/hooks/useGuard.js";
|
||||||
|
import useAccountStore from "@/store/account.js";
|
||||||
|
|
||||||
|
import certPopup from "./components/cert-popup.vue";
|
||||||
import EmptyData from "@/components/empty-data.vue";
|
import EmptyData from "@/components/empty-data.vue";
|
||||||
|
import fullPage from '@/components/full-page.vue';
|
||||||
|
import { toast } from '@/utils/widget';
|
||||||
|
|
||||||
const { useLoad } = useGuard();
|
const certConfig = {
|
||||||
|
failed: { text: '认证失败', classnames: 'bg-danger text-white' },
|
||||||
// 完善信息
|
verified: { text: '已认证', classnames: 'bg-success text-white' },
|
||||||
const handleCompleteInfo = () => {
|
verifying: { text: '认证中', classnames: 'bg-warning text-white' },
|
||||||
uni.navigateTo({
|
unverified: { text: '未认证', classnames: 'bg-gray text-dark' },
|
||||||
url: "/pages/work/profile",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { useLoad, useShow } = useGuard();
|
||||||
|
const { getDoctorInfo } = useAccountStore();
|
||||||
|
const { account, doctorInfo } = storeToRefs(useAccountStore());
|
||||||
|
const list = ref([1]);
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
const certStatus = computed(() => doctorInfo.value?.verifyStatus ? certConfig[doctorInfo.value.verifyStatus] : null)
|
||||||
|
|
||||||
// 认证
|
// 认证
|
||||||
const handleVerify = () => {
|
const handleVerify = () => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -101,15 +116,41 @@ const handleMore = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function editProfile() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/work/profile",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCert() {
|
||||||
|
if (doctorInfo.value.verifyStatus === 'verifying') {
|
||||||
|
toast('信息认证中,请耐心等待!')
|
||||||
|
} else {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useLoad(() => {
|
useLoad(() => {
|
||||||
console.log("工作台页面加载");
|
console.log("工作台页面加载");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useShow(() => {
|
||||||
|
getDoctorInfo();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.work-page {
|
.edit-sub {
|
||||||
min-height: 100vh;
|
width: 36rpx;
|
||||||
background: #f5f5f5;
|
height: 36rpx;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-header {
|
.user-header {
|
||||||
@ -119,9 +160,6 @@ useLoad(() => {
|
|||||||
.user-avatar {
|
.user-avatar {
|
||||||
width: 80rpx;
|
width: 80rpx;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #e5e5e5;
|
|
||||||
|
|
||||||
.avatar-img {
|
.avatar-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -133,6 +171,11 @@ useLoad(() => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.py-3 {
|
||||||
|
padding-top: 6rpx;
|
||||||
|
padding-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.status-tag {
|
.status-tag {
|
||||||
padding: 6rpx 16rpx;
|
padding: 6rpx 16rpx;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
@ -162,17 +205,8 @@ useLoad(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.qrcode-icon {
|
.qrcode-icon {
|
||||||
width: 48rpx;
|
width: 40rpx;
|
||||||
height: 48rpx;
|
height: 40rpx;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.qrcode-text {
|
|
||||||
font-size: 40rpx;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-text {
|
.action-text {
|
||||||
@ -209,5 +243,4 @@ useLoad(() => {
|
|||||||
// align-items: center;
|
// align-items: center;
|
||||||
// justify-content: center;
|
// justify-content: center;
|
||||||
// width: 100%;
|
// width: 100%;
|
||||||
// }
|
// }</style>
|
||||||
</style>
|
|
||||||
@ -9,8 +9,12 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/message/message',
|
path: 'pages/message/message',
|
||||||
meta: { title: '消息', login: false },
|
meta: { title: '消息' }
|
||||||
style: { navigationStyle: 'custom' }
|
},
|
||||||
|
{
|
||||||
|
path: 'pages/message/index',
|
||||||
|
meta: { title: '聊天' },
|
||||||
|
style: { enablePullDownRefresh: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/message/index',
|
path: 'pages/message/index',
|
||||||
@ -103,14 +107,30 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/work',
|
path: 'pages/work/work',
|
||||||
meta: { title: '工作台', login: false }
|
meta: { title: '工作台' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pages/case/case',
|
||||||
|
meta: { title: '病例' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/profile',
|
path: 'pages/work/profile',
|
||||||
meta: { title: '完善个人信息', login: false }
|
meta: { title: '完善个人信息' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'pages/work/department-select',
|
path: 'pages/work/department-select',
|
||||||
meta: { title: '选择科室', login: false }
|
meta: { title: '选择科室' }
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: 'pages/work/verify/assistant',
|
||||||
|
meta: { title: '上传证照', login: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pages/work/verify/doctor',
|
||||||
|
meta: { title: '上传证照', login: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pages/login/login',
|
||||||
|
meta: { title: '授权登录' }
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
BIN
static/work/aIDCard.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
static/work/camera.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/work/cardBack.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
static/work/cardFront.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
static/work/error.png
Normal file
|
After Width: | Height: | Size: 960 B |
BIN
static/work/fIDCard.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
static/work/flashCard.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
static/work/hook.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/work/lackCard.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
static/work/licenseBack.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
static/work/licenseFront.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
1
static/work/more.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769069890058" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14332" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M378.112 481.536H159.488c-51.2 0-92.928-41.728-92.928-92.928V169.984c0-51.2 41.728-92.928 92.928-92.928h218.624c51.2 0 92.928 41.728 92.928 92.928v218.624c0 51.2-41.728 92.928-92.928 92.928zM159.488 138.752c-17.408 0-31.488 14.08-31.488 31.488v218.624c0 17.408 14.08 31.488 31.488 31.488h218.624c17.408 0 31.488-14.08 31.488-31.488V169.984c0-17.408-14.08-31.488-31.488-31.488H159.488zM378.112 949.504H159.488c-51.2 0-92.928-41.728-92.928-92.928v-218.624c0-51.2 41.728-92.928 92.928-92.928h218.624c51.2 0 92.928 41.728 92.928 92.928v218.624c0 51.456-41.728 92.928-92.928 92.928zM159.488 606.72c-17.408 0-31.488 14.08-31.488 31.488v218.624c0 17.408 14.08 31.488 31.488 31.488h218.624c17.408 0 31.488-14.08 31.488-31.488v-218.624c0-17.408-14.08-31.488-31.488-31.488H159.488zM680.448 480l-143.616-144.128c-33.536-33.792-33.536-88.576 0.256-122.112l144.128-143.616c33.792-33.536 88.576-33.536 122.112 0.256l143.616 144.128c33.536 33.792 33.536 88.576-0.256 122.112l-144.128 143.616c-33.792 33.536-88.32 33.536-122.112-0.256zM762.88 110.848c-11.264-11.52-29.952-11.52-41.216 0l-144.128 143.616c-11.52 11.264-11.52 29.952 0 41.216l143.616 144.128c11.264 11.52 29.952 11.52 41.216 0l144.128-143.616c11.52-11.264 11.52-29.952 0-41.216L762.88 110.848zM840.96 949.504l-218.624-0.512c-51.2 0-92.672-41.728-92.672-92.928l0.512-218.624c0-51.2 41.728-92.672 92.928-92.672l218.624 0.512c51.2 0 92.672 41.728 92.672 92.928l-0.512 218.624c0 51.2-41.728 92.928-92.928 92.672z m-217.856-343.04c-17.408 0-31.488 14.08-31.488 31.232l-0.512 218.624c0 17.408 14.08 31.488 31.232 31.488l218.624 0.512c17.408 0 31.488-14.08 31.488-31.232l0.512-218.624c0-17.408-14.08-31.488-31.232-31.488l-218.624-0.512z" fill="#2c2c2c" p-id="14333"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
1
static/work/pen.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769067872142" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10445" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M581.50172 186.48l256.04 256.04-555.98 555.98-228.28 25.2C22.72172 1027.08-3.09828 1001.24 0.30172 970.68l25.4-228.44 555.8-555.76z m414.4-38.12l-120.22-120.22c-37.5-37.5-98.32-37.5-135.82 0l-113.1 113.1 256.04 256.04 113.1-113.1c37.5-37.52 37.5-98.32 0-135.82z" fill="#ffffff" p-id="10446"></path></svg>
|
||||||
|
After Width: | Height: | Size: 638 B |
1
static/work/qrcode.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769069475491" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11505" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M85.312 85.312V384H384V85.312H85.312zM0 0h469.248v469.248H0V0z m170.624 170.624h128v128h-128v-128zM0 554.624h469.248v469.248H0V554.624z m85.312 85.312v298.624H384V639.936H85.312z m85.312 85.312h128v128h-128v-128zM554.624 0h469.248v469.248H554.624V0z m85.312 85.312V384h298.624V85.312H639.936z m383.936 682.56H1024v85.376h-298.752V639.936H639.936V1023.872H554.624V554.624h255.936v213.248h128V554.624h85.312v213.248z m-298.624-597.248h128v128h-128v-128z m298.624 853.248h-85.312v-85.312h85.312v85.312z m-213.312 0h-85.312v-85.312h85.312v85.312z" fill="#262626" p-id="11506"></path></svg>
|
||||||
|
After Width: | Height: | Size: 919 B |
BIN
static/work/vagueCard.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
@ -8,17 +8,29 @@ const env = __VITE_ENV__;
|
|||||||
|
|
||||||
export default defineStore("accountStore", () => {
|
export default defineStore("accountStore", () => {
|
||||||
const appid = env.MP_WX_APP_ID;
|
const appid = env.MP_WX_APP_ID;
|
||||||
|
const corpId = env.MP_CORP_ID;
|
||||||
const account = ref(null);
|
const account = ref(null);
|
||||||
const loading = ref(false)
|
const loading = ref(false);
|
||||||
|
const loginPromise = ref(null);
|
||||||
// IM 相关
|
// IM 相关
|
||||||
const openid = ref("");
|
const openid = ref("");
|
||||||
const isIMInitialized = ref(false);
|
const isIMInitialized = ref(false);
|
||||||
// 医生信息
|
// 医生信息
|
||||||
const doctorInfo = ref(null);
|
const doctorInfo = ref(null);
|
||||||
|
|
||||||
async function login(phoneCode = '') {
|
function getLoginPromise(phoneCode = '') {
|
||||||
if (loading.value) return;
|
if (loginPromise.value) return loginPromise.value;
|
||||||
loading.value = true;
|
loginPromise.value = loginByCode(phoneCode);
|
||||||
|
return loginPromise.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(phoneCode) {
|
||||||
|
const res = await getLoginPromise(phoneCode);
|
||||||
|
loginPromise.value = null;
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loginByCode(phoneCode = '') {
|
||||||
try {
|
try {
|
||||||
const { code } = await uni.login({
|
const { code } = await uni.login({
|
||||||
appid,
|
appid,
|
||||||
@ -29,8 +41,8 @@ export default defineStore("accountStore", () => {
|
|||||||
const res = await api('wxAppLogin', {
|
const res = await api('wxAppLogin', {
|
||||||
phoneCode,
|
phoneCode,
|
||||||
code,
|
code,
|
||||||
|
corpId,
|
||||||
});
|
});
|
||||||
loading.value = false;
|
|
||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
if (!res.data.mobile) {
|
if (!res.data.mobile) {
|
||||||
const target = '/pages/login/login';
|
const target = '/pages/login/login';
|
||||||
@ -39,27 +51,26 @@ export default defineStore("accountStore", () => {
|
|||||||
}
|
}
|
||||||
account.value = res.data;
|
account.value = res.data;
|
||||||
openid.value = res.data.openid;
|
openid.value = res.data.openid;
|
||||||
|
|
||||||
|
|
||||||
// 登录成功后初始化腾讯IM
|
// 登录成功后初始化腾讯IM
|
||||||
try {
|
// try {
|
||||||
console.log('开始初始化腾讯IM,userID:', res.data.openid);
|
// console.log('开始初始化腾讯IM,userID:', res.data.openid);
|
||||||
await initGlobalTIM(res.data.openid);
|
// await initGlobalTIM(res.data.openid);
|
||||||
isIMInitialized.value = true;
|
// isIMInitialized.value = true;
|
||||||
console.log('腾讯IM初始化成功');
|
// console.log('腾讯IM初始化成功');
|
||||||
} catch (imError) {
|
// } catch (imError) {
|
||||||
console.error('腾讯IM初始化失败:', imError);
|
// console.error('腾讯IM初始化失败:', imError);
|
||||||
// IM初始化失败不影响登录流程
|
// // IM初始化失败不影响登录流程
|
||||||
}
|
// }
|
||||||
await getDoctorInfo(openid.value);
|
await getDoctorInfo(openid.value);
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toast('登录失败,请重新登录');
|
toast('登录失败,请重新登录');
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast('登录失败,请重新登录');
|
toast('登录失败,请重新登录');
|
||||||
}
|
}
|
||||||
loading.value = false
|
return Promise.reject()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDoctorInfo() {
|
async function getDoctorInfo() {
|
||||||
|
|||||||
79
utils/api.js
@ -12,13 +12,41 @@ const urlsConfig = {
|
|||||||
getHospitalList: 'getRealHospital',
|
getHospitalList: 'getRealHospital',
|
||||||
addCorpMember: 'addCorpMember',
|
addCorpMember: 'addCorpMember',
|
||||||
getCorpMemberData: 'getCorpMemberData',
|
getCorpMemberData: 'getCorpMemberData',
|
||||||
updateCorpMember: 'updateCorpMember'
|
updateCorpMember: 'updateCorpMember',
|
||||||
|
addCorpMemberFromWxapp: "addCorpMemberFromWxapp",
|
||||||
|
updateCorpMemberFromWxapp: "updateCorpMemberFromWxapp",
|
||||||
|
submitCertProfile: 'submitCertProfile',
|
||||||
|
getMemberVerifyStatus: "getMemberVerifyStatus"
|
||||||
},
|
},
|
||||||
|
|
||||||
knowledgeBase: {
|
knowledgeBase: {
|
||||||
getArticleByIds: 'getArticleByIds',
|
getArticleByIds: 'getArticleByIds',
|
||||||
// 诊断库(对齐 ykt-management-mobile/src/api/knowledgeBase.js)
|
// 诊断库(对齐 ykt-management-mobile/src/api/knowledgeBase.js)
|
||||||
getDisease: 'getDisease',
|
getDisease: 'getDisease',
|
||||||
|
getCommonPhrases: 'getCommonPhrases',
|
||||||
|
saveCommonPhrase: 'saveCommonPhrase',
|
||||||
|
deleteCommonPhrase: 'deleteCommonPhrase',
|
||||||
|
getCommonPhraseCategories: 'getCommonPhraseCategories',
|
||||||
|
saveCommonPhraseCategory: 'saveCommonPhraseCategory',
|
||||||
|
// 个人常用语接口
|
||||||
|
getPersonalPhrases: 'getPersonalPhrases',
|
||||||
|
savePersonalPhrase: 'savePersonalPhrase',
|
||||||
|
deletePersonalPhrase: 'deletePersonalPhrase',
|
||||||
|
getPersonalPhraseCategories: 'getPersonalPhraseCategories',
|
||||||
|
savePersonalPhraseCategory: 'savePersonalPhraseCategory',
|
||||||
|
deletePersonalPhraseCategory: 'deletePersonalPhraseCategory',
|
||||||
|
// 宣教文章接口
|
||||||
|
getArticleCateList: 'getArticleCateList',
|
||||||
|
getArticleList: 'getArticleList',
|
||||||
|
getArticle: 'getArticle',
|
||||||
|
addArticleSendRecord: 'addArticleSendRecord'
|
||||||
|
},
|
||||||
|
|
||||||
|
survery: {
|
||||||
|
getSurveyCateList: 'getSurveryCateList',
|
||||||
|
getSurveyList: 'getList',
|
||||||
|
createSurveyRecord: 'createRecord',
|
||||||
|
getSurveyDetail: 'getDetail'
|
||||||
},
|
},
|
||||||
member: {
|
member: {
|
||||||
addCustomer: 'add',
|
addCustomer: 'add',
|
||||||
@ -70,6 +98,8 @@ const urlsConfig = {
|
|||||||
addServiceRecord: 'addServiceRecord',
|
addServiceRecord: 'addServiceRecord',
|
||||||
updateServiceRecord: 'updateServiceRecord',
|
updateServiceRecord: 'updateServiceRecord',
|
||||||
removeServiceRecord: 'removeServiceRecord',
|
removeServiceRecord: 'removeServiceRecord',
|
||||||
|
getChatRecordsByGroupId: "getChatRecordsByGroupId",
|
||||||
|
sendConsultRejectedMessage: "sendConsultRejectedMessage"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -102,3 +132,50 @@ export default async function api(urlId, data) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 宣教文章相关 API
|
||||||
|
export async function getArticleCateList(data) {
|
||||||
|
return api('getArticleCateList', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArticleList(data) {
|
||||||
|
return api('getArticleList', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArticle(data) {
|
||||||
|
return api('getArticle', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addArticleSendRecord(data) {
|
||||||
|
return api('addArticleSendRecord', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 问卷相关 API
|
||||||
|
export async function getSurveyCateList(data) {
|
||||||
|
return api('getSurveyCateList', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSurveyList(data) {
|
||||||
|
return api('getSurveyList', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createSurveyRecord(data) {
|
||||||
|
return api('createSurveyRecord', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSurveyDetail(data) {
|
||||||
|
return api('getSurveyDetail', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// IM 系统消息相关 API
|
||||||
|
export async function sendConsultRejectedMessage(data) {
|
||||||
|
return request({
|
||||||
|
url: '/getYoucanData/im',
|
||||||
|
data: {
|
||||||
|
type: 'sendConsultRejectedMessage',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -25,10 +25,10 @@ export const checkConsultationStatus = (waitingForDoctor, consultationEnded) =>
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// 检查IM连接状态
|
// 检查IM连接状态
|
||||||
export const checkIMConnection = (timChatManager) => {
|
export const checkIMConnection = (timChatManager) => {
|
||||||
if (!timChatManager.tim || !timChatManager.isLoggedIn) {
|
if (!timChatManager.tim || !timChatManager.isLoggedIn) {
|
||||||
|
// showMessage("IM连接异常,请重新进入");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -346,133 +346,31 @@ export const chooseMedia = async (options, onSuccess, onFail) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择图片(针对 TIM SDK 优化)
|
* 选择图片
|
||||||
* @param {function} onSuccess - 成功回调
|
* @param {function} onSuccess - 成功回调
|
||||||
* @param {function} onFail - 失败回调
|
* @param {function} onFail - 失败回调
|
||||||
*/
|
*/
|
||||||
export const chooseImage = async (onSuccess, onFail) => {
|
export const chooseImage = (onSuccess, onFail) => {
|
||||||
// 检查权限
|
|
||||||
const sourceType = ['album', 'camera'];
|
|
||||||
if (sourceType.includes('album')) {
|
|
||||||
const hasPermission = await checkAlbumPermission();
|
|
||||||
if (!hasPermission) {
|
|
||||||
console.log('用户未授予相册权限');
|
|
||||||
if (onFail) {
|
|
||||||
onFail({ errMsg: '未授权相册权限' });
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 wx.chooseImage 以确保与 TIM SDK 兼容
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
wx.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
sizeType: ['original', 'compressed'],
|
|
||||||
sourceType: sourceType,
|
|
||||||
success: function (res) {
|
|
||||||
console.log('wx.chooseImage 成功,完整返回数据:', JSON.stringify(res));
|
|
||||||
console.log('tempFilePaths:', res.tempFilePaths);
|
|
||||||
console.log('tempFiles:', res.tempFiles);
|
|
||||||
|
|
||||||
// TIM SDK 需要完整的 wx.chooseImage 返回对象,而不是单个文件
|
|
||||||
// 直接传递整个 res 对象
|
|
||||||
if (onSuccess) onSuccess(res);
|
|
||||||
},
|
|
||||||
fail: function (err) {
|
|
||||||
// 用户取消选择
|
|
||||||
if (err.errMsg && err.errMsg.includes('cancel')) {
|
|
||||||
console.log('用户取消选择');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 权限相关错误
|
|
||||||
if (err.errMsg && (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('选择图片失败,请重试');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
// 非微信小程序环境,使用 uni.chooseMedia
|
|
||||||
chooseMedia({
|
chooseMedia({
|
||||||
count: 1,
|
count: 1,
|
||||||
mediaType: ['image'],
|
mediaType: ['image'],
|
||||||
sizeType: ['original', 'compressed'],
|
sizeType: ['original', 'compressed'],
|
||||||
sourceType: sourceType
|
sourceType: ['album', 'camera']
|
||||||
}, onSuccess, onFail);
|
}, onSuccess, onFail);
|
||||||
// #endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拍照(针对 TIM SDK 优化)
|
* 拍照
|
||||||
* @param {function} onSuccess - 成功回调
|
* @param {function} onSuccess - 成功回调
|
||||||
* @param {function} onFail - 失败回调
|
* @param {function} onFail - 失败回调
|
||||||
*/
|
*/
|
||||||
export const takePhoto = (onSuccess, onFail) => {
|
export const takePhoto = (onSuccess, onFail) => {
|
||||||
// 使用 wx.chooseImage 以确保与 TIM SDK 兼容
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
wx.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
sizeType: ['original', 'compressed'],
|
|
||||||
sourceType: ['camera'],
|
|
||||||
success: function (res) {
|
|
||||||
console.log('wx.chooseImage (拍照) 成功,完整返回数据:', JSON.stringify(res));
|
|
||||||
console.log('tempFilePaths:', res.tempFilePaths);
|
|
||||||
console.log('tempFiles:', res.tempFiles);
|
|
||||||
|
|
||||||
// TIM SDK 需要完整的 wx.chooseImage 返回对象
|
|
||||||
if (onSuccess) onSuccess(res);
|
|
||||||
},
|
|
||||||
fail: function (err) {
|
|
||||||
// 用户取消
|
|
||||||
if (err.errMsg && err.errMsg.includes('cancel')) {
|
|
||||||
console.log('用户取消拍照');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('拍照失败:', err);
|
|
||||||
if (onFail) {
|
|
||||||
onFail(err);
|
|
||||||
} else {
|
|
||||||
showMessage('拍照失败,请重试');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
// 非微信小程序环境
|
|
||||||
chooseMedia({
|
chooseMedia({
|
||||||
count: 1,
|
count: 1,
|
||||||
mediaType: ['image'],
|
mediaType: ['image'],
|
||||||
sizeType: ['original', 'compressed'],
|
sizeType: ['original', 'compressed'],
|
||||||
sourceType: ['camera']
|
sourceType: ['camera']
|
||||||
}, onSuccess, onFail);
|
}, onSuccess, onFail);
|
||||||
// #endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================== 录音相关工具方法 ====================
|
// ==================== 录音相关工具方法 ====================
|
||||||
@ -588,8 +486,6 @@ export const sendCustomMessage = async (messageData, timChatManager, validateBef
|
|||||||
* @param {function} onSuccess - 成功回调
|
* @param {function} onSuccess - 成功回调
|
||||||
*/
|
*/
|
||||||
export const sendMessage = async (messageType, data, timChatManager, validateBeforeSend, onSuccess, cloudCustomData) => {
|
export const sendMessage = async (messageType, data, timChatManager, validateBeforeSend, onSuccess, cloudCustomData) => {
|
||||||
console.log('chat-utils sendMessage 被调用:', { messageType, data });
|
|
||||||
|
|
||||||
if (!validateBeforeSend()) {
|
if (!validateBeforeSend()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -601,9 +497,7 @@ export const sendMessage = async (messageType, data, timChatManager, validateBef
|
|||||||
result = await timChatManager.sendTextMessage(data, cloudCustomData);
|
result = await timChatManager.sendTextMessage(data, cloudCustomData);
|
||||||
break;
|
break;
|
||||||
case 'image':
|
case 'image':
|
||||||
console.log('准备发送图片消息,数据:', data);
|
|
||||||
result = await timChatManager.sendImageMessage(data, cloudCustomData);
|
result = await timChatManager.sendImageMessage(data, cloudCustomData);
|
||||||
console.log('图片消息发送结果:', result);
|
|
||||||
break;
|
break;
|
||||||
case 'voice':
|
case 'voice':
|
||||||
result = await timChatManager.sendVoiceMessage(data.file, data.duration,cloudCustomData);
|
result = await timChatManager.sendVoiceMessage(data.file, data.duration,cloudCustomData);
|
||||||
@ -614,7 +508,6 @@ export const sendMessage = async (messageType, data, timChatManager, validateBef
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result && result.success) {
|
if (result && result.success) {
|
||||||
console.log('消息发送成功');
|
|
||||||
if (onSuccess) onSuccess();
|
if (onSuccess) onSuccess();
|
||||||
} else {
|
} else {
|
||||||
console.error('发送消息失败:', result?.error);
|
console.error('发送消息失败:', result?.error);
|
||||||
|
|||||||
@ -97,7 +97,7 @@ async function refreshAccessToken() {
|
|||||||
const request = async (options = {}, showLoading = true) => {
|
const request = async (options = {}, showLoading = true) => {
|
||||||
// 合并用户传入的配置和默认配置
|
// 合并用户传入的配置和默认配置
|
||||||
if (!options.data) options.data = {};
|
if (!options.data) options.data = {};
|
||||||
if(!options.data.corpId) {
|
if (!options.data.corpId) {
|
||||||
options.data.corpId = env.MP_CORP_ID;
|
options.data.corpId = env.MP_CORP_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,5 +206,28 @@ export default request;
|
|||||||
export const uploadUrl = `${baseUrl}/upload`;
|
export const uploadUrl = `${baseUrl}/upload`;
|
||||||
|
|
||||||
export function getFullPath(path) {
|
export function getFullPath(path) {
|
||||||
return `${baseUrl}${path}`;
|
return `${baseUrl}/${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function upload(path) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: uploadUrl, // 替换为你的上传接口地址
|
||||||
|
filePath: path,
|
||||||
|
name: 'file',
|
||||||
|
fileType: 'image',
|
||||||
|
success: (res) => {
|
||||||
|
try {
|
||||||
|
const url = JSON.parse(res.data).filePath;
|
||||||
|
resolve(url ? getFullPath(url) : '')
|
||||||
|
} catch (e) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: res => {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -363,7 +363,6 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户信息并登录
|
|
||||||
async getUserInfoAndLogin(userID) {
|
async getUserInfoAndLogin(userID) {
|
||||||
try {
|
try {
|
||||||
if (userID) {
|
if (userID) {
|
||||||
@ -376,7 +375,6 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
this.currentUserID = userInfo.userID
|
this.currentUserID = userInfo.userID
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentUserSig = await this.getUserSig(this.currentUserID)
|
this.currentUserSig = await this.getUserSig(this.currentUserID)
|
||||||
await this.loginTIM()
|
await this.loginTIM()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -711,26 +709,37 @@ class TimChatManager {
|
|||||||
// 获取消息所属的会话ID
|
// 获取消息所属的会话ID
|
||||||
const messageConversationID = convertedMessage.conversationID
|
const messageConversationID = convertedMessage.conversationID
|
||||||
|
|
||||||
|
// 检查是否为系统消息
|
||||||
|
const isSystemMsg = this.isSystemMessage(convertedMessage)
|
||||||
|
|
||||||
console.log('收到新消息:', {
|
console.log('收到新消息:', {
|
||||||
messageID: convertedMessage.ID,
|
messageID: convertedMessage.ID,
|
||||||
messageConversationID: messageConversationID,
|
messageConversationID: messageConversationID,
|
||||||
currentConversationID: this.currentConversationID,
|
currentConversationID: this.currentConversationID,
|
||||||
messageType: convertedMessage.type,
|
messageType: convertedMessage.type,
|
||||||
from: convertedMessage.from
|
from: convertedMessage.from,
|
||||||
|
isSystemMessage: isSystemMsg
|
||||||
})
|
})
|
||||||
// 判断是否为当前会话的消息(必须有currentConversationID且匹配才显示)
|
|
||||||
const isCurrentConversation = this.currentConversationID &&
|
// 判断是否为当前会话的消息
|
||||||
messageConversationID === this.currentConversationID
|
// 系统消息:只要会话ID匹配就显示(不要求必须有currentConversationID)
|
||||||
|
// 普通消息:必须有currentConversationID且匹配才显示
|
||||||
|
const isCurrentConversation = isSystemMsg
|
||||||
|
? messageConversationID === this.currentConversationID
|
||||||
|
: (this.currentConversationID && messageConversationID === this.currentConversationID)
|
||||||
|
|
||||||
console.log('消息会话匹配检查:', {
|
console.log('消息会话匹配检查:', {
|
||||||
isCurrentConversation,
|
isCurrentConversation,
|
||||||
|
isSystemMessage: isSystemMsg,
|
||||||
hasCurrentConversationID: !!this.currentConversationID,
|
hasCurrentConversationID: !!this.currentConversationID,
|
||||||
conversationIDMatch: messageConversationID === this.currentConversationID
|
conversationIDMatch: messageConversationID === this.currentConversationID
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isCurrentConversation) {
|
if (isCurrentConversation) {
|
||||||
// 当前会话的消息,触发回调
|
// 当前会话的消息,触发回调
|
||||||
console.log('✓ 消息属于当前会话,触发显示')
|
console.log('✓ 消息属于当前会话,触发显示')
|
||||||
this.triggerCallback('onMessageReceived', convertedMessage)
|
this.triggerCallback('onMessageReceived', convertedMessage)
|
||||||
// 处理已读状态
|
// 处理已读状态(系统消息也标记为已读)
|
||||||
if (this.currentConversationID) {
|
if (this.currentConversationID) {
|
||||||
this.markConversationAsRead(this.currentConversationID)
|
this.markConversationAsRead(this.currentConversationID)
|
||||||
}
|
}
|
||||||
@ -1093,7 +1102,52 @@ class TimChatManager {
|
|||||||
} else if (lastMessage.type === 'TIMSoundElem') {
|
} else if (lastMessage.type === 'TIMSoundElem') {
|
||||||
lastMessageText = '[语音]'
|
lastMessageText = '[语音]'
|
||||||
} else if (lastMessage.type === 'TIMCustomElem') {
|
} else if (lastMessage.type === 'TIMCustomElem') {
|
||||||
lastMessageText = lastMessage.payload.data || '[自定义消息]'
|
// 解析自定义消息
|
||||||
|
try {
|
||||||
|
const customData = JSON.parse(lastMessage.payload.data)
|
||||||
|
const messageType = customData.messageType
|
||||||
|
// 根据消息类型返回不同的预览文本
|
||||||
|
switch (messageType) {
|
||||||
|
case 'system_message':
|
||||||
|
lastMessageText = '[系统消息]'
|
||||||
|
break
|
||||||
|
case 'symptom':
|
||||||
|
lastMessageText = '[病情描述]'
|
||||||
|
break
|
||||||
|
case 'prescription':
|
||||||
|
lastMessageText = '[处方单]'
|
||||||
|
break
|
||||||
|
case 'refill':
|
||||||
|
lastMessageText = '[续方申请]'
|
||||||
|
break
|
||||||
|
case 'survey':
|
||||||
|
lastMessageText = '[问卷调查]'
|
||||||
|
break
|
||||||
|
case 'article':
|
||||||
|
lastMessageText = '[文章]'
|
||||||
|
break
|
||||||
|
case "consult_pending":
|
||||||
|
lastMessageText = '患者向团队发起咨询,请在1小时内接诊,超时将自动关闭会话'
|
||||||
|
break
|
||||||
|
case "consult_rejected":
|
||||||
|
lastMessageText = '患者向团队发起咨询,由于有紧急事务要处理暂时无法接受咨询.本次会话丿关闭'
|
||||||
|
break
|
||||||
|
case "consult_timeout":
|
||||||
|
lastMessageText = '患者向团队发起咨询,团队成员均未接受咨询,本次会话已自动关闭'
|
||||||
|
break
|
||||||
|
case "consult_accepted":
|
||||||
|
lastMessageText = '已接诊,会话已开始'
|
||||||
|
break
|
||||||
|
case "consult_ended":
|
||||||
|
lastMessageText = '已结束当前会话'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
lastMessageText = '[自定义消息]'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析自定义消息失败:', error)
|
||||||
|
lastMessageText = '[自定义消息]'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lastMessageText = '[未知消息类型]'
|
lastMessageText = '[未知消息类型]'
|
||||||
}
|
}
|
||||||
@ -2119,72 +2173,33 @@ class TimChatManager {
|
|||||||
|
|
||||||
// 发送图片消息
|
// 发送图片消息
|
||||||
async sendImageMessage(imageFile) {
|
async sendImageMessage(imageFile) {
|
||||||
console.log('sendImageMessage 被调用,参数:', imageFile);
|
|
||||||
|
|
||||||
if (!this.tim) {
|
if (!this.tim) {
|
||||||
this.triggerCallback('onError', 'IM未初始化')
|
this.triggerCallback('onError', 'IM未初始化')
|
||||||
return { success: false, error: 'IM未初始化' }
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查登录状态
|
if (!this.conversation) {
|
||||||
if (!this.isLoggedIn) {
|
this.triggerCallback('onError', '群聊会话不存在')
|
||||||
console.error('IM未登录,无法发送消息');
|
return { success: false, error: '群聊会话不存在' }
|
||||||
this.triggerCallback('onError', 'IM未登录,请稍后重试')
|
|
||||||
return { success: false, error: 'IM未登录' }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先使用 currentConversationID,如果没有则尝试从 conversation 获取
|
let groupID = null
|
||||||
let conversationID = this.currentConversationID;
|
if (this.conversation.groupProfile && this.conversation.groupProfile.groupID) {
|
||||||
if (!conversationID && this.conversation) {
|
groupID = this.conversation.groupProfile.groupID
|
||||||
conversationID = this.conversation.conversationID;
|
} else if (this.conversation.conversationID) {
|
||||||
}
|
groupID = this.conversation.conversationID.replace('GROUP', '')
|
||||||
|
|
||||||
if (!conversationID) {
|
|
||||||
console.error('会话ID不存在');
|
|
||||||
this.triggerCallback('onError', '会话不存在,请重新进入聊天')
|
|
||||||
return { success: false, error: '会话ID不存在' }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从 conversationID 提取 groupID
|
|
||||||
let groupID = null;
|
|
||||||
if (conversationID.startsWith('GROUP')) {
|
|
||||||
groupID = conversationID.replace('GROUP', '');
|
|
||||||
} else if (this.conversation?.groupProfile?.groupID) {
|
|
||||||
groupID = this.conversation.groupProfile.groupID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groupID) {
|
if (!groupID) {
|
||||||
console.error('无法获取群聊ID,conversationID:', conversationID);
|
|
||||||
this.triggerCallback('onError', '无法获取群聊ID')
|
this.triggerCallback('onError', '无法获取群聊ID')
|
||||||
return { success: false, error: '无法获取群聊ID' }
|
return { success: false, error: '无法获取群聊ID' }
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('发送图片消息,conversationID:', conversationID, 'groupID:', groupID);
|
// 确保使用当前会话的conversationID
|
||||||
|
const conversationID = this.conversation.conversationID || this.currentConversationID
|
||||||
|
|
||||||
// imageFile 现在是完整的 wx.chooseImage 返回对象
|
// 获取图片尺寸信息
|
||||||
console.log('接收到的图片选择结果:', imageFile);
|
const imageInfo = await this.getImageInfo(imageFile);
|
||||||
console.log('类型:', typeof imageFile);
|
|
||||||
console.log('keys:', imageFile ? Object.keys(imageFile) : 'null');
|
|
||||||
|
|
||||||
// 验证对象
|
|
||||||
if (!imageFile) {
|
|
||||||
console.error('图片选择结果为空');
|
|
||||||
this.triggerCallback('onError', '图片文件无效');
|
|
||||||
return { success: false, error: '图片选择结果为空' };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取文件路径用于显示预览
|
|
||||||
let previewPath = '';
|
|
||||||
if (imageFile.tempFilePaths && imageFile.tempFilePaths.length > 0) {
|
|
||||||
previewPath = imageFile.tempFilePaths[0];
|
|
||||||
} else if (imageFile.tempFiles && imageFile.tempFiles.length > 0) {
|
|
||||||
previewPath = imageFile.tempFiles[0].tempFilePath || imageFile.tempFiles[0].path;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('预览路径:', previewPath);
|
|
||||||
|
|
||||||
// 获取图片尺寸信息(用于本地预览)
|
|
||||||
const imageInfo = await this.getImageInfo(previewPath);
|
|
||||||
|
|
||||||
const localMessage = {
|
const localMessage = {
|
||||||
ID: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
ID: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
@ -2192,14 +2207,14 @@ class TimChatManager {
|
|||||||
type: 'TIMImageElem',
|
type: 'TIMImageElem',
|
||||||
payload: {
|
payload: {
|
||||||
imageInfoArray: [{
|
imageInfoArray: [{
|
||||||
url: previewPath,
|
url: this.getImageUrl(imageFile),
|
||||||
width: imageInfo.width,
|
width: imageInfo.width,
|
||||||
height: imageInfo.height
|
height: imageInfo.height
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
lastTime: Date.now(),
|
lastTime: Date.now(),
|
||||||
status: 'sending',
|
status: 'sending',
|
||||||
avatar: '/static/center/user-avatar.png',
|
avatar: '',
|
||||||
conversationID: conversationID,
|
conversationID: conversationID,
|
||||||
from: this.currentUserID
|
from: this.currentUserID
|
||||||
}
|
}
|
||||||
@ -2211,45 +2226,22 @@ class TimChatManager {
|
|||||||
// 触发消息接收回调,让UI立即显示
|
// 触发消息接收回调,让UI立即显示
|
||||||
this.triggerCallback('onMessageReceived', localMessage)
|
this.triggerCallback('onMessageReceived', localMessage)
|
||||||
|
|
||||||
console.log('准备创建 TIM 图片消息,groupID:', groupID, 'imageFile:', imageFile);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 创建图片消息 - 直接传递 wx.chooseImage 的完整返回对象
|
|
||||||
const message = this.tim.createImageMessage({
|
const message = this.tim.createImageMessage({
|
||||||
to: groupID,
|
to: groupID,
|
||||||
conversationType: TIM.TYPES.CONV_GROUP,
|
conversationType: TIM.TYPES.CONV_GROUP,
|
||||||
payload: {
|
payload: { file: imageFile }
|
||||||
file: imageFile // 传递完整的 wx.chooseImage 返回对象
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('TIM 图片消息已创建:', message);
|
try {
|
||||||
|
await this.tim.sendMessage(message)
|
||||||
console.log('开始发送图片消息...');
|
|
||||||
const sendResult = await this.tim.sendMessage(message);
|
|
||||||
console.log('图片消息发送成功:', sendResult);
|
|
||||||
localMessage.status = 'success'
|
localMessage.status = 'success'
|
||||||
return { success: true, message: localMessage }
|
return { success: true, message: localMessage }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('图片消息发送失败:', error)
|
console.error('图片消息发送失败:', error)
|
||||||
console.error('错误详情:', {
|
|
||||||
message: error.message,
|
|
||||||
stack: error.stack,
|
|
||||||
imageFile: imageFile
|
|
||||||
});
|
|
||||||
localMessage.status = 'failed'
|
localMessage.status = 'failed'
|
||||||
|
|
||||||
// 如果是因为未登录导致的失败,尝试重连
|
|
||||||
if (error.message && (error.message.includes('not login') || error.message.includes('sdk not ready'))) {
|
|
||||||
console.log('检测到未登录错误,尝试重连...');
|
|
||||||
this.isLoggedIn = false;
|
|
||||||
this.ensureIMConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: false, error }
|
return { success: false, error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送语音消息
|
// 发送语音消息
|
||||||
async sendVoiceMessage(voiceFile, duration) {
|
async sendVoiceMessage(voiceFile, duration) {
|
||||||
if (!this.tim) {
|
if (!this.tim) {
|
||||||
@ -2424,6 +2416,41 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 工具方法
|
// 工具方法
|
||||||
|
// 判断是否为系统消息
|
||||||
|
isSystemMessage(message) {
|
||||||
|
if (message.type !== 'TIMCustomElem') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 payload.data 是否包含系统消息标记
|
||||||
|
try {
|
||||||
|
if (message.payload && message.payload.data) {
|
||||||
|
const data = typeof message.payload.data === 'string'
|
||||||
|
? JSON.parse(message.payload.data)
|
||||||
|
: message.payload.data
|
||||||
|
|
||||||
|
// 检查是否为系统消息类型
|
||||||
|
if (data.type === 'system_message') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 description 是否为系统消息标记
|
||||||
|
if (message.payload && message.payload.description === '系统消息标记') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧的系统消息格式
|
||||||
|
if (message.payload && message.payload.description === 'SYSTEM_NOTIFICATION') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('判断系统消息失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
filterMessage(message) {
|
filterMessage(message) {
|
||||||
if (message.type === 'TIMCustomElem' && message.payload && message.payload.data) {
|
if (message.type === 'TIMCustomElem' && message.payload && message.payload.data) {
|
||||||
if (message.payload.data === 'group_create' || message.payload.data === 'purchased') {
|
if (message.payload.data === 'group_create' || message.payload.data === 'purchased') {
|
||||||
@ -2497,40 +2524,42 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getImageUrl(imageFile) {
|
getImageUrl(imageFile) {
|
||||||
// 支持 tempFilePath 或 path
|
// 处理 tempFiles 数组格式
|
||||||
if (imageFile?.tempFilePath) {
|
if (imageFile?.tempFiles?.length > 0) {
|
||||||
return imageFile.tempFilePath;
|
return imageFile.tempFiles[0].tempFilePath
|
||||||
}
|
}
|
||||||
if (imageFile?.path) {
|
// 处理单个文件对象
|
||||||
return imageFile.path;
|
if (imageFile?.tempFilePath) {
|
||||||
|
return imageFile.tempFilePath
|
||||||
}
|
}
|
||||||
// 处理字符串路径
|
// 处理字符串路径
|
||||||
if (typeof imageFile === 'string') {
|
if (typeof imageFile === 'string') {
|
||||||
return imageFile;
|
return imageFile
|
||||||
}
|
}
|
||||||
console.warn('无法获取图片URL,使用默认图片:', imageFile);
|
console.warn('无法获取图片URL,使用默认图片:', imageFile);
|
||||||
return '/static/home/photo.png';
|
return '/static/home/photo.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取图片尺寸信息
|
// 获取图片尺寸信息
|
||||||
getImageInfo(imagePath) {
|
getImageInfo(imageFile) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// 如果传入的是对象,尝试提取路径
|
let imagePath = '';
|
||||||
if (typeof imagePath === 'object') {
|
|
||||||
if (imagePath?.tempFilePath) {
|
|
||||||
imagePath = imagePath.tempFilePath;
|
|
||||||
} else if (imagePath?.path) {
|
|
||||||
imagePath = imagePath.path;
|
|
||||||
} else {
|
|
||||||
console.warn('无法从对象中获取图片路径,使用默认尺寸:', imagePath);
|
|
||||||
resolve({ width: 400, height: 300 });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果不是字符串,使用默认尺寸
|
// 获取图片路径 - 处理多种格式
|
||||||
if (typeof imagePath !== 'string' || !imagePath) {
|
if (imageFile?.tempFilePaths?.length > 0) {
|
||||||
console.warn('图片路径无效,使用默认尺寸:', imagePath);
|
// wx.chooseImage 返回的对象
|
||||||
|
imagePath = imageFile.tempFilePaths[0];
|
||||||
|
} else if (imageFile?.tempFiles?.length > 0) {
|
||||||
|
// 从 tempFiles 中提取路径
|
||||||
|
const tempFile = imageFile.tempFiles[0];
|
||||||
|
imagePath = tempFile.path || tempFile.tempFilePath;
|
||||||
|
} else if (imageFile?.tempFilePath) {
|
||||||
|
imagePath = imageFile.tempFilePath;
|
||||||
|
} else if (typeof imageFile === 'string') {
|
||||||
|
imagePath = imageFile;
|
||||||
|
} else {
|
||||||
|
console.warn('无法获取图片路径,使用默认尺寸:', imageFile);
|
||||||
|
// 默认尺寸
|
||||||
resolve({ width: 400, height: 300 });
|
resolve({ width: 400, height: 300 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||