王家程 пре 10 месеци
родитељ
комит
eec340f309

+ 5 - 1
.vscode/settings.json

@@ -1,3 +1,7 @@
 {
 {
-  "nuxt.isNuxtApp": false
+  "nuxt.isNuxtApp": false,
+  "i18n-ally.localesPaths": [
+    "src/locale",
+    "src/locale/lang"
+  ]
 }
 }

+ 15 - 3
README.md

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-05-27 17:21:43
  * @Date: 2024-05-27 17:21:43
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-05-28 17:59:29
+ * @LastEditTime: 2024-07-11 11:27:30
  * @Description:
  * @Description:
 -->
 -->
 
 
@@ -12,16 +12,28 @@ hbuilderx 创建模板工程 + 第三方依赖支持。
 
 
 使用 vscode 编辑,提交代码,hbuilderx 运行和构建。
 使用 vscode 编辑,提交代码,hbuilderx 运行和构建。
 
 
+可以使用 `dev:h5` 模式来加快开发调试进度,但最终还需要以手机模拟器和真机的运行效果为准。
+
 ### 名词解释
 ### 名词解释
 
 
 - node_modules: 第三方依赖库
 - node_modules: 第三方依赖库
 - uni_modules:uni-app 插件市场依赖
 - uni_modules:uni-app 插件市场依赖
 
 
+### 运行
+
+> nodejs >= 18,如 v18.14.2
+> pnpm >= 9,如 v9.1.4
+
+```bash
+pnpm install
+pnpm dev:h5
+```
+
 ### husky 配置
 ### husky 配置
 
 
-安装 husky
+> 本项目已经配置好完整的husky及相关校验工具,此处用于记录
 
 
-> nodejs >= 18
+安装 husky
 
 
 ```bash
 ```bash
 pnpm add husky@8.0.3 -D
 pnpm add husky@8.0.3 -D

+ 1 - 10
build/vite-plugin-uni-provider.ts

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-07-10 11:21:52
  * @Date: 2024-07-10 11:21:52
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-10 11:23:48
+ * @LastEditTime: 2024-07-11 20:20:48
  * @Description:
  * @Description:
  */
  */
 import path from 'path'
 import path from 'path'
@@ -23,8 +23,6 @@ export default function (options: Partial<Options> = {}) {
   let {
   let {
     pagesRE = /src[\/\\]pages[\/\\]((?!.+(component(s)?|static).+).)*\.vue$/,
     pagesRE = /src[\/\\]pages[\/\\]((?!.+(component(s)?|static).+).)*\.vue$/,
     name = 'sys',
     name = 'sys',
-    configFile = 'vite.config.js',
-    pagesBasePath = 'src/pages',
     pluginName = 'uni-provider',
     pluginName = 'uni-provider',
     DEBUG = process.env.DEBUG,
     DEBUG = process.env.DEBUG,
   } = options
   } = options
@@ -56,17 +54,10 @@ export default function (options: Partial<Options> = {}) {
     },
     },
   }
   }
 
 
-  function normalizePagePath(file) {
-    return normallize(path.relative(pagesBasePath, file))
-  }
-
   function normalizePagePathFromBase(file) {
   function normalizePagePathFromBase(file) {
     return normallize(path.relative(process.cwd(), file))
     return normallize(path.relative(process.cwd(), file))
   }
   }
 
 
-  function log(...args) {
-    console.log(c.dim(new Date().toLocaleTimeString()), c.bold(c.red(`[${pluginName}]`)), ...args)
-  }
   function debug(...args) {
   function debug(...args) {
     DEBUG &&
     DEBUG &&
       console.log(
       console.log(

+ 2 - 2
src/components/MIcon/index.vue

@@ -2,8 +2,8 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-07-01 10:05:11
  * @Date: 2024-07-01 10:05:11
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-02 15:20:40
- * @Description: 
+ * @LastEditTime: 2024-07-11 11:49:28
+ * @Description: 仅支持 H5
 -->
 -->
 <template>
 <template>
   <svg v-bind="$attrs" aria-hidden="true" class="m-icon">
   <svg v-bind="$attrs" aria-hidden="true" class="m-icon">

+ 2 - 2
src/layouts/default.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-06-25 15:42:58
  * @Date: 2024-06-25 15:42:58
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-09 17:44:34
+ * @LastEditTime: 2024-07-11 09:30:19
  * @Description: 
  * @Description: 
 -->
 -->
 <template>
 <template>
@@ -24,6 +24,6 @@
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
   .default-layout {
   .default-layout {
-    @apply relative flex-1 h-full overflow-y-auto color-text-1;
+    @apply relative flex flex-col flex-1 h-full overflow-y-auto color-text-1;
   }
   }
 </style>
 </style>

+ 4 - 0
src/locale/en.json

@@ -0,0 +1,4 @@
+{
+  "index.custom.icon": "Custom Icon",
+  "index.lang.params": "hello,{appName}"
+}

+ 79 - 0
src/locale/index.ts

@@ -0,0 +1,79 @@
+/*
+ * @Author: wjc
+ * @Date: 2024-07-11 20:29:20
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-12 10:13:36
+ * @Description:
+ */
+import { createI18n } from 'vue-i18n'
+
+import en from './en.json' // 英文
+import zhHans from './zh-Hans.json' // 简体中文
+
+const messages = {
+  en,
+  'zh-Hans': zhHans,
+}
+
+export type LangTypes = keyof typeof messages
+
+const i18n = createI18n({
+  locale: uni.getLocale(), // 获取已设置的语言
+  messages,
+})
+
+console.log(uni.getLocale())
+console.log(i18n.global.locale)
+
+/**
+ * 可以拿到原始的语言模板,非 vue 文件使用这个方法,
+ * @param { string } key 多语言的key,eg: "app.name"
+ * @returns {string} 返回原始的多语言模板,eg: "{heavy}KG"
+ */
+export const getTemplateByKey = (key: string) => {
+  if (!key) {
+    console.error(`[i18n] Function getTemplateByKey(), key param is required`)
+    return ''
+  }
+  const locale = uni.getLocale()
+  console.log('locale:', locale)
+
+  const message = messages[locale] // 拿到某个多语言的所有模板(是一个对象)
+  if (Object.keys(message).includes(key)) {
+    return message[key]
+  }
+  console.error(`[i18n] Function getTemplateByKey(), key param ${key} is not existed.`)
+  return ''
+}
+
+/**
+ * 处理非 h5 端多语言传参无效问题
+ * formatI18n('我是{name},身高{detail.height},体重{detail.weight}',{name:'张三',detail:{height:178,weight:'75kg'}})
+ * 暂不支持数组
+ * @param template 多语言模板字符串,eg: `我是{name}`
+ * @param {Object|undefined} data 需要传递的数据对象,里面的key与多语言字符串对应,eg: `{name:'菲鸽'}`
+ * @returns
+ */
+export function formatI18n(template: string, data?: any) {
+  return template.replace(/\{([^}]+)\}/g, function (match, key: string) {
+    const arr = key.trim().split('.')
+    let result = data
+    while (arr.length) {
+      const first = arr.shift()
+      result = result[first]
+    }
+    return result
+  })
+}
+
+export function $$t(key, data?) {
+  return formatI18n(getTemplateByKey(key), data)
+}
+
+export function setupI18n(app) {
+  app.use(i18n)
+  // 注册全局的多语言函数
+  app.config.globalProperties.$$t = $$t
+}
+
+export default i18n

+ 4 - 0
src/locale/zh-Hans.json

@@ -0,0 +1,4 @@
+{
+  "index.custom.icon": "自定义图标",
+  "index.lang.params": "你好,{appName}"
+}

+ 4 - 1
src/main.ts

@@ -2,12 +2,13 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-05-27 11:49:45
  * @Date: 2024-05-27 11:49:45
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-02 10:27:33
+ * @LastEditTime: 2024-07-12 10:01:58
  * @Description:
  * @Description:
  */
  */
 import { createSSRApp } from 'vue'
 import { createSSRApp } from 'vue'
 import * as Pinia from 'pinia'
 import * as Pinia from 'pinia'
 import uviewPlus from 'uview-plus'
 import uviewPlus from 'uview-plus'
+import { setupI18n } from '@/locale'
 import 'virtual:uno.css'
 import 'virtual:uno.css'
 import 'virtual:svg-icons-register'
 import 'virtual:svg-icons-register'
 
 
@@ -22,6 +23,8 @@ export function createApp() {
   const app = createSSRApp(App)
   const app = createSSRApp(App)
 
 
   app.use(uviewPlus)
   app.use(uviewPlus)
+
+  setupI18n(app)
   setupStores(app)
   setupStores(app)
   setupInterceptors(app)
   setupInterceptors(app)
 
 

+ 9 - 0
src/models/appTypes.ts

@@ -1,4 +1,13 @@
+import { LangTypes } from '@/locale'
+
+export interface Lang {
+  label: string
+  value: LangTypes
+}
+
 export interface AppState {
 export interface AppState {
   selectedTabbar: number
   selectedTabbar: number
   isDark: boolean
   isDark: boolean
+  lang: string
+  langs: Lang[]
 }
 }

+ 6 - 3
src/pages/index/index.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2019-08-22 19:41:20
  * @Date: 2019-08-22 19:41:20
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-10 17:10:32
+ * @LastEditTime: 2024-07-12 10:26:56
  * @Description: 
  * @Description: 
 -->
 -->
 <template>
 <template>
@@ -10,7 +10,9 @@
   <view class="content">
   <view class="content">
     <image class="logo" src="/static/logo.png"></image>
     <image class="logo" src="/static/logo.png"></image>
     <view class="text-area">
     <view class="text-area">
-      <text class="title">{{ title }}</text>
+      <view class="title">多语言文本:{{ $$t('index.custom.icon') }}</view>
+      <view class="title">多语言传参:{{ $$t('index.lang.params', { appName }) }}</view>
+      <view>多语言2: {{ $t('index.custom.icon') }}</view>
     </view>
     </view>
     <view :class="`i-custom-${icon}`" class="wh-32px text-32px"></view>
     <view :class="`i-custom-${icon}`" class="wh-32px text-32px"></view>
     <view>
     <view>
@@ -27,8 +29,8 @@
 <script setup lang="ts">
 <script setup lang="ts">
   import { onLaunch, onShow } from '@dcloudio/uni-app'
   import { onLaunch, onShow } from '@dcloudio/uni-app'
 
 
-  const title = ref('自定义图标')
   const icon = ref('anl')
   const icon = ref('anl')
+  const appName = ref('绘管家')
 
 
   const onTest = () => {
   const onTest = () => {
     uni.navigateTo({
     uni.navigateTo({
@@ -37,6 +39,7 @@
   }
   }
 
 
   onLaunch(() => {
   onLaunch(() => {
+    uni.setLocale('en')
     uni.hideTabBar()
     uni.hideTabBar()
   })
   })
   onShow(() => {
   onShow(() => {

+ 28 - 4
src/pages/mine/index.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-06-17 16:02:59
  * @Date: 2024-06-17 16:02:59
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-10 16:59:31
+ * @LastEditTime: 2024-07-12 09:52:48
  * @Description: 
  * @Description: 
 -->
 -->
 <template>
 <template>
@@ -13,6 +13,21 @@
     <view>深色模式</view>
     <view>深色模式</view>
     <up-switch v-model="appStore.isDark"></up-switch>
     <up-switch v-model="appStore.isDark"></up-switch>
   </MCard>
   </MCard>
+  <MCard :space="true" direction="horizontal" justify="between">
+    <view>语言</view>
+    <view class="flex-1 text-right" @click="() => (showLang = true)">
+      <view>
+        {{ appStore.langs.find((item) => item.value === appStore.lang).label }}
+      </view>
+      <up-picker
+        :show="showLang"
+        :columns="langs"
+        key-name="label"
+        @cancel="cancel"
+        @confirm="confirm"
+      ></up-picker>
+    </view>
+  </MCard>
   <up-button type="primary" class="btn-primary" @click="handleConfirmLogout">退出登录</up-button>
   <up-button type="primary" class="btn-primary" @click="handleConfirmLogout">退出登录</up-button>
   <up-modal :show="logoutShow">
   <up-modal :show="logoutShow">
     <view class="text-18px py-24px color-text-1">确认退出登录?</view>
     <view class="text-18px py-24px color-text-1">确认退出登录?</view>
@@ -35,19 +50,28 @@
 
 
   const userStore = useUserStore()
   const userStore = useUserStore()
   const appStore = useAppStore()
   const appStore = useAppStore()
-  const logoutShow = ref(false)
 
 
+  const showLang = ref(false)
+  const langs = ref([appStore.langs])
+  const confirm = (e) => {
+    appStore.setLang(e.value[0].value)
+    showLang.value = false
+    console.log('lang-', uni.getLocale())
+  }
+  const cancel = (e) => {
+    showLang.value = false
+  }
+
+  const logoutShow = ref(false)
   const handleCancel = () => {
   const handleCancel = () => {
     logoutShow.value = false
     logoutShow.value = false
   }
   }
   const handleConfirmLogout = () => {
   const handleConfirmLogout = () => {
     logoutShow.value = true
     logoutShow.value = true
   }
   }
-
   const handleConfirm = () => {
   const handleConfirm = () => {
     onLogout()
     onLogout()
   }
   }
-
   const onLogout = () => {
   const onLogout = () => {
     userStore.logoutAction().then((res) => {
     userStore.logoutAction().then((res) => {
       if (res) {
       if (res) {

+ 18 - 1
src/stores/modules/appStore.ts

@@ -2,11 +2,12 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-06-05 11:22:45
  * @Date: 2024-06-05 11:22:45
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-05 17:16:15
+ * @LastEditTime: 2024-07-12 09:44:20
  * @Description:
  * @Description:
  */
  */
 import { defineStore } from 'pinia'
 import { defineStore } from 'pinia'
 
 
+import i18n, { LangTypes } from '@/locale'
 import type { AppState } from '@/models/appTypes'
 import type { AppState } from '@/models/appTypes'
 
 
 export const useAppStore = defineStore('app', {
 export const useAppStore = defineStore('app', {
@@ -14,6 +15,17 @@ export const useAppStore = defineStore('app', {
     return {
     return {
       selectedTabbar: 0,
       selectedTabbar: 0,
       isDark: false,
       isDark: false,
+      lang: 'zh-Hans',
+      langs: [
+        {
+          label: '中文',
+          value: 'zh-Hans',
+        },
+        {
+          label: 'English',
+          value: 'en',
+        },
+      ],
     }
     }
   },
   },
   persist: true,
   persist: true,
@@ -21,5 +33,10 @@ export const useAppStore = defineStore('app', {
     setTabbar(val: number) {
     setTabbar(val: number) {
       this.selectedTabbar = val
       this.selectedTabbar = val
     },
     },
+    setLang(val: LangTypes) {
+      uni.setLocale(val)
+      i18n.global.locale = val
+      this.lang = val
+    },
   },
   },
 })
 })

+ 1 - 1
types/global.d.ts

@@ -2,6 +2,6 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-05-31 09:26:09
  * @Date: 2024-05-31 09:26:09
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-05 16:00:04
+ * @LastEditTime: 2024-07-12 10:17:37
  * @Description:
  * @Description:
  */
  */

+ 5 - 2
types/shims-uni.d.ts

@@ -2,13 +2,16 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-06-05 15:57:41
  * @Date: 2024-06-05 15:57:41
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-05 17:42:44
+ * @LastEditTime: 2024-07-12 10:33:30
  * @Description:
  * @Description:
  */
  */
 /// <reference types='@dcloudio/types' />
 /// <reference types='@dcloudio/types' />
 import 'vue'
 import 'vue'
+import { formatI18n } from '@/locale'
 
 
 declare module '@vue/runtime-core' {
 declare module '@vue/runtime-core' {
   type Hooks = App.AppInstance & Page.PageInstance
   type Hooks = App.AppInstance & Page.PageInstance
-  interface ComponentCustomOptions extends Hooks {}
+  interface ComponentCustomOptions extends Hooks {
+    $$t: typeof formatI18n
+  }
 }
 }

+ 2 - 1
vite.config.ts

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Author: wjc
  * @Date: 2024-05-27 10:17:11
  * @Date: 2024-05-27 10:17:11
  * @LastEditors: wjc
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-10 16:37:37
+ * @LastEditTime: 2024-07-11 11:49:11
  * @Description:
  * @Description:
  */
  */
 import path from 'node:path'
 import path from 'node:path'
@@ -49,6 +49,7 @@ export default defineConfig(({ mode }) => {
       //自动注册页面全局组件
       //自动注册页面全局组件
       UniProvider(),
       UniProvider(),
       UnoCSS(),
       UnoCSS(),
+      // 仅支持 H5
       createSvgIconsPlugin({
       createSvgIconsPlugin({
         iconDirs: [pathResolve('src/static/icons')],
         iconDirs: [pathResolve('src/static/icons')],
         symbolId: 'icon-[dir]-[name]',
         symbolId: 'icon-[dir]-[name]',