03创建Vue项目并实现全球化

03创建Vue项目并实现全球化

创建Vue项目

vue create client-vue

使用Vuex作为状态管理,用于保存从后台获取的应用程序配置ApplicationConfigurationDto,该对象包含本地化的所有数据。

安装Vue-Cookies

npm install vue-cookies --force

vue-cookies用于管理cookie,用户选择的语言类型存放在cookie中。

main.ts中添加以下代码:

import Cookies from 'vue-cookies'
  .use(Cookies)

安装nswag

npm install nswag --force

配置nswag

使用nswag生成客户端,在package.json文件中添加以下代码

  "scripts": {
    "generate-client": "nswag openapi2tsclient /input:https://localhost:7027/swagger/v1/swagger.json /Template:Fetch /UseGetBaseUrlMethod:true /GenerateClientClasses:true /operationGenerationMode:MultipleClientsFromPathSegments /useTransformOptionsMethod:true /useTransformResultMethod:true /clientBaseClass:ClientBase /configurationClass:IConfig /ImportRequiredTypes:true /ExtensionCode:\"import { ClientBase, IConfig } from './clientBase'\" /output:src/api-client/client.ts"
  },

/Key:value 为命令参数 详情请见

请求参数加工/响应数据加工

openapi2tsclient:从Swagger/OpenAPI规范生成TypeScript客户端代码。

input: 数据或JSON数据本身的文件路径或URL。

Template: 异步处理的类型('JQueryCallbacks', 'JQueryPromises', 'AngularJS', 'Angular', 'Fetch', 'Aurelia')

UseGetBaseUrlMethod:指定是否使用基类中的'getBaseUrl(defaultUrl: string)'方法(默认值:false)

GenerateClientClasses:指定是否生成客户端类。

operationGenerationMode:操作生成方式('SingleClientFromOperationId' or 'MultipleClientsFromPathSegments').")]

clientBaseClass客户端代码的基类

configurationClass客户端代码的配置类

ImportRequiredTypes:指定是否应该导入所需的类型(默认值:true)。

ExtensionCode生成的客户端TS文件中的扩展代码,参数为字符串或文件路径

useTransformOptionsMethod启用请求参数转换方法

useTransformResultMethod启用响应结果转换方法

output:输出文件路径(可选)。

nswag 为node_modules\.bin中的可执行程序,如果没有配置node_modules文件夹的环境变量,则这里需要用node_modules/.bin/替代nswag

执行命令

npm run generate-client

创建ClientBase.ts

api-client文件夹中创建ClientBase.ts文件,添加以下代码

/**
 * Configuration class needed in base class.
 * The config is provided to the API client at initialization time.
 * API clients inherit from #ClientBase and provide the config.
 */
import packageConfig from '../../package.json'
import Cookies from 'vue-cookies'

export class IConfig {
  public static readonly acceptLanguageKey = 'acceptLanguage'
  /**
   * Returns a valid value for the Authorization header.
   * Used to dynamically inject the current auth header.
   */
  public getAuthorization (): string {
    return ''
    // return 'the-authentication-token'
  }

  // 从cookie中获取当前语言
  public getAcceptLanguage (): string {
    const cookies: any = Cookies
    const acceptLanguage = cookies.get(IConfig.acceptLanguageKey)
    return acceptLanguage || 'zh-Hans'
  }
}

export class ClientBase {
  private readonly config: IConfig;

  protected constructor (config: IConfig) {
    this.config = config
  }

  // 请求参数转换方法
  protected transformOptions (options: RequestInit): Promise<RequestInit> {
    options.headers = {
      ...options.headers,
      Authorization: this.config.getAuthorization(),
      myHeader: 'myValue',
      'accept-language': this.config.getAcceptLanguage() || '' // 在请求头中添加当前语言
    }

    // 设置跨域请求发送cookie
    options.credentials = 'include'
    // 指示请求将使用CORS
    options.mode = 'cors'
    options.cache = 'default'
    console.log('transformOptions', 'options:', options)
    return Promise.resolve(options)
  }

  // 响应结果转换方法
  protected transformResult (url: string, response: Response, processor: (response: Response) => Promise<any>): Promise<any> {
    console.log('transformResult', 'url:', url, response)
    if (response === null) return Promise.resolve()
    // 判断响应头内容类型为 text/plain 则直接返回响应文本
    const contentType = response.headers.get('content-type')
    if (contentType != null && contentType.indexOf('text/plain') >= 0) {
      return response.text()
    }

    // 调用回调函数
    return processor(response)
  }

  // 基类中的 getBaseUrl 用于 client 类拼接API请求地址
  protected getBaseUrl (defaultUrl: string, baseUrl?: string): string {
    if (baseUrl && baseUrl.length > 0) return baseUrl
    if (defaultUrl && defaultUrl.length > 0) return defaultUrl
    // 从package.json文件中获取baseUrl
    return packageConfig.baseUrl
  }
}

.json配置文件读取配置信息必须在shims-vue.ts文件中添加以下配置

declare module '*.json' {
  const value: any;
  export default value;
}

实现状态管理

store文件夹中添加文件夹Modules\ApplicationConfiguration。在store\Modules\ApplicationConfiguration中添加文件interfaces.ts,添加以下代码

import { ApplicationConfigurationDto } from '@/api-client/client'

export default interface Configuration{
  applicationConfiguration: ApplicationConfigurationDto // 应用程序配置数据
}

store\Modules\ApplicationConfiguration中添加文件index.ts,添加以下代码

import { AbpClient, ApplicationConfigurationDto } from '@/api-client/client'
import { IConfig } from '@/api-client/clientBase'
import RootStateTypes from '@/store/interfaces'
import { Module } from 'vuex'
import Configuration from './interfaces'

const ConfigurationModule: Module<Configuration, RootStateTypes> = {
  namespaced: true,
  state: {
    applicationConfiguration: new ApplicationConfigurationDto()
  },
  getters: {
    getLocalization (state) {
      return state.applicationConfiguration.localization
    },
    l: (state) => (key:string) => { // 全球化的方法,通过该方法显示各国对应的语言
      const localization = state.applicationConfiguration.localization
      if (localization?.values && localization?.defaultResourceName) {
        return localization?.values[localization.defaultResourceName][key]
      }
      return key
    }
  },
  mutations: {
    Set_ApplicationConfiguration (state, data: ApplicationConfigurationDto) {
      state.applicationConfiguration = data
    }
  },
  actions: {
    // 初始化应用程序配置
    initApplicationConfiguration (store): Promise<ApplicationConfigurationDto> {
      if (store.state.applicationConfiguration.timing) {
        return Promise.resolve(store.state.applicationConfiguration)
      }

      const client = new AbpClient(new IConfig())
      // 获取应用程序配置,并更新状态
      return client.applicationConfiguration()
        .then(result => {
          this.commit('ConfigurationModule/Set_ApplicationConfiguration', result)

          return Promise.resolve(store.state.applicationConfiguration)
        })
    },
    // 重置应用程序配置
    resetApplicationConfiguration (store): Promise<ApplicationConfigurationDto> {
      store.state.applicationConfiguration.timing = undefined
      return this.dispatch('ConfigurationModule/initApplicationConfiguration')
    }
  }
}

export default ConfigurationModule

store\interfaces.ts文件的代码给为:

import Configuration from './Modules/ApplicationConfiguration/interfaces'

// 定义根状态类型
export default interface RootStateTypes {
  isLogin: boolean
}

// 扩展更状态类型
export interface AllStateTypes extends RootStateTypes {
  // 应用程序配置
  ConfigurationModule: Configuration
  // other state type
}

修改store\index.ts文件的代码为:

import { InjectionKey } from 'vue'
import { createStore, Store, useStore as baseUseStore } from 'vuex'
import RootStateTypes, { AllStateTypes } from './interfaces'
import ConfigurationModule from './Modules/ApplicationConfiguration'

export default createStore<RootStateTypes>({
  state: {
    isLogin: false
  },
  getters: {},
  mutations: {
    //
  },
  actions: {},
  modules: {
    ConfigurationModule
  }
})

export const key: InjectionKey<Store<RootStateTypes>> = Symbol('CA3227F5-D73C-4E65-AA3F-BA781CA32231')

export function useStore<T = AllStateTypes> (): Store<T> {
  return baseUseStore<T>(key)
}

vuex模块(Module)

TypeScript 支持

main.ts中添加以下代码:

import store, { key } from '@/store/index'
  .use(store, key)

main.ts完整代码:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store, { key } from '@/store/index'
import Cookies from 'vue-cookies'

createApp(App)
  .use(store, key)
  .use(router)
  .use(Cookies)
  .mount('#app')

App.vue里初始化应用程序配置,添加以下代码:

<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from './store'

export default defineComponent({
  mounted () {
    const store = useStore()
    store.dispatch('ConfigurationModule/initApplicationConfiguration')
  }
})
</script>

修改AboutView.vue文件的代码为:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <div>
      {{ l('LongWelcomeMessage') }} <!--显示对应的语言信息 -->
    </div>
  </div>
</template>

<script lang="ts">

import { defineComponent } from 'vue'
import { mapGetters } from 'vuex'

export default defineComponent({
  components: {
    //
  },
  data () {
    return {
    }
  },
  beforeMount () {
    //
  },
  mounted () {
    //
  },
  computed: {
    // 从状态管理Vuex中映射Get方法到计算属性l中
    // 对应的是 `store\Modules\ApplicationConfiguration\index.ts`中Getters中的 l 
    ...mapGetters({
      l: 'ConfigurationModule/l'
    })
  },
  methods: {
    //
  }
})

</script>

到此Vue中实现全球化初步完成,还缺语言的切换,现在是默认的中文。

语言选择

components\selects文件夹中添加文件LanguageSelect.vue,添加以下代码:

<template>
  <select
      @change="change">
    <option
      v-for="item in options"
      :key="item.cultureName"
      :value="item.cultureName"
      :selected="isSelected(item)"
      >
        {{ item.displayName }}
    </option>
  </select>
</template>

<script lang="ts">
import { LanguageInfo } from '@/api-client/client'
import { IConfig } from '@/api-client/clientBase'
import { useStore } from '@/store'
import { defineComponent } from 'vue'

export default defineComponent({
  setup () {
    //
  },
  data () {
    return {
      data: this.value
    }
  },
  emits: ['onchange'],
  watch: {
    //
  },
  computed: {
    store () {
      return useStore()
    },
    // 当前语言
    currentCulture () {
      return this.store.state.ConfigurationModule.applicationConfiguration.localization?.currentCulture?.cultureName
    },
    // 支持的语言项
    options () {
      return this.store.state.ConfigurationModule.applicationConfiguration.localization?.languages || new Array<LanguageInfo>()
    },
    isSelected () {
      return (item: LanguageInfo) => {
        return item.cultureName === this.currentCulture
      }
    }
  },
  mounted () {
    //
  },
  methods: {
    // 当选项更改时,将更改后的值保存进cookie中,并重置应用程序配置
    change (event: any) {
      console.log(event, this.store)
      this.data = event.target.value
      // 将选择的语言保存进cookie中
      this.$cookies.set(IConfig.acceptLanguageKey, this.data)
      // 状态管理重置应用程序配置
      this.store.dispatch('ConfigurationModule/resetApplicationConfiguration')
      this.$emit('onchange', this.data)
    }
  }
})
</script>

LanguageSelect.vue组件添加进AboutView.vue,就可自由切换想显示的语言了。

如此全球化实现完成

posted @ 2022-12-30 18:28  $("#阿飞")  阅读(88)  评论(2编辑  收藏  举报