聆听历史的回响——多源异构数据采集与融合应用综合实践

这个项目属于哪个课程 2024数据采集与融合技术实践
组名、项目简介 组名:scrapy能帮我爬到美味蟹黄堡的秘方吗
项目需求:文物不能很好的融入我们的生活,它们仿佛一具冰冷的尸体躺在博物馆的展示柜中,静静地接受着岁月的侵蚀和尘埃的覆盖。
项目目标:赋予文物新的生命力,让它们“动”起来。通过人工智能技术,为文物创造更加精彩动人的故事,生成曼妙的声音,制作生动震撼的展示视频,让历史以全新的方式触动人心。
技术路线:vue3前端web网站搭建,python flask后端,华为云平台(服务器接口调用),阿里云平台(部分数据存储),kimi背景故事AI生成,百度ai文本转语音,快手可灵ai平台对口型视频生成和通过接口实现视频生成
团队成员学号 曹星才-072208130
张诗悦-052205144
朱佳杰-012202239
黄悦佳-102202142
詹镇壕-102202149
这个项目目标 将静止的文物与现代科技相结合,赋予它们新的生命和表现力。我们通过先进的人工智能技术,让文物“动”起来,呈现出它们沉睡千年的故事与情感。AI将为每件文物创作更加丰富和感人的背景故事,让它们不再只是历史的见证者,而是活生生的叙述者。同时,通过AI生成曼妙的声音,让这些故事更加生动,引人入胜;借助视觉技术,生成精美的展示视频,生动再现文物的诞生过程、文化背景与历史场景。最终,我们希望通过这一系列创新方式,让更多人触及文物的灵魂,感受历史与文化的魅力。
其他参考文献 https://element-plus.org/zh-CN/component/overview.html
https://cn.vuejs.org/guide/introduction
w3school 在线教程
uiverse.io
Apache ECharts
https://docs.qingque.cn/d/home/eZQClW07IFEuX1csc-VejdY2M?identityId=1oEG9JKKMFv#section=h.a6acy8mosh
码云链接(代码已汇总,各小组成员代码不分开放) 前端:综合设计实践——前端 ·2022级数据采集与融合技术 - 码云 - 开源中国
后端:综合设计实践——后端 ·2022级数据采集与融合技术 - 码云 - 开源中国
爬虫:https://gitee.com/jia1666372886/museumspider/tree/master/

一、项目背景

文物不能很好的融入我们的生活,它们仿佛一具冰冷的尸体躺在博物馆的展示柜中,静静地接受着岁月的侵蚀和尘埃的覆盖。那些曾经承载着历史脉络与文化荣光的物品,如今只剩下了沉默的外壳,无法向我们诉说它们曾经的故事。它们被时间的长河淹没,渐渐失去了与我们生活的联系,变成了一个个静止的符号,等待着被无数的目光定格,却难以真正融入现代人的日常。

这些文物或许曾在千百年前激荡起社会的波澜,或许曾是某个伟大文明的象征,但如今它们的存在仿佛和现代世界隔绝了。我们虽然能够从它们的外观上看到历史的痕迹,感受到当时的技艺与智慧,却难以触摸到那段历史背后的人情冷暖、风云变幻。它们不再是生活的一部分,而是被孤立在某个遥远的过去,只是某种文化的遗物。

然而,尽管它们远离了现代的喧嚣,文物依然是人类历史与文化的见证者。每一件文物都有自己独特的生命力,它们承载着前人的思想、情感和智慧。要想让文物重新焕发出活力,我们需要跨越时空的藩篱,用现代科技去唤醒它们沉睡的记忆。通过人工智能、虚拟现实等技术,文物可以不再是冰冷的展品,而是成为我们与过去沟通的桥梁。它们能够重新讲述那个时代的故事,带领我们走进千年之前的世界,让我们重新感受到历史的脉搏和文化的力量。

在这个数字化、智能化的时代,我们不妨通过创新的方式,去打破文物与现代生活之间的隔阂,让这些承载着历史与文明的珍宝重新走进我们的生活,让它们不再只是博物馆里的静物,而是活跃在我们日常的对话和思考之中。

二、项目分工

成员 任务
曹星才 前端页面设计,接口调度与路由跳转逻辑,js代码编写
张诗悦 前端页面设计,web原型设计,css样式与html框架设计
朱佳杰 文物信息爬取
黄悦佳 后端服务器搭建与数据库管理
詹镇壕 后端服务器搭建与api接口编写

三、个人贡献

路由、接口以及相关js代码介绍

(一)路由跳转逻辑

3.1.1 导航栏

导航栏跳转:主页、展示页、列表(管理)页、管理员中心(登陆)页

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      component: NavigationBar,

      children: [
        { path: '/home', component: Home },
        { path: '/exhibit', component: Exhibit },
        { path: '/management', component: Management },
        { path: '/personal', component: Personal },
        { path: '', redirect: '/home' },
      ],
    },
  ],
})

3.1.2 根据文物id跳转到相应的详细页

        {
          path: '/artifact/:id', // 路径包含文物的 ID
          name: 'ArtifactDetail',
          component: Detail,
          props: (route) => ({
            id: route.params.id,
          }),
        },

3.1.3 根据文物id跳转到相应的文物修改页

        {
          path: '/artifactEdit/:id', // 路径包含文物的 ID
          name: 'ArtifactEdit',
          component: Add,
          props: (route) => ({
            id: route.params.id,

            name: route.query.name,
            period: route.query.period,
            category: route.query.category,
            parameter: route.query.parameter,
            material: route.query.material,
            thumbnail_path: route.query.thumbnail_path,
            description: route.query.description,

          }),
        },

3.1.4 添加页与分析页

        
        { path: '/add', name:'add',component: Add },
        { path: '/analyse', name:'analyse',component: Analyse },

(二)接口介绍

接口地址设计:

const axiosInstance = axios.create({
baseURL: 'http://***.***.***.***:****', //后端地址
timeout: 1000000,
});

3.2.1 获取文物

全部获取:

export const get_artifacts = () => {
  return axiosInstance.get(`/api/artifacts`)
}

部分获取:

export const get_artifacts2 = async (limit = 20) => {
  try {
    const response = await axiosInstance.get(`/api/artifacts`, {
      params: {
        limit,
      }
    });
    return response.data; // 返回文物数据
  } catch (error) {
    console.error("Error fetching artifacts:", error);
    throw error;
  }
};

根据id获取:

export const get_artifacts_by_id = (id) => {
  return axiosInstance.get(`/api/artifacts/${id}`)
}

根据名字获取:

export const get_artifact_by_name = async (name, limit = 1) => {
  try {
    const response = await axiosInstance.get(`/api/artifacts/search`, {
      params: {
        name: name,
        limit,
      }
    });
    return response.data; // 返回文物数据
  } catch (error) {
    console.error("Error fetching artifacts:", error);
    throw error;
  }
};

调用:

const loading = ref(true)
// 获取文物列表的函数
const fetchArtifacts = async () => {
  try {
    const response = await get_artifacts() // 调用获取文物的 API 函数
    console.log(response)
    artifactStore.setArtifactData(response.data.data) // 获取全部
    artifacts.value = artifactStore.$state.artifact // 更新本地的数据
  } catch (error) {
    console.error('获取文物列表失败:', error)
    alert('获取文物列表失败')
  } finally {
    loading.value = false // 结束加载状态
  }
}

3.2.2 添加文物

export const add_artifact = (artifactData) => {
  // 获取当前存储的 token(例如从 Pinia store 或 localStorage)
  const accessToken = localStorage.getItem('access_token');  // 或者使用 Pinia store 获取 token

  if (!accessToken) {
    // 如果没有 token,提示用户登录
    return Promise.reject(new Error('请先登录'));
  }

  // 发送 POST 请求并携带 Authorization header
  return axiosInstance.post('/api/artifacts', artifactData, {
    headers: {
      Authorization: `Bearer ${accessToken}`,  // 将 token 加入请求头
    },
  }).then(response => {
    if (response.status === 201) {
      console.log('添加文物信息成功');
      return response.data; // 返回成功的响应数据
    } else {
      throw new Error(`添加文物信息失败,状态码:${response.status}`);
    }
  }).catch(error => {
    console.error('添加文物信息请求失败:', error);
    throw error;
  });
};

3.2.3 更新文物

export const update_artifact = (id, artifactData) => {
  // 获取当前存储的 token(例如从 localStorage 或 Pinia)
  const accessToken = localStorage.getItem('access_token');  // 或者使用 Pinia store 获取 token
  
  if (!accessToken) {
    // 如果没有 token,提示用户登录
    return Promise.reject(new Error('请先登录'));
  }

  // 构建请求头
  const headers = {
    Authorization: `Bearer ${accessToken}`,  // 将 token 加入请求头
  };

  // 发送 PUT 请求以更新文物信息
  return axiosInstance.put(`/api/artifacts/${id}`, artifactData, {
    headers: headers,
  })
  .then((response) => {
    if (response.status === 200) {
      console.log('文物信息更新成功');
      return response.data;  // 返回响应数据
    } else {
      console.error(`更新文物失败,状态码:${response.status}`);
      throw new Error(`更新文物失败,状态码:${response.status}`);
    }
  })
  .catch((error) => {
    console.error('更新文物信息失败:', error);
    throw error;
  });
};

调用:

const submitForm = async (formEl) => {
  if (!formEl) return
  await formEl.validate(async (valid, fields) => {
    if (valid) {
      if (isEdit.value) {
        // 更新文物
        await update_artifact(ruleForm.id, ruleForm)
        fetchArtifacts()
      } else {
        // 添加文物
        await add_artifact(ruleForm)
        fetchArtifacts()
      }
      // 跳转或显示成功信息
      router.push('/management')
    } else {
      console.log('error submit!', fields)
    }
  })
}

3.2.4 删除文物

export const test_delete_artifact = (id) => {
  // 获取当前存储的 token(例如从 Pinia store)
  const accessToken = localStorage.getItem('access_token');  // 或者使用 Pinia store 获取 token

  if (!accessToken) {
    // 如果没有 token,提示用户登录
    return Promise.reject(new Error('请先登录'));
  }

  // 发送 DELETE 请求并携带 Authorization header
  return axiosInstance.delete(`/api/artifacts/${id}`, {
    headers: {
      Authorization: `Bearer ${accessToken}`,  // 将 token 加入请求头
    },
  });
};

调用:

const handleDelete = async (id) => {
  try {
    await test_delete_artifact(id) // 调用删除函数
    alert('文物删除成功')
    // 删除成功后更新界面
  } catch (error) {
    alert(error.message) // 显示错误消息(如没有登录)
  }
}

3.2.5 登陆

export const login = (account, password) => {
  return axiosInstance.post('/api/users/login', {
    phone: account,
    password: password
  })
}

调用:

const handleLogin = async () => {
  try {
    const response = await login(account.value, password.value)

    if (response.status === 200) {
      // 获取并存储 JWT 令牌
      const accessToken = response.data.access_token

      authStore.setAccessToken(accessToken)

      ElMessage({
        message: '登录成功',
        type: 'success',
        customClass: 'el-message-success',
      })
    }
  } catch (error) {
    console.error('登录失败', error)

    ElMessage({
      message: '登录失败,请检查账号和密码,如果没有管理员账号请联系平台开发者',
      type: 'error',
    })
  }
}

3.2.6 添加用户交互记录

export const add_user_interaction = (artifactId, interactionType='view') => {
  // 获取当前存储的 token(例如从 localStorage 或 Pinia store)
  const accessToken = localStorage.getItem('access_token');  // 或者使用 Pinia store 获取 token

  if (!accessToken) {
    // 如果没有 token,提示用户登录
    return Promise.reject(new Error('请先登录'));
  }

  // 请求数据
  const interactionData = {
    artifact_id: artifactId,
    interaction_type: interactionType,  // 交互类型,如 'view'
  };

  // 发送 POST 请求并携带 Authorization header
  return axiosInstance.post('/api/interactions', interactionData, {
    headers: {
      Authorization: `Bearer ${accessToken}`,  // 将 token 加入请求头
    },
  });
};

调用:

const handleAction = async (id_) => {
  try {
    // 先记录用户的交互行为
    await add_user_interaction(id_, 'view') // 'view' 表示查看交互类型

    console.log(`交互记录已添加,文物 ID: ${id_}`)
  } catch (error) {
    console.error('添加交互记录失败:', error.message)
  } finally {
    // 然后跳转到文物详情页面
    router.push({
      name: 'ArtifactDetail',
      params: { id: id_ },
    })
  }
}

3.2.7 获取交互记录

根据类别获取:

export const get_interactions_count_by_category = async () => {
  try {
    const response = await axiosInstance.get('/api/interactions/category/interactions_count');
    return response.data;  // 返回文物类别交互数量统计的数据
  } catch (error) {
    console.error("Error fetching interactions count by category:", error);
    throw error;  // 抛出错误,供上层处理
  }
};

调用:

const fetchAndRenderChart = async () => {
  try {
    // 获取文物类别统计数据
    const countData = await get_artifacts_count_by_category()

    // 提取数据部分
    const categoryData = countData.data
    console.log(categoryData)

    // 格式化数据,转换为图表需要的形式
    const chartData = categoryData.map((item) => ({
      value: item.artifact_count,
      name: item.category,
    }))

    // 图表配置
    const option1 = {
      title: {
        text: '各类别数量占比',
        left: 'center',
        textStyle: {
          fontSize: 30,
        },
      },
      tooltip: {
        trigger: 'item',
        formatter: '{a} <br/>{b} : {c} ({d}%)',
      },
      legend: {
        top: 'bottom',
      },
      toolbox: {
        show: true,
        feature: {
          mark: { show: true },
          dataView: { show: true, readOnly: false },
          restore: { show: true },
          saveAsImage: { show: true },
        },
      },
      series: [
        {
          name: 'Percentage!',
          type: 'pie',
          radius: [50, 250],
          center: ['50%', '50%'],
          roseType: 'area',
          itemStyle: {
            borderRadius: 8,
          },

          data: chartData, // 动态填充数据
        },
      ],
    }

    // 获取图表 DOM 容器

    const myChart1 = echarts.init(chart1.value)
    // 渲染图表
    myChart1.setOption(option1)
  } catch (error) {
    console.error('获取文物类别统计数量失败', error)
  }
}

获取每个文物的交互记录:

export const get_artifact_interaction_counts = async (limit = 10) => {
  try {
    const response = await axiosInstance.get('/api/interactions/count', {
      params: {
        limit,  // 设置请求的 limit 参数
      },
    });
    return response.data; // 返回数据
  } catch (error) {
    console.error("Error fetching artifact interaction counts:", error);
    throw error;
  }
};

调用:

const fetchAndRenderInteractionChart = async () => {
  try {
    // 获取文物交互次数数据
    const interactionData = await get_interactions_count_by_category()
    console.log('dasdas', interactionData.data)

    // 格式化数据
    const artifactIds = interactionData.data.map((item) => item.category) // 文物ID(或者名称)
    const interactionCounts = interactionData.data.map((item) => item.category_interaction_count) // 交互次数

    // 图表配置
    const option2 = {
      title: {
        text: '各类别文物热度',
        left: 'center',
        textStyle: {
          fontSize: 30,
        },
      },
      tooltip: {
        trigger: 'axis',
        axisPointer: {
          type: 'shadow',
        },
      },
      toolbox: {
        show: true,
        feature: {
          mark: { show: true },
          dataView: { show: true, readOnly: false },
          restore: { show: true },
          saveAsImage: { show: true },
        },
      },
      grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true,
      },
      xAxis: [
        {
          type: 'category',
          data: artifactIds, // 更新为从 API 获取的文物 ID
          axisTick: {
            alignWithLabel: true,
          },
        },
      ],
      yAxis: [
        {
          type: 'value',
        },
      ],
      series: [
        {
          name: 'Count',
          type: 'bar',
          barWidth: '60%',
          data: interactionCounts, // 更新为从 API 获取的交互次数
        },
      ],
    }

    // 获取图表 DOM 容器

    const myChart2 = echarts.init(chart2.value)
    // 渲染图表
    myChart2.setOption(option2)
  } catch (error) {
    console.error('获取文物统计数量失败', error)
  }
}

3.2.8 AI接口

AI故事:

export const get_artifact_story_by_id = (id) => {
  return axiosInstance.get(`/api/ai_story/${id}`)
}

AI语音:

export const generateAudio = async (text) => {
  try {
    const response = await axiosInstance.post('/api/tts', {
      text: text, // 传递需要转换为语音的文本
    });
    return response.data.audio_url; // 返回音频的URL
  } catch (error) {
    console.error("Error generating audio:", error);
    throw error; // 抛出错误,便于外部捕获
  }
};

AI视频:

export const generateVideo = async (imageUrl) => {
  try {
    // 构建查询参数
    const params = {
      url: imageUrl
    };

    // 发送 GET 请求
    const response = await axiosInstance.get('/api/ai_video', { params });

    // 检查响应状态
    if (response.status === 200) {
      console.log('生成视频测试通过');
      console.log(response.data); // 输出返回的 JSON 数据
      return response.data; // 返回视频生成的相关信息
    } else {
      console.error(`生成视频信息失败,状态码:${response.status}, 响应内容:${response.data}`);
      throw new Error(`生成视频信息失败,状态码:${response.status}`);
    }
  } catch (error) {
    console.error('请求错误:', error);
    throw error; // 抛出错误,便于外部捕获
  }
};

调用:

const loading = ref(false)
const handleAI = async (id) => {
  try {
    ai_button.value=false
    loading.value = true
    const response =await get_artifact_story_by_id(id) 
    console.log(response)

      story.value=response.data.data


      compiledMarkdown.value = marked( story.value)

if (compiledMarkdown.value) {
      generateAudioAndPlay();  // 使用 `compiledMarkdown.value`
    }


    generateVideoPlay()

  } catch (error) {
    alert(error.message) 
  }finally{
    loading.value = false
v.value=true
  }
}



// 用于存储音频的 URL
const audioUrl = ref(null)
// 用于显示加载状态
const loading2 = ref(false)

// 用 ref 创建对音频元素的引用
const audioPlayer = ref(null)

// 生成音频并播放
const generateAudioAndPlay = async () => {
  // 先清除之前的音频 URL
  audioUrl.value = null
  loading2.value = true

  try {

    // 调用 API 生成音频并获取音频 URL
    const url = await generateAudio(extractTextFromHtml(compiledMarkdown.value))
    
    // 设置音频 URL
    audioUrl.value = url

    // 使用 ref 引用播放音频
    if (audioPlayer.value) {
      audioPlayer.value.play()
    }

  } catch (error) {
    console.error('音频生成失败:', error)
    alert('音频生成失败,请重试')
  } finally {
    loading2.value = false
  }
}


const videoUrl=ref(null)
const loading3=ref(false)
// 生成视频并获取 URL
const generateVideoPlay = async () => {
  // 先清除之前的音频 URL
  videoUrl.value = null
  loading3.value = true

  try {
    console.log(artifact.value.thumbnail_path)
    const videoData = await generateVideo(artifact.value.thumbnail_path); // 示例图片 URL
    console.log("22222",videoData)
    videoUrl.value = videoData.data; // 假设返回的数据中有 `videoUrl` 字段
  } catch (error) {
    console.error('视频生成失败:', error);
  }finally{
loading3.value=false

  }
};



const extractTextFromHtml = (html) => {
  console.log(html)
  const doc = new DOMParser().parseFromString(html, 'text/html');
  return doc.body.textContent || '';  // 返回纯文本
};

(三)js代码相关

3.3.1 文物筛选

// 筛选的类别
const selectedCategory = ref('')
// const search=ref(false)


// 过滤后的文物列表(根据筛选条件)
const filteredArtifacts = computed(() => {

  return selectedCategory.value
    ? artifacts.value.filter((item) => item.category === selectedCategory.value)
    : artifacts.value


})

3.3.2 分析图表初始化

onMounted(() => {
  // 调用函数来获取数据并渲染图表
  fetchAndRenderChart()

  fetchAndRenderInteractionChart()

  initChart3()
})

3.3.3 页面详细页接收跳转参数

onMounted(() => {
  const { id } = route.params
  if (id) {
    isEdit.value = true
    formTitle.value = '更新文物'
    const { name, period, category, parameter, material, thumbnail_path, description } = route.query

    ruleForm.id = id || ''
    ruleForm.name = name || ''
    ruleForm.period = period || ''
    ruleForm.category = category || ''
    ruleForm.parameter = parameter || ''
    ruleForm.material = material || ''
    ruleForm.description = description || ''
    ruleForm.thumbnail_path = thumbnail_path || ''
  }
})

3.3.4 首页轮播图随机播放

const randomArtifacts = computed(() => {
  const numToSelect = 10 // 选择6个文物
  return getRandomItems(artifacts.value, numToSelect)
})

function getRandomItems(array, num) {
  const result = []
  const seenIndexes = new Set()

  while (result.length < num && result.length < array.length) {
    const randomIndex = Math.floor(Math.random() * array.length)

    // 确保索引不重复
    if (!seenIndexes.has(randomIndex)) {
      result.push(array[randomIndex])
      seenIndexes.add(randomIndex)
    }
  }

  return result
}

3.3.5 展示页子组件向父组件传参

import Checkbox from '@/components/Checkbox.vue'


// 传世品、革命文物、国史文物、货币、考古发掘品、名族名俗文物、古籍文献、外国文物、艺术品
const sorts = ref([
  {
    text: '传世品',
    isChecked: true,
  },
  {
    text: '货币',
    isChecked: true,
  },
  {
    text: '艺术品',
    isChecked: true,
  },
  {
    text: '革命文物',
    isChecked: true,
  },
  {
    text: '国史文物',
    isChecked: true,
  },
  {
    text: '考古发掘品',
    isChecked: true,
  },
  {
    text: '民族民俗文物',
    isChecked: true,
  },
  {
    text: '古籍文献',
    isChecked: true,
  },
  {
    text: '外国文物',
    isChecked: true,
  },
]);

// 父组件传递的更新数据函数
const emit = defineEmits(['categoryChanged'])

// 监听复选框变化
const handleCheckboxChange = () => {

  // 过滤选中的类别
    const selectedCategories = sorts.value
    .filter(item => item.isChecked)
    .map(item => item.text)
    .filter(Boolean);  // 过滤掉任何假值,如 undefined 或 null


  // 发送选中的类别给父组件
  emit('categoryChanged',selectedCategories);
};


onMounted(() => {
  handleCheckboxChange()
});

3.3.6 监听管理员转换管理员模式

watch(isAdmin, (newVal) => {
  if (newVal) {
    router.push({
      name: 'ArtifactEdit',
      params: { id: artifact.value.id },
      query: {
        name: artifact.value.name,
        period: artifact.value.period,
        category: artifact.value.category,
        parameter: artifact.value.parameter,
        material: artifact.value.material,
        thumbnail_path: artifact.value.thumbnail_path,
        description: artifact.value.description,
      },
    })
  }
})

3.3.7 按钮点击事件及AI事件触发

const handleAI = async (id) => {
  try {
    ai_button.value=false
    loading.value = true
    const response =await get_artifact_story_by_id(id) 
    console.log(response)

      story.value=response.data.data


      compiledMarkdown.value = marked( story.value)

if (compiledMarkdown.value) {
      generateAudioAndPlay();  // 使用 `compiledMarkdown.value`
    }

    generateVideoPlay()

  } catch (error) {
    alert(error.message) 
  }finally{
    loading.value = false
v.value=true
  }
}

(四)全局变量stores存储

3.4.1 全部文物

export const useArtifactStore = defineStore('artifact', {
    state: () => ({
      artifact: JSON.parse(localStorage.getItem('artifactData')) || [], // 从 localStorage 获取数据,如果没有就初始化为空数组
    }),
    actions: {
      setArtifactData(artifact) {
        console.log("store", artifact);
  
        this.artifact = artifact;
  
        // 将数据保存到 localStorage
        localStorage.setItem('artifactData', JSON.stringify(this.artifact));
  
        console.log("store2", this.artifact);
      },
  
      clearArtifactData() {
        this.artifact = [];
        localStorage.removeItem('artifactData'); // 清除缓存
      },
    },
  });

3.4.2 管理员模式全局状态管理

export const useAdminStore = defineStore('admin', {
  state: () => ({
    isAdmin: false, // 管理员模式的状态
  }),
  actions: {
    toggleAdminMode() {
        // console.log('Before toggle:', this.isAdmin);
      this.isAdmin = !this.isAdmin; // 切换管理员模式状态
    //   console.log('aaaaa:', this.isAdmin);
    }
  }
});



export const useAuthStore = defineStore('auth', {
  state: () => ({
    accessToken: localStorage.getItem('access_token') || null, // 初始化时从 localStorage 获取令牌(如果存在)
  }),
  actions: {
    setAccessToken(token) {
      this.accessToken = token
      localStorage.setItem('access_token', token)  // 存储到 localStorage
    },
    clearAccessToken() {
      this.accessToken = null
      localStorage.removeItem('access_token')  // 从 localStorage 删除
    },
  },
  getters: {
    isAuthenticated: (state) => !!state.accessToken,  // 判断是否登录
  }
})

四、收获

对我来说,这次实践真是一次宝贵的成长经历。我负责的是前端部分,使用了Vue 3的组合式API,并通过单文件组件进行开发。刚开始时,面临了很多挑战,例如页面跳转不成功、参数无法正确传递到下一页、JS代码编写的难题以及接口请求不正常或返回的参数不对等问题。每一个问题都曾让我感到有些迷茫,但在不断的摸索和调试中,我最终找到了合适的解决方案。

通过这次实践,我不仅学到了更多关于Vue 3的使用技巧,还提升了问题解决能力。学会了如何在Vue中灵活地管理状态和处理异步请求,如何优化页面跳转流程以及如何确保接口的数据传输准确无误;学会了如何与后端顺利的对接;学会了如何协调组员,互帮互助,做好每一步工作。

前端部分是繁琐的,每调整一步都要全局修改探索,在不断摸爬滚打中,我战胜了自己,让自己掌握了更多的专业知识。

posted @ 2024-12-16 01:36  starryship  阅读(24)  评论(0编辑  收藏  举报