当前位置: 首页 > news >正文

Vue笔记(九)

一、文章分类架子--PageContainer

学习PageContainer组件的封装,这一组件用于搭建页面基础结构,为后续内容展示提供统一布局。它可能包含通用的页面样式、导航栏、侧边栏等基础元素的结构搭建。

 在Vue组件中, <template> 标签内定义基础结构

<template>
  <div class="page-container">
    <!-- 导航栏部分 -->
    <div class="nav-bar">...</div>
    <!-- 内容区域 -->
    <div class="content-area">
      <!-- 这里后续会放置文章分类相关内容 -->
    </div>
  </div>
</template>
<script setup>
// 组件逻辑代码,可能包含数据定义、方法等
</script>
<style scoped>
.page-container {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.nav-bar {
  height: 60px;
  background-color: #333;
  color: white;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
}
.content-area {
  flex: 1;
  padding: 20px;
}
</style>
 

二、文章分类渲染

 

<template>
  <div class="category-list">
    <div v-for="category in categories" :key="category.id" class="category-item">
      {{ category.name }}
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
// 模拟数据,实际开发中从接口获取
const categories = ref([
  { id: 1, name: '技术' },
  { id: 2, name: '生活' },
  { id: 3, name: '娱乐' }
]);
</script>
<style scoped>
.category-list {
  list-style: none;
  padding: 0;
}
.category-item {
  padding: 10px;
  border-bottom: 1px solid #ccc;
}
</style>

三、添加分类

(一)显示弹层封装弹层组件

 创建添加分类的弹层组件,实现点击按钮弹出弹层,用于用户输入新分类信息。包括弹层的显示隐藏逻辑、样式设计以及与父组件的交互。

弹层组件 <AddCategoryModal>

<template>
  <div v-if="isVisible" class="add-category-modal">
    <div class="modal-content">
      <h2>添加分类</h2>
      <input v-model="newCategoryName" placeholder="请输入分类名称">
      <button @click="handleAdd">添加</button>
      <button @click="handleClose">关闭</button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const isVisible = ref(false);
const newCategoryName = ref('');
const handleAdd = () => {
  // 这里可以添加将新分类数据发送到后端的逻辑
  console.log('添加分类:', newCategoryName.value);
  handleClose();
};
const handleClose = () => {
  isVisible.value = false;
  newCategoryName.value = '';
};
</script>
<style scoped>
.add-category-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-content {
  background-color: white;
  padding: 20px;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
</style>

(二)添加完成

 处理添加分类的逻辑,在用户点击添加按钮后,将新分类数据发送到后端保存,并在成功后更新前端分类列表,实现数据的实时同步。

<template>
  <!-- 同176集弹层模板,主要修改handleAdd方法 -->
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const isVisible = ref(false);
const newCategoryName = ref('');
const handleAdd = async () => {
  try {
    const response = await axios.post('/api/categories', { name: newCategoryName.value });
    if (response.status === 200) {
      // 假设获取到新分类数据
      const newCategory = response.data;
      // 这里更新前端分类列表,假设在父组件中定义了categories数组
      // 并通过props传递到当前组件,需要更新后重新传递
      console.log('添加成功,新分类:', newCategory);
    }
  } catch (error) {
    console.error('添加分类失败:', error);
  }
  handleClose();
};
const handleClose = () => {
  isVisible.value = false;
  newCategoryName.value = '';
};
</script>
 

四、删除分类

 为每个分类添加删除功能,点击删除按钮后,向后端发送删除请求,并同步更新前端分类列表,确保前后端数据一致。

<template>
  <div class="category-list">
    <div v-for="category in categories" :key="category.id" class="category-item">
      {{ category.name }}
      <button @click="handleDelete(category.id)">删除</button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
// 假设从父组件获取分类数据
const categories = ref([
  { id: 1, name: '技术' },
  { id: 2, name: '生活' },
  { id: 3, name: '娱乐' }
]);
const handleDelete = async (categoryId) => {
  try {
    const response = await axios.delete(`/api/categories/${categoryId}`);
    if (response.status === 200) {
      // 更新前端分类列表,过滤掉被删除的分类
      categories.value = categories.value.filter(category => category.id!== categoryId);
      console.log('删除成功');
    }
  } catch (error) {
    console.error('删除分类失败:', error);
  }
};
</script>
 

五、文章管理--静态结构搭建

 

<template>
  <div class="article - management">
    <h1 class="page - title">文章管理</h1>
    <div class="article - list"></div>
    <button class="add - article - btn">添加文章</button>
  </div>
</template>
<style scoped>
.article - management {
  padding: 20px;
}
.page - title {
  font - size: 24px;
  margin - bottom: 20px;
}
.article - list {
  border: 1px solid #ccc;
  padding: 10px;
  min - height: 300px;
}
.add - article - btn {
  background - color: #007BFF;
  color: white;
  border: none;
  padding: 10px 20px;
  border - radius: 5px;
  margin - top: 20px;
  cursor: pointer;
}
</style>

六、中文国际化处理和文章分类组件封装

 使用工具(如vue - i18n)实现中文国际化,根据语言环境切换页面文字显示;封装文章分类组件,提高代码复用性,将分类展示逻辑抽离到单独组件。

<template>
  <div>
    <span>{{ $t('articleList') }}</span>
    <ArticleCategory></ArticleCategory>
  </div>
</template>
<script setup>
import { useI18n } from 'vue - i18n';
import ArticleCategory from './ArticleCategory.vue';
const { t } = useI18n();
</script>

 

<template>
  <div class="category - list">
    <div v - for="category in categories" : key="category.id" class="category - item">{{ category.name }}</div>
  </div>
</template>
<script setup>
const categories = [
  { id: 1, name: '技术' },
  { id: 2, name: '生活' }
];
</script>
<style scoped>
.category - list {
  list - style: none;
  padding: 0;
}
.category - item {
  padding: 5px 0;
}
</style>

七、文章列表

(一)封装API接口,请求渲染

1.封装文章列表数据请求的API接口,使用 axios 库发送HTTP请求获取文章数据,在组件中调用接口并将数据渲染到页面。

2.创建 api/article.js 封装接口

import axios from 'axios';
export const getArticleList = () => {
  return axios.get('/api/articles');
};

 

<template>
  <div class="article - list">
    <div v - for="article in articles" : key="article.id" class="article - item">{{ article.title }}</div>
  </div>
</template>
<script setup>
import { getArticleList } from '@/api/article.js';
import { ref } from 'vue';
const articles = ref([]);
const fetchArticles = async () => {
  try {
    const response = await getArticleList();
    articles.value = response.data;
  } catch (error) {
    console.error('获取文章列表失败', error);
  }
};
fetchArticles();
</script>
<style scoped>
.article - list {
  list - style: none;
  padding: 0;
}
.article - item {
  padding: 10px 0;
  border - bottom: 1px solid #ccc;
}
</style>

(二)分页渲染

 

<template>
  <div class="article - management">
    <div class="article - list">
      <div v - for="article in articles" : key="article.id" class="article - item">{{ article.title }}</div>
    </div>
    <div class="pagination">
      <button @click="prevPage" : disabled="page === 1">上一页</button>
      <span>{{ page }}</span>
      <button @click="nextPage">下一页</button>
    </div>
  </div>
</template>
<script setup>
import { getArticleList } from '@/api/article.js';
import { ref } from 'vue';
const articles = ref([]);
const page = ref(1);
const fetchArticles = async () => {
  try {
    const response = await getArticleList(page.value);
    articles.value = response.data;
  } catch (error) {
    console.error('获取文章列表失败', error);
  }
};
const prevPage = () => {
  if (page.value > 1) {
    page.value--;
    fetchArticles();
  }
};
const nextPage = () => {
  page.value++;
  fetchArticles();
};
fetchArticles();
</script>
<style scoped>
.pagination {
  margin - top: 20px;
}
.pagination button {
  margin: 0 5px;
}
</style>

(三)添加loading和处理搜索重置

 在文章数据请求时展示loading状态,让用户知道数据正在加载;添加搜索功能和重置按钮,实现根据关键词搜索文章,重置按钮用于清空搜索条件。

<template>
  <div class="article - management">
    <input v - model="searchKey" placeholder="搜索文章">
    <button @click="resetSearch">重置</button>
    <div v - if="loading" class="loading">加载中...</div>
    <div class="article - list">
      <div v - for="article in filteredArticles" : key="article.id" class="article - item">{{ article.title }}</div>
    </div>
    <div class="pagination">
      <button @click="prevPage" : disabled="page === 1">上一页</button>
      <span>{{ page }}</span>
      <button @click="nextPage">下一页</button>
    </div>
  </div>
</template>
<script setup>
import { getArticleList } from '@/api/article.js';
import { ref } from 'vue';
const articles = ref([]);
const filteredArticles = ref([]);
const searchKey = ref('');
const page = ref(1);
const loading = ref(false);
const fetchArticles = async () => {
  loading.value = true;
  try {
    const response = await getArticleList(page.value);
    articles.value = response.data;
    filteredArticles.value = articles.value.filter(article => article.title.includes(searchKey.value));
  } catch (error) {
    console.error('获取文章列表失败', error);
  } finally {
    loading.value = false;
  }
};
const resetSearch = () => {
  searchKey.value = '';
  fetchArticles();
};
const prevPage = () => {
  if (page.value > 1) {
    page.value--;
    fetchArticles();
  }
};
const nextPage = () => {
  page.value++;
  fetchArticles();
};
fetchArticles();
</script>
<style scoped>
.loading {
  text - align: center;
  margin - top: 20px;
}
</style>

八、文章新增

(一)准备抽屉组件--封装抽屉组件

 

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>新增文章</h2>
      <!-- 后续添加表单内容 -->
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const isOpen = ref(false);
const closeDrawer = () => {
  isOpen.value = false;
};
</script>
<style scoped>
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  width: 300px;
  height: 100vh;
  background - color: #fff;
  box - shadow: -5px 0 10px rgba(0, 0, 0, 0.3);
  z - index: 1000;
  transition: transform 0.3s ease - in - out;
  transform: translateX(100%);
  &.open {
    transform: translateX(0);
  }
}
.drawer - content {
  padding: 20px;
}
</style>

(二)完善抽屉表单结构

 

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>新增文章</h2>
      <form>
        <label for="title">标题:</label>
        <input type="text" id="title" v - model="article.title">
        <label for="category">分类:</label>
        <select id="category" v - model="article.category">
          <option value="技术">技术</option>
          <option value="生活">生活</option>
        </select>
        <label for="summary">摘要:</label>
        <textarea id="summary" v - model="article.summary"></textarea>
      </form>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const isOpen = ref(false);
const article = ref({
  title: '',
  category: '',
  summary: ''
});
const closeDrawer = () => {
  isOpen.value = false;
};
</script>

(三)上传文件

 借助 input 的文件选择和 FormData 对象,将文件数据发送到后端服务器。

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>新增文章</h2>
      <form>
        <!-- 其他表单元素 -->
        <label for="file">上传图片:</label>
        <input type="file" id="file" @change="handleFileUpload">
      </form>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const isOpen = ref(false);
const selectedFile = ref(null);
const handleFileUpload = (e) => {
  selectedFile.value = e.target.files[0];
};
const uploadFile = async () => {
  if (selectedFile.value) {
    const formData = new FormData();
    formData.append('file', selectedFile.value);
    try {
      const response = await axios.post('/api/upload', formData, {
        headers: {
          'Content - Type':'multipart/form - data'
        }
      });
      console.log('文件上传成功', response.data);
    } catch (error) {
      console.error('文件上传失败', error);
    }
  }
};
</script>

(四)富文本编辑器--vue-quill

 

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>新增文章</h2>
      <form>
        <!-- 其他表单元素 -->
        <vue - quill - editor v - model="article.content"></vue - quill - editor>
      </form>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import VueQuillEditor from 'vue - quill - editor';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
const isOpen = ref(false);
const article = ref({
  title: '',
  category: '',
  summary: '',
  content: ''
});
const closeDrawer = () => {
  isOpen.value = false;
};
</script>

(五)添加文章完成

 

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>新增文章</h2>
      <form @submit.prevent="addArticle">
        <!-- 表单元素和富文本编辑器 -->
        <button type="submit">提交</button>
      </form>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const isOpen = ref(false);
const article = ref({
  title: '',
  category: '',
  summary: '',
  content: ''
});
const closeDrawer = () => {
  isOpen.value = false;
};
const addArticle = async () => {
  try {
    const response = await axios.post('/api/articles', article.value);
    console.log('文章添加成功', response.data);
    closeDrawer();
  } catch (error) {
    console.error('文章添加失败', error);
  }
};
</script>

九、文章编辑--编辑文章完成

实现文章编辑功能,从后端获取文章原有数据填充到编辑表单和富文本编辑器中,用户修改后提交更新请求,后端更新数据。

<template>
  <div v-if="isOpen" class="drawer">
    <div class="drawer - content">
      <button @click="closeDrawer">关闭</button>
      <h2>编辑文章</h2>
      <form @submit.prevent="editArticle">
        <!-- 表单元素和富文本编辑器,绑定文章数据 -->
        <button type="submit">保存</button>
      </form>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const isOpen = ref(false);
const articleId = ref(null);
const article = ref({
  title: '',
  category: '',
  summary: '',
  content: ''
});
const closeDrawer = () => {
  isOpen.value = false;
};
const getArticleData = async (id) => {
  articleId.value = id;
  try {
    const response = await axios.get(`/api/articles/${id}`);
    article.value = response.data;
    isOpen.value = true;
  } catch (error) {
    console.error('获取文章数据失败', error);
  }
};
const editArticle = async () => {
  try {
    const response = await axios.put(`/api/articles/${articleId.value}`, article.value);
    console.log('文章编辑成功', response.data);
    closeDrawer();
  } catch (error) {
    console.error('文章编辑失败', error);
  }
};
</script>

相关文章:

  • ADC入门准备(九):信号与系统知识回顾
  • JVM学习
  • JPA应用@Formula注解
  • Windows 11 卸载 Edge
  • intellij idea篇
  • 工厂设计模式
  • PySide (PyQt)的视图(QGraphicsView)和场景(QGraphicsScene)
  • 【鸿蒙Next】优秀鸿蒙博客集锦
  • 简单了解低代码Low Code
  • repo学习使用
  • HTTP/2 由来及特性
  • 探寻氧化铈:催化剂领域的璀璨明珠-京煌科技
  • 第39周:猫狗识别 2(Tensorflow实战第九周)
  • 上课啦 | 2月17日软考高项【5月备考班】
  • DeepSeek神经网络:技术架构与实现原理探析
  • VSCode选择编译工具(CMake)
  • HarmonyOS 5.0应用开发——Canvas制作个人签名
  • Linux开源生态与开发工具链的探索之旅
  • 通过openresty和lua实现随机壁纸
  • 基于SSM的农产品供销小程序+LW示例参考
  • 做电商网站需要会些什么条件/2022百度收录越来越难了
  • 如何在网站上做社交的链接/关键词在线听
  • 用什么技术做网站/app推广
  • 网站建设找酷风/对网站和网页的认识
  • 学校网站 建设措施/高端网站建设深圳
  • 北京通州区网站制作/汕头seo优化