Skip to content

データ辞書ドキュメント - 辞書管理とコンポーネント使用

概要

このドキュメントは、VJSP Vue3フレームワークのデータ辞書管理システムについて詳しく説明します。データ辞書は統一された列挙値管理ソリューションを提供し、ステータスコード、タイプ識別子などのデータの集中管理と動的ローディングをサポートし、データの一貫性と保守性を確保します。

辞書アーキテクチャ

コア機能

  • 統一管理: すべての列挙データを集中管理し、ハードコーディングを回避
  • 動的ローディング: ランタイムでの辞書データの動的ローディングをサポート
  • インテリジェントキャッシュ: 自動キャッシュメカニズムによるパフォーマンス向上
  • タイプセーフ: 完全なTypeScriptタイプ定義サポート
  • エラーハンドリング: 包括的なエラーハンドリングとリトライメカニズム

技術スタック

  • Pinia状態管理: 辞書データの状態管理とキャッシュ
  • 非同期ローディング: 辞書データの非同期取得と更新をサポート
  • コンポーネント統合: Element Plusコンポーネントライブラリとの深い統合

辞書管理メカニズム

辞書データストレージ

辞書データは集中ストレージ管理を使用:

  • メモリキャッシュ: 辞書データをメモリにキャッシュして高速アクセス
  • 自動期限切れ: キャッシュデータは自動的に期限切れになり、データの鮮度を確保
  • 強制更新: 辞書データの手動強制更新をサポート

辞書ローディングプロセス

  1. アプリケーション起動: キャッシュの有効性を確認
  2. データローディング: バックエンドAPIから辞書データを取得
  3. キャッシュ更新: メモリキャッシュとタイムスタンプを更新
  4. エラーハンドリング: ネットワークエラーとリトライロジックを処理

キャッシュ戦略

フレームワークはインテリジェントなキャッシュ戦略を実装:

  • キャッシュ期間: 5分間の自動期限切れ
  • リトライメカニズム: 最大3回のリトライと指数バックオフ遅延
  • 同時実行制御: 重複リクエストを防止してリソースの浪費を回避

辞書ユーティリティ関数

基本辞書アクセス

フレームワークはローディングとエラーを自動的に処理する安全な辞書アクセス関数を提供:

  • getSafeDictLabel: 安全に辞書ラベルを取得
  • getSafeDictValue: 安全に辞書値を取得
  • getSafeDictOptions: 安全に辞書オプションリストを取得
  • batchGetDictLabels: バッチで辞書ラベルを取得
  • hasDictType: 辞書タイプの存在を確認

辞書状態管理

辞書ストアモジュールを通じて辞書データを管理:

  • getDictData: 辞書データを取得(キャッシュを自動的に処理)
  • getDictByType: タイプ別に辞書アイテムを取得
  • getDictLabel: 辞書ラベルを取得
  • getDictValue: 辞書値を取得
  • refreshDictData: 辞書データを更新
  • clearDictData: 辞書キャッシュをクリア

辞書コンポーネント使用

コンポーネントコレクション

フレームワークは完全な辞書コンポーネントコレクションを提供:

  • DictSelect: 辞書セレクター
  • DictTag: 辞書タグ表示
  • DictRadio: 辞書ラジオコンポーネント
  • DictCheckbox: 辞書チェックボックスコンポーネント
  • DictSwitch: 辞書スイッチコンポーネント
  • DictCascader: 辞書カスケードセレクター

コンポーネント機能

すべての辞書コンポーネントは以下の機能を持ちます:

  • 自動ローディング: 対応する辞書タイプデータを自動的にロード
  • エラーハンドリング: 辞書データのローディング失敗を処理
  • タイプセーフ: 完全なTypeScriptタイプサポート
  • スタイル一貫性: Element Plusコンポーネントスタイルとの一貫性

ビジネスモジュール開発ガイド(Productモジュール)

1. 辞書タイプの定義

辞書管理システムで製品関連の辞書タイプを定義:

  • product_status: 製品ステータス辞書
  • product_category: 製品カテゴリ辞書
  • product_type: 製品タイプ辞書

2. 製品管理ページの作成

src/views/modules/product/list.vue

vue
<template>
  <div class="product-list">
    <!-- 検索フォーム -->
    <el-form :model="queryParams" inline>
      <el-form-item label="製品ステータス">
        <DictSelect
          v-model="queryParams.status"
          dict-type="product_status"
          placeholder="製品ステータスを選択してください"
          clearable
        />
      </el-form-item>

      <el-form-item label="製品カテゴリ">
        <DictSelect
          v-model="queryParams.category"
          dict-type="product_category"
          placeholder="製品カテゴリを選択してください"
          clearable
        />
      </el-form-item>

      <el-button type="primary" @click="handleQuery">検索</el-button>
      <el-button @click="resetQuery">リセット</el-button>
    </el-form>

    <!-- データテーブル -->
    <el-table :data="productList" v-loading="loading">
      <el-table-column prop="productName" label="製品名" />
      <el-table-column prop="productCode" label="製品コード" />
      <el-table-column prop="status" label="製品ステータス">
        <template #default="{ row }">
          <DictTag :value="row.status" dict-type="product_status" />
        </template>
      </el-table-column>
      <el-table-column prop="category" label="製品カテゴリ">
        <template #default="{ row }">
          <DictTag :value="row.category" dict-type="product_category" />
        </template>
      </el-table-column>
      <el-table-column prop="type" label="製品タイプ">
        <template #default="{ row }">
          <DictTag :value="row.type" dict-type="product_type" />
        </template>
      </el-table-column>
      <el-table-column label="操作" width="200">
        <template #default="{ row }">
          <el-button size="small" @click="handleEdit(row)">編集</el-button>
          <el-button size="small" type="danger" @click="handleDelete(row)">削除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useProductApi } from '@/api/modules/product'

const queryParams = ref({
  status: '',
  category: '',
  productName: '',
})

const productList = ref([])
const loading = ref(false)

const loadProductList = async () => {
  loading.value = true
  try {
    const response = await useProductApi().getList(queryParams.value)
    productList.value = response.data.list
  } finally {
    loading.value = false
  }
}

const handleQuery = () => {
  loadProductList()
}

const resetQuery = () => {
  queryParams.value = {
    status: '',
    category: '',
    productName: '',
  }
  loadProductList()
}

onMounted(() => {
  loadProductList()
})
</script>

3. 製品作成/編集フォーム

src/views/modules/product/form.vue

vue
<template>
  <el-form :model="formData" :rules="rules" ref="formRef" label-width="120px">
    <el-form-item label="製品名" prop="productName">
      <el-input v-model="formData.productName" placeholder="製品名を入力してください" />
    </el-form-item>

    <el-form-item label="製品ステータス" prop="status">
      <DictRadio v-model="formData.status" dict-type="product_status" :options="statusOptions" />
    </el-form-item>

    <el-form-item label="製品カテゴリ" prop="category">
      <DictSelect
        v-model="formData.category"
        dict-type="product_category"
        placeholder="製品カテゴリを選択してください"
      />
    </el-form-item>

    <el-form-item label="製品タイプ" prop="type">
      <DictCheckbox v-model="formData.type" dict-type="product_type" :options="typeOptions" />
    </el-form-item>

    <el-form-item>
      <el-button type="primary" @click="handleSubmit">送信</el-button>
      <el-button @click="handleCancel">キャンセル</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const formRef = ref()

const formData = reactive({
  productName: '',
  status: '',
  category: '',
  type: [],
})

const rules = {
  productName: [{ required: true, message: '製品名は必須です', trigger: 'blur' }],
  status: [{ required: true, message: '製品ステータスは必須です', trigger: 'change' }],
}

const handleSubmit = async () => {
  try {
    await formRef.value.validate()
    // フォームデータを送信
    console.log('フォーム送信:', formData)
    router.push('/product/list')
  } catch (error) {
    console.error('フォーム検証失敗:', error)
  }
}

const handleCancel = () => {
  router.push('/product/list')
}
</script>

高度な辞書使用

カスタム辞書コンポーネント

特定のビジネスニーズに合わせてカスタム辞書コンポーネントを作成:

vue
<!-- src/components/DictColorTag.vue -->
<template>
  <el-tag :color="getTagColor(value)" effect="dark">
    {{ getDictLabel(value, dictType) }}
  </el-tag>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useDictStore } from '@/stores/dict'

interface Props {
  value: string | number
  dictType: string
}

const props = defineProps<Props>()
const dictStore = useDictStore()

const getTagColor = (value: string | number) => {
  const colorMap = {
    active: '#67C23A',
    inactive: '#909399',
    pending: '#E6A23C',
    rejected: '#F56C6C',
  }
  return colorMap[value] || '#909399'
}

const getDictLabel = computed(() => {
  return (value: string | number, type: string) => {
    return dictStore.getDictLabel(type, value) || value
  }
})
</script>

辞書データ検証

辞書ベースのフォーム検証を実装:

typescript
// src/utils/validation.ts
import { useDictStore } from '@/stores/dict'

export const createDictValidator = (dictType: string) => {
  return (value: any) => {
    const dictStore = useDictStore()
    const dictOptions = dictStore.getDictByType(dictType)
    const validValues = dictOptions.map(item => item.value)

    if (!validValues.includes(value)) {
      return new Error(`辞書タイプの無効な値: ${dictType}`)
    }
    return true
  }
}

// フォーム検証での使用
const rules = {
  status: [
    { required: true, message: 'ステータスは必須です' },
    { validator: createDictValidator('product_status'), trigger: 'change' },
  ],
}

パフォーマンス最適化

遅延ローディング辞書

オンデマンドで辞書をロードして初期ロードパフォーマンスを向上:

typescript
// src/hooks/useLazyDict.ts
import { ref } from 'vue'
import { useDictStore } from '@/stores/dict'

export function useLazyDict(dictType: string) {
  const dictStore = useDictStore()
  const loading = ref(false)
  const loaded = ref(false)

  const loadDict = async () => {
    if (loaded.value || dictStore.hasDictType(dictType)) {
      return
    }

    loading.value = true
    try {
      await dictStore.loadDictData(dictType)
      loaded.value = true
    } finally {
      loading.value = false
    }
  }

  return {
    loading,
    loaded,
    loadDict,
  }
}

辞書事前ローディング

アプリケーション初期化時に一般的に使用される辞書を事前ロード:

typescript
// src/utils/dictPreloader.ts
import { useDictStore } from '@/stores/dict'

export const preloadCommonDicts = async () => {
  const dictStore = useDictStore()
  const commonDictTypes = ['product_status', 'product_category', 'user_status', 'order_status']

  await Promise.allSettled(commonDictTypes.map(type => dictStore.loadDictData(type)))
}

エラーハンドリングとデバッグ

辞書エラーモニタリング

辞書ローディングエラーを監視し、フォールバックメカニズムを提供:

typescript
// src/utils/dictErrorHandler.ts
export class DictErrorHandler {
  static handleLoadError(error: Error, dictType: string) {
    console.error(`辞書のロードに失敗しました: ${dictType}`, error)

    // エラートラッキングサービスに送信
    if (import.meta.env.PROD) {
      // 監視サービスに送信
    }

    // フォールバックデータを提供
    return this.getFallbackDict(dictType)
  }

  static getFallbackDict(dictType: string) {
    const fallbackDicts = {
      product_status: [
        { label: '有効', value: 'active' },
        { label: '無効', value: 'inactive' },
      ],
      // その他のフォールバック辞書を追加
    }

    return fallbackDicts[dictType] || []
  }
}

デバッグツール

辞書開発用のデバッグツールを追加:

typescript
// src/utils/dictDebug.ts
export const dictDebug = {
  logDictState: (dictType: string) => {
    const dictStore = useDictStore()
    console.log(`辞書 [${dictType}]:`, dictStore.getDictByType(dictType))
  },

  monitorDictChanges: (dictType: string) => {
    const dictStore = useDictStore()
    watch(
      () => dictStore.getDictByType(dictType),
      (newDict, oldDict) => {
        console.log(`辞書 [${dictType}] が変更されました:`, { old: oldDict, new: newDict })
      }
    )
  },
}

この包括的な辞書管理システムは、VJSP Vue3フレームワークで列挙データを処理するための堅牢な基盤を提供し、すべてのビジネスモジュールでデータの一貫性、タイプセーフ、最適なパフォーマンスを確保します。