feat:完善discuss主题相关功能及界面

This commit is contained in:
橙子
2023-03-19 23:12:27 +08:00
parent 10fef4e2d9
commit 019c73ceca
56 changed files with 474 additions and 134 deletions

View File

@@ -6,6 +6,23 @@ export function getList(data){
params:data
})
};
export function getTopList(data){
if(data==undefined)
{
data={isTop:true}
}
else
{
data["isTop"]=true;
}
return myaxios({
url: '/discuss',
method: 'get',
params:data
})
};
export function get(id){
return myaxios({
url: `/discuss/${id}`,

View File

@@ -1,24 +1,26 @@
<template >
<div class="avatar">
<div class="avatar-left">
<el-avatar :size="props.size" :src="iconUrl" />
<div class="avatar-left" >
<el-avatar :size="props.size" :src="userInfo.icon ?? '/src/assets/logo.ico'" />
<div v-if="props.isSelf">
<div class="nick" > {{userInfo.name}}</div>
</div>
<div v-if="props.isSelf">
<div class="nick"> {{ userInfo.nick }}</div>
</div>
<div v-if="!props.isSelf">
<div class="nick" :class="{mt_1: props.time!='undefined'}"> {{userInfo.name}}</div>
<div class="remarks" v-if="props.time"> {{props.time}}</div>
<div class="remarks"> <slot name="bottom" /></div>
<div class="nick" :class="{ mt_1: props.time != 'undefined' }"> {{ userInfo.nick }}</div>
<div class="remarks" v-if="props.time"> {{ props.time }}</div>
<div class="remarks">
<slot name="bottom" />
</div>
</div>
<div class="info" v-if="!props.isSelf">
<el-tag class="ml-2" type="warning">V6</el-tag>
<el-tag class="ml-2" type="danger">管理</el-tag>
<el-tag class="ml-2" type="warning">V8</el-tag>
<el-tag class="ml-2" type="danger"></el-tag>
</div>
</div>
<el-button v-if="props.showWatching" type="primary" size="default" icon="Plus">关注</el-button>
@@ -26,57 +28,84 @@
</template>
<script setup>
import useUserStore from '@/stores/user'
import { reactive, watch ,onMounted } from 'vue';
import { reactive, watch, onMounted, computed } from 'vue';
//userInfo
//{icon,name,role,id},根据判断userInfo是否等于未定义来觉得是当前登录用户信息还是其他人信息
const props = defineProps(['size','showWatching','time','userInfo','isSelf'])
const userStore=useUserStore();
const userInfo=reactive({
icon:"",
name:"",
role:[],
id:""
});
const props = defineProps(['size', 'showWatching', 'time', 'userInfo', 'isSelf'])
const userStore = useUserStore();
const userInfo = reactive({
icon: "",
nick: "",
role: [],
id: ""
});
const iconUrl = computed(() => {
if (userInfo.icon == null || userInfo.icon == undefined || userInfo.icon == '') {
return '/src/assets/logo.ico';
}
if (userInfo.icon.includes(import.meta.env.VITE_APP_BASEAPI)) {
return userInfo.icon;
}
return `${import.meta.env.VITE_APP_BASEAPI}/file/${userInfo.icon}`;
})
watch(userStore, (n) => {
if (props.userInfo == undefined) {
userInfo.nick = n.name;
}
})
watch(() => props, (n) => {
Init();
}, { deep: true })
onMounted(() => {
Init();
})
const Init = () => {
//使用传入值
if(props.userInfo!= undefined)
{
userInfo.icon=props.userInfo.icon;
userInfo.name=props.userInfo.name;
userInfo.role=props.userInfo.role;
userInfo.id=props.userInfo.id;
if (props.userInfo != undefined) {
userInfo.icon = props.userInfo.icon;
userInfo.nick = props.userInfo.nick;
userInfo.role = props.userInfo.role;
userInfo.id = props.userInfo.id;
}
//使用当前登录用户
else
{
userInfo.icon=userStore.icon;
userInfo.name=userStore.name;
userInfo.role=userStore.role;
userInfo.id=userStore.id;
else {
userInfo.icon = userStore.icon;
userInfo.nick = userStore.name;
userInfo.role = userStore.role;
userInfo.id = userStore.id;
}
})
}
</script>
<style scoped>
.mt_1
{
margin-top: 0.5rem;
.mt_1 {
margin-top: 0.5rem;
}
.nick
{
font-weight:bold;
.nick {
font-weight: bold;
}
.info
{
.info {
margin-top: 0.6rem;
margin-left: 1rem;
}
.info .el-tag
{
margin-right:1rem;
.info .el-tag {
margin-right: 1rem;
}
.el-icon {
color: white;

View File

@@ -1,8 +1,10 @@
<template>
<el-card class="box-card" shadow="never">
<el-badge :value="props.badge??''" class="box-card" >
<el-card shadow="never" :style="{'border-color':props.color}" >
<div class="card-header">
<AvatarInfo src="asdsadas" :userInfo="{name:'大白子'}" />
<AvatarInfo :userInfo="props.user" :time="props.creationTime"/>
</div>
@@ -13,7 +15,7 @@
<div class=" item item-bottom">
<el-space :size="10" :spacer="spacer">
<div class="item-description">
{{ props.createTime }}
{{ props.creationTime }}
</div>
@@ -21,21 +23,22 @@
点赞</el-button>
<el-button icon="Star" text>
收藏</el-button>
<el-button icon="ChatDotRound" text>
评论</el-button>
<el-button icon="View" text>
浏览数:{{ props.seeNum??0 }}</el-button>
</el-space>
</div>
</el-card>
</el-badge>
</template>
<script setup>
import { h, ref } from 'vue'
import { useRouter } from 'vue-router'
import AvatarInfo from './AvatarInfo.vue';
const props = defineProps(['title','introduction','createTime','id'])
const props = defineProps(['title','introduction','creationTime','id','user','badge',"color","seeNum"])
const router = useRouter()
const spacer = h(ElDivider, { direction: 'vertical' })
@@ -44,6 +47,9 @@ const enterDiscuss = (id) => {
}
</script>
<style scoped>
.el-card{
border: 2px solid white
}
.item-bottom .el-icon {
margin-right: 0.4rem;
@@ -63,6 +69,9 @@ const enterDiscuss = (id) => {
.box-card {
width: 100%;
min-height: 15rem;
/* right: calc(1px + var(--el-badge-size)/ 2) !important; */
/* top: 0 !important; */
}
.item-title {
@@ -86,4 +95,5 @@ const enterDiscuss = (id) => {
font-size: initial;
font-weight: 700;
}
</style>

View File

@@ -1,10 +1,17 @@
<template>
<mavon-editor ref='md' v-model="text" :subfield="true" :codeStyle="props.codeStyle" :ishljs="true"
:style="{ minHeight: props.height, maxHeight: '100%' }" class="edit" @imgAdd="imgAdd">
<!-- 引用视频链接的自定义按钮 -->
<template v-slot:left-toolbar-after>
<!--点击按钮触发的事件是打开表单对话框-->
<el-button @click="fileDialogVisible=true" aria-hidden="true" class="op-icon fa" title="上传文件">
<el-icon ><FolderChecked /></el-icon>
</el-button>
<el-dropdown :hide-on-click='false'>
<el-button aria-hidden="true" class="op-icon fa" title="表情包">
😊
@@ -37,9 +44,45 @@
</el-dropdown>
<el-dialog
:modal=false
:draggable=true
v-model="fileDialogVisible"
title="文件上传"
width="30%"
:before-close="fileHandleClose"
>
<span>选择你的文件</span>
<el-upload
class="upload-demo"
drag
:action="fileUploadUrl"
multiple
:on-success="onSuccess"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
可将文件拖拽到这里 <em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
文件需小于100MB以内
</div>
</template>
</el-upload>
<p v-for="(item,i) in fileUrlList" :key="i">{{` ${i+1}: ${getUrl(item)}` }} <el-button></el-button></p>
<template #footer>
<span class="dialog-footer">
<el-button @click="fileDialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
确认
</el-button>
</span>
</template>
</el-dialog>
</template>
</mavon-editor>
@@ -51,10 +94,16 @@ import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
import { ref, computed, watch, onMounted } from 'vue';
import { upload } from '@/apis/fileApi'
const md = ref(null);
const props = defineProps(['height', 'modelValue', "codeStyle"])
const emit = defineEmits(['update:modelValue'])
const fileDialogVisible=ref(false)
//已经上传好的文件列表url
const fileUrlList=ref([])
const fileUploadUrl=`${import.meta.env.VITE_APP_BASEAPI}/file`
// //v-model传值出去
const text = computed({
get() {
@@ -65,6 +114,18 @@ const text = computed({
}
})
const getUrl= (str)=>{
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
}
//关闭文件上传弹窗
const fileHandleClose=()=>{
fileDialogVisible=false;
}
//文件上传成功后
const onSuccess=(response)=>{
fileUrlList.value.push(response[0].id)
}
//图片上传
const imgAdd = async (pos, $file) => {
// 第一步.将图片上传到服务器.
@@ -77,9 +138,13 @@ const imgAdd = async (pos, $file) => {
}
</script>
<style scoped>
<style scoped>
.edit {
width: 100%;
width: 100%;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<el-tree
:data="data"
:props="props.defaultProps"
:data="props.data==''?[]:props.data"
:props="defaultProps"
@node-click="handleNodeClick"
:expand-on-click-node="false"
node-key="id"

View File

@@ -43,8 +43,8 @@
</el-menu-item>
<el-sub-menu index="6">
<template #title>个人中心</template>
<el-menu-item index="6-1">学习 one</el-menu-item>
<el-menu-item index="6-2">学习 two</el-menu-item>
<el-menu-item index="6-1" @click="enterProfile">进入个人中心</el-menu-item>
<el-menu-item index="6-2" @click="enterProfile">其他</el-menu-item>
<el-menu-item index="6-3" @click="logout">登出</el-menu-item>
</el-sub-menu>

View File

@@ -7,6 +7,7 @@ const useUserStore = defineStore('user',
id:'',
token: getToken(),
name: '游客',
userName:'',
icon: null,
roles: [],
permissions: []
@@ -36,7 +37,7 @@ const useUserStore = defineStore('user',
getInfo().then(response => {
const res=response;
const user = res.user
const avatar = (user.icon == "" || user.icon == null) ? "" : import.meta.env.VITE_APP_BASE_API + "/file/"+user.icon;
const avatar = (user.icon == "" || user.icon == null) ? "/src/assets/logo.ico" : import.meta.env.VITE_APP_BASEAPI + "/file/"+user.icon;
if (res.roleCodes && res.roleCodes.length > 0) { // 验证返回的roles是否是一个非空数组
this.roles = res.roleCodes
@@ -50,7 +51,8 @@ const useUserStore = defineStore('user',
// this.roles = ["admin"];
// this.permissions=["*:*:*"]
this.name = user.nick
this.avatar = avatar;
this.icon = avatar;
this.userName=user.userName
resolve(res)
}).catch(error => {
reject(error)

View File

@@ -36,8 +36,9 @@
<el-col :span="14">
<el-row class="left-div">
<el-col :span="24">
<AvatarInfo :size="50" :showWatching="true" :time="'2023-03-08 21:09:02'"></AvatarInfo>
<!-- {{ discuss.user }} -->
<AvatarInfo :size="50" :showWatching="true" :time="discuss.creationTime" :userInfo="discuss.user"></AvatarInfo>
<!-- :userInfo="{nick:'qwe'} -->
<el-divider />
<h2>{{ discuss.title }}</h2>
<ArticleContentInfo :code="discuss.content??''"></ArticleContentInfo>
@@ -137,6 +138,8 @@ const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }];
const articleData = ref([]);
//主题内容
const discuss = ref({});
//当前默认选择的子文章
const currentNodeKey=route.params.articleId;
//目录数据

View File

@@ -42,13 +42,30 @@
</div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="推荐" name="first"> </el-tab-pane>
<el-tab-pane label="最新" name="second"> </el-tab-pane>
<el-tab-pane label="最热" name="third"> </el-tab-pane>
<el-tabs v-model="activeName" @tab-change="handleClick">
<el-tab-pane label="最新" name="new"> </el-tab-pane>
<el-tab-pane label="推荐" name="suggest"> </el-tab-pane>
<el-tab-pane label="最热" name="host"> </el-tab-pane>
</el-tabs>
<el-collapse >
<el-collapse-item >
<template #title>
<div class="collapse-top">
已置顶主题<el-icon class="header-icon">
<info-filled />
</el-icon>
</div>
</template>
<div class="div-item" v-for="i in topDiscussList" >
<DisscussCard :title="i.title" :introduction="i.introduction" :creationTime="i.creationTime" :id="i.id" :user="i.user" :color="i.color" :seeNum="i.seeNum" badge="置顶"/>
</div>
</el-collapse-item>
</el-collapse>
<el-divider v-show="topDiscussList.length>0" />
<div class="div-item" v-for="i in discussList" >
<DisscussCard :title="i.title" :introduction="i.introduction" :createTime="i.createTime" :id="i.id"/>
<DisscussCard :title="i.title" :introduction="i.introduction" :creationTime="i.creationTime" :id="i.id" :color="i.color" :seeNum="i.seeNum" :user="i.user"/>
</div>
<div>
<el-pagination
@@ -70,25 +87,30 @@
<script setup>
import DisscussCard from '@/components/DisscussCard.vue'
import {getList} from '@/apis/discussApi.js'
import {getList,getTopList} from '@/apis/discussApi.js'
import { onMounted, ref,reactive } from 'vue'
import { useRoute,useRouter } from 'vue-router'
//数据定义
const route=useRoute()
const router=useRouter()
const activeName = ref('first')
const activeName = ref('new')
//主题内容
const discussList=ref([]);
//置顶主题内容
const topDiscussList = ref([]);
const total=ref(100)
const query=reactive({
pageNum:1,
pageSize:10,
title:'',
plateId:route.params.plateId
plateId:route.params.plateId,
type:activeName.value
})
const handleClick = (tab, event) => {
console.log(tab, event)
const handleClick =async (tab, event) => {
query.type=activeName.value ;
await loadDiscussList();
}
onMounted(async()=>{
@@ -100,6 +122,10 @@ const loadDiscussList=async()=>{
const response= await getList(query);
discussList.value=response.items;
total.value=Number( response.total);
//全查,无需参数
const topResponse=await getTopList();
topDiscussList.value=topResponse.items;
}
//进入添加主题页面
@@ -130,6 +156,10 @@ background-color: #FFFFFF;
padding: 1rem;
margin: 1rem 0rem ;
}
.collapse-top
{
padding-left: 2rem;
}
.header .el-input
{

View File

@@ -18,7 +18,7 @@
</el-form-item>
<el-form-item
v-if="route.query.artType == 'article'"
label="名称:"
label="子文章名称:"
prop="name"
>
<el-input placeholder="请输入" v-model="editForm.name" />
@@ -100,7 +100,10 @@ const ruleFormRef = ref(null);
const rules = reactive({
title: [
{ required: true, message: "请输入标题", trigger: "blur" },
{ min: 3, max: 20, message: "长度 3 到 20", trigger: "blur" },
{ min: 3, max: 40, message: "长度 3 到 20", trigger: "blur" },
],
name: [
{ required: true, message: "请输入子文章名称", trigger: "blur" },
],
content: [
{ required: true, message: "请输入内容", trigger: "blur" },

View File

@@ -14,7 +14,7 @@
</el-col>
<el-col :span="24" v-for="i in discussList">
<DisscussCard :title="i.title" :introduction="i.introduction" :createTime="i.createTime" :id="i.id"/>
<DisscussCard :title="i.title" :introduction="i.introduction" :creationTime="i.creationTime" :id="i.id" :user="i.user" :seeNum="i.seeNum"/>
</el-col>
</el-row>

View File

@@ -3,6 +3,8 @@
<el-input v-model="loginForm.userName" placeholder="用户名" />
<el-input v-model="loginForm.password" placeholder="密码" show-password />
<el-button class="login-btn" type="primary" @click="login" >登录</el-button>
<br>
<el-button class="login-btn" type="primary" @click="guestlogin" >游客临时登录</el-button>
</template>
<script setup>
import { reactive } from 'vue';
@@ -16,7 +18,12 @@ const loginForm=reactive({
uuid:"",
code:""
})
const guestlogin=async ()=>{
loginForm.userName="guest";
loginForm.password="123456"
await userStore.login(loginForm);
router.push("/index")
}
const login=async ()=>{
const response= await userStore.login(loginForm);
if( response.code==undefined)
@@ -38,5 +45,6 @@ margin:0rem 0 0.5rem 0;
.login-btn
{
width: 100%;
margin-bottom: 0.5rem;
}
</style>

View File

@@ -1,49 +1,44 @@
<template>
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" /></div>
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
</div>
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
@realTime="realTime"
v-if="visible"
/>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox"
@realTime="realTime" v-if="visible" />
</el-col>
<el-col :xs="24" :md="12" :style="{height: '350px'}">
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="options.previews.url" :style="options.previews.img"/>
<img :src="options.previews.url" :style="options.previews.img" />
</div>
</el-col>
</el-row>
<br/>
<br />
<el-row>
<el-col :lg="2" :md="2">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button>
选择
<el-icon class="el-icon--right"><Upload /></el-icon>
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{span: 1, offset: 2}" :md="2">
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :md="2">
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :md="2">
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :md="2">
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{span: 2, offset: 6}" :md="2">
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="uploadImg()">上传</el-button>
</el-col>
</el-row>
@@ -56,11 +51,11 @@ import { VueCropper } from "vue-cropper";
import { upload } from "@/apis/fileApi";
import { updateUserIcon } from "@/apis/userApi";
import useUserStore from '@/stores/user'
import { ref ,reactive } from "vue";
import { ref, reactive } from "vue";
const userStore = useUserStore()
const cropper=ref(null);
const cropper = ref(null);
const open = ref(false);
const visible = ref(false);
@@ -89,7 +84,7 @@ function requestUpload() {
};
/** 向左旋转 */
function rotateLeft() {
cropper.value.rotateLeft();
cropper.value.rotateLeft();
};
/** 向右旋转 */
function rotateRight() {
@@ -110,30 +105,20 @@ function beforeUpload(file) {
reader.onload = () => {
options.img = reader.result;
};
}
};
/** 上传图片 */
function uploadImg() {
cropper.value.getCropBlob(data => {
async function uploadImg() {
await cropper.value.getCropBlob(async data => {
let formData = new FormData();
formData.append("file", data);
upload(formData).then(response => {
open.value = false;
options.img = import.meta.env.VITE_APP_BASE_API +"/file/"+response[0].id;
userStore.icon = options.img;
updateUserIcon(response[0].id).then(response2=>{
alert("修改成功");
visible.value = false;
})
});
const response = await upload(formData)
open.value = false;
options.img = import.meta.env.VITE_APP_BASEAPI + "/file/" + response[0].id;
userStore.icon = options.img;
await updateUserIcon(response[0].id);
alert("上传成功")
});
};
/** 实时预览 */
@@ -142,11 +127,12 @@ function realTime(data) {
};
/** 关闭窗口 */
function closeDialog() {
options.img = userStore.iocn;
options.img = userStore.icon;
options.visible = false;
};
</script>
<style lang='scss' scoped>
.user-info-head {
position: relative;
@@ -171,4 +157,24 @@ function closeDialog() {
line-height: 110px;
border-radius: 50%;
}
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
</style>

View File

@@ -1,6 +1,9 @@
<template>
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="用户昵称" prop="nickName">
<el-form-item label="账号" prop="userName">
<el-input v-model="user.userName" disabled />
</el-form-item>
<el-form-item label="用户昵称" prop="nick">
<el-input v-model="user.nick" maxlength="30" />
</el-form-item>
<el-form-item label="手机号码" prop="phone">
@@ -24,8 +27,9 @@
<script setup>
import { updateUserProfile } from "@/apis/userApi";
import useUserStore from "@/stores/user"
import { ref } from "vue";
const userStore=useUserStore();
const props = defineProps({
user: {
type: Object
@@ -45,6 +49,10 @@ function submit() {
userRef.value.validate(valid => {
if (valid) {
updateUserProfile(props.user).then(response => {
console.log(props.user.nick,"props.user.nick")
console.log(userStore,"userStore.nick")
userStore.name=props.user.nick
console.log(userStore.name,"userStore.name");
alert("修改成功");
});
}

View File

@@ -11,6 +11,7 @@
<Compile Include="..\GlobalUsings.cs" Link="Properties\GlobalUsings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\rbac\Yi.RBAC.Application.Contracts\Yi.RBAC.Application.Contracts.csproj" />
<ProjectReference Include="..\Yi.BBS.Domain.Shared\Yi.BBS.Domain.Shared.csproj" />
</ItemGroup>

View File

@@ -64,6 +64,13 @@
Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息
</summary>
</member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetAsync(System.Int64)">
<summary>
单查
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetListAsync(Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)">
<summary>
查询

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.BBS.Domain.Shared.Forum.EnumClasses
{
public enum QueryDiscussTypeEnum
{
New,
Suggest,
Host
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.BBS.Domain.Shared.Forum.Etos
{
public class SeeDiscussEventArgs
{
public long DiscussId { get; set; }
public int OldSeeNum { get; set; }
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cike.EventBus.EventHandlerAbstracts;
using Yi.BBS.Domain.Forum.Entities;
using Yi.BBS.Domain.Shared.Forum.Etos;
using Yi.Framework.Ddd.Repositories;
using Yi.RBAC.Domain.Shared.Identity.Etos;
namespace Yi.BBS.Domain.Forum.Event
{
public class SeeDiscussEventHandler : IDistributedEventHandler<SeeDiscussEventArgs>
{
private IRepository<DiscussEntity> _repository;
public SeeDiscussEventHandler(IRepository<DiscussEntity> repository)
{
_repository = repository;
}
public async Task HandlerAsync(SeeDiscussEventArgs eventData)
{
var entity= await _repository.GetByIdAsync(eventData.DiscussId);
if (entity is not null) {
entity.SeeNum += 1;
await _repository.UpdateAsync(entity);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.BBS.Domain.Shared.Forum.ConstClasses;
using Yi.BBS.Domain.Shared.Forum.EnumClasses;
using Yi.Framework.Ddd.Dtos;
namespace Yi.BBS.Application.Contracts.Forum.Dtos.Discuss
@@ -12,5 +14,11 @@ namespace Yi.BBS.Application.Contracts.Forum.Dtos.Discuss
public string? Title { get; set; }
public long? PlateId { get; set; }
//Ĭ<>ϲ<EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD>ö<EFBFBD>
public bool IsTop { get; set; } = false;
//<2F><>ѯ<EFBFBD><D1AF>ʽ
public QueryDiscussTypeEnum Type { get; set; } = QueryDiscussTypeEnum.New;
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Ddd.Dtos;
using Yi.RBAC.Application.Contracts.Identity.Dtos;
namespace Yi.BBS.Application.Contracts.Forum.Dtos.Discuss
{
@@ -15,12 +16,24 @@ namespace Yi.BBS.Application.Contracts.Forum.Dtos.Discuss
public string Title { get; set; }
public string Types { get; set; }
public string? Introduction { get; set; }
public DateTime? CreateTime { get; set; }
public int AgreeNum { get; set; }
public int SeeNum { get; set; }
public string Content { get; set; }
public string? Color { get; set; }
public long PlateId { get; set; }
//<2F>Ƿ<EFBFBD><C7B7>ö<EFBFBD><C3B6><EFBFBD>Ĭ<EFBFBD><C4AC>false
public bool IsTop { get; set; }
//<2F>Ƿ<EFBFBD>˽<EFBFBD>У<EFBFBD>Ĭ<EFBFBD><C4AC>false
public bool IsPrivate { get; set; }
//˽<><CBBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ж<EFBFBD>codeȨ<65><C8A8>
public string? PrivateCode { get; set; }
public DateTime CreationTime { get; set; }
public UserGetListOutputDto User { get; set; }
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Ddd.Dtos;
using Yi.RBAC.Application.Contracts.Identity.Dtos;
namespace Yi.BBS.Application.Contracts.Forum.Dtos
{
@@ -15,12 +16,23 @@ namespace Yi.BBS.Application.Contracts.Forum.Dtos
public string Title { get; set; }
public string Types { get; set; }
public string? Introduction { get; set; }
public DateTime? CreateTime { get; set; }
public int AgreeNum { get; set; }
public int SeeNum { get; set; }
public string Content { get; set; }
public string? Color { get; set; }
public long PlateId { get; set; }
//<2F>Ƿ<EFBFBD><C7B7>ö<EFBFBD><C3B6><EFBFBD>Ĭ<EFBFBD><C4AC>false
public bool IsTop { get; set; }
//<2F>Ƿ<EFBFBD>˽<EFBFBD>У<EFBFBD>Ĭ<EFBFBD><C4AC>false
public bool IsPrivate { get; set; }
//˽<><CBBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ж<EFBFBD>codeȨ<65><C8A8>
public string? PrivateCode { get; set; }
public DateTime CreationTime { get; set; }
public UserGetListOutputDto User { get; set; }
}
}

View File

@@ -11,6 +11,7 @@
<Compile Include="..\GlobalUsings.cs" Link="Properties\GlobalUsings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\rbac\Yi.RBAC.Application.Contracts\Yi.RBAC.Application.Contracts.csproj" />
<ProjectReference Include="..\Yi.BBS.Domain.Shared\Yi.BBS.Domain.Shared.csproj" />
</ItemGroup>

View File

@@ -64,6 +64,13 @@
Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息
</summary>
</member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetAsync(System.Int64)">
<summary>
单查
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetListAsync(Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)">
<summary>
查询

View File

@@ -12,6 +12,11 @@ using SqlSugar;
using Microsoft.AspNetCore.Routing;
using Yi.BBS.Domain.Shared.Forum.ConstClasses;
using Yi.Framework.Ddd.Repositories;
using Yi.RBAC.Domain.Identity.Entities;
using Yi.RBAC.Application.Contracts.Identity.Dtos;
using Cike.EventBus.DistributedEvent;
using Yi.BBS.Domain.Shared.Forum.Etos;
using Yi.BBS.Domain.Shared.Forum.EnumClasses;
namespace Yi.BBS.Application.Forum
{
@@ -28,6 +33,28 @@ namespace Yi.BBS.Application.Forum
[Autowired]
private IRepository<PlateEntity> _plateEntityRepository { get; set; }
[Autowired]
private IDistributedEventBus _distributedEventBus { get; set; }
/// <summary>
/// 单查
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async override Task<DiscussGetOutputDto> GetAsync(long id)
{
//查询主题发布 浏览主题 事件,浏览数+1
var item = await _DbQueryable.LeftJoin<UserEntity>((discuss, user) => discuss.CreatorId == user.Id)
.Select((discuss, user) => new DiscussGetOutputDto
{
User = new UserGetListOutputDto() { UserName = user.UserName, Nick = user.Nick,Icon=user.Icon }
}, true).SingleAsync(discuss => discuss.Id==id);
_distributedEventBus.PublishAsync(new SeeDiscussEventArgs { DiscussId= item.Id, OldSeeNum= item .SeeNum});
return item;
}
/// <summary>
/// 查询
/// </summary>
@@ -36,13 +63,19 @@ namespace Yi.BBS.Application.Forum
public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync( [FromQuery] DiscussGetListInputVo input)
{
//需要关联创建者用户
RefAsync<int> total = 0;
var entities = await _DbQueryable
var items = await _DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.OrderByDescending(x => x.CreateTime)
.Where(x=>x.IsTop==input.IsTop)
.OrderByIF(input.Type==QueryDiscussTypeEnum.New, x =>x.CreationTime,OrderByType.Desc )
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, x => x.SeeNum, OrderByType.Desc)
.LeftJoin<UserEntity>((discuss, user) => discuss.CreatorId==user.Id)
.Select((discuss,user) =>new DiscussGetListOutputDto {
User=new UserGetListOutputDto() { UserName=user.UserName,Nick=user.Nick, Icon = user.Icon }
},true)
.ToPageListAsync(input.PageNum, input.PageSize, total);
var items = await MapToGetListOutputDtosAsync(entities);
return new PagedResultDto<DiscussGetListOutputDto>(total, items);
}

View File

@@ -5,13 +5,14 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Yi.Framework.Data.Auditing;
using Yi.Framework.Data.Entities;
using Yi.Framework.Ddd.Entities;
namespace Yi.BBS.Domain.Forum.Entities
{
[SugarTable("Discuss")]
public class DiscussEntity : IEntity<long>, ISoftDelete
public class DiscussEntity : IEntity<long>, ISoftDelete,IAuditedObject
{
public DiscussEntity()
{
@@ -26,7 +27,6 @@ namespace Yi.BBS.Domain.Forum.Entities
public string Title { get; set; }
public string Types { get; set; }
public string? Introduction { get; set; }
public DateTime? CreateTime { get; set; }
public int AgreeNum { get; set; }
public int SeeNum { get; set; }
@@ -36,7 +36,23 @@ namespace Yi.BBS.Domain.Forum.Entities
public bool IsDeleted { get; set; }
//是否置顶默认false
public bool IsTop { get; set; }
//是否私有默认false
public bool IsPrivate { get; set; }
//私有需要判断code权限
public string? PrivateCode { get; set; }
public long PlateId { get; set; }
public DateTime CreationTime { get; set; }
public long? CreatorId { get; set; }
public long? LastModifierId { get; set; }
public DateTime? LastModificationTime { get; set; }
}
}

View File

@@ -34,7 +34,7 @@ namespace Yi.BBS.Domain.Forum
entity.Types = types;
entity.Introduction = introduction;
entity.Content = content;
entity.CreateTime = DateTime.Now;
entity.CreationTime = DateTime.Now;
entity.AgreeNum = 0;
entity.SeeNum = 0;
return await _discussRepository.InsertReturnEntityAsync(entity);

View File

@@ -121,7 +121,7 @@ namespace Yi.RBAC.Domain.Identity
if (!user.JudgePassword(oldPassword))
{
throw new UserFriendlyException("无效更新!密码不能与老密码相同");
throw new UserFriendlyException("无效更新!密码错误!");
}
user.Password = newPassword;
user.BuildPassword();