Skip to content

13.8 开发沸点页面

沸点页面是独立于文章的另一个页面。沸点的发布和列表都在同一个页面展示,且没有修改和删除,相比文章简单不少。沸点页面也是和首页一样的左中右布局。

13.8.1 开发左侧沸点圈子组件

沸点圈子组件与首页的文章分类组件大部分一致,只是数据不一样,我们只需要修改一些接受的 Props 名称即可。

(1)创建文件 views/short-msg/nav.vue 表示沸点圈子组件,拷贝文章分类组件的代码,然后修改 JS 代码如下:

js
const props = defineProps<{
  groups: any[]
}>()

(2)修改模版代码如下:

html
<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 分别存储沸点列表和沸点圈子数据。代码如下:

js
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)添加创建沸点、删除沸点、点赞/取消点赞沸点的方法,如下:

js
// 点赞/取消点赞沸点
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,沸点类型如下:

js
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:

js
export { default as shortmsgStore } from "./short-msg";

13.8.3 开发沸点列表组件,展示和操作沸点

沸点列表组件是一个公共组件,在用户中心页也会使用到。因此该组件只接收一个列表数据并渲染,同时在列表项中添加了删除的功能。

(1)创建文件 views/short-msg/lists.vue 表示公共沸点列表组件,模版代码如下:

html
<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() 方法,代码如下:

vue
<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)添加沸点列表的样式,主要代码如下:

less
.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 表示沸点入口组件,添加创建沸点区域的模版代码如下:

html
<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 并定义沸点数据和创建沸点的方法,如下:

vue
<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 标签,引入沸点列表组件,模版代码如下:

html
<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() 方法,并在组件初始化时获取当前排序。添加代码如下:

js
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)添加组件的样式代码(省略创建沸点区域),如下:

vue
<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 章创建的云函数接口,是与真实的接口交互,并且按照模块逐步实现了我们的仿掘金项目大部分功能。本章完成表示我们的综合实战项目已经完成了。

恭喜你跟着笔者一起阅读到这里,感谢你的支持。