feat: 添加文章页面

This commit is contained in:
橙子
2023-03-11 17:00:36 +08:00
parent 9593b68d33
commit 3de32945f2
16 changed files with 206 additions and 47 deletions

View File

@@ -11,10 +11,13 @@
"@element-plus/icons-vue": "^2.1.0", "@element-plus/icons-vue": "^2.1.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"element-plus": "^2.2.32", "element-plus": "^2.2.32",
"highlight": "^0.2.4",
"marked": "^4.2.12",
"mavon-editor": "^3.0.0", "mavon-editor": "^3.0.0",
"pinia": "^2.0.32", "pinia": "^2.0.32",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-router": "^4.1.6" "vue-router": "^4.1.6",
"yarm": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
@@ -2289,6 +2292,12 @@
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
"dev": true "dev": true
}, },
"node_modules/highlight": {
"version": "0.2.4",
"resolved": "https://registry.npmmirror.com/highlight/-/highlight-0.2.4.tgz",
"integrity": "sha512-TEcWU6BolpDYIaVD91KmaYe/kRZwOmQlLWZGO8DK+Cs555+7mawk2KUnF/dBwcLnrvlCDk/xC+BXfz7Zva+Jfg==",
"deprecated": "Not maintained anymore"
},
"node_modules/highlight.js": { "node_modules/highlight.js": {
"version": "9.18.5", "version": "9.18.5",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-9.18.5.tgz", "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-9.18.5.tgz",
@@ -2581,6 +2590,17 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/marked": {
"version": "4.2.12",
"resolved": "https://registry.npmmirror.com/marked/-/marked-4.2.12.tgz",
"integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mavon-editor": { "node_modules/mavon-editor": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/mavon-editor/-/mavon-editor-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/mavon-editor/-/mavon-editor-3.0.0.tgz",
@@ -3775,6 +3795,11 @@
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true "dev": true
},
"node_modules/yarm": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/yarm/-/yarm-0.4.0.tgz",
"integrity": "sha512-yCoX5QCA5Upb+VP7/UBuNGBz7MO5oWyc6degv5AifeGnakpaHeNwE/SNOPMefFKpQBionZyUeRdBo63Dl+awDQ=="
} }
}, },
"dependencies": { "dependencies": {
@@ -5517,6 +5542,11 @@
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
"dev": true "dev": true
}, },
"highlight": {
"version": "0.2.4",
"resolved": "https://registry.npmmirror.com/highlight/-/highlight-0.2.4.tgz",
"integrity": "sha512-TEcWU6BolpDYIaVD91KmaYe/kRZwOmQlLWZGO8DK+Cs555+7mawk2KUnF/dBwcLnrvlCDk/xC+BXfz7Zva+Jfg=="
},
"highlight.js": { "highlight.js": {
"version": "9.18.5", "version": "9.18.5",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-9.18.5.tgz", "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-9.18.5.tgz",
@@ -5751,6 +5781,11 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"marked": {
"version": "4.2.12",
"resolved": "https://registry.npmmirror.com/marked/-/marked-4.2.12.tgz",
"integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw=="
},
"mavon-editor": { "mavon-editor": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/mavon-editor/-/mavon-editor-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/mavon-editor/-/mavon-editor-3.0.0.tgz",
@@ -6673,6 +6708,11 @@
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true "dev": true
},
"yarm": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/yarm/-/yarm-0.4.0.tgz",
"integrity": "sha512-yCoX5QCA5Upb+VP7/UBuNGBz7MO5oWyc6degv5AifeGnakpaHeNwE/SNOPMefFKpQBionZyUeRdBo63Dl+awDQ=="
} }
} }
} }

View File

@@ -11,10 +11,13 @@
"@element-plus/icons-vue": "^2.1.0", "@element-plus/icons-vue": "^2.1.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"element-plus": "^2.2.32", "element-plus": "^2.2.32",
"highlight": "^0.2.4",
"marked": "^4.2.12",
"mavon-editor": "^3.0.0", "mavon-editor": "^3.0.0",
"pinia": "^2.0.32", "pinia": "^2.0.32",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-router": "^4.1.6" "vue-router": "^4.1.6",
"yarm": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",

View File

@@ -2,9 +2,16 @@
</script> </script>
<template> <template>
<el-config-provider :locale="locale">
<RouterView /> <RouterView />
</el-config-provider>
</template> </template>
<script setup>
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const locale= zhCn;
</script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -6,10 +6,9 @@ export function getList(data){
params:data params:data
}) })
}; };
export function get(id){
export function getListByPlateId(plateId){
return myaxios({ return myaxios({
url: `/discuss/plate-id/${plateId}`, url: `/discuss/${id}`,
method: 'get' method: 'get'
}) })
}; };

View File

@@ -0,0 +1,43 @@
<template>
<div>
<div class="md" v-html="code"></div>
{{ code }}
<button @click="code='1234'">你好</button>
</div>
</template>
<script setup>
import { marked } from 'marked';
import hljs from "highlight.js";
import javascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/monokai-sublime.css';
import { onMounted,ref } from 'vue';
const code =ref( "```javascript\nfunction(){\n\tconsole.log(123)\n}\n```\n"
+"# 你好世界\n"+
"## 是我的"
)
// const props = defineProps(['code'])
onMounted(() => {
// code.value=props.code;
marked.setOptions({
renderer: new marked.Renderer(),
highlight: function (code) {
return hljs.highlightAuto(code.value).value;
},
pedantic: false,
gfm: true,
tables: true,
breaks: false,
sanitize: false,
smartLists: true,
smartypants: false,
xhtml: false
}
);
code.value = marked(code.value)
console.log( code.value," code.value");
})
</script>

View File

@@ -20,6 +20,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
const props = defineProps(['size', 'src','showWatching','time']) const props = defineProps(['size', 'src','showWatching','time'])

View File

@@ -7,7 +7,7 @@
<div class=" item item-title "> <el-link size="100" :underline="false" @click="enterDiscuss">{{props.title}}</el-link></div> <div class=" item item-title "> <el-link size="100" :underline="false" @click="enterDiscuss(props.id)">{{props.title}}</el-link></div>
<div class=" item item-description">{{props.introduction}}</div> <div class=" item item-description">{{props.introduction}}</div>
<div class=" item item-tag"><el-tag v-for="i in 4" :key="i">教程</el-tag></div> <div class=" item item-tag"><el-tag v-for="i in 4" :key="i">教程</el-tag></div>
<div class=" item item-bottom"> <div class=" item item-bottom">
@@ -35,12 +35,12 @@ import { h, ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import AvatarInfo from './AvatarInfo.vue'; import AvatarInfo from './AvatarInfo.vue';
const props = defineProps(['title','introduction','createTime']) const props = defineProps(['title','introduction','createTime','id'])
const router = useRouter() const router = useRouter()
const spacer = h(ElDivider, { direction: 'vertical' }) const spacer = h(ElDivider, { direction: 'vertical' })
const enterDiscuss = () => { const enterDiscuss = (id) => {
router.push("/article") router.push(`/article/${id}`)
} }
</script> </script>
<style scoped> <style scoped>

View File

@@ -6,11 +6,13 @@ import router from './router'
import './assets/main.css' import './assets/main.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App) const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component) app.component(key, component)
} }
app.use(createPinia()) app.use(createPinia())
app.use(router) app.use(router)
app.mount('#app') app.mount('#app')

View File

@@ -17,7 +17,7 @@ const router = createRouter({
}, },
{ {
name:'article', name:'article',
path: '/article', path: '/article/:discussId',
component: () => import('../views/Article.vue') component: () => import('../views/Article.vue')
}, },
{ {

View File

@@ -19,7 +19,13 @@
</template> </template>
</InfoCard> </InfoCard>
</el-col> </el-col>
<el-col :span="24">
<InfoCard :items=items header="推荐好友" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
</el-col> </el-col>
</el-row> </el-row>
@@ -33,9 +39,10 @@
<AvatarInfo :size="50" :showWatching="true" :time="'2023-03-08 21:09:02'"></AvatarInfo> <AvatarInfo :size="50" :showWatching="true" :time="'2023-03-08 21:09:02'"></AvatarInfo>
<el-divider /> <el-divider />
<h2>面试题挑战</h2>
文章详情 <h2>{{discuss.title}}</h2>
<!-- {{discuss.content}} -->
<ArticleContentInfo/>
<el-divider class="tab-divider" /> <el-divider class="tab-divider" />
@@ -53,7 +60,7 @@
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24" class="comment">
文章评论 文章评论
</el-col> </el-col>
</el-row> </el-row>
@@ -83,8 +90,7 @@
<div> <div>
<ul class="art-info-ul"> <ul class="art-info-ul">
<li v-for="i in 6"> <li v-for="i in 6">
<el-button style="width: 100%; <el-button style="width: 100%;justify-content: left" type="primary" text>{{ i }}第一小结</el-button>
justify-content: left" :key="你好" type="primary" text>{{ i }}第一小结</el-button>
</li> </li>
</ul> </ul>
</div> </div>
@@ -111,15 +117,21 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { h, ref } from 'vue' import { h, ref ,onMounted } from 'vue'
import AvatarInfo from '@/components/AvatarInfo.vue' import AvatarInfo from '@/components/AvatarInfo.vue'
import InfoCard from '../components/InfoCard.vue'; import InfoCard from '@/components/InfoCard.vue';
import ArticleContentInfo from '@/components/ArticleContentInfo.vue'
import { useRoute } from 'vue-router'
import {get as discussGet} from '@/apis/discussApi.js';
//数据定义
const route=useRoute()
const spacer = h(ElDivider, { direction: 'vertical' }) const spacer = h(ElDivider, { direction: 'vertical' })
const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }] const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }]
const handleNodeClick = (data) => { const handleNodeClick = (data) => {
console.log(data) console.log(data)
} }
const data = [ const data = [
{ {
label: 'HTML', label: 'HTML',
@@ -189,9 +201,25 @@ const data = [
label: 'Tcp/ip', label: 'Tcp/ip',
} }
] ]
//主题内容
const discuss=ref({});
//主题初始化
const loadDiscuss=async()=>{
discuss.value= await discussGet(route.params.discussId);
}
onMounted(async()=>{
await loadDiscuss();
})
</script> </script>
<style scoped > <style scoped >
.comment
{
height:40rem;
}
.art-info-left { .art-info-left {
margin-bottom: 1rem; margin-bottom: 1rem;
} }

View File

@@ -2,20 +2,20 @@
<div style="width: 1200px;" class="body-div"> <div style="width: 1200px;" class="body-div">
<div class="header"> <div class="header">
<el-form :inline="true" > <el-form :inline="true" >
<el-form-item label="标" > <el-form-item label="标" >
<el-input placeholder="请输入标" <el-input v-model="query.title" placeholder="请输入标"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="内容"> <el-form-item label="标签">
<el-input <el-input
placeholder="搜索当下分类下的内容" placeholder="搜索当下分类下的标签"
/> />
</el-form-item> </el-form-item>
<div class="form-right"> <div class="form-right">
<el-button>重置</el-button> <el-button>重置</el-button>
<el-button type="primary">查询</el-button> <el-button type="primary" @click="async()=>{ await loadDiscussList();}">查询</el-button>
<el-dropdown> <el-dropdown>
<span class="el-dropdown-link"> <span class="el-dropdown-link">
@@ -47,31 +47,64 @@
<el-tab-pane label="最热" name="third"> </el-tab-pane> <el-tab-pane label="最热" name="third"> </el-tab-pane>
</el-tabs> </el-tabs>
<div class="div-item" v-for="i in discussList" > <div class="div-item" v-for="i in discussList" >
<DisscussCard :title="i.title" :introduction="i.introduction" :createTime="i.createTime"/> <DisscussCard :title="i.title" :introduction="i.introduction" :createTime="i.createTime" :id="i.id"/>
</div> </div>
<div>
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
:page-sizes="[10, 20, 30, 50]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="async(val)=>{ await loadDiscussList();}"
@current-change="async(val)=>{ await loadDiscussList();}"
/>
</div>
<el-empty v-if="discussList.length==0" description="空空如也" />
</div> </div>
</template> </template>
<script setup> <script setup>
import DisscussCard from '@/components/DisscussCard.vue' import DisscussCard from '@/components/DisscussCard.vue'
import {getListByPlateId} from '@/apis/discussApi.js' import {getList} from '@/apis/discussApi.js'
import { onMounted, ref } from 'vue' import { onMounted, ref,reactive } from 'vue'
import { useRouter,useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const router = useRouter()
//数据定义
const route=useRoute() const route=useRoute()
const activeName = ref('first') const activeName = ref('first')
const discussList=ref([]); const discussList=ref([]);
const total=ref(100)
const query=reactive({
pageNum:1,
pageSize:10,
title:'',
plateId:''
})
const handleClick = (tab, event) => { const handleClick = (tab, event) => {
console.log(tab, event) console.log(tab, event)
} }
onMounted(async()=>{ onMounted(async()=>{
const response= await getListByPlateId(route.params.plateId); await loadDiscussList();
discussList.value=response.items;
}) })
//加载discuss
const loadDiscussList=async()=>{
query.plateId=route.params.plateId;
const response= await getList(query);
discussList.value=response.items;
total.value=Number( response.total);
}
</script> </script>
<style scoped> <style scoped>
.el-pagination
{margin: 2rem 0rem 2rem 0rem;justify-content: right;}
.body-div{ .body-div{
min-height: 1000px; min-height: 1000px;
} }

View File

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

View File

@@ -64,11 +64,10 @@
Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息 Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息
</summary> </summary>
</member> </member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetPlateIdAsync(System.Int64,Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)"> <member name="M:Yi.BBS.Application.Forum.DiscussService.GetListAsync(Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)">
<summary> <summary>
获取改板块下的主题 查询
</summary> </summary>
<param name="plateId"></param>
<param name="input"></param> <param name="input"></param>
<returns></returns> <returns></returns>
</member> </member>

View File

@@ -9,5 +9,8 @@ namespace Yi.BBS.Application.Contracts.Forum.Dtos.Discuss
{ {
public class DiscussGetListInputVo : PagedAndSortedResultRequestDto public class DiscussGetListInputVo : PagedAndSortedResultRequestDto
{ {
public string? Title { get; set; }
public long? PlateId { get; set; }
} }
} }

View File

@@ -64,11 +64,10 @@
Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息 Discuss应用服务实现,用于参数效验、领域服务业务组合、日志记录、事务处理、账户信息
</summary> </summary>
</member> </member>
<member name="M:Yi.BBS.Application.Forum.DiscussService.GetPlateIdAsync(System.Int64,Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)"> <member name="M:Yi.BBS.Application.Forum.DiscussService.GetListAsync(Yi.BBS.Application.Contracts.Forum.Dtos.Discuss.DiscussGetListInputVo)">
<summary> <summary>
获取改板块下的主题 查询
</summary> </summary>
<param name="plateId"></param>
<param name="input"></param> <param name="input"></param>
<returns></returns> <returns></returns>
</member> </member>

View File

@@ -29,17 +29,19 @@ namespace Yi.BBS.Application.Forum
private IRepository<PlateEntity> _plateEntityRepository { get; set; } private IRepository<PlateEntity> _plateEntityRepository { get; set; }
/// <summary> /// <summary>
/// 获取改板块下的主题 /// 查询
/// </summary> /// </summary>
/// <param name="plateId"></param>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
public async Task<PagedResultDto<DiscussGetListOutputDto>> GetPlateIdAsync([FromRoute] long plateId, [FromQuery] DiscussGetListInputVo input) public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync( [FromQuery] DiscussGetListInputVo input)
{ {
var entities = await _repository.GetPageListAsync(x => x.PlateId == plateId, input); RefAsync<int> total = 0;
var entities = await _DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.ToPageListAsync(input.PageNum, input.PageSize, total);
var items = await MapToGetListOutputDtosAsync(entities); var items = await MapToGetListOutputDtosAsync(entities);
var total = await _repository.CountAsync(x => x.IsDeleted == false);
return new PagedResultDto<DiscussGetListOutputDto>(total, items); return new PagedResultDto<DiscussGetListOutputDto>(total, items);
} }