lcw
This commit is contained in:
219
src/views/forumPost/components/changeTheAvatar.vue
Normal file
219
src/views/forumPost/components/changeTheAvatar.vue
Normal file
@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="modelValue"
|
||||
center
|
||||
width="500px"
|
||||
:destroy-on-close="true"
|
||||
:title="title"
|
||||
@close="close"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="avatar-upload-container">
|
||||
<div class="avatar-preview">
|
||||
<img v-if="avatarUrl" :src="avatarUrl" alt="预览头像" class="preview-image">
|
||||
<div v-else class="preview-placeholder">
|
||||
<el-icon class="placeholder-icon"><User /></el-icon>
|
||||
<span>请上传头像</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-section">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-change="handleAvatarChange"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-button size="small" type="primary">选择图片</el-button>
|
||||
</el-upload>
|
||||
<div class="upload-tips">
|
||||
<p>• 支持 JPG、PNG 格式</p>
|
||||
<p>• 图片大小不超过 2MB</p>
|
||||
<p>• 建议尺寸为 200x200 像素</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="confirmUpload"
|
||||
:loading="uploading"
|
||||
:disabled="!avatarUrl || uploading"
|
||||
>
|
||||
{{ uploading ? '上传中...' : '确认更换' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { User } from '@element-plus/icons-vue';
|
||||
import { upImageUploadId } from '@/api/commit';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "更换头像"
|
||||
},
|
||||
heightNumber: {
|
||||
type: Number,
|
||||
default: 250
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(["update:modelValue", "avatarUpdated"]);
|
||||
|
||||
// 头像相关状态
|
||||
const avatarUrl = ref('');
|
||||
const uploading = ref(false);
|
||||
|
||||
// 监听对话框显示状态,重置头像预览
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (!newVal) {
|
||||
// 对话框关闭时重置头像预览
|
||||
avatarUrl.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// 当前选择的文件
|
||||
const selectedFile = ref(null);
|
||||
|
||||
// 处理头像选择
|
||||
const handleAvatarChange = (file) => {
|
||||
selectedFile.value = file.raw;
|
||||
// 创建临时预览URL
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
avatarUrl.value = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file.raw);
|
||||
};
|
||||
|
||||
// 上传前检查
|
||||
const beforeAvatarUpload = (file) => {
|
||||
const isJPG = file.type === 'image/jpeg';
|
||||
const isPNG = file.type === 'image/png';
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
|
||||
if (!isJPG && !isPNG) {
|
||||
ElMessage.error('请上传 JPG 或 PNG 格式的图片!');
|
||||
return false;
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error('图片大小不能超过 2MB!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 确认上传头像
|
||||
const confirmUpload = async () => {
|
||||
if (!avatarUrl.value || !selectedFile.value) {
|
||||
ElMessage.warning('请先选择头像图片');
|
||||
return;
|
||||
}
|
||||
|
||||
uploading.value = true;
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedFile.value);
|
||||
// 调用实际的上传接口
|
||||
const response = await upImageUploadId(formData);
|
||||
console.log(response);
|
||||
emits('avatarUpdated', response);
|
||||
// 关闭对话框
|
||||
close();
|
||||
|
||||
} catch (error) {
|
||||
console.error('上传头像失败:', error);
|
||||
ElMessage.error('上传头像失败,请重试');
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭对话框
|
||||
const close = () => {
|
||||
emits("update:modelValue", false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-upload-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid #e8e8e8;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f5f7fa;
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.preview-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.upload-tips {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
418
src/views/forumPost/components/particulars.vue
Normal file
418
src/views/forumPost/components/particulars.vue
Normal file
@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">{{ title }} </span>
|
||||
<div>
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 标题 -->
|
||||
<div class="contentTitle">
|
||||
<div class="content">
|
||||
{{ listQuery.title }}
|
||||
</div>
|
||||
<div class="contentButton">
|
||||
<el-button size="small" type="primary" @click="showModel = true">回复</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contentDeit" v-for="(item, index) in ListData" :key="index">
|
||||
<div class="deitleft">
|
||||
<div class="media_left">
|
||||
<el-image style="width: 120px; height:120px"
|
||||
:src="item.fbrtx ? setAddress(item.fbrtx) : setAddress(item.hfrtx)" show-progress>
|
||||
<template #error>
|
||||
<div class="image-slot error">
|
||||
<img src="@/assets/images/mr.png" class="user-avatar" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="head_name">{{ item.fbrxm ? item.fbrxm : item.hfrxm }}</div>
|
||||
</div>
|
||||
<div class="deitright">
|
||||
<div class="deitContent">
|
||||
{{ item.content ? item.content : item.hfnr }}
|
||||
<template v-if="item.tp && Array.isArray(item.tp) && item.tp.length > 0">
|
||||
<div style="display: flex;">
|
||||
<div v-for="(img, imgIndex) in item.tp" :key="imgIndex" class="image-item">
|
||||
<el-image :src="setAddress(img)" show-progress style="max-width: 250px; max-height: 400px; margin: 5px;">
|
||||
<template #error>
|
||||
<div class="image-slot error">
|
||||
<img src="@/assets/images/default_male.png" width="80" height="80" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
<div class="deitTime">
|
||||
<div>{{ item.time ? item.time : item.hfsj }}</div>
|
||||
<div class="reply" v-if="!item.replyShow" @click="sendXhf(item)">
|
||||
<span v-if="listQuery.id != item.id">({{ item.xjfhList ? item.xjfhList.length : 0 }})</span> 回复
|
||||
</div>
|
||||
<div class="reply" v-else @click="item.replyShow = !item.replyShow">收起回复</div>
|
||||
</div>
|
||||
<transition name="fade-slide">
|
||||
<div class="responseContent" v-if="item.replyShow">
|
||||
<div class="comment" v-for="(items, indexs) in item.xjfhList" :key="indexs">
|
||||
<div class="head_img">
|
||||
<el-image style="width: 100%; height:100%" :src="setAddress(items.hfrtx)" show-progress>
|
||||
<template #error>
|
||||
<div class="image-slot error">
|
||||
<img src="@/assets/images/mr.png" class="user-avatar" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="commentContent">
|
||||
<span class="name">{{ items.hfrxm }}</span><span v-if="items.sjhfrxm">回复:{{ items.sjhfrxm }}</span>
|
||||
<div class="commentContentText">{{ items.hfnr }}</div>
|
||||
<div class="clickreply">
|
||||
<div>{{ items.hfsj }}</div>
|
||||
<div @click="openXhf(items)">回复</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="end">
|
||||
<el-input v-model="hfContent" type="textarea" placeholder="请输入内容" class="input"></el-input>
|
||||
<div class="send">
|
||||
<V3Emoji :options-name="optionsName" @click-emoji="onVue3Emoje" :recent="true" style="width: 40px;">
|
||||
</V3Emoji>
|
||||
<el-button type="primary" @click="sendHf(item)">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<WriteBack v-model="showModel" :ItemData="listQuery" @SaveReport="getbGsxtXxltHfSelectPage" title="回复帖子"
|
||||
:showCancel="false" :heightNumber="436"></WriteBack>
|
||||
</template>
|
||||
<script setup>
|
||||
import { tbGsxtXxltHfid, tbGsxtXxltHfSave, tbGsxtXxltHfSelectList } from '@/api/tbGsxtXxltHf.js'
|
||||
import V3Emoji from "vue3-emoji";
|
||||
import WriteBack from './writeBack.vue'
|
||||
import { setAddress } from '@/utils/tools.js'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getItem } from '@/utils/storage.js'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
ref,
|
||||
defineExpose,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
} from "vue";
|
||||
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
const optionsName = {
|
||||
'Smileys & Emotion': '笑脸&表情',
|
||||
'Food & Drink': '食物&饮料',
|
||||
'Animals & Nature': '动物&自然',
|
||||
'Travel & Places': '旅行&地点',
|
||||
'People & Body': '人物&身体',
|
||||
Objects: '物品',
|
||||
Symbols: '符号',
|
||||
Flags: '旗帜',
|
||||
Activities: '活动'
|
||||
};
|
||||
const dialogForm = ref(false); //弹窗
|
||||
|
||||
const title = ref("回复帖子");
|
||||
const listQuery = ref({});
|
||||
const ListData = ref([])
|
||||
// 初始化数据
|
||||
const init = (row) => {
|
||||
dialogForm.value = true;
|
||||
console.log(row);
|
||||
listQuery.value = {
|
||||
...row,
|
||||
tp: row.tp && Array.isArray(row.tp) ? row.tp : []
|
||||
}
|
||||
ListData.value = [row]
|
||||
getbGsxtXxltHfSelectPage()
|
||||
};
|
||||
const showModel = ref(false)
|
||||
const getbGsxtXxltHfSelectPage = () => {
|
||||
tbGsxtXxltHfid(listQuery.value.id).then(res => {
|
||||
let data
|
||||
if (res.replyList) {
|
||||
data = res.replyList.map(item => {
|
||||
return {
|
||||
...item,
|
||||
tp: item.hftp ? item.hftp.split(',') : []
|
||||
}
|
||||
})
|
||||
} else {
|
||||
data = []
|
||||
}
|
||||
ListData.value = [...[listQuery.value], ...data].map(item => {
|
||||
return {
|
||||
...item,
|
||||
replyShow: false
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
const hfContent = ref('')
|
||||
const onVue3Emoje = (val) => {
|
||||
hfContent.value += val
|
||||
}
|
||||
const sfwxhf = ref(true)
|
||||
const xhfMsg = ref({})
|
||||
// 回复
|
||||
const sendHf = async (val) => {
|
||||
let item
|
||||
if (sfwxhf) {
|
||||
item = val
|
||||
} else {
|
||||
item = xhfMsg.value
|
||||
}
|
||||
const ltmasg = getItem("ltmasg")
|
||||
// 处理hfContent.value,移除@用户名:前缀
|
||||
let pureContent = hfContent.value;
|
||||
// 检查是否以@开头并且包含冒号
|
||||
if (pureContent.startsWith('@') && pureContent.includes(':')) {
|
||||
// 提取冒号后面的内容作为纯文本
|
||||
const colonIndex = pureContent.indexOf(':');
|
||||
if (colonIndex !== -1 && colonIndex < pureContent.length - 1) {
|
||||
pureContent = pureContent.substring(colonIndex + 1).trim();
|
||||
}
|
||||
}
|
||||
if (hfContent.value === '') {
|
||||
ElMessage.warning('请输入内容');
|
||||
return
|
||||
}
|
||||
const promes = {
|
||||
hfnr: pureContent, // 只使用处理后的纯内容
|
||||
hfrsfzh: ltmasg.sfzh,
|
||||
hfrtx: ltmasg.tx,
|
||||
hfrxm: ltmasg.xm,
|
||||
ltid: listQuery.value.id,
|
||||
sfyjhf: 0,
|
||||
sjhfid: item.id,
|
||||
sjhfrxm: sfwxhf.value ? '' : item.hfrxm
|
||||
}
|
||||
try {
|
||||
const res = await tbGsxtXxltHfSave(promes)
|
||||
if (res) {
|
||||
const dataxhf = await tbGsxtXxltHfSelectList({ sjhfid: item.id })
|
||||
item.xjfhList = dataxhf
|
||||
hfContent.value = ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
sfwxhf.value = true
|
||||
}
|
||||
// 小回复
|
||||
const sendXhf = (item) => {
|
||||
if (item.id === listQuery.value.id) {
|
||||
showModel.value = true
|
||||
} else {
|
||||
item.replyShow = !item.replyShow
|
||||
}
|
||||
};
|
||||
// 打开回复
|
||||
const openXhf = (item) => {
|
||||
hfContent.value = '@' + item.hfrxm + ':'
|
||||
xhfMsg.value = item
|
||||
sfwxhf.value = false
|
||||
}
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
// 关闭回复
|
||||
const close = () => {
|
||||
if (route.query.id) {
|
||||
const query = { ...route.query };
|
||||
delete query.id;
|
||||
router.replace({ query });
|
||||
}
|
||||
// 关闭对话框
|
||||
dialogForm.value = false;
|
||||
};
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.contentTitle {
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.content {
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.contentButton {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.contentDeit {
|
||||
display: flex;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.deitleft {
|
||||
width: 130px;
|
||||
background-color: #f6f7fb;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
|
||||
.head_img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
padding: 2px;
|
||||
border: 1px solid #ccc;
|
||||
// margin-right: 12px;
|
||||
}
|
||||
|
||||
.head_img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.head_name {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.deitright {
|
||||
width: calc(100% - 130px);
|
||||
// background-color: aqua;
|
||||
padding: 15px;
|
||||
|
||||
.deitContent {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
min-height: 180px;
|
||||
|
||||
}
|
||||
|
||||
.deitTime {
|
||||
// width:180px;
|
||||
width: 230px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-left: auto;
|
||||
|
||||
.reply {
|
||||
border-bottom: 0;
|
||||
color: #1D53BF;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
}
|
||||
|
||||
/* 过渡动画样式 */
|
||||
.fade-slide-enter-active,
|
||||
.fade-slide-leave-active {
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.fade-slide-enter-from,
|
||||
.fade-slide-leave-to {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fade-slide-enter-to,
|
||||
.fade-slide-leave-from {
|
||||
max-height: 500px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.responseContent {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #f0f1f2;
|
||||
margin-top: -1px;
|
||||
|
||||
.comment {
|
||||
display: flex;
|
||||
|
||||
.head_img {
|
||||
float: left;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px #ccc solid;
|
||||
padding: 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.commentContent {
|
||||
width: calc(100% - 50px);
|
||||
font-size: 14px;
|
||||
|
||||
.name {
|
||||
color: #1D53BF;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.clickreply {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 200px;
|
||||
margin-left: auto;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.head_img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.end {
|
||||
.send {
|
||||
margin-top: 10px;
|
||||
margin-right: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
// margin-top:auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
130
src/views/forumPost/components/release.vue
Normal file
130
src/views/forumPost/components/release.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" center width="1000px" :destroy-on-close="true" :title="title" @close="close"
|
||||
:close-on-click-modal="false">
|
||||
<div class="cntBox">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform" :rules="rules" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loding" @click="downloadWithStyles">发布</el-button>
|
||||
<el-button type="primary" @click="close">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "@wangeditor/editor/dist/css/style.css";
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbGsxtXxltSave, tbGsxtXxltUpdate } from "@/api/tbGsxtXxltHf.js";
|
||||
import { ref, shallowRef, onBeforeUnmount, defineEmits, defineProps, watch, reactive, computed } from "vue";
|
||||
import { getItem } from "@/utils/storage";
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "报告模板"
|
||||
},
|
||||
heightNumber: {
|
||||
type: Number,
|
||||
default: 448
|
||||
},
|
||||
ItemData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(["update:modelValue", "changeFn", "SaveReport"]);
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emits('update:modelValue', value)
|
||||
});
|
||||
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (newVal && props.ItemData.id) {
|
||||
listQuery.value = { ...props.ItemData}
|
||||
} else if (newVal) {
|
||||
listQuery.value = { }
|
||||
}
|
||||
}, { deep: true })
|
||||
const listQuery = ref({tp:[]})
|
||||
const elform = ref()
|
||||
const formData = ref([
|
||||
{ label: "帖子标题", prop: "title", type: "input", width: '100%' },
|
||||
{ label: "帖子内容", prop: "content", type: "textarea", width: '100%' },
|
||||
{
|
||||
label: "是否公开", prop: "fbfw", type: "select", width: '100%', options: [
|
||||
{ label: '公开', value: '02' },
|
||||
{ label: '不公开', value: '01' }
|
||||
]
|
||||
},
|
||||
{ label: "图片", prop: "tp", type: "upload", width: '100%', limit: 10 },
|
||||
])
|
||||
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{ required: true, message: "请输入帖子标题", trigger: "blur" }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: "请输入帖子内容", trigger: "blur" }
|
||||
],
|
||||
fbfw: [
|
||||
{ required: true, message: "请选择是否公开", trigger: "blur" }
|
||||
],
|
||||
})
|
||||
const loding=ref(false)
|
||||
const downloadWithStyles = () => {
|
||||
const ltmasg= getItem('ltmasg')
|
||||
elform.value.submit((valid) => {
|
||||
loding.value=true
|
||||
if (valid) {
|
||||
const formData = {
|
||||
fbrtx: ltmasg.tx,
|
||||
fbrxm: ltmasg.xm,
|
||||
fbrxm: ltmasg.sfzh,
|
||||
...listQuery.value,
|
||||
tp: listQuery.value.tp ? listQuery.value.tp.join(',') : ''
|
||||
}
|
||||
if (props.ItemData.id) {
|
||||
tbGsxtXxltUpdate(formData).then((res) => {
|
||||
emits("SaveReport");
|
||||
close();
|
||||
}).finally(()=>{
|
||||
loding.value=false
|
||||
})
|
||||
} else {
|
||||
tbGsxtXxltSave(formData).then((res) => {
|
||||
emits("SaveReport");
|
||||
close();
|
||||
}).finally(()=>{
|
||||
loding.value=false
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const close = () => {
|
||||
// 重置表单数据
|
||||
listQuery.value.tp = null
|
||||
listQuery.value = {}
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cntBox {
|
||||
height: 60vh;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
border: 1px dashed #e9e9e9;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__label {
|
||||
width: 80px !important;
|
||||
}
|
||||
</style>
|
||||
311
src/views/forumPost/components/theLeft.vue
Normal file
311
src/views/forumPost/components/theLeft.vue
Normal file
@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="user-profile-card">
|
||||
<!-- <div class="profile-header">
|
||||
<h3>信息</h3>
|
||||
</div> -->
|
||||
|
||||
<div class="profile-content">
|
||||
<div class="avatar-section">
|
||||
<div class="avatar-wrapper" @click="showAvatarModel = true">
|
||||
<el-image style="width: 120px; height:120px" :src="setAddress(userInfo.tx)"
|
||||
show-progress>
|
||||
<template #error>
|
||||
<div class="image-slot error">
|
||||
<img src="@/assets/images/mr.png" class="user-avatar" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="avatar-upload-overlay">
|
||||
<el-icon class="upload-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="avatar-hint">点击更换头像</div> -->
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<div class="info-item" @click="showEditModel = true">
|
||||
<span class="info-label">昵称:</span>
|
||||
<span class="info-value">{{ userInfo.nc }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">姓名:</span>
|
||||
<span class="info-value">{{ userInfo.xm }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">部门:</span>
|
||||
<span class="info-value single-line-ellipsis">{{ userInfo.deptName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 更换头像对话框 -->
|
||||
<ChangeTheAvatar v-model="showAvatarModel" title="更换头像" :heightNumber="250" @avatarUpdated="handleAvatarUpdated">
|
||||
</ChangeTheAvatar>
|
||||
|
||||
<!-- 编辑信息对话框 -->
|
||||
<el-dialog v-model="showEditModel" title="编辑昵称" width="600px" center :close-on-click-modal="false">
|
||||
<el-form ref="editForm" :model="editUserInfo" label-width="100px" :rules="rules">
|
||||
<el-form-item label="昵称" prop="nc">
|
||||
<el-input v-model="userInfo.nc" placeholder="请输入昵称"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showEditModel = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import ChangeTheAvatar from './changeTheAvatar.vue';
|
||||
import { tbGsxtXxltTxTxQueryBySfzh, tbGsxtXxltTxTxSave } from '@/api/tbGsxtXxltHf.js'
|
||||
import { getItem,setItem ,removeItem} from '@/utils/storage.js'
|
||||
import { setAddress } from '@/utils/tools'
|
||||
|
||||
// 控制显示状态的变量
|
||||
const showAvatarModel = ref(false);
|
||||
const showEditModel = ref(false);
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref({});
|
||||
|
||||
// 编辑表单数据
|
||||
const editUserInfo = reactive({});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
nc: [
|
||||
{ required: true, message: '请输入昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '昵称长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
]
|
||||
|
||||
};
|
||||
|
||||
// 编辑表单引用
|
||||
const editForm = ref();
|
||||
// 监听编辑对话框显示,复制用户信息到编辑表单
|
||||
watch(() => showEditModel.value, (newVal) => {
|
||||
if (newVal) {
|
||||
// 深拷贝用户信息到编辑表单
|
||||
Object.assign(editUserInfo, userInfo.value);
|
||||
}
|
||||
});
|
||||
// 处理头像更新
|
||||
const handleAvatarUpdated = (newAvatar) => {
|
||||
|
||||
userInfo.value.tx = newAvatar
|
||||
tbGsxtXxltTxTxSave(userInfo.value).then(res => {
|
||||
removeItem('ltmasg')
|
||||
gettbGsxtXxltTxTxQueryBySfzh()
|
||||
ElMessage({ message: '头像更新成功', type: 'success' });
|
||||
|
||||
})
|
||||
};
|
||||
|
||||
// 处理保存编辑信息
|
||||
const handleSave = () => {
|
||||
editForm.value.validate((valid) => {
|
||||
if (valid) {
|
||||
showEditModel.value = false;
|
||||
tbGsxtXxltTxTxSave(userInfo.value).then(res => {
|
||||
removeItem('ltmasg')
|
||||
gettbGsxtXxltTxTxQueryBySfzh()
|
||||
ElMessage({ message: '信息保存成功', type: 'success' });
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
const gettbGsxtXxltTxTxQueryBySfzh = () => {
|
||||
const sfzh = getItem('idEntityCard')
|
||||
const ltmasg= getItem('ltmasg')
|
||||
if (!ltmasg) {
|
||||
tbGsxtXxltTxTxQueryBySfzh({
|
||||
sfzh: sfzh
|
||||
}).then(res => {
|
||||
const deptId = getItem('deptId')[0]
|
||||
userInfo.value = { ...res, deptName: deptId.deptName }
|
||||
setItem('ltmasg', userInfo.value)
|
||||
})
|
||||
} else { userInfo.value = ltmasg
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
gettbGsxtXxltTxTxQueryBySfzh()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
// height: 100%;
|
||||
// padding: 20px;
|
||||
background-color: #f6f7fb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-profile-card {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
transition: box-shadow 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.avatar-wrapper {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border: 3px solid #e8e8e8;
|
||||
transition: border-color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-upload-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
width: 100%;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
// width: 100px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.single-line-ellipsis {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.app-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.user-profile-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.avatar-section .avatar-wrapper {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.info-section .info-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.info-label {
|
||||
width: auto;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
460
src/views/forumPost/components/theRight.vue
Normal file
460
src/views/forumPost/components/theRight.vue
Normal file
@ -0,0 +1,460 @@
|
||||
<template>
|
||||
|
||||
<div class="app-container" v-show="cs">
|
||||
<div class="publish-section"><el-button type="primary" @click="handleEdit()">发布帖子</el-button></div>
|
||||
<div v-if="list.length > 0" v-infinite-scroll="load">
|
||||
<div class="post-card" v-for="(item, index) in list" :key="index">
|
||||
<div class="post-content" @click="handleOpen(item)" @mouseenter="showActions[index] = true"
|
||||
@mouseleave="showActions[index] = false">
|
||||
<div class="post-header">
|
||||
<div class="post-title">
|
||||
{{ item.title || '无标题' }}
|
||||
</div>
|
||||
<div class="post-meta">
|
||||
<div class="post-time" style="margin-right: 20px;" title="发布人">{{ item.fbrxm || '暂无发布人' }}</div>
|
||||
<div class="post-time" title="发布时间">{{ item.time || '暂无时间' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-body">
|
||||
<div class="post-text">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
<div class="post-images">
|
||||
<div class="image-list">
|
||||
<div v-for="(img, imgIndex) in item.tp" :key="imgIndex" class="image-item" v-if="item.tp && item.tp.length > 0">
|
||||
<el-image :src="setAddress(img)" show-progress>
|
||||
<template #error>
|
||||
<div class="image-slot error">
|
||||
<img src="@/assets/images/default_male.png" width="80" height="80" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;justify-content: flex-end;margin-bottom: 10px;" v-if="sfzh == item.fbrsfzh">
|
||||
<el-button type="text" size="small" class="action-btn edit-btn" @click.stop="handleEdit(item)">
|
||||
<el-icon>
|
||||
<Edit />
|
||||
</el-icon> 修改
|
||||
</el-button>
|
||||
<el-button type="text" size="small" class="action-btn delete-btn" @click.stop="handleDelete(item)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon> 删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空数据占位 -->
|
||||
<div v-else class="empty-data">
|
||||
<div class="empty-icon">
|
||||
<el-icon size="64">
|
||||
<Document />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="empty-text">暂无帖子数据</div>
|
||||
<div class="empty-hint">点击上方按钮发布第一条帖子吧</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<Release v-model="showModel" :ItemData="ItemData" @SaveReport="SaveReport" title="帖子发布" :showCancel="false"
|
||||
:heightNumber="436"></Release>
|
||||
<Particulars ref="particulars"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Release from './release.vue'
|
||||
import { setAddress } from '@/utils/tools'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { tbGsxtXxltSelectPage, tbGsxtXxltDelete ,tbGsxtXxltHfid} from '@/api/tbGsxtXxltHf'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Edit, Delete, Document } from '@element-plus/icons-vue'
|
||||
import { getItem } from '@/utils/storage.js'
|
||||
import Particulars from "./particulars.vue";
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const particulars = ref()
|
||||
const showModel = ref(false)
|
||||
const listQuery = reactive({
|
||||
pageCurrent: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
const total = ref(0)
|
||||
const list = ref([])
|
||||
const showActions = ref({})
|
||||
const sfzh = ref()
|
||||
const ItemData = ref()
|
||||
const cs=ref(false)//需要删除
|
||||
onMounted(() => {
|
||||
sfzh.value = getItem('idEntityCard')
|
||||
SaveReport()
|
||||
if (route.query.id) {
|
||||
tbGsxtXxltHfid(route.query.id).then(res => {
|
||||
ItemData.value = res
|
||||
particulars.value.init(ItemData.value)
|
||||
cs.value=true
|
||||
})
|
||||
} else {
|
||||
cs.value=true
|
||||
}
|
||||
|
||||
})
|
||||
const SaveReport = () => {
|
||||
tbGsxtXxltSelectPage(listQuery).then(res => {
|
||||
// 使用可选链操作符和箭头函数隐式返回优化代码
|
||||
const data = (res.records || []).map(item => ({
|
||||
...item,
|
||||
tp: item.tp ? item.tp.split(',') : []
|
||||
}))
|
||||
total.value = res.total || 0
|
||||
// 初始化所有操作按钮为隐藏状态
|
||||
const actions = {}
|
||||
list.value = data
|
||||
list.value.forEach((_, index) => {
|
||||
actions[index] = false
|
||||
})
|
||||
|
||||
showActions.value = actions
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpen = (item) => {
|
||||
particulars.value.init(item);
|
||||
}
|
||||
|
||||
// 修改帖子
|
||||
const handleEdit = (item) => {
|
||||
ItemData.value = item
|
||||
// 这里可以添加实际的修改逻辑
|
||||
showModel.value = true
|
||||
}
|
||||
|
||||
// 删除帖子
|
||||
const handleDelete = (item) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除帖子「${item.title}」吗?`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
tbGsxtXxltDelete(item.id).then(res => {
|
||||
if (res) {
|
||||
ElMessage({ message: `帖子「${item.title}」已删除`, type: 'success' })
|
||||
SaveReport() // 删除后刷新列表
|
||||
} else {
|
||||
ElMessage({ message: `删除帖子「${item.title}」失败:${res.msg || '未知错误'}`, type: 'error' })
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
ElMessage({ message: '已取消删除', type: 'info' })
|
||||
})
|
||||
}
|
||||
const load = () => {
|
||||
|
||||
if (listQuery.pageCurrent * listQuery.pageSize < total.value) {
|
||||
listQuery.pageCurrent++
|
||||
SaveReport()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 主容器样式
|
||||
.app-container {
|
||||
padding: 16px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
// 发布按钮区域
|
||||
.publish-section {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
// 帖子卡片样式
|
||||
.post-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
// 帖子内容区域
|
||||
.post-content {
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// 帖子头部
|
||||
.post-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
// 帖子标题
|
||||
.post-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
margin-right: 16px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
// 帖子元信息
|
||||
.post-meta {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
// 发布时间
|
||||
.post-time {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 帖子主体
|
||||
.post-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// 帖子文本内容
|
||||
.post-text {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 12px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// 帖子图片容器
|
||||
.post-images {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
// 图片列表
|
||||
.image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: -4px;
|
||||
}
|
||||
|
||||
// 图片项
|
||||
.image-item {
|
||||
margin: 4px;
|
||||
width: 100px;
|
||||
height: 160px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// 帖子图片
|
||||
.post-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
// 更多图片提示
|
||||
.image-more {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
// 操作按钮容器
|
||||
.post-actions {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
background: white;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
// 操作按钮显示状态
|
||||
.post-actions-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
// 操作按钮基础样式
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
background: #f5f7fa;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
// 修改按钮样式
|
||||
.edit-btn:hover {
|
||||
color: #409eff;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
// 删除按钮样式
|
||||
.delete-btn:hover {
|
||||
color: #f56c6c;
|
||||
background: #fef0f0;
|
||||
}
|
||||
|
||||
// 空数据样式
|
||||
.empty-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
margin-bottom: 16px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
// 移动端适配
|
||||
@media (max-width: 768px) {
|
||||
.app-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.post-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
font-size: 13px;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.image-more {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.post-actions {
|
||||
right: 12px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 3px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
padding: 40px 16px;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
134
src/views/forumPost/components/writeBack.vue
Normal file
134
src/views/forumPost/components/writeBack.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" center width="1000px" :destroy-on-close="true" :title="title" @close="close"
|
||||
:close-on-click-modal="false">
|
||||
<div class="cntBox">
|
||||
<FormMessage :formList="formData" v-model="hfrsfzh" ref="elform" :rules="rules" >
|
||||
<template #content>
|
||||
<el-input type="textarea" placeholder="请输入内容" v-model="hfrsfzh.hfnr"></el-input>
|
||||
<div style="width: 100%;border-bottom: 1px solid #eee;margin-top: 10px">
|
||||
<V3Emoji :options-name="optionsName" width="40px" title="表情" @click-emoji="onVue3Emoje"
|
||||
:recent="true" ></V3Emoji>
|
||||
</div>
|
||||
</template>
|
||||
</FormMessage>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="downloadWithStyles" :loading="loading">发布</el-button>
|
||||
<el-button type="primary" @click="close">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "@wangeditor/editor/dist/css/style.css";
|
||||
import V3Emoji from "vue3-emoji";
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { getItem } from '@/utils/storage.js'
|
||||
import { tbGsxtXxltHfSave } from '@/api/tbGsxtXxltHf'
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ref, defineEmits, defineProps, watch, reactive ,computed} from "vue";
|
||||
const optionsName = {
|
||||
'Smileys & Emotion': '笑脸&表情',
|
||||
'Food & Drink': '食物&饮料',
|
||||
'Animals & Nature': '动物&自然',
|
||||
'Travel & Places': '旅行&地点',
|
||||
'People & Body': '人物&身体',
|
||||
Objects: '物品',
|
||||
Symbols: '符号',
|
||||
Flags: '旗帜',
|
||||
Activities: '活动'
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "回复帖子"
|
||||
},
|
||||
heightNumber: {
|
||||
type: Number,
|
||||
default: 448
|
||||
},ItemData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
const emits = defineEmits(["changeFn", "update:modelValue", "SaveReport"]);
|
||||
const hfrsfzh = ref({hftp:[]})
|
||||
watch(() => props.ItemData, (newVal) => {
|
||||
if (newVal) {
|
||||
listQuery.value = {
|
||||
...newVal,
|
||||
hftp:newVal.hftp?newVal.hftp.split(','):[]
|
||||
}
|
||||
console.log(listQuery.value);
|
||||
}
|
||||
}, { deep: true })
|
||||
const listQuery = ref({hftp:[]})
|
||||
const elform = ref()
|
||||
const formData = ref([
|
||||
{ label: "帖子内容", prop: "content", type: "slot", width: '100%' },
|
||||
{ label: "图片", prop: "hftp", type: "upload", width: '100%', limit: 10 },
|
||||
])
|
||||
const loading = ref(false)
|
||||
const rules = reactive({
|
||||
hfnr: [
|
||||
{ required: true, message: "请输入帖子内容", trigger: "blur" }
|
||||
]
|
||||
})
|
||||
|
||||
const onVue3Emoje = (val) => {
|
||||
hfrsfzh.value.hfnr += val
|
||||
}
|
||||
const downloadWithStyles = () => {
|
||||
const ltmasg = getItem('ltmasg')
|
||||
const params = {
|
||||
hfnr: hfrsfzh.value.hfnr,
|
||||
hfrsfzh: ltmasg.sfzh,
|
||||
hfrtx: ltmasg.tx,
|
||||
hfrxm: ltmasg.xm,
|
||||
ltid: listQuery.value.id,
|
||||
sfyjhf: "1",
|
||||
hftp:hfrsfzh.value.hftp?hfrsfzh.value.hftp.join(','):''
|
||||
}
|
||||
loading.value = true
|
||||
tbGsxtXxltHfSave(params).then(res => {
|
||||
close()
|
||||
emits("SaveReport", res);
|
||||
ElMessage.success('回复成功');
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emits('update:modelValue', value)
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
hfrsfzh.value.hftp = []
|
||||
hfrsfzh.value = {}
|
||||
dialogVisible.value = false
|
||||
// emits("update:modelValue", false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cntBox {
|
||||
height: 60vh;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
border: 1px dashed #e9e9e9;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__label {
|
||||
width: 80px !important;
|
||||
}
|
||||
</style>
|
||||
40
src/views/forumPost/index.vue
Normal file
40
src/views/forumPost/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="titleBox">
|
||||
<PageTitle title="情报论坛"> </PageTitle>
|
||||
</div>
|
||||
<div class=" bilateral">
|
||||
<div class="the-left"> <TheLeft/></div>
|
||||
<div class="the-right"> <TheRight @handleOpen="handleOpen"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
import TheLeft from "./components/theLeft.vue";
|
||||
import TheRight from "./components/theRight.vue";
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.bilateral{
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 190px);
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
color: #000;
|
||||
justify-content: space-between;
|
||||
.the-left{
|
||||
width: 210px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.the-right{
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user