category.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { defineStore } from 'pinia'
  2. import type { BizCategoryVo } from '~/types/api'
  3. import { categoryApi } from '~/api/category'
  4. import { CATEGORY_TREE_CACHE_KEY } from '~/constants'
  5. interface CategoryState {
  6. // 完整品类树(一级 + 二级)
  7. categoryTree: BizCategoryVo[]
  8. // 当前选中的一级品类 code
  9. activeFirstCode: string
  10. // 当前选中的二级品类 code
  11. activeSecondCode: string
  12. // 是否正在加载
  13. loading: boolean
  14. // 是否已经初始化过(避免重复请求)
  15. initialized: boolean
  16. }
  17. export const useCategoryStore = defineStore('category', {
  18. state: (): CategoryState => ({
  19. categoryTree: [],
  20. activeFirstCode: '',
  21. activeSecondCode: '',
  22. loading: false,
  23. initialized: false,
  24. }),
  25. actions: {
  26. /**
  27. * 加载品类树
  28. * 默认只在首次调用时请求,可以通过 force=true 强制刷新
  29. */
  30. async loadCategories(force = false) {
  31. // 先尝试从 localStorage 读取缓存(仅在客户端且尚未初始化时执行)
  32. if (import.meta.client && !this.initialized && this.categoryTree.length === 0) {
  33. try {
  34. const cached = localStorage.getItem(CATEGORY_TREE_CACHE_KEY)
  35. if (cached) {
  36. const parsed = JSON.parse(cached) as BizCategoryVo[]
  37. if (Array.isArray(parsed) && parsed.length > 0) {
  38. this.categoryTree = parsed
  39. }
  40. }
  41. }
  42. catch (e) {
  43. console.warn('Failed to read category tree from localStorage:', e)
  44. }
  45. }
  46. // 如果已经通过接口拿到过最新数据,且不强制刷新,则直接返回
  47. if (this.initialized && !force)
  48. return
  49. this.loading = true
  50. try {
  51. const res = await categoryApi.listTree()
  52. if (!res || !res.list || res.list.length === 0)
  53. return
  54. // 保留完整的品类树;展示逻辑由组件自己控制(如只展示前三个)
  55. this.categoryTree = res.list
  56. this.initialized = true
  57. // 成功后将最新数据写入 localStorage,供下次快速恢复
  58. if (import.meta.client) {
  59. try {
  60. localStorage.setItem(CATEGORY_TREE_CACHE_KEY, JSON.stringify(this.categoryTree))
  61. }
  62. catch (e) {
  63. console.warn('Failed to write category tree to localStorage:', e)
  64. }
  65. }
  66. }
  67. catch (error) {
  68. // 这里的错误已经在 http 拦截器里有统一处理,这里仅做兜底日志
  69. console.error('Failed to load category tree:', error)
  70. }
  71. finally {
  72. this.loading = false
  73. }
  74. },
  75. /**
  76. * 根据当前品类树初始化默认选中项(第一个一级 + 其第一个二级)
  77. * 仅在还没有选中任何一级品类时生效
  78. */
  79. initDefaultSelection() {
  80. if (this.activeFirstCode)
  81. return
  82. const first = this.categoryTree[0]
  83. if (!first)
  84. return
  85. this.activeFirstCode = first.code
  86. const firstChildren = first.children
  87. if (firstChildren && firstChildren.length > 0 && firstChildren[0]) {
  88. this.activeSecondCode = firstChildren[0].code
  89. }
  90. },
  91. /**
  92. * 切换一级品类时,同时联动重置二级为当前一级下的第一个子品类
  93. */
  94. setActiveFirst(code: string) {
  95. if (this.activeFirstCode === code)
  96. return
  97. this.activeFirstCode = code
  98. const current = this.categoryTree.find(item => item.code === code)
  99. const firstChild = current?.children?.[0]
  100. this.activeSecondCode = firstChild?.code ?? ''
  101. },
  102. /**
  103. * 更新当前二级品类 code
  104. */
  105. setActiveSecond(code: string) {
  106. if (this.activeSecondCode === code)
  107. return
  108. this.activeSecondCode = code
  109. },
  110. },
  111. })