2025-07-29 15:39:33 +08:00

767 lines
19 KiB
Vue

<template>
<view class="page">
<view class="navbox">
<view class="bg"></view>
<view class="namebox">
<view class="back" @click="closeSv">
<u-icon name="arrow-left" color="#000" size="24"></u-icon>
</view>
<!-- <view class="logo">logo</view> -->
<view class="name">{{editorType=='info'?'病例信息':'总结与讨论'}}</view>
</view>
</view>
<view class="container">
<view class="row">
<view class="left">{{editorType=='info'?'病例信息':'总结与讨论'}}</view>
<view class="right" @click="clearMuBan" v-if="editorType=='info'">
<u-icon name="trash" color="#6B7280" size="18"></u-icon>
清除模板
</view>
</view>
<view class="editorbox">
<sv-editor
class="ql-container"
:placeholder="placeholder"
eid="peditor"
@ready="ready"
pasteMode="origin"
@focus="focus"
@blur="blur"
></sv-editor>
</view>
</view>
<view class="toolbar" v-show="keyboardHeight > 0" :style="{bottom: isIOS?keyboardHeight : 0+'px'}">
<view class="toolbox">
<view class="cellbox">
<view class="cell"
@click="insertImage"
>
<up--image
:src="photoImg"
class="headImg"
width="32rpx"
height="32rpx"
></up--image>
<view class="name">添加图片</view>
</view>
<view
class="cell"
@click="insertVideo"
>
<up--image
:src="videoImg"
class="headImg"
width="32rpx"
height="32rpx"
></up--image>
<view class="name">添加视频</view>
</view>
<view
v-if="editorType=='info'"
class="cell"
@click="alertTitle"
>
<up--image
:src="addImg"
class="headImg"
width="32rpx"
height="32rpx"
></up--image>
<view class="name">添加小标题</view>
</view>
</view>
<view class="btn" @click="confirm">确定</view>
</view>
</view>
<up-popup
:round="10"
zIndex="999999"
:show="showTitle"
mode="bottom"
@close="closeTitle"
@open="openTitle"
>
<view class="draftpop titlepop">
<view class="titlebox"
>添加小标题
<view class="close" @click="closeTitle"
><up-icon name="close" color="#4B5563" size="20"></up-icon
></view>
</view>
<view class="con">
<view class="top">
<up-icon
name="plus-circle"
color="#3CC7C0"
size="20"
@click="insertAllWord"
></up-icon>
<view class="desc" @click="insertAllWord">一键添加全部</view>
</view>
<view class="cellbox">
<view class="cell" @click="insertWord('患者信息')">患者信息</view>
<view class="cell" @click="insertWord('主诉')">主诉</view>
<view class="cell" @click="insertWord('现病史及既往史')"
>现病史及既往史</view
>
</view>
<view class="cellbox">
<view class="cell" @click="insertWord('检查')">检查</view>
<view class="cell" @click="insertWord('临床诊断')">临床诊断</view>
<view class="cell" @click="insertWord('治疗经过及结果')"
>治疗经过及结果</view
>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import photoImg from "@/static/photo.png";
import throttle from "@/utils/throttle"
import addImg from "@/static/add.png";
import videoImg from "@/static/videoicon.png";
import { nextTick, reactive, ref,watch } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import api from "@/api/api";
import dayjs from "dayjs";
import {
addImage,
addVideo,
addText,
} from "@/uni_modules/sv-editor/components/common/utils.js";
import svEditor from "@/uni_modules/sv-editor/components/sv-editor/sv-editor.vue";
const editorCtx=ref(null);
const keyboardHeight=ref(0);
const editorHeight=ref(300);
const navName=ref('病例信息');
const placeholder=ref('患者基本情况,检查结果,诊疗图片或视频');
const showTitle=ref(false)
const props=defineProps({
editorType:{
type:String,
default:'info'
},
editorCotent:{
type:String,
default:''
}
})
watch(()=>props.editorType,(newVal)=>{
console.log(11111111)
console.log(newVal)
if(newVal=="info"){
placeholder.value='患者基本情况,检查结果,诊疗图片或视频'
}else{
placeholder.value='分享经验和心得,如:诊断与鉴别诊断易错点,治疗过程难点,病例的相关知识总结及讨论等'
}
},{immediate: true })
const emits=defineEmits(['closeEditor','changeEditor'])
const isIOS=ref(false);
const insertWord = (word) => {
addText(word);
showTitle.value = false;
};
const insertAllWord = () => {
let word = [
"患者信息",
"主诉",
"现病史及既往史:",
"检查",
"临床诊断",
"治疗经过及结果",
];
word.forEach((item) => {
addText(item);
});
showTitle.value = false;
};
const ready = (e) => {
console.log(e);
editorCtx.value = e;
if(props.editorType=="info"){
if(props.editorCotent){
editorCtx.value.initHtml(props.editorCotent,async (videoUrl) => {
let res='';
// #ifdef APP || H5
// res = await this.editorCtx.createVideoThumbnail(videoUrl)
// #endif
const fox = 'https://cn.bing.com//th?id=OHR.FlamingosNamibia_ZH-CN3639748956_1920x1080.jpg'
res = await editorCtx.value.createCoverThumbnail(fox)
showEditor.value=false;
return res
})
}else{
let html =
"<p>【患者信息】:</p><br/><p>【主诉】:</p><br/><p>【现病史及既往史】:</p><br/><p>【检查】:</p><br/><p>【临床诊断】:</p><br/><p>【治疗经过及结果】:</p><br/><br/>";
editorCtx.value.initHtml(html);
}
}else{
if(props.editorCotent){
editorCtx.value.initHtml(props.editorCotent,async (videoUrl) => {
let res='';
// #ifdef APP || H5
// res = await this.editorCtx.createVideoThumbnail(videoUrl)
// #endif
const fox = 'https://cn.bing.com//th?id=OHR.FlamingosNamibia_ZH-CN3639748956_1920x1080.jpg'
res = await editorCtx.value.createCoverThumbnail(fox)
showEditor.value=false;
return res
})
}else{
let html =
"";
editorCtx.value.initHtml(html);
}
}
uni.hideLoading();
};
const closeSv =throttle(()=>{
emits('closeEditor')
})
const confirm=async ()=>{
console.log('qqq')
const res = await editorCtx.value.getLastContent();
emits('changeEditor',{
type:props.editorType,
content:res.html
})
}
const foucus=()=>{
}
const blur=()=>{
}
const clearMuBan=()=>{
editorCtx.value.initHtml("");
}
const updatePosition=(c_keyboardHeight)=>{
const toolbarHeight = 50
const { windowHeight, platform } = uni.getSystemInfoSync();
let c_editorHeight = keyboardHeight > 0 ? (windowHeight - keyboardHeight - toolbarHeight) : windowHeight
//this.setData({ editorHeight, keyboardHeight })
editorHeight.value=c_editorHeight;
keyboardHeight.value=c_keyboardHeight;
}
onLoad(()=>{
const platform =uni.getSystemInfoSync().platform
isIOS.value=platform === 'ios'
updatePosition(0)
let keyboardHeight = 0
uni.onKeyboardHeightChange(res => {
if (res.height === keyboardHeight) return
const duration = res.height > 0 ? res.duration * 1000 : 0
keyboardHeight = res.height
setTimeout(() => {
uni.pageScrollTo({
scrollTop: 0,
success() {
updatePosition(keyboardHeight)
editorCtx.value.scrollView()
}
})
}, duration)
})
});
const generateRandomNumber = () => {
let randomNumber = Math.floor(1000 + Math.random() * 9000);
return randomNumber;
};
const getImageFormat = (imageUrl) => {
console.log(imageUrl);
const lastDotIndex = imageUrl.lastIndexOf(".");
if (lastDotIndex !== -1) {
return imageUrl.substring(lastDotIndex + 1);
}
return "unknown";
};
const handleUpload = (file) => {
api
.getOss({
scene: 1,
})
.then((rep) => {
let result = rep.data;
if (result.code == 200) {
let { access_id, dir, policy, signature, host } = result.data;
let time = dayjs().format("YYYYMMDDHHmmss");
let random = generateRandomNumber();
let filename = time + random;
let imgType = "." + getImageFormat(file);
return new Promise((resolve, reject) => {
uni.uploadFile({
url: host, // 仅为示例,非真实的接口地址
filePath: file,
name: "file",
formData: {
OSSAccessKeyId: access_id,
policy,
key: dir + time + random + imgType,
signature,
},
success(res) {
if (res.statusCode === 204) {
let url = host + "/" + dir + filename + imgType;
//imgList.value.push(url);
addImage([url]);
}
},
fail: (err) => {
console.log(err);
},
});
});
}
});
};
const pFun = (files) => {
uni.showLoading({
title: "正在上传图片...",
mask: true,
});
let promiseFun = [];
for (let i = 0; i < files.length; i++) {
promiseFun.push(handleUpload(files[i]));
}
Promise.all(promiseFun).then((res) => {
uni.hideLoading();
// setTimeout(()=>{
// console.log(imgList.value.length)
// addImage(imgList.value)
// })
});
};
const insertImage = (file) => {
//if (!(!isFocusInfo.value && !isFocusResult.value)) {
uni.chooseImage({
count: 9, //默认9
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album"], //从相册选择
extension: [".jpg", ".png", ".jpeg"],
success: function (res) {
pFun(res.tempFilePaths);
},
});
//}
};
const HandleAddVideo = async (file) => {
uni.showLoading({
title: "正在上传视频...",
mask: true,
});
const videos = await addVideo(async (editorCtx) => {
return new Promise((resolve) => {
api
.getOss({
scene:2,
})
.then((rep) => {
let result = rep.data;
if (result.code == 200) {
let { access_id, dir, policy, signature, host } = result.data;
let time = dayjs().format("YYYYMMDDHHmmss");
let random = generateRandomNumber();
let filename = time + random;
let imgType = "." + getImageFormat(file);
return new Promise((res, reject) => {
uni.uploadFile({
url: host, // 仅为示例,非真实的接口地址
filePath: file,
name: "file",
formData: {
OSSAccessKeyId: access_id,
policy,
key: dir + time + random + imgType,
signature,
},
async success(res) {
if (res.statusCode === 204) {
let url = host + "/" + dir + filename + imgType;
console.log(editorCtx);
let imgUrl =
"https://cn.bing.com//th?id=OHR.FlamingosNamibia_ZH-CN3639748956_1920x1080.jpg";
const fileThumbnail = await editorCtx.createCoverThumbnail(
imgUrl
);
resolve([
{
videoUrl: url,
videoImg: fileThumbnail,
},
]);
}
},
fail: (err) => {
console.log(err);
},
});
});
}
});
});
});
if (videos) {
uni.showLoading();
uni.showToast({ title: "添加视频成功", icon: "none" });
} else {
uni.showToast({ title: "添加视频失败", icon: "none" });
}
};
const insertVideo = (file) => {
//if (!(!isFocusInfo.value && !isFocusResult.value)) {
uni.chooseVideo({
count: 5, //默认9//可以指定是原图还是压缩图,默认二者都有
sourceType: ["album"], //从相册选择
extension: [".mp4", ".webm", ".ogg"],
success: function (res) {
console.log(res.tempFilePath);
HandleAddVideo(res.tempFilePath);
//pFun([res.tempFilePath], "video");
},
});
//}
};
const closeTitle = () => {
showTitle.value = false;
};
const openTitle = () => {
showTitle.value = true;
};
const alertTitle = () => {
console.log('qqqqqq')
showTitle.value = true;
};
</script>
<style lang='scss'scoped>
.row{
padding: 30rpx 30rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
.left{
font-size: 38rpx;
color: #000000;
}
.right{
display: flex;
align-items: center;
color: #6b7280;
font-size: 31rpx;
}
}
.navbox {
padding-bottom: 20rpx;
background-color: #f9fafb;
position: relative;
height: 200rpx;
background: radial-gradient(
60% 90% at 4% 2%,
#43c9c3 0%,
rgba(255, 255, 255, 0) 100%
);
.bg {
z-index: 0;
top: 0;
bottom: 0;
width: 100%;
position: absolute;
background: radial-gradient(
43% 90% at 84% 6%,
#ffd6c9 0%,
rgba(255, 255, 255, 0) 100%
);
}
.namebox {
padding-top: 102rpx;
justify-content: center;
margin: 0rpx 30rpx 0rpx;
position: relative;
display: flex;
.back {
position: absolute;
left: 0;
}
.name {
margin-left: 16rpx;
font-size: 30rpx;
color: #111827;
}
}
.search {
margin: 40rpx 30rpx 0rpx;
display: flex;
align-items: center;
justify-content: space-between;
.searchwrap {
display: flex;
align-items: center;
flex: 1;
padding-left: 28rpx;
margin-right: 23rpx;
height: 80rpx;
background: #fbfbfb;
box-shadow: 0px 4rpx 10rpx 0px rgba(153, 153, 153, 0.5);
border-radius: 40rpx;
.ipt {
margin-left: 15rpx;
font-size: 28rpx;
}
}
}
}
.container {
position: absolute;
top: 180rpx;
left: 0;
bottom:250rpx;
width: 100%;
}
.editorbox{
height:100%;
width: 100%;
height: 100%;
font-size: 16px;
box-sizing:border-box;
line-height: 1.5;
overflow-y: scroll;
padding: 10rpx 30rpx 10rpx ;
}
.ql-active {
color: #22C704;
}
.btn{
flex:1;
width:180rpx;
background:#3cc7c0;
display:flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
color:#fff;
height: 60rpx;
border-radius: 8rpx;
}
.iconfont {
display: inline-block;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 20px;
}
.toolbar {
box-sizing: border-box;
padding: 0 10px;
height: 50px;
width: 100%;
position: fixed;
left: 0;
right: 100%;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #ECECEC;
border-left: none;
border-right: none;
}
.page{
position: fixed;
width:100%;
height:100vh;
background-color: #fff;
bottom:0;
z-index:999;
}
.toolbox {
/* border-top: 2rpx solid #f3f4f6; */
padding: 22rpx 30rpx;
width:100%;
box-sizing:border-box;
display: flex;
align-items: center;
.cellbox{
width:480rpx;
display: flex;
align-items: center;
/* justify-content: space-between; */
}
.cell {
width:132rpx;
margin-right: 20rpx;
display: flex;
flex-wrap: nowrap;
align-items: center;
.name {
font-size: 25rpx;
margin-left: 4rpx;
color: #4b5563;
white-space: nowrap;
}
}
.cell:last-child{
margin-right: 0;
}
.cell.active {
opacity: 0.65;
}
}
.draftpop {
.titlebox {
text-align: center;
padding: 30rpx;
font-size: 31rpx;
color: #111827;
position: relative;
.close {
position: absolute;
top: 20rpx;
right: 30rpx;
}
}
.draftlist {
height: calc(100vh - 500rpx);
overflow-y: scroll;
.cell {
padding-bottom: 34rpx;
border-bottom: 2rpx solid #e5e7eb;
.title {
margin: 15rpx 30rpx 0;
font-size: 36rpx;
color: #111827;
line-height: 46rpx;
}
.smalltitle {
margin: 4px 0rpx 0;
font-size: 30rpx;
color: #666666;
line-height: 38rpx;
}
.con {
font-size: 30rpx;
color: #666666;
line-height: 38rpx;
font-size: 30rpx;
color: #666666;
line-height: 38rpx;
}
.deal {
margin: 36rpx 30rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
.time {
font-size: 26rpx;
color: #9ca3af;
}
.right {
display: flex;
align-items: center;
}
.del {
width: 138rpx;
height: 62rpx;
background: #f3f4f6;
display: flex;
border-radius: 20rpx;
align-items: center;
font-size: 27rpx;
color: #4b5563;
justify-content: center;
}
.edit {
margin-left: 23rpx;
display: flex;
align-items: center;
justify-content: center;
width: 192rpx;
height: 62rpx;
font-size: 27rpx;
color: #ffffff;
background: #3cc7c0;
border-radius: 20rpx;
}
}
}
}
}
.titlepop {
.top {
display: flex;
align-items: center;
justify-content: flex-end;
margin: 0 30rpx;
margin-bottom: 30rpx;
font-size: 30rpx;
color: #3cc7c0;
}
.con {
padding-bottom: 50rpx;
}
.cellbox {
margin: 20rpx 30rpx;
display: flex;
justify-content: space-between;
.cell {
width: 200rpx;
height: 60rpx;
border-radius: 25rpx;
display: flex;
justify-content: center;
align-items: center;
background: #e5e7eb78;
font-size: 24rpx;
}
}
}
/* #ifdef H5 */
:deep(.ql-editor){
position: absolute;
top: 0;
bottom: 0;
width:100%;
}
/* #endif */
</style>