chore: 构建目录
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<el-button text @click="agree">
|
||||
<el-icon v-if="data.isAgree" color="#409EFF">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<el-icon v-else color="#1E1E1E">
|
||||
<Pointer />
|
||||
</el-icon> 点赞:{{ data.agreeNum ?? 0 }}</el-button>
|
||||
</template>
|
||||
<script setup>
|
||||
import {onMounted,reactive,watch} from 'vue'
|
||||
import { operate } from '@/apis/agreeApi'
|
||||
|
||||
|
||||
//'isAgree','agreeNum','id'
|
||||
const props = defineProps([ 'data'])
|
||||
|
||||
watch(()=>props,(n)=>{
|
||||
data.id=n.data.id;
|
||||
data.isAgree=n.data.isAgree;
|
||||
data.agreeNum=n.data.agreeNum;
|
||||
},{deep:true})
|
||||
|
||||
|
||||
const data=reactive({
|
||||
id:'',
|
||||
isAgree:false,
|
||||
agreeNum:0
|
||||
})
|
||||
// onMounted(()=>{
|
||||
|
||||
// })
|
||||
//点赞操作
|
||||
const agree = async () => {
|
||||
const response = await operate(data.id)
|
||||
const res = response.data;
|
||||
//提示框,颜色区分
|
||||
if (res.isAgree) {
|
||||
data.isAgree = true;
|
||||
data.agreeNum += 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
else {
|
||||
data.isAgree = false;
|
||||
data.agreeNum-= 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="markdown-body" v-html="outputHtml"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { marked } from 'marked';
|
||||
|
||||
import hljs from "highlight.js";
|
||||
//可以设置加载样式切换主题
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
import '@/assets/github-markdown.css'
|
||||
import { ref,watch } from 'vue';
|
||||
|
||||
|
||||
|
||||
const outputHtml=ref("")
|
||||
const props = defineProps(['code'])
|
||||
watch(props,(n,o)=>{
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
highlight: function(code) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,//允许 Git Hub标准的markdown
|
||||
tables: true,//支持表格
|
||||
|
||||
breaks: true,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
xhtml: false,
|
||||
smartLists: true,
|
||||
}
|
||||
);
|
||||
//需要注意代码块样式
|
||||
outputHtml.value = marked(n.code).replace(/<pre>/g, "<pre class='hljs'>")
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
<template >
|
||||
<div class="avatar">
|
||||
<div class="avatar-left">
|
||||
<el-avatar :size="props.size" :src="iconUrl" />
|
||||
|
||||
<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.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">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>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import useUserStore from '@/stores/user'
|
||||
import { reactive, watch, onMounted, computed, ref } from 'vue';
|
||||
//userInfo
|
||||
//{icon,name,role,id},根据判断userInfo是否等于未定义,来觉得是当前登录用户信息,还是其他人信息
|
||||
const props = defineProps(['size', 'showWatching', 'time', 'userInfo', 'isSelf'])
|
||||
const userStore = useUserStore();
|
||||
const userInfo = reactive({
|
||||
icon: "",
|
||||
nick: "",
|
||||
role: [],
|
||||
id: ""
|
||||
});
|
||||
const iconUrl=ref('/src/assets/logo.ico');
|
||||
const iconUrlHandler = () => {
|
||||
if (userInfo.icon == null || userInfo.icon == undefined || userInfo.icon == '') {
|
||||
|
||||
return '/src/assets/logo.ico';
|
||||
}
|
||||
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.nick = props.userInfo.nick;
|
||||
userInfo.role = props.userInfo.role;
|
||||
userInfo.id = props.userInfo.id;
|
||||
iconUrl.value=iconUrlHandler(userInfo.icon)
|
||||
}
|
||||
|
||||
//使用当前登录用户
|
||||
else {
|
||||
|
||||
userInfo.icon = userStore.icon;
|
||||
userInfo.nick = userStore.name;
|
||||
userInfo.role = userStore.role;
|
||||
userInfo.id = userStore.id;
|
||||
iconUrl.value=userInfo.icon;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.mt_1 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.nick {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 0.6rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.info .el-tag {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: white;
|
||||
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.avatar-left {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-avatar {
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
|
||||
.remarks {
|
||||
padding-top: 0.5rem;
|
||||
color: #8C8C8C;
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div class="botton-div">
|
||||
<a><el-icon><UserFilled /></el-icon>站长:{{configStore.author}}</a>
|
||||
<a><el-icon><Search /></el-icon>{{configStore.bottom}}</a>
|
||||
<a><el-icon><View /></el-icon>关于本站</a>
|
||||
<a><el-icon><Message /></el-icon>建议反馈</a>
|
||||
<p></p>
|
||||
<a ><el-icon><Position /></el-icon>{{configStore.icp}}</a>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import useConfigStore from "@/stores/config";
|
||||
const configStore= useConfigStore();
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-icon
|
||||
{margin: 0 0.2rem;}
|
||||
a{
|
||||
margin-right: 2rem;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
a:hover {
|
||||
color: #40a9ff;
|
||||
cursor:pointer;
|
||||
}
|
||||
.botton-div
|
||||
{
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,.45);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
width: auto;
|
||||
justify-content: center;
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,204 +0,0 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="评论" name="comment"></el-tab-pane>
|
||||
<el-tab-pane label="相关内容" name="interrelated"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="total">
|
||||
<div style="align-self: center;"> 共{{total}}个评论</div>
|
||||
<div> <el-radio-group v-model="selectRadio">
|
||||
<el-radio-button label="new" name="new">最新</el-radio-button>
|
||||
<el-radio-button label="host" name="host">最热</el-radio-button>
|
||||
</el-radio-group></div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-divider />
|
||||
<div v-hasPer="['bbs:comment:add']">
|
||||
<el-input v-model="topContent" placeholder="发表一个友善的评论吧~" :rows="5" type="textarea"></el-input>
|
||||
<el-button @click="addTopComment" type="primary" class="btn-top-comment" >发表评论</el-button>
|
||||
<el-button class="btn-top-comment">其他</el-button>
|
||||
|
||||
<el-divider />
|
||||
</div>
|
||||
|
||||
<!-- 开始评论主体 -->
|
||||
|
||||
<div v-for="item in commentList" :key="item.id" class="comment1">
|
||||
<AvatarInfo :userInfo="item.createUser" />
|
||||
<div class="content">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
<span class="time"> {{ item.creationTime }} </span>
|
||||
<span class="pointer"><el-icon>
|
||||
<Pointer />
|
||||
</el-icon> 0</span>
|
||||
<el-button type="primary" @click="replay(item.createUser.nick, item.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
<el-button type="danger" @click="delComment(item.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
|
||||
<div v-show="replayId == item.id" class="input-reply">
|
||||
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
|
||||
<div class="btn-reply">
|
||||
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 开始子评论主体 -->
|
||||
<div v-for="children in item.children" :key="children.id" class="comment2">
|
||||
|
||||
<div style="display: flex ;">
|
||||
<AvatarInfo :userInfo="children.createUser" />
|
||||
<span style="align-self: center;color:#606266;"> 回复@{{ children.commentedUser.nick }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
{{ children.content }}
|
||||
</div>
|
||||
<span class="time">{{ children.creationTime }} </span>
|
||||
<span class="pointer"> <el-icon>
|
||||
<Pointer />
|
||||
</el-icon>0</span>
|
||||
<el-button type="primary" @click="replay(children.createUser.nick, children.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
<el-button type="danger" @click="delComment(children.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
|
||||
<div v-show="replayId == children.id" class="input-reply">
|
||||
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
|
||||
<div class="btn-reply">
|
||||
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
</div>
|
||||
<el-empty v-show="commentList.length<=0" description="评论空空如也,快来抢占沙发~" />
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getListByDiscussId, add ,del} from "@/apis/commentApi.js";
|
||||
import AvatarInfo from './AvatarInfo.vue';
|
||||
//数据定义
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const commentList = ref([]);
|
||||
const query = reactive({});
|
||||
const topContent=ref('');
|
||||
//当前回复id
|
||||
const replayId = ref('');
|
||||
//回复文本框
|
||||
const placeholder = ref('')
|
||||
|
||||
//选择类型:评论
|
||||
const activeName = ref('comment');
|
||||
//选择 最新
|
||||
const selectRadio = ref('new');
|
||||
//评论总数
|
||||
const total=ref(0);
|
||||
const form = reactive({
|
||||
content: "",
|
||||
discussId: route.params.discussId,
|
||||
query,
|
||||
parentId: 0,
|
||||
rootId: 0,
|
||||
});
|
||||
onMounted(async () => {
|
||||
await loadComment();
|
||||
});
|
||||
const loadComment = async () => {
|
||||
topContent.value='';
|
||||
form.content = '';
|
||||
const response = await getListByDiscussId(route.params.discussId, query);
|
||||
commentList.value = response.data.items;
|
||||
total.value=response.data.total
|
||||
};
|
||||
const addTopComment = async () => {
|
||||
form.parentId = 0;
|
||||
form.rootId = 0;
|
||||
form.content=topContent.value;
|
||||
await addComment();
|
||||
}
|
||||
const addComment = async () => {
|
||||
if(form.content.length<=0)
|
||||
{
|
||||
ElMessage.error('输入评论不能为空!')
|
||||
return
|
||||
}
|
||||
await add(form);
|
||||
await loadComment();
|
||||
ElMessage({
|
||||
message: '评论发表成功!',
|
||||
type: 'success',
|
||||
})
|
||||
};
|
||||
const delComment=async(ids)=>{
|
||||
ElMessageBox.confirm(`确定是否删除编号[${ids}]的评论吗?`, "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
|
||||
await del(ids);
|
||||
await loadComment();
|
||||
ElMessage({
|
||||
message: '评论已删除!',
|
||||
type: 'success',
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
const replay = async (parentUserName, parentId, rootId) => {
|
||||
replayId.value = parentId;
|
||||
form.parentId = parentId;
|
||||
form.rootId = rootId;
|
||||
placeholder.value = `回复@${parentUserName}`;
|
||||
}
|
||||
|
||||
//切换 评论、相关内容
|
||||
const handleClick = () => { }
|
||||
</script>
|
||||
<style scoped>
|
||||
.input-reply {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-reply {
|
||||
margin: 1rem 0;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.comment1 .pointer {
|
||||
margin: 0 0 0 1rem;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #8C8C8C;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.total {
|
||||
display: flex;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
.comment2 {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.btn-top-comment {
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,197 +0,0 @@
|
||||
<template>
|
||||
<el-badge :value="props.badge ?? ''" class="box-card">
|
||||
<el-card shadow="never" :style="{ 'border-color': discuss.color }">
|
||||
<el-row>
|
||||
|
||||
<!-- 头部 -->
|
||||
<el-col :span=24 class="card-header">
|
||||
<AvatarInfo :userInfo="discuss.user" :time="discuss.creationTime" />
|
||||
</el-col>
|
||||
|
||||
|
||||
<!-- 身体 -->
|
||||
|
||||
<el-col :span=18 >
|
||||
<el-row>
|
||||
|
||||
<el-col v-if="discuss.isBan" :span=24 class=" item item-title "> <el-link size="100" :underline="false"
|
||||
style="color:#F56C6C;" >{{ discuss.title }}</el-link></el-col>
|
||||
|
||||
<el-col v-else :span=24 class=" item item-title "> <el-link size="100" :underline="false"
|
||||
@click="enterDiscuss(discuss.id)">{{ discuss.title }}</el-link></el-col>
|
||||
|
||||
|
||||
|
||||
<el-col :span=24 class=" item item-description">{{ discuss.introduction }}</el-col>
|
||||
<el-col :span=24 class=" item item-tag"><el-tag v-for="i in 4" :key="i">教程</el-tag></el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span=6 style=" display: flex;justify-content: center;">
|
||||
<el-image :preview-src-list="[getUrl(discuss.cover)]" v-if="discuss.cover" :src="getUrl(discuss.cover)" style="width: 100px;height: 100px;" />
|
||||
</el-col>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 底部 -->
|
||||
<el-col :span=24 class=" item item-bottom " style=" margin-bottom: 0;">
|
||||
<el-space :size="10" :spacer="spacer">
|
||||
<div class="item-description">
|
||||
{{ discuss.creationTime }}
|
||||
</div>
|
||||
<AgreeInfo :data="discuss"/>
|
||||
<!--
|
||||
<el-button text @click="agree">
|
||||
<el-icon v-if="discuss.isAgree" color="#409EFF">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<el-icon v-else color="#1E1E1E">
|
||||
<Pointer />
|
||||
</el-icon> 点赞:{{ discuss.agreeNum ?? 0 }}</el-button>
|
||||
<el-button icon="Star" text>
|
||||
收藏</el-button> -->
|
||||
|
||||
<el-button icon="View" text>
|
||||
浏览数:{{ discuss.seeNum ?? 0 }}</el-button>
|
||||
|
||||
|
||||
</el-space>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</el-card>
|
||||
</el-badge>
|
||||
</template>
|
||||
<script setup>
|
||||
import { h, ref, toRef, onMounted ,reactive} from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AvatarInfo from './AvatarInfo.vue';
|
||||
import AgreeInfo from './AgreeInfo.vue'
|
||||
import { operate } from '@/apis/agreeApi'
|
||||
|
||||
const props = defineProps(['discuss','badge'])
|
||||
const discuss=reactive({
|
||||
id:'',
|
||||
title:"",
|
||||
introduction:"",
|
||||
creationTime:"",
|
||||
user:{},
|
||||
color:"",
|
||||
seeNum:0,
|
||||
agreeNum:0,
|
||||
isAgree:false,
|
||||
cover:"",
|
||||
isBan:false,
|
||||
isAgree:false,
|
||||
agreeNum:0
|
||||
})
|
||||
const router = useRouter()
|
||||
const spacer = h(ElDivider, { direction: 'vertical' })
|
||||
const enterDiscuss = (id) => {
|
||||
router.push(`/article/${id}`)
|
||||
}
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
//点赞操作
|
||||
const agree = async () => {
|
||||
const response = await operate(discuss.id)
|
||||
const res = response.data;
|
||||
//提示框,颜色区分
|
||||
if (res.isAgree) {
|
||||
discuss.isAgree = true;
|
||||
discuss.agreeNum += 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
else {
|
||||
discuss.isAgree = false;
|
||||
discuss.agreeNum-= 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
// id:'',
|
||||
// title:"",
|
||||
// introduction:"",
|
||||
// creationTime:"",
|
||||
// user:{},
|
||||
// color:"",
|
||||
// seeNum:0,
|
||||
// agreeNum:0,
|
||||
// isAgree:""
|
||||
discuss.id=props.discuss.id;
|
||||
discuss.title=props.discuss.title;
|
||||
discuss.introduction=props.discuss.introduction;
|
||||
discuss.creationTime=props.discuss.creationTime;
|
||||
discuss.user=props.discuss.user;
|
||||
discuss.color=props.discuss.color;
|
||||
discuss.seeNum=props.discuss.seeNum;
|
||||
discuss.isAgree=props.discuss.isAgree;
|
||||
discuss.agreeNum=props.discuss.agreeNum;
|
||||
discuss.isBan=props.discuss.isBan;
|
||||
discuss.cover=props.discuss.cover;
|
||||
discuss.value = props.isAgree;
|
||||
discuss.value = props.agreeNum;
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-card {
|
||||
border: 2px solid white
|
||||
}
|
||||
|
||||
.item-bottom .el-icon {
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
margin-bottom: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-size: 14px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 100%;
|
||||
min-height: 15rem;
|
||||
/* right: calc(1px + var(--el-badge-size)/ 2) !important; */
|
||||
/* top: 0 !important; */
|
||||
|
||||
}
|
||||
|
||||
.item-title {
|
||||
/* font-size: var(--el-font-size-large); */
|
||||
}
|
||||
|
||||
.item-description {
|
||||
font-size: var(--el-font-size-small);
|
||||
color: #8C8C8C;
|
||||
}
|
||||
|
||||
.item .el-tag {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
font-size: initial;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
@@ -1,55 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps(['items','header','text','hideDivider'])
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{props.header}}</span>
|
||||
<el-link :underline="false" type="primary">{{props.text}}</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot name="content" />
|
||||
|
||||
|
||||
<div v-for="(item,i) in props.items " >
|
||||
<div class="text item">
|
||||
<slot name="item" v-bind="item"/>
|
||||
</div>
|
||||
<el-divider v-if="i!=props.items.length-1&&hideDivider==undefined" />
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.el-divider
|
||||
{
|
||||
margin: 0.2rem 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,151 +0,0 @@
|
||||
<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="表情包">
|
||||
😊
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu >
|
||||
<el-dropdown-item>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td @click="text+='😊'">😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</el-dropdown-item>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
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() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
//关闭文件上传弹窗
|
||||
const fileHandleClose=()=>{
|
||||
fileDialogVisible.value=false;
|
||||
}
|
||||
//文件上传成功后
|
||||
const onSuccess=(response)=>{
|
||||
fileUrlList.value.push(response.data[0].id)
|
||||
|
||||
}
|
||||
//图片上传
|
||||
const imgAdd = async (pos, $file) => {
|
||||
// 第一步.将图片上传到服务器.
|
||||
var formdata = new FormData();
|
||||
formdata.append('file', $file);
|
||||
const response = await upload(formdata)
|
||||
const url = `${import.meta.env.VITE_APP_BASEAPI}/file/${response.data[0].id}/true`;
|
||||
console.log(url)
|
||||
md.value.$img2Url(pos, url);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.edit {
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col
|
||||
>
|
||||
<el-card :body-style="{ padding: '0px' }" shadow="never">
|
||||
<img
|
||||
src=""
|
||||
class="image"
|
||||
/>
|
||||
<div style="padding: 14px">
|
||||
<span>{{props.name}}</span>
|
||||
<div class="bottom">
|
||||
<time class="remarks">{{ props.introduction }}</time>
|
||||
<RouterLink :to="`/discuss/${props.id}`"> <el-button text class="button" type="primary">进入<el-icon><CaretRight /></el-icon></el-button> </RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const props = defineProps(['name','introduction','id'])
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.remarks {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 13px;
|
||||
line-height: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<el-scrollbar>
|
||||
<div class="scrollbar-flex-content">
|
||||
<p v-for="item in 50" :key="item" class="scrollbar-item">
|
||||
推荐
|
||||
</p>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
||||
.scrollbar-flex-content {
|
||||
display: flex;
|
||||
}
|
||||
.scrollbar-item {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 4rem;
|
||||
height: 2.6rem;
|
||||
margin: 0 0.2rem ;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
background-color:#FAFAFA;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<el-tree
|
||||
:data="props.data==''?[]:props.data"
|
||||
:props="defaultProps"
|
||||
@node-click="handleNodeClick"
|
||||
:expand-on-click-node="false"
|
||||
node-key="id"
|
||||
:default-expand-all='true'
|
||||
:highlight-current="true"
|
||||
:current-node-key="currentNodeKey"
|
||||
>
|
||||
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span>{{data.name}}</span>
|
||||
<span>
|
||||
|
||||
<a style="color: #409EFF; margin-left: 8px" @click="$emit('create',node, data)" v-hasPer="['bbs:article:add']"
|
||||
|
||||
> + </a>
|
||||
<a style="color: #409EFF; margin-left: 8px" @click="$emit('update',node, data)" v-hasPer="['bbs:article:edit']"
|
||||
|
||||
> 编辑 </a>
|
||||
<a style="color: #f56c6c; margin-left: 8px" @click="$emit('remove',node, data)" v-hasPer="['bbs:article:remove']"
|
||||
|
||||
> 删除 </a>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
|
||||
const props = defineProps(['data',"currentNodeKey"])
|
||||
const emits= defineEmits(["handleNodeClick"])
|
||||
|
||||
const currentNodeKey=props.currentNodeKey;
|
||||
//数据定义
|
||||
//子文章数据
|
||||
// const articleData =ref([]);
|
||||
//树形子文章选项
|
||||
const defaultProps = {
|
||||
children: "children",
|
||||
label: "name",
|
||||
};
|
||||
// //子文章初始化
|
||||
// const loadArticleData=async()=>
|
||||
// {
|
||||
// articleData.value= await articleall(route.params.discussId);
|
||||
// }
|
||||
//点击事件
|
||||
const handleNodeClick = (data) => {
|
||||
emits('handleNodeClick',data)
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,129 +0,0 @@
|
||||
<template>
|
||||
<el-select
|
||||
style="width: 600px;"
|
||||
v-model="value"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入用户账号(可多选)"
|
||||
remote-show-suffix
|
||||
:remote-method="remoteMethod"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref,computed } from 'vue'
|
||||
import {listUser} from '@/apis/userApi'
|
||||
const props = defineProps(['modelValue'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
|
||||
//这个为可选择的列表,{value,label},value为用户id,label为账号名称(不可重复)
|
||||
const options = ref([])
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted( async()=>{
|
||||
|
||||
const response= await listUser({ids:value.value.join()});
|
||||
const res=response.data.items;
|
||||
//下拉列表
|
||||
options.value = res
|
||||
.map((item) => {
|
||||
return { value: `${item.id}`, label: `用户:${item.userName}` }
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const loadUser=async(query)=>{
|
||||
const response= await listUser({userName:query});
|
||||
const res=response.data.items;
|
||||
//下拉列表
|
||||
options.value = res
|
||||
.map((item) => {
|
||||
return { value: `${item.id}`, label: `用户:${item.userName}` }
|
||||
})
|
||||
}
|
||||
|
||||
const remoteMethod =async (query) => {
|
||||
if (query) {
|
||||
loading.value = true
|
||||
await loadUser(query);
|
||||
loading.value = false
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const states = [
|
||||
'Alabama',
|
||||
'Alaska',
|
||||
'Arizona',
|
||||
'Arkansas',
|
||||
'California',
|
||||
'Colorado',
|
||||
'Connecticut',
|
||||
'Delaware',
|
||||
'Florida',
|
||||
'Georgia',
|
||||
'Hawaii',
|
||||
'Idaho',
|
||||
'Illinois',
|
||||
'Indiana',
|
||||
'Iowa',
|
||||
'Kansas',
|
||||
'Kentucky',
|
||||
'Louisiana',
|
||||
'Maine',
|
||||
'Maryland',
|
||||
'Massachusetts',
|
||||
'Michigan',
|
||||
'Minnesota',
|
||||
'Mississippi',
|
||||
'Missouri',
|
||||
'Montana',
|
||||
'Nebraska',
|
||||
'Nevada',
|
||||
'New Hampshire',
|
||||
'New Jersey',
|
||||
'New Mexico',
|
||||
'New York',
|
||||
'North Carolina',
|
||||
'North Dakota',
|
||||
'Ohio',
|
||||
'Oklahoma',
|
||||
'Oregon',
|
||||
'Pennsylvania',
|
||||
'Rhode Island',
|
||||
'South Carolina',
|
||||
'South Dakota',
|
||||
'Tennessee',
|
||||
'Texas',
|
||||
'Utah',
|
||||
'Vermont',
|
||||
'Virginia',
|
||||
'Washington',
|
||||
'West Virginia',
|
||||
'Wisconsin',
|
||||
'Wyoming',
|
||||
]
|
||||
</script>
|
||||
Reference in New Issue
Block a user