Compare commits

..

2 Commits

Author SHA1 Message Date
huxuejian
cd74929a41 fix: 问题修复 2026-04-21 21:37:50 +08:00
huxuejian
83ca3a2ef9 fix: 问题修复 2026-04-21 20:49:35 +08:00
7 changed files with 424 additions and 45 deletions

View File

@ -96,6 +96,13 @@
"disableScroll": true "disableScroll": true
} }
}, },
{
"path": "survey-fill",
"style": {
"navigationBarTitleText": "问卷填写",
"disableScroll": true
}
},
{ {
"path": "survey-list", "path": "survey-list",
"style": { "style": {

View File

@ -30,10 +30,21 @@
<view v-for="r in records" :key="r._id" class="card record" @click="edit(r)"> <view v-for="r in records" :key="r._id" class="card record" @click="edit(r)">
<view class="record-head"> <view class="record-head">
<view class="record-date">{{ r.dateStr || '--' }}</view> <view class="record-date">{{ r.dateStr || '--' }}</view>
<view class="record-tag" :class="tagClass[r.templateType] || 'bg-blue'">{{ r.tempName || '病历' }}</view> <view v-if="r.templateType === 'surveyFill'" class="record-tag bg-rose">问卷填写</view>
<view v-else class="record-tag" :class="tagClass[r.templateType] || 'bg-blue'">{{ r.tempName || '病历' }}</view>
<view v-if="r.corpName === '其他' || r.corp === '其他'" class="record-tag bg-rose">外院</view> <view v-if="r.corpName === '其他' || r.corp === '其他'" class="record-tag bg-rose">外院</view>
</view> </view>
<template v-if="r.templateType === 'surveyFill'">
<view class="record-body">
<text>患者填写了问卷{{ r.surveryName || '--' }}</text>
<text v-if="r.score > 0">评分结果{{ r.score }}</text>
<text></text>
</view>
<view class="record-foot">
<view class="text-base text-gray">发送时间: {{ dayjs(r.sendTime).format('YYYY-MM-DD HH:mm:ss') }}</view>
</view>
</template>
<template v-else>
<view class="record-body"> <view class="record-body">
<view v-for="(l, idx) in getDisplayLines(r)" :key="`${r._id}_${idx}`" class="line"> <view v-for="(l, idx) in getDisplayLines(r)" :key="`${r._id}_${idx}`" class="line">
<text class="line-label">{{ l.label }}</text> <text class="line-label">{{ l.label }}</text>
@ -41,7 +52,8 @@
</view> </view>
<view v-if="getFiles(r).length" class="thumbs"> <view v-if="getFiles(r).length" class="thumbs">
<view v-for="(f, idx) in getFiles(r).slice(0, 3)" :key="idx" class="thumb" @click.stop="previewFiles(r, idx)"> <view v-for="(f, idx) in getFiles(r).slice(0, 3)" :key="idx" class="thumb"
@click.stop="previewFiles(r, idx)">
<image class="thumb-img" :src="f.url" mode="aspectFill" /> <image class="thumb-img" :src="f.url" mode="aspectFill" />
</view> </view>
<view v-if="getFiles(r).length > 3" class="thumb-more">+{{ getFiles(r).length - 3 }}</view> <view v-if="getFiles(r).length > 3" class="thumb-more">+{{ getFiles(r).length - 3 }}</view>
@ -50,19 +62,14 @@
<view class="record-foot"> <view class="record-foot">
<view class="foot-left">{{ getCreateFooter(r) }}</view> <view class="foot-left">{{ getCreateFooter(r) }}</view>
</view> </view>
</template>
</view> </view>
<view v-if="records.length === 0" class="empty">暂无数据</view> <view v-if="records.length === 0" class="empty">暂无数据</view>
</view> </view>
<picker <picker class="fab-picker" mode="selector" :range="selectableTemplates" range-key="name"
class="fab-picker" :disabled="fabPickerDisabled" :style="{ bottom: `${floatingBottom}px` }" @change="pickAddType">
mode="selector"
:range="selectableTemplates"
range-key="name"
:disabled="fabPickerDisabled"
:style="{ bottom: `${floatingBottom}px` }"
@change="pickAddType"
>
<view class="fab" :class="{ 'fab--disabled': selectableTemplates.length === 0 }" @tap="onFabTap"> <view class="fab" :class="{ 'fab--disabled': selectableTemplates.length === 0 }" @tap="onFabTap">
<uni-icons type="plusempty" size="24" color="#fff" /> <uni-icons type="plusempty" size="24" color="#fff" />
</view> </view>
@ -100,9 +107,9 @@ const templateMap = computed(() => templates.value.reduce((m, t) => {
if (t?.templateType) m[String(t.templateType)] = t; if (t?.templateType) m[String(t.templateType)] = t;
return m; return m;
}, {})); }, {}));
const availableTypes = computed(() => (templates.value.length ? templates.value.map((i) => i.templateType) : FALLBACK_TEMPLATE_TYPES)); const availableTypes = computed(() => (templates.value.length ? [...templates.value.map((i) => i.templateType), 'surveyFill'] : FALLBACK_TEMPLATE_TYPES));
const typeRange = computed(() => [{ name: '全部', value: 'ALL' }, ...templates.value.map((t) => ({ name: t.name, value: t.templateType }))]); const typeRange = computed(() => [{ name: '全部', value: 'ALL' }, ...templates.value.map((t) => ({ name: t.name, value: t.templateType })), { name: '问卷填写', value: 'surveyFill' }]);
const currentType = ref({ name: '全部', value: 'ALL' }); const currentType = ref({ name: '全部', value: 'ALL' });
const records = ref([]); const records = ref([]);
@ -643,6 +650,19 @@ async function onFabTap() {
} }
function edit(record) { function edit(record) {
if (record.medicalType === 'surveyFill') {
const {
corpId = '',
answerId = '',
surveryId = '',
memberId = '',
customer = ''
} = record;
const url = `/pages/message/survey-fill?corpId=${corpId}&answerId=${answerId}&surveryId=${surveryId}&memberId=${memberId}&customerName=${customer || ''}`
uni.navigateTo({
url
})
};
const type = String(record?.medicalType || record?.templateType || '') || ''; const type = String(record?.medicalType || record?.templateType || '') || '';
uni.navigateTo({ uni.navigateTo({
url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}&type=${encodeURIComponent(type)}`, url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}&type=${encodeURIComponent(type)}`,
@ -692,6 +712,7 @@ watch(
gap: 20rpx; gap: 20rpx;
flex: 1; flex: 1;
} }
.pill-text { .pill-text {
font-size: 26rpx; font-size: 26rpx;
color: #333; color: #333;
@ -700,12 +721,14 @@ watch(
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.pill-icons { .pill-icons {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12rpx; gap: 12rpx;
flex-shrink: 0; flex-shrink: 0;
} }
.pill-clear { .pill-clear {
display: flex; display: flex;
align-items: center; align-items: center;
@ -715,6 +738,7 @@ watch(
.share-tip { .share-tip {
padding: 20rpx 28rpx 0; padding: 20rpx 28rpx 0;
} }
.share-tip-text { .share-tip-text {
display: inline-block; display: inline-block;
background: #eef2ff; background: #eef2ff;
@ -735,28 +759,34 @@ watch(
overflow: hidden; overflow: hidden;
box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, 0.06); box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, 0.06);
} }
.record { .record {
padding: 0; padding: 0;
} }
.record-head { .record-head {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 24rpx 24rpx 20rpx; padding: 24rpx 24rpx 20rpx;
gap: 16rpx; gap: 16rpx;
} }
.record-title { .record-title {
font-size: 30rpx; font-size: 30rpx;
font-weight: 600; font-weight: 600;
color: #1f1f1f; color: #1f1f1f;
} }
.record-date { .record-date {
font-size: 28rpx; font-size: 28rpx;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.record-body { .record-body {
padding: 0 24rpx 24rpx; padding: 0 24rpx 24rpx;
} }
.line { .line {
display: flex; display: flex;
padding-top: 20rpx; padding-top: 20rpx;
@ -764,10 +794,12 @@ watch(
color: #333; color: #333;
line-height: 36rpx; line-height: 36rpx;
} }
.line-label { .line-label {
flex-shrink: 0; flex-shrink: 0;
color: #666; color: #666;
} }
.line-value { .line-value {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
@ -783,6 +815,7 @@ watch(
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.thumb { .thumb {
width: 168rpx; width: 168rpx;
height: 128rpx; height: 128rpx;
@ -791,14 +824,17 @@ watch(
background: #f3f4f6; background: #f3f4f6;
border: 2rpx solid #e5e7eb; border: 2rpx solid #e5e7eb;
} }
.thumb-img { .thumb-img {
width: 168rpx; width: 168rpx;
height: 128rpx; height: 128rpx;
} }
.thumb-more { .thumb-more {
font-size: 24rpx; font-size: 24rpx;
color: #6b7280; color: #6b7280;
} }
.record-foot { .record-foot {
display: flex; display: flex;
align-items: center; align-items: center;
@ -808,6 +844,7 @@ watch(
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
} }
.foot-left { .foot-left {
flex-shrink: 0; flex-shrink: 0;
margin-right: 20rpx; margin-right: 20rpx;
@ -819,21 +856,27 @@ watch(
padding: 8rpx 16rpx; padding: 8rpx 16rpx;
border-radius: 16rpx; border-radius: 16rpx;
} }
.bg-blue { .bg-blue {
background: #0877F1; background: #0877F1;
} }
.bg-amber { .bg-amber {
background: #d97706; background: #d97706;
} }
.bg-teal { .bg-teal {
background: #0f766e; background: #0f766e;
} }
.bg-indigo { .bg-indigo {
background: #4f46e5; background: #4f46e5;
} }
.bg-green { .bg-green {
background: #16a34a; background: #16a34a;
} }
.bg-rose { .bg-rose {
background: #f43f5e; background: #f43f5e;
} }
@ -852,6 +895,7 @@ watch(
height: 104rpx; height: 104rpx;
z-index: 20; z-index: 20;
} }
.fab { .fab {
width: 104rpx; width: 104rpx;
height: 104rpx; height: 104rpx;
@ -862,6 +906,7 @@ watch(
justify-content: center; justify-content: center;
box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35); box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35);
} }
.fab--disabled { .fab--disabled {
opacity: 0.5; opacity: 0.5;
} }

View File

@ -48,7 +48,7 @@
</view> </view>
<!-- 问卷消息 --> <!-- 问卷消息 -->
<view v-else-if="customMessageType === 'survey'" class="survey-card" @click="handleSurveyClick(message)"> <view v-else-if="customMessageType === 'survey'" class="survey-card" @click.stop="handleSurveyClick(message)">
<view class="survey-content"> <view class="survey-content">
<view class="survey-title">{{ surveyData.title }}</view> <view class="survey-title">{{ surveyData.title }}</view>
<view class="survey-desc">{{ surveyData.desc }}</view> <view class="survey-desc">{{ surveyData.desc }}</view>
@ -230,13 +230,25 @@ const handleArticleClick = (message) => {
// //
const handleSurveyClick = (message) => { const handleSurveyClick = (message) => {
if (surveyData.value.url) { try {
// const {
console.log("打开问卷:", surveyData.value.url); corpId = '',
answerId = '',
surveryId = '',
memberId = '',
name = ''
} = payloadData.value;
const url = `/pages/message/survey-fill?corpId=${corpId}&answerId=${answerId}&surveryId=${surveryId}&memberId=${memberId}&customerName=${name}`
uni.navigateTo({
url
})
} catch (e) {
console.log(e)
}
// uni.navigateTo({ // uni.navigateTo({
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.value.url)}` // url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.value.url)}`
// }); // });
} // }
}; };
function isWechatChannels(data) { function isWechatChannels(data) {

View File

@ -0,0 +1,93 @@
<template>
<view class="h-full flex flex-col">
<view class="flex-shrink-0 px-10 pt-5 text-base font-semibold text-center truncate">{{ survey.name }}</view>
<view v-if="survey.description"
class="flex-shrink-0 px-10 mt-10 text-sm leading-normal text-gray line-clamp-2 text-center">
{{ survey.description }}
</view>
<view class="flex items-center text-gray text-sm px-10 mt-10">
<view v-if="survey.enableScore">
当前得分<text class="text-primary text-base font-semibold min-w-5 inline-block">{{ allScore }}
</text>
</view>
<view class="ml-auto">
客户<text class="text-black text-base font-semibold min-w-5 inline-block">{{ customerName }} </text>
</view>
</view>
<view class="flex-grow relative">
<scroll-view :scroll-y="true" class="absolute inset-0">
<view v-for="(quesiton, index) in list" :key="quesiton.id">
<view v-if="quesiton" class="my-4 text-base px-10 leading-normal">
<text v-if="quesiton.require" class="text-base text-red-500">*</text> {{ index + 1 }}{{ quesiton.title }}
</view>
<view class="px-10">
<template v-if="quesiton.type === 'radio'">
<view v-for="(opt, idx) in quesiton.options" :key="opt.value" class="flex py-15 border-b"
:class="idx === 0 ? 'border-t' : ''">
<view class="flex-grow pl-4 text-gray leading-normal"
:class="quesiton.value && quesiton.value === opt.value ? 'text-primary' : ''">
{{ opt.label }}
<text v-if="survey.enableScore && opt.score >= 0">
{{ opt.score }}
</text>
</view>
<uni-icons v-if="quesiton.value && quesiton.value === opt.value" class="ml-1 text-primary flex-shrink-0"
color=" " type="checkbox-filled" size="24"></uni-icons>
</view>
</template>
<view v-else-if="quesiton.type === 'input'"
class="my-4 p-10 text-base leading-normal border rounded">
{{ quesiton.value || '未填写' }}
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'Question',
props: {
customerName: { type: String, default: '' },
list: { type: Array, default: () => ([]) },
survey: { type: Object, default: () => ({}) }
},
computed: {
allScore() {
return this.list.reduce((score, item) => {
if (item.type === 'radio') {
const opt = item.options.find(i => i.value && i.value === item.value && item.value)
if (opt && opt.score >= 0) {
score = Math.floor(score * 100 + opt.score * 100) / 100
}
}
return score
}, 0)
}
}
}
</script>
<style lang="scss" scoped>
.min-w-5 {
min-width: 40rpx
}
.pt-5 {
padding-top: 40rpx;
}
.my-4 {
margin-top: 32rpx;
margin-bottom: 32rpx;
}
.border-t {
border-top: 1px solid #eee;
}
.pl-4 {
padding-left: 30rpx;
}
</style>

View File

@ -0,0 +1,220 @@
<template>
<full-page :customScroll="true">
<survey-record v-if="survey" :customerName="customerName" :survey="survey" :list="showList" />
<view v-else class="empty">
<image class="empty__icon" src="/static/empty.svg"></image>
<text class="empty__txt">{{ emptyTxt }}</text>
</view>
</full-page>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import api from "@/utils/api.js";
import { loading, toast, hideLoading } from "@/utils/widget";
import FullPage from '@/components/full-page.vue';
import surveyRecord from "./components/survey-record.vue";
const corpId = ref('');
const surveryId = ref('');
const answerId = ref('');
const memberId = ref('');
const survey = ref(null);
const emptyTxt = ref('')
const customerName = ref('');
const list = computed(() => survey.value && Array.isArray(survey.value.list) ? survey.value.list : []);
const showList = computed(() => {
return list.value.filter(item => {
const showCase = Array.isArray(item.showCase) ? item.showCase : [];
if (showCase.length) {
const res = showCase.every(({
type,
id,
value
}) => {
const question = this.list.find(i => i.id === id);
const val = question ? question.value : '';
return type === 'in' ? value.includes(val) : val === value.join()
})
return res
}
return true
})
})
async function init() {
loading('加载中...');
try {
const record = await getAnswerRecord();
if (record && record.submitTime) {
record.status = 'enable';
survey.value = record;
hideLoading()
return;
}
survey.value = await getSurvey();
if (survey.value.status == 'stop') {
emptyTxt.value = '问卷已经停用'
} else if (survey.value.status == 'init') {
emptyTxt.value = '问卷暂未启用'
}
} catch (e) {
hideLoading();
toast('获取问卷失败');
uni.navigateBack();
}
console.log(survey.value);
}
async function getAnswerRecord() {
const res = await api('getAnswer', { corpId: corpId.value, surveryId: surveryId.value, answerId: answerId.value, memberId: memberId.value });
if (res && res.success) {
return res.record
}
return Promise.reject();
}
async function getSurvey() {
const res = await api('getSurveyDetail', { corpId: corpId.value, id: surveryId.value })
if (res && res.success && res.data) {
return res.data
}
return Promise.reject();
}
onLoad(opts => {
corpId.value = opts.corpId;
surveryId.value = opts.surveryId;
answerId.value = opts.answerId;
memberId.value = opts.memberId;
customerName.value = opts.customerName;
init();
})
</script>
<style scoped lang="scss">
.survey {
position: relative;
height: 100%;
width: 100%;
height: 100vh;
width: 100vw;
color: ragb(0, 0, 0, .9);
background: #fff;
@at-root &__wrapper {
padding: 30rpx 40rpx;
}
@at-root &__scroll {
height: 100%;
}
@at-root &__customer {
text-align: right;
font-size: 28rpx;
padding-bottom: 16rpx;
margin-bottom: 24rpx;
color: #333;
border-bottom: 1px solid #eee;
}
@at-root &__title {
text-align: center;
font-size: 32rpx;
font-weight: 600;
margin-bottom: 24rpx;
}
@at-root &__desc {
text-align: left;
color: #666;
font-size: 28rpx;
margin-bottom: 24rpx;
}
@at-root &__item {
padding: 0 10rpx;
margin-bottom: 24rpx;
}
@at-root &__question {
position: relative;
font-size: 32rpx;
margin-bottom: 24rpx;
@at-root &--require::before {
position: absolute;
left: -20rpx;
top: 0;
content: '*';
color: #f56c6c;
}
}
@at-root &__input {
padding: 10rpx 24rpx;
font-size: 28rpx;
border: 1px solid #eee;
border-radius: 8rpx;
}
@at-root &__radio {
display: flex;
align-items: center;
padding-bottom: 24rpx;
cursor: pointer;
}
@at-root &__btn {
margin-top: 30rpx;
padding: 24rpx 30rpx;
border-radius: 8rpx;
font-size: 28rpx;
color: #fff;
background: #006eff;
text-align: center;
}
}
.radio {
@at-root &__icon {
flex-shrink: 0;
margin-top: 4rpx;
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
}
@at-root &__label {
flex-grow: 1;
font-size: 32rpx;
}
}
.empty {
position: absolute;
left: 50%;
top: 40%;
transform: translate(-50%, -50%);
text-align: center;
@at-root &__icon {
display: block;
margin-bottom: 16rpx;
width: 240rpx;
height: 240rpx;
}
@at-root &__txt {
font-size: 32rpx;
color: #666;
}
}
</style>

View File

@ -7,12 +7,12 @@
<view class="flex justify-center overflow-hidden"> <view class="flex justify-center overflow-hidden">
<image :show-menu-by-longpress="true" class="qrcode" src="/static/work/service.png" /> <image :show-menu-by-longpress="true" class="qrcode" src="/static/work/service.png" />
</view> </view>
<!-- <view class="mt-10 px-15 text-base text-dark leading-normal text-center"> <view class="mt-10 px-15 text-base text-dark leading-normal text-center">
扫码或长按添加柚康企微客服 扫码或长按添加柚助手客服
</view> </view>
<view class="mt-10 px-15 text-base text-dark leading-normal text-center"> <view class="mt-10 px-15 text-base text-dark leading-normal text-center">
我们将为您提供软件使用咨询服务并支持补充病历宣教问卷回访等多种工作模板 我们将为您提供软件使用咨询服务并支持补充病历宣教问卷回访等多种工作模板
</view> --> </view>
</view> </view>
</view> </view>
</template> </template>

View File

@ -63,7 +63,9 @@ const urlsConfig = {
getSurveyCateList: 'getSurveryCateList', getSurveyCateList: 'getSurveryCateList',
getSurveyList: 'getList', getSurveyList: 'getList',
createSurveyRecord: 'createRecord', createSurveyRecord: 'createRecord',
getSurveyDetail: 'getDetail' getSurveyDetail: 'getDetail',
getAnswer: 'getAnswer'
}, },
member: { member: {
addCustomer: 'add', addCustomer: 'add',