case-data/pages/publish/publish.vue
zoujiandong 197d26b22c 6.11
2025-06-11 19:00:02 +08:00

1007 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="upage">
<backNav :navName="'发病例交流帖'"></backNav>
<view class="form">
<view class="textbox">
<up--textarea
autoHeight
v-model="form.title"
placeholder="输入标题,可包含患者信息、主诉"
></up--textarea>
</view>
<view class="row">
<view class="left"> 病例信息 </view>
<view class="right">
<u-icon name="trash" color="#6B7280" size="18"></u-icon>
清除模板
</view>
</view>
<view class="textcon page-editor-container" id="editor">
<sv-editor eid="editor-id" @ready="ready" @focus="focusInfo" @blur="blurInfo"></sv-editor>
<!-- <editor
id="editor"
placeholder="写点什么吧~~"
show-img-resize
show-img-toolbar
show-img-size
@ready="ready"
@focus="OnFocus"
@statuschange="statuschange"
></editor> -->
</view>
<view class="textcon" style="border: none">
<view class="row">
<view class="left">
总结与讨论 <text class="tip">(可不填)</text>
</view>
</view>
<view class="textcell">
<view class="iptbox">
<up--textarea
maxlength="-1"
autoHeight
v-model="form.title"
placeholder="分享经验和心得,如:诊断与鉴别诊断易错点,治疗过程难点,病例的相关知识总结及讨论等"
></up--textarea>
</view>
</view>
</view>
<view class="textcon" style="border:none">
<view class="sickbox" >
<view class="sick">
治疗经过及结果
<view class="del">
<up-icon name="close-circle" color="#3CC7C0" size="20"></up-icon>
</view>
</view>
<view class="add">
<up-icon name="plus" color="#3CC7C0" size="16"></up-icon>
添加疾病
</view>
</view>
<!-- <view class="imgbox">
<view class="imgcell">
<up--image src="https://cdn.uviewui.com/uview/album/1.jpg" mode="widthFix"></up--image>
</view>
</view> -->
</view>
</view>
<view class="bottom">
<view class="toolbox">
<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 class="cell" :class="{active:!isFocusInfo}" @click="alertTitle">
<up--image
:src="addImg"
class="headImg"
width="32rpx"
height="32rpx"
></up--image>
<view class="name" >添加小标题</view>
</view>
</view>
<view class="bottombtn">
<view class="left">
<view class="draft">存草稿</view>
<view class="vote">
投票
<up--image
:src="voteImg"
class="headImg"
width="47rpx"
height="47rpx"
></up--image>
</view>
</view>
<view class="right" @click="publish">发布</view>
</view>
</view>
<up-popup
:round="10"
:show="showVote"
mode="bottom"
@close="closeVote"
@open="openVote"
>
<view class="votepop">
<view class="titlebox">
<view class="left" @click="closeVote">取消</view>
<view class="right" @click="closeVote">保存</view>
</view>
<view class="votecon">
<view class="titlebox">
<view class="title">投票标题</view>
<view class="desc">(最多20字)</view>
</view>
<view class="row first">
<up-input
placeholder="请输入投票标题"
border="surround"
v-model="value"
clearable
></up-input>
</view>
<view class="row">
<up-input
placeholder="输入选项建议少于16个字"
border="surround"
v-model="value"
clearable
></up-input>
</view>
<view class="row">
<up-input
placeholder="输入选项建议少于16个字"
border="surround"
v-model="value"
clearable
></up-input>
</view>
<view class="row">
<up-input
placeholder="输入选项建议少于16个字"
border="surround"
v-model="value"
clearable
></up-input>
</view>
<view class="add">
<up-icon name="plus" color="#4B5563" size="18"></up-icon>
<view class="desc">添加选项</view>
</view>
<view class="expire">
<view class="name">有效期</view>
<view class="right">
<view class="minus">-</view>
<view class="day">7</view>
<view class="plus">+</view>
</view>
</view>
<view class="tips"
>友情提醒为保证投票结果准确性帖子发布后投票无法修改</view
>
</view>
<view class="confirm">确认</view>
<view class="del">删除投票</view>
</view>
</up-popup>
<up-popup
:round="10"
zIndex="999999"
:show="showDraft"
mode="bottom"
@close="closeDraft"
@open="openDraft"
>
<view class="draftpop">
<view class="titlebox"
>草稿箱
<view class="close" @click="closeDraft"
><up-icon name="close" color="#4B5563" size="20"></up-icon
></view>
</view>
<view class="draftlist">
<view class="cell">
<view class="title"
>2025:
肝硬化门静脉高压症食管胃底静脉曲张破裂出血诊治专家共识</view
>
<view class="smalltitle">版2025 APASL临床实践指南</view>
<view class="deal">
<view class="time">编辑于03-11</view>
<view class="right">
<view class="del"
><up-icon name="trash" color="#4B5563" size="16"></up-icon
>删除</view
>
<view class="edit"
><up-icon name="edit-pen" color="#fff" size="17"></up-icon
>编辑</view
>
</view>
</view>
</view>
</view>
</view>
</up-popup>
</div>
<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>
</template>
<script setup>
import { reactive, ref } from "vue";
import backNav from "@/components/backNav/backNav";
import photoImg from "@/static/photo.png";
import addImg from "@/static/add.png";
import videoImg from "@/static/videoicon.png";
import voteImg from "@/static/vote.png";
import api from "@/api/api";
import dayjs from "dayjs";
import svEditor from "@/uni_modules/sv-editor/components/sv-editor/sv-editor.vue";
import SvEditorToolbar from "@/uni_modules/sv-editor/components/sv-editor/sv-editor-toolbar.vue";
import {
addImage,
addVideo,
addText,
} from "@/uni_modules/sv-editor/components/common/utils.js";
const form = reactive({
exchange_title: "",
exchange_content: "",
exchange_summary: "",
case_exchange_vote: {
vote_title: "",
valid_day: null,
case_exchange_vote_option: [],
},
case_exchange_label: [],
case_exchange_img: [],
case_exchange_video: [],
});
const value = ref("");
const showVote = ref(false);
const showDraft = ref(false);
const showTitle = ref(false);
const draftList = ref([]);
const imgList = ref([]);
const videoList = ref([]);
const editorCtx = ref(null);
const isFocusInfo = ref(true);
const ready = (e) => {
console.log("==== ready ==== :", e);
console.log(e);
console.log(e.createCoverThumbnail);
editorCtx.value = e;
let html =
"<p>【患者信息】:</p><br/><br/><p>【主诉】:</p><br/><br/><p>【现病史及既往史】:</p><br/><br/><p>【检查】:</p><br/><br/><p>【临床诊断】:</p><br/><br/><p>【治疗经过及结果】:</p><br/><br/>";
editorCtx.value.initHtml(html);
};
const focusInfo=()=>{
isFocusInfo.value = true
}
const blurInfo=()=>{
isFocusInfo.value = false
}
const closeVote = () => {
showVote.value = false;
};
const openVote = () => {
showVote.value = true;
};
const closeDraft = () => {
showDraft.value = false;
};
const delTitle = () => {
showMinTitle.value = false;
};
const openDraft = () => {
showDraft.value = true;
};
const closeTitle = () => {
showTitle.value = false;
};
const openTitle = () => {
showTitle.value = true;
};
const alertTitle=()=>{
if(isFocusInfo.value){
showTitle.value=true
}
}
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 addExchange = () => {
api.addExchange(form).then((res) => {
uni.showToast({
title: "发布成功",
icon: "none",
duration: 2000,
});
});
};
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 insertWord=(word)=>{
addText(word);
showTitle.value=false;
}
const insertAllWord=()=>{
let word=['患者信息','主诉','现病史及既往史:','检查','临床诊断','治疗经过及结果']
word.forEach(item=>{
addText(item);
})
showTitle.value=false;
}
const uploadVideo = (file) => {
return new Promise((res) => {
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,
},
async success(res) {
if (res.statusCode === 204) {
let url = host + "/" + dir + filename + imgType;
res(url);
}
},
fail: (err) => {
console.log(err);
},
});
});
}
});
});
};
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 insertImage = (file) => {
uni.chooseImage({
count: 9, //默认9
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album"], //从相册选择
extension: [".jpg", ".png", ".jpeg"],
success: function (res) {
pFun(res.tempFilePaths);
},
});
};
const insertVideo = (file) => {
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 publish = () => {
editorCtx.value.getLastContent().then((res) => {
let html = editorCtx.value.exportHtml(res.html);
console.log(html);
});
};
const HandleAddVideo = async (file) => {
uni.showLoading({
title: "正在上传视频...",
mask: true,
});
const videos = await addVideo(async (editorCtx) => {
return new Promise((resolve) => {
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((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: "success" });
} else {
uni.showToast({ title: "添加视频失败", icon: "error" });
}
};
const getList = () => {
api.getDraftList().then((res) => {
draftList.value = res.data.data;
});
};
const previewImg = () => {
uni.previewImage({
current: "https://example.com/image1.jpg",
urls: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"],
});
};
</script>
<style lang='scss' scoped>
.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: #e5e7eb;
font-size: 24rpx;
}
}
}
#editor {
padding: 0 30rpx;
height: calc(100vh - 600rpx);
}
.myVideo {
width: 100%;
border-radius: 15rpx;
}
.imgbox {
margin: 0 30rpx 30rpx;
position: relative;
.close {
position: absolute;
top: 0rpx;
right: 0;
z-index: 9999;
}
.imgunit {
width: 150rpx;
height: 150rpx;
position: relative;
.close {
position: absolute;
top: 0rpx;
right: 0;
}
}
}
.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 30rpx 0;
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;
}
}
}
}
}
.expire {
margin-top: 46rpx;
padding: 0 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
.name {
font-size: 31rpx;
color: #111827;
}
.right {
display: flex;
align-items: center;
.day {
min-width: 50rpx;
text-align: center;
}
.minus {
width: 62rpx;
height: 62rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 50%;
font-size: 50rpx;
}
.plus {
display: flex;
align-items: center;
font-size: 50rpx;
justify-content: center;
width: 62rpx;
height: 62rpx;
background: #f5f5f5;
border-radius: 50%;
}
}
}
.row {
padding: 0 30rpx;
margin-bottom: 23rpx;
:deep(.u-input) {
background: #f5f5f5;
}
:deep(.u-input--radius) {
border-radius: 15rpx;
}
:deep(.u-input__content__field-wrapper__field) {
height: 92rpx;
}
}
.first {
margin-bottom: 47rpx;
}
.votepop {
.confirm {
margin: 39rpx 30rpx 0;
height: 92rpx;
background: #3cc7c0;
border-radius: 15rpx;
display: flex;
align-items: center;
font-size: 31rpx;
color: #ffffff;
justify-content: center;
}
.del {
margin: 30rpx 30rpx 30rpx;
height: 92rpx;
background: #fff;
border-radius: 15rpx;
font-size: 31rpx;
color: #666666;
display: flex;
align-items: center;
justify-content: center;
}
.tips {
margin-top: 30rpx;
font-size: 27rpx;
color: #9ca3af;
line-height: 38rpx;
padding: 0 30rpx;
}
.add {
margin: 0 30rpx;
display: flex;
align-items: center;
height: 92rpx;
justify-content: center;
background: #f5f5f5;
border-radius: 15rpx;
font-size: 31rpx;
color: #4b5563;
.desc {
margin-left: 10rpx;
}
}
.titlebox {
padding: 0 30rpx;
height: 86rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2rpx solid #f3f4f6;
.left {
font-size: 31rpx;
color: #4b5563;
}
.right {
font-size: 31rpx;
color: #3cc7c0;
}
}
.votecon {
max-height: calc(100vh - 530rpx);
overflow-y: scroll;
.titlebox {
border: none;
margin: 30rpx 0 20rpx;
.title {
font-size: 31rpx;
color: #111827;
}
.desc {
font-size: 27rpx;
color: #9ca3af;
}
}
}
}
.upage {
display: flex;
flex-direction: column;
height: 100vh;
:deep(.u-image) {
background: transparent !important;
}
}
.toolbox {
border-top: 2rpx solid #f3f4f6;
padding: 22rpx 30rpx;
display: flex;
align-items: center;
.cell {
margin-right: 20rpx;
display: flex;
align-items: center;
.name {
font-size: 25rpx;
margin-left: 4rpx;
color: #4b5563;
}
}
.cell.active{
opacity: 0.65;
}
}
.bottom {
position: fixed;
bottom: 0;
width: 100%;
z-index: 3;
background: #ffffff;
.bottombtn {
display: flex;
align-items: center;
border-top: 2rpx solid #e5e7eb;
height: 133rpx;
}
.right {
margin-right: 30rpx;
flex: 1;
margin-left: 32rpx;
height: 86rpx;
background: #3cc7c0;
border-radius: 23rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 31rpx;
color: #ffffff;
}
.left {
margin-left: 30rpx;
display: flex;
.draft {
width: 162rpx;
height: 86rpx;
background: rgba(0, 0, 0, 0);
border-radius: 16rpx;
border: 2rpx solid #e5e7eb;
display: flex;
align-items: center;
justify-content: center;
}
.vote {
margin-left: 25rpx;
width: 162rpx;
height: 86rpx;
background: rgba(0, 0, 0, 0);
border-radius: 16rpx;
border: 2rpx solid #e5e7eb;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.form {
height: calc(100vh - 133rpx);
margin: -20rpx 0rpx 134rpx;
padding-bottom: 80rpx;
overflow-y: scroll;
.sickbox {
display: flex;
padding: 25rpx 25rpx 0;
margin-bottom: 20rpx;
}
.add {
display: flex;
font-size: 26rpx;
justify-content: center;
align-items: center;
color: #3cc7c0;
margin-left: 18rpx;
}
.sick {
position: relative;
padding: 0 60rpx 0 30rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
background: #f9f9f9;
border-radius: 40rpx;
font-size: 25rpx;
color: #3cc7c0;
.del {
position: absolute;
right: 12rpx;
}
}
.textcon {
border-bottom: 2rpx solid #f3f4f6;
}
.textbox {
padding: 0 30rpx;
border-bottom: 2rpx solid #f3f4f6;
:deep(.u-textarea__field) {
min-height: 55rpx !important;
}
}
.textcell {
padding: 0 30rpx;
margin-top: 25rpx;
.titlebox {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.row {
padding: 30rpx 30rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
.left {
font-size: 38rpx;
color: #000000;
.tip {
color: #9ca3af;
font-size: 32rpx;
}
}
.right {
display: flex;
align-items: center;
color: #6b7280;
font-size: 31rpx;
}
}
}
</style>