13.8 开发沸点页面
沸点页面是独立于文章的另一个页面。沸点的发布和列表都在同一个页面展示,且没有修改和删除,相比文章简单不少。沸点页面也是和首页一样的左中右布局。
13.8.1 开发左侧沸点圈子组件
沸点圈子组件与首页的文章分类组件大部分一致,只是数据不一样,我们只需要修改一些接受的 Props 名称即可。
(1)创建文件 views/short-msg/nav.vue 表示沸点圈子组件,拷贝文章分类组件的代码,然后修改 JS 代码如下:
const props = defineProps<{
groups: any[]
}>()
(2)修改模版代码如下:
<div
:class="['cato-item', { active: active == item.key }]"
v-for="item in props.groups"
@click="onClick(item)"
>
<el-icon :size="18"><Opportunity /></el-icon>
<span class="text">{{ item.label }}</span>
</div>
13.8.2 创建沸点 Store,定义相关状态和方法
沸点 Store 主要存储沸点列表数据和沸点圈子数据,并定义获取数据的方法。此外还要添加创建、点赞、删除等沸点操作方法,实现步骤如下。
(1)创建文件 store/short-msg/index.ts 表示沸点 Store,定义状态 shortmsgs 和 groups 分别存储沸点列表和沸点圈子数据。代码如下:
import { defineStore } from 'pinia'
import request from '@/request'
const stmsgStore = defineStore('short-msg', {
state: () => ({
shortmsgs: [] as ShortMsgType[],
groups: [] as GroupType[],
meta: {
page: 1,
per_page: 10,
total: 0,
},
}),
actions: {
// 沸点列表
async getShortmsgs(
params: Record<string, string> = {},
fun?: (data: any) => void
) {
try {
if (params.group == 'all') {
params.group = null
}
let res: any = await request.get('/stmsgs/lists', { params })
if (res && !fun) {
this.shortmsgs = res.data
this.meta = res.meta
}
if (fun) fun(res)
} catch (error) {
console.log(error)
}
},
// 沸点圈子
async getGroups() {
try {
let res: any = await request.get('/stmsgs/group')
this.groups = res
} catch (error) {
console.log(error)
}
},
},
})
export default stmsgStore
上放代码中的 getShortmsgs() 和 getGroups() 方法从接口获取数据后为状态赋值。
(2)添加创建沸点、删除沸点、点赞/取消点赞沸点的方法,如下:
// 点赞/取消点赞沸点
async togglePraise(data: any, fun: (bool: boolean) => void) {
try {
data.type = 1
data.target_type = 2
let res: any = await request.post('/praises/toggle', data)
fun(res.action == 'create' ? true : false)
} catch (error) {
console.log(error)
}
},
// 创建沸点
async createMsg(data: Partial<ShortMsgType>, fun: (data: any) => void) {
try {
let res: any = await request.post('/stmsgs/create', data)
fun(res)
} catch (error) {
fun(false)
console.log(error)
}
},
// 删除沸点
async removeMsg(id: string, fun: () => void) {
try {
await request.delete('/stmsgs/remove/' + id)
fun()
} catch (error) {
console.log(error)
}
}
(3)在沸点 Store 文件的同级目录下创建沸点类型文件 type.d.ts,沸点类型如下:
interface ShortMsgType {
_id: string
group: string
comments: number
images: string[]
content: string
created_at: string
created_by: string
is_praise: boolean
praises: number
updated_at: string
user: UserInfoType
}
interface GroupType {
key: string
label: string
}
(4)在 store/index.ts 文件中导出沸点 Store:
export { default as shortmsgStore } from "./short-msg";
13.8.3 开发沸点列表组件,展示和操作沸点
沸点列表组件是一个公共组件,在用户中心页也会使用到。因此该组件只接收一个列表数据并渲染,同时在列表项中添加了删除的功能。
(1)创建文件 views/short-msg/lists.vue 表示公共沸点列表组件,模版代码如下:
<div class="shortmsg-lists">
<div class="msgs-item" v-for="item in props.shortmsgs">
<div class="pad-wrap">
<div class="user-meta fx">
<el-avatar :size="48" :src="item.user.avatar">
<img src="@/assets/avatar.png" />
</el-avatar>
<div class="desc-area">
<h3 @click="toUser(item.user._id)">{{ item.user.username }}</h3>
<span class="desc fx">
{{ item.user.position || '程序员' }} <i /> {{
getTimer(item.created_at) }}</span
>
</div>
<div class="action">
<el-dropdown trigger="click" @command="toDelete(item._id)">
<span class="icon-wrap">
<el-icon><MoreFilled /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div class="content-box">
<p>{{ item.content }}</p>
</div>
</div>
</div>
</div>
上方代码中循环 shortmsgs 数据并展示沸点内容和作者信息,并添加了删除沸点的按钮。
(2)编写 JS 代码导入沸点 Store,定义 Props 和 自定义事件、以及删除沸点的 toDelete() 方法,代码如下:
<script setup lang="ts">
import { shortmsgStore } from "@/stores";
import { MoreFilled } from "@element-plus/icons-vue";
import { getTimer } from "@/utils";
import { ElMessage, ElMessageBox } from "element-plus";
const store = shortmsgStore();
const toUser = (id: string) => {
window.open("/user/" + id);
};
const props = defineProps<{
shortmsgs: ShortMsgType[];
}>();
const emit = defineEmits<{
(e: "onFilter", json: Record<string, string>): void;
}>();
const toDelete = (id: string) => {
ElMessageBox.confirm("确认删除沸点?", "操作提醒", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
store.removeMsg(id, () => {
ElMessage.success("已删除");
emit("onFilter", {});
});
});
};
</script>
(3)添加沸点列表的样式,主要代码如下:
.shortmsg-lists {
flex: 1;
background: #f5f5f5;
.msgs-item {
background: #fff;
border-radius: 4px;
margin-bottom: 8px;
.pad-wrap {
padding: 20px 20px 12px;
}
.user-meta {
color: var(--font-color3);
font-size: 14px;
line-height: 24px;
.desc-area {
flex: 1;
margin-left: 12px;
h3 {
line-height: 28px;
font-size: 16px;
cursor: pointer;
}
}
.action {
.icon-wrap {
cursor: pointer;
.el-icon {
transform: rotate(90deg);
color: #888;
}
}
}
}
.content-box {
margin: 8px 0 0 60px;
p {
font-size: 14px;
color: var(--font-color1);
}
}
}
}
最终沸点列表的界面如图所示:
13.8.4 开发沸点入口组件,添加创建沸点区域
沸点入口组件包含了创建沸点区域、沸点排序 tab 标签,并导入上一步创建的沸点列表组件。该组件中含有沸点的所有功能。
(1)创建文件 views/short-msg/index.vue 表示沸点入口组件,添加创建沸点区域的模版代码如下:
<div class="editor-area">
<el-input
v-model="form.content"
type="textarea"
placeholder="快来和掘友一起分享新鲜事!"
maxlength="100"
show-word-limit
:rows="4"
></el-input>
<div class="actions fx-b">
<el-popover
placement="bottom-start"
:width="200"
trigger="click"
transition="none"
:hide-after="50"
>
<template #reference>
<div class="topic fx">
{{ form.group == 'all' ? '请选择圈子' : store.getGpLabel(form.group)
}}
</div>
</template>
<div class="groups-pop">
<el-radio-group v-model="form.group">
<el-radio label="all">--不选择圈子--</el-radio>
<el-radio v-for="item in store.groups.slice(1)" :label="item.key"
>{{ item.label }}</el-radio
>
</el-radio-group>
</div>
</el-popover>
<el-button
type="primary"
:disabled="form.content.length == 0"
@click="toCreate"
>发布</el-button
>
</div>
</div>
上方代码中,提供了一个内容输入框和发布沸点的按钮。同时还有一个选择圈子的选项,用户可以决定将沸点发布在哪个圈子下。
(2)编写 JS 文件,引入沸点 Store 并定义沸点数据和创建沸点的方法,如下:
<script setup lang="ts">
import { shortmsgStore } from "@/stores";
import { onMounted, ref } from "vue";
import { ElMessage } from "element-plus";
const store = shortmsgStore();
const loading = ref(false);
const form = ref({
content: "",
group: "all",
});
const toCreate = () => {
loading.value = true;
store.createMsg(form.value, (res) => {
loading.value = false;
if (res) {
ElMessage.success("发布成功!");
form.value = { content: "", group: "all" };
store.getShortmsgs(filter.value);
}
});
};
</script>
最终创建沸点区域的界面如图所示。
(3)创建沸点切换 tab 标签,引入沸点列表组件,模版代码如下:
<div class="cus-tabs-header">
<ul @click="tagChange">
<li data-val="hot" :class="{ active: orderby == 'hot' }">最热</li>
<li data-val="new" :class="{ active: orderby == 'new' }">最新</li>
</ul>
</div>
<ShortMsgs :shortmsgs="store.shortmsgs" @on-filter="onFilter" />
(4)在 JS 代码中添加模版中的 tagChange() 和 onFilter() 方法,并在组件初始化时获取当前排序。添加代码如下:
import { useRoute, useRouter } from "vue-router";
const router = useRouter();
const route = useRoute();
const filter = ref({});
const orderby = ref("hot");
const onFilter = (json: Record<string, string>) => {
filter.value = {
...filter.value,
...json,
};
router.push({
query: filter.value,
});
store.getShortmsgs(filter.value);
};
const tagChange = (e: MouseEvent) => {
let dom: any = e.target;
orderby.value = dom.dataset.val;
onFilter({ orderby: orderby.value });
};
onMounted(() => {
filter.value = route.query;
store.getGroups();
store.getShortmsgs(filter.value);
});
(5)添加组件的样式代码(省略创建沸点区域),如下:
<style lang="less">
.main-box {
display: flex;
align-items: flex-start;
.content-wrap {
flex: 1;
.cus-tabs-header {
background: #fff;
}
.editor-area {
}
.main-ctx {
flex: 1;
display: flex;
align-items: flex-start;
}
}
}
</style>
最终沸点页面效果如图所示:
本章小节
本章按照需求分析从零开始搭建前端项目,并按照之前章节介绍的知识配置了公共组件、公共请求、公共函数等。配置好后开始进行项目的实战开发。
项目中用到的接口部分直接使用第 12 章创建的云函数接口,是与真实的接口交互,并且按照模块逐步实现了我们的仿掘金项目大部分功能。本章完成表示我们的综合实战项目已经完成了。
恭喜你跟着笔者一起阅读到这里,感谢你的支持。