Skip to content

VJSP Vue3 フレームワーク メイン文書

プロジェクト概要

VJSP Vue3 Framework は、Vue 3.5 + TypeScript 5.7 + Element Plus に基づくエンタープライズレベルのミドル・バックオフィス向けフロントエンドソリューションです。このフレームワークは、開発者がモダンなWebアプリケーションを迅速に構築するための完全な開発ツールとコンポーネントライブラリを提供します。

技術スタックアーキテクチャ

実際のプロジェクト設定に基づき、フレームワークは以下のコア技術スタックを採用しています:

  • フロントエンドフレームワーク: Vue 3.5.13 + Composition API
  • ビルドツール: Vite 6.0.7
  • 型システム: TypeScript 5.7.3
  • UIコンポーネントライブラリ: Element Plus 2.11.7
  • 状態管理: Pinia 3.0.0 + 永続化プラグイン
  • ルーティング管理: Vue Router 4.5.0
  • 国際化: Vue I18n 11.0.1
  • HTTPクライアント: Axios 1.12.0

プロジェクト構造

プロジェクトはモジュール化アーキテクチャ設計を採用し、主なディレクトリ構造は以下の通りです:

shell
src/
├── api/                 # APIインターフェース層
   ├── login/          # ログイン関連API
   ├── user/           # ユーザー管理API
   └── system/         # システム管理API
├── components/         # コンポーネント層
   ├── SvgIcon/        # SVGアイコンコンポーネント
   └── dict/           # 辞書コンポーネント
├── stores/             # 状態管理
   ├── app.ts          # アプリケーション状態
   ├── user.ts         # ユーザー状態
   └── modules/        # モジュール化状態
├── utils/              # ユーティリティ関数
   ├── cache.ts        # キャッシュ管理
   ├── encryption.ts   # 暗号化ツール
   └── tree.ts         # ツリーデータ処理
├── views/              # ページコンポーネント
   ├── system/         # システム管理ページ
   └── dashboard/      # ダッシュボードページ
└── main.ts            # アプリケーションエントリー

コア機能メカニズム

1. アプリケーション起動メカニズム

フレームワークは非同期起動メカニズムを採用し、すべての依存関係が正しく初期化されることを保証します:

typescript
// src/main.ts の起動プロセス
async function bootstrap() {
  const app = createApp(App)

  // 1. Element Plus アイコンの登録
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }

  // 2. Pinia 状態管理の初期化
  app.use(createPinia())

  // 3. 国際化の初期化
  await setupI18n(app)

  // 4. Element Plus の設定
  app.use(ElementPlus, {
    locale: currentLocale.elLocale,
  })

  // 5. ルーターの設定(動的ルートの読み込みを待機)
  await setupRouter(app)

  app.mount('#app')
}

2. 権限管理メカニズム

フレームワークはロールベースの権限制御システムを実装しています:

  • メニュー権限: 動的ルートフィルタリング、権限のあるメニューのみ表示
  • ボタン権限: カスタムディレクティブによるボタン表示制御
  • データ権限: ユーザーロールに基づくデータアクセス範囲のフィルタリング

権限検証はルートガードとカスタムディレクティブを通じて実装されます:

typescript
// ルートガード権限チェック
router.beforeEach(async (to, from, next) => {
  // ユーザーログイン状態のチェック
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login')
    return
  }

  // ルート権限のチェック
  if (to.meta.permission && !hasPermission(to.meta.permission)) {
    next('/403')
    return
  }

  next()
})

3. 国際化メカニズム

フレームワークは多言語切り替えをサポートし、vue-i18n に基づいて実装されています:

  • 言語パッケージ管理: モジュールごとに言語ファイルを整理
  • 動的切り替え: ランタイムでの言語切り替えをサポート
  • コンポーネント統合: Element Plus コンポーネントが現在の言語に自動適応

言語設定は独立したプラグインモジュールを通じて管理されます:

typescript
// src/plugins/vueI18n の設定
export async function setupI18n(app: App) {
  const options = await createI18nOptions()
  const i18n = createI18n(options)
  app.use(i18n)
}

4. 状態管理メカニズム

Pinia を使用した状態管理で、モジュール化組織と永続化をサポート:

  • モジュール化設計: 機能モジュールごとに Store を分割
  • 型安全: 完全な TypeScript 型サポート
  • 永続化: 重要な状態をローカルストレージに自動永続化

状態永続化は pinia-plugin-persistedstate を通じて実装されます:

typescript
// Store 設定例
const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: {},
    permissions: [],
  }),

  persist: {
    key: 'user-store',
    storage: localStorage,
  },
})

5. ビルド最適化メカニズム

Vite ビルドツールは以下の最適化メカニズムを提供します:

  • 依存関係の事前ビルド: サードパーティ依存関係の事前ビルドによる読み込み速度向上
  • コード分割: ベンダーパッケージとビジネスコードの自動分割
  • ツリーシェイキング: 未使用コードの自動削除
  • 圧縮最適化: 本番環境での自動圧縮有効化

ビルド設定は vite.config.ts ファイルで管理されます:

typescript
// ビルド最適化設定
export default defineConfig({
  build: {
    // コード分割設定
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue'],
          'element-plus': ['element-plus'],
        },
      },
    },
  },
})

Product モジュール開発例

1. Product モジュール構造の作成

Product モジュールの開発を想定し、プロジェクト統一仕様に従ってディレクトリ構造を作成:

shell
src/
├── api/                 # APIインターフェース層(統一管理)
   └── product/        # Product 関連API
       └── index.ts    # Product API サービス
├── types/               # 型定義(統一管理)
   └── api/            # API 関連型
       └── product.ts  # Product 関連型定義
├── locales/             # 国際化テキスト
   └── module/         # モジュールテキスト
       └── product/    # Product モジュールテキスト
           └── ja-JP.ts
└── views/modules/product/  # Product ページコンポーネント
    ├── components/          # Product 関連コンポーネント(必要に応じて)
   ├── ProductForm.vue  # 製品フォームコンポーネント
   └── ProductTable.vue # 製品テーブルコンポーネント
    └── index.vue           # 製品管理メインページ

2. Product 型の定義

統一型定義ディレクトリに製品関連の TypeScript 型定義を作成:

typescript
// src/types/api/product.ts
export interface Product {
  id: number
  name: string
  price: number
  description: string
  category: string
  status: 'active' | 'inactive'
  createdAt: string
  updatedAt: string
}

export interface ProductForm {
  name: string
  price: number
  description?: string
  category: string
}

export interface ProductFilters {
  category?: string
  status?: string
  search?: string
  page: number
  pageSize: number
}

export interface ProductListResponse {
  list: Product[]
  total: number
}

3. Product API サービスの実装

統一APIディレクトリに製品関連の API インターフェースをカプセル化:

typescript
// src/api/product/index.ts
import request from '@/axios'
import type { Product, ProductForm, ProductFilters, ProductListResponse } from '@/types/api/product'

export class ProductApiService {
  // 製品リストの取得
  static async getProducts(params: ProductFilters) {
    return request.get<ProductListResponse>({
      url: '/api/products',
      params,
    })
  }

  // 製品詳細の取得
  static async getProduct(id: number) {
    return request.get<Product>({
      url: `/api/products/${id}`,
    })
  }

  // 製品の作成
  static async createProduct(data: ProductForm) {
    return request.post<Product>({
      url: '/api/products',
      data,
    })
  }

  // 製品の更新
  static async updateProduct(id: number, data: ProductForm) {
    return request.put<Product>({
      url: `/api/products/${id}`,
      data,
    })
  }

  // 製品の削除
  static async deleteProduct(id: number) {
    return request.delete({
      url: `/api/products/${id}`,
    })
  }
}

export default ProductApiService

4. Product 国際化テキストの作成

統一国際化ディレクトリに製品モジュールのテキストを作成:

typescript
// src/locales/module/product/ja-JP.ts
export default {
  product: {
    title: '製品管理',
    list: '製品一覧',
    name: '製品名',
    category: '製品カテゴリ',
    price: '価格',
    status: 'ステータス',
    createdAt: '作成日時',
    operation: '操作',
    search: '検索',
    reset: 'リセット',
    add: '製品追加',
    edit: '編集',
    delete: '削除',
    export: 'データエクスポート',
    placeholder: {
      name: '製品名を入力してください',
      category: 'カテゴリを選択してください',
    },
    statusText: {
      active: '有効',
      inactive: '無効',
    },
    message: {
      loadFailed: '製品リストの取得に失敗しました',
      deleteSuccess: '削除が成功しました',
      deleteFailed: '削除に失敗しました',
      exportSuccess: 'エクスポート機能開発中',
      exportFailed: 'エクスポートに失敗しました',
      confirmDelete: '製品「{name}」を削除してもよろしいですか?この操作は元に戻せません。',
    },
    categories: {
      electronics: '電子製品',
      home: '家庭用品',
      clothing: '衣料品・アクセサリー',
      food: '食品・飲料',
    },
  },
}

5. Product 管理ページの作成

レスポンシブデザインの製品管理メインページを実装し、デスクトップとモバイルをサポート、国際化、権限制御、データ管理機能を統合:

ページ構造

製品管理ページはカードベースのレイアウトを使用し、以下の主要エリアを含みます:

  1. フィルター条件エリア - 製品名、製品コード、カテゴリ、ステータスなどによる条件フィルタリングをサポート
  2. データ表示エリア - デスクトップ用テーブルビューとモバイル用カードビュー
  3. 操作ボタンエリア - 追加、一括削除、エクスポートなどの機能
  4. フォームダイアログ - 製品追加/編集フォーム
  5. エクスポートダイアログ - データエクスポート設定

スタイルは src\styles\components\tables.less に統合済み、特別な要件がない限り追加設定不要

コア機能特性

  • レスポンシブデザイン: デスクトップとモバイル表示に自動適応
  • 国際化サポート: t('product.xxx') 構文を使用した多言語テキスト呼び出し
  • 権限制御: v-permission ディレクティブによるボタン表示制御
  • データ管理: ページネーション、フィルタリング、ソート、ステータス切り替えをサポート
  • 一括操作: 一括削除とエクスポートをサポート
  • フォーム検証: 完全なフォーム検証ルール

主要コンポーネント構造

vue
<template>
  <div class="product-container query-container">
    <!-- フィルター条件エリア -->
    <el-card class="filter-container">
      <!-- モバイル展開/折りたたみ機能 -->
      <template #header v-if="isMobile">
        <div class="filter-header">
          <span>{{ t('common.filter') }}</span>
          <el-button @click="filterExpanded = !filterExpanded">
            {{ filterExpanded ? t('common.collapse') : t('common.expand') }}
          </el-button>
        </div>
      </template>

      <!-- フィルターフォーム -->
      <el-form :inline="!isMobile" :model="queryParams">
        <el-row>
          <el-col :xs="24" :sm="12" :md="8" :lg="8">
            <el-form-item :label="t('product.productName')">
              <el-input v-model="queryParams.productName" />
            </el-form-item>
          </el-col>
          <!-- その他のフィルター条件 -->
        </el-row>
      </el-form>
    </el-card>

    <!-- データ表示エリア -->
    <el-card class="table-container">
      <template #header>
        <div class="card-header">
          <span v-if="!isMobile">{{ t('product.list') }}</span>
          <div class="header-actions">
            <el-button type="primary" @click="handleAdd">
              {{ t('common.add') }}
            </el-button>
            <!-- その他の操作ボタン -->
          </div>
        </div>
      </template>

      <!-- デスクトップテーブルビュー -->
      <div v-if="!isMobile" class="desktop-view">
        <el-table :data="productList" v-loading="loading">
          <!-- テーブル列定義 -->
        </el-table>
      </div>

      <!-- モバイルカードビュー -->
      <div v-else class="mobile-view">
        <div v-loading="loading" class="mobile-cards-container">
          <!-- カードリスト -->
        </div>
      </div>

      <!-- ページネーションとローディングヒント -->
    </el-card>

    <!-- エクスポートダイアログ -->
    <el-dialog :title="t('product.exportTitle')" v-model="exportVisible">
      <!-- エクスポート設定フォーム -->
    </el-dialog>

    <!-- 製品編集ダイアログ -->
    <el-dialog :title="dialog.title" v-model="dialog.visible">
      <!-- 製品フォーム -->
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage, ElMessageBox } from 'element-plus'
import ProductFormDialog from './components/ProductFormDialog.vue'
import ProductApiService from '@/api/product'
import type { Product, ProductFilters } from '@/types/api/product'

const { t } = useI18n()

// リアクティブデータ
const loading = ref(false)
const productList = ref<Product[]>([])

// 検索フォーム
const searchForm = reactive({
  name: '',
  category: '',
})

// ページネーション設定
const pagination = reactive({
  current: 1,
  size: 10,
  total: 0,
})

// フォームダイアログ設定
const formDialog = reactive({
  visible: false,
  data: null as Product | null,
  mode: 'add' as 'add' | 'edit',
})

// 製品カテゴリオプション(国際化テキストに基づく)
const categories = computed(() => {
  return Object.entries(t('product.categories', {}, { returnObjects: true })).map(
    ([value, label]) => ({
      value,
      label,
    })
  )
})

// 製品リストの読み込み
const loadProductList = async () => {
  loading.value = true

  try {
    const params: ProductFilters = {
      ...searchForm,
      page: pagination.current,
      pageSize: pagination.size,
    }

    const response = await ProductApiService.getProducts(params)
    productList.value = response.data.list
    pagination.total = response.data.total
  } catch (error) {
    ElMessage.error(t('product.message.loadFailed'))
    console.error('製品リスト読み込みエラー:', error)
  } finally {
    loading.value = false
  }
}

// 検索処理
const handleSearch = () => {
  pagination.current = 1
  loadProductList()
}

// 検索リセット
const handleReset = () => {
  Object.assign(searchForm, { name: '', category: '' })
  handleSearch()
}

// ページネーション処理
const handleSizeChange = (size: number) => {
  pagination.size = size
  pagination.current = 1
  loadProductList()
}

const handleCurrentChange = (current: number) => {
  pagination.current = current
  loadProductList()
}

// 製品追加
const handleAdd = () => {
  formDialog.mode = 'add'
  formDialog.data = null
  formDialog.visible = true
}

// 製品編集
const handleEdit = (product: Product) => {
  formDialog.mode = 'edit'
  formDialog.data = product
  formDialog.visible = true
}

// 製品削除
const handleDelete = async (product: Product) => {
  try {
    await ElMessageBox.confirm(
      t('product.message.confirmDelete', { name: product.name }),
      t('product.delete'),
      {
        type: 'warning',
        confirmButtonText: t('common.confirm'),
        cancelButtonText: t('common.cancel'),
      }
    )

    await ProductApiService.deleteProduct(product.id)
    ElMessage.success(t('product.message.deleteSuccess'))
    loadProductList()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(t('product.message.deleteFailed'))
      console.error('製品削除エラー:', error)
    }
  }
}

// データエクスポート
const handleExport = async () => {
  try {
    // エクスポートロジックの実装
    ElMessage.success(t('product.message.exportSuccess'))
  } catch (error) {
    ElMessage.error(t('product.message.exportFailed'))
    console.error('製品データエクスポートエラー:', error)
  }
}

// フォーム送信成功処理
const handleFormSuccess = () => {
  formDialog.visible = false
  loadProductList()
}

// ページ読み込み時のデータ初期化
onMounted(() => {
  loadProductList()
})
</script>

6. Product モジュールルートの設定

ルート設定に製品管理モジュールを追加:

フロントエンド制御モード

typescript
// src/router/modules/product.ts
import type { RouteRecordRaw } from 'vue-router'

const productRoutes: RouteRecordRaw[] = [
  {
    path: '/product',
    name: 'Product',
    component: () => import('@/views/modules/product/index.vue'),
    meta: {
      title: 'product.title', // 国際化キーを使用
      icon: 'shopping-bag',
      requiresAuth: true,
      permissions: ['product:view'],
    },
    children: [
      {
        path: 'list',
        name: 'ProductList',
        component: () => import('@/views/modules/product/index.vue'),
        meta: {
          title: 'product.list', // 国際化キーを使用
          requiresAuth: true,
          permissions: ['product:view'],
        },
      },
    ],
  },
]

export default productRoutes

メインルートファイルでインポート:

typescript
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import productRoutes from './modules/product'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    // ... その他のルート
    ...productRoutes,
    // ... その他のルート
  ],
})

export default router

バックエンド制御モード

事前構築済みのシステム管理 - メニュー管理の下で製品モジュールのルートを設定し、一覧、追加、編集、削除などの操作を含む。

フレームワークのコア利点

1. 開発効率の向上

  • コンポーネントベース開発: Element Plus コンポーネントライブラリに基づく豊富な UI コンポーネント
  • 型安全: 完全な TypeScript サポートによるランタイムエラーの削減
  • ホットリロード: Vite 開発サーバーによるリアルタイムプレビュー
  • コード規約: ESLint + Prettier によるコード品質の確保

2. パフォーマンス最適化

  • ビルド最適化: Vite による高速なコールドスタートとホットアップデート
  • コード分割: ベンダーパッケージの自動分割による読み込みパフォーマンスの最適化
  • キャッシュ戦略: マルチレベルキャッシュメカニズムによるアプリケーション応答速度の向上
  • 遅延読み込み: ルートとコンポーネントの遅延読み込みによる初期バンドルサイズの削減

3. エンタープライズレベルの機能

  • 権限管理: 完全な RBAC 権限制御システム
  • 国際化: グローバルニーズに対応する多言語サポート
  • データ辞書: 統一されたデータ管理メカニズム
  • エラー処理: 完全なエラー捕捉と処理メカニズム

4. 保守性

  • モジュール化アーキテクチャ: 明確なディレクトリ構造とモジュール分割
  • 型定義: 完全なインターフェースと型定義
  • 包括的な文書: 詳細な開発文書と例
  • ツール統合: 開発ツールとの深い統合

まとめ

VJSP Vue3 Framework は、モダンな技術スタックとベストプラクティスに基づく完全なエンタープライズレベルのフロントエンドソリューションを提供します。フレームワークのコア利点は、包括的な権限管理、国際化サポート、パフォーマンス最適化、開発効率の向上にあります。Product モジュールの開発例を通じて、フレームワーク基盤上でビジネス機能モジュールを迅速に構築する方法を示しています。

フレームワークの設計理念は「設定より規約」であり、合理的なデフォルト設定とモジュール化設計を通じて開発の複雑さを軽減し、開発効率を向上させます。同時に、フレームワークは良好な拡張性を維持し、特定のビジネスニーズに応じてカスタマイズと拡張が可能です。