feat:设备授权问题

This commit is contained in:
dzc committed 2026-04-28 20:36:27 +08:00
1 parent 50b7ba4601
commit c9c9c365dd
19 files changed
+196 -138

No files matched your search

+6 -4
View File
@@ -1,5 +1,5 @@
<template>
<view title="支付成功" class="w-full h-full">
<crud-layout title="支付成功" :bg="filePath(SERVICE_IMAGE.blueBg)" ref="crudLayoutRef">
<view class="flex-col-center p-30rpx">
<view class="flex-col-center m-y-50rpx">
<svg t="1742978804614" class="icon" viewBox="0 0 1024 1024" version="1.1"
@@ -31,7 +31,7 @@
<view class="p-40rpx fixed bottom-30rpx left-0 right-0 font-bold">
<tm-button :block="true" @click="goBack">完成</tm-button>
</view>
</view>
</crud-layout>
</template>
<script setup lang="ts">
@@ -39,6 +39,9 @@ import { ref } from "vue"
import { onShow } from "@dcloudio/uni-app"
import { usePayStore } from "@/sheep/stores/pay"
import { filePath } from "@/sheep/utils/file"
import { SERVICE_IMAGE } from "@@/constant/index"
const scanPayData = ref<any>({})
onShow(() => {
@@ -47,7 +50,6 @@ onShow(() => {
})
const goBack = () => {
const url: string = window.location.href.split("h5")[0] + "h5/?launchParam=coopMerchant#/pages/tabbar/order"
window.location.replace(url)
uni.navigateBack()
}
</script>
+3 -3
View File
@@ -35,7 +35,7 @@ const minAppGetphone = async (e) => {
}
try {
uni.showLoading({ title: '登录中...', mask: true })
const { code: loginCode }: any = await uni.login({ provider: 'weixin' });
const { code: loginCode }: any = await uni.login({ provider: "weixin" });
// 获取用户手机号
const { data, code }: any = await miniPlatformApi.autoAdminLogin({
miniPlatformType: await getAppInfo(),
@@ -97,14 +97,14 @@ const affirmConfirm = () => {
<view class="flex-col-center m-t-160rpx">
<view class="size-220rpx bg-white rounded-full flex-row-center">
<image class="size-200rpx rounded-full" src="/static/logo/jgsw.jpeg" mode="aspectFit" />
<image class="size-200rpx rounded-full" :src="filePath(SERVICE_IMAGE.logo)" mode="aspectFit" />
</view>
<view class="m-t-20rpx sub-text-28">浦江县机关事务智慧后勤一体化平台</view>
</view>
<view class="m-t-160rpx">
<button v-if="!state.isAgree" class="bg-#0094ff color-white" @click="doLogin">
手机号一键登录
一键授权
</button>
<button v-else class="bg-#0094ff color-white" open-type="getPhoneNumber" @getphonenumber="minAppGetphone">
手机号一键登录
+1 -1
View File
@@ -19,7 +19,7 @@
</div>
</div>
<view
class="position-absolute bottom-16rpx left-0 right-0 second-text-26 text-center flex items-center justify-center">
class="position-absolute bottom-30rpx left-0 right-0 second-text-26 text-center flex items-center justify-center">
<view>联系热线:</view>
<view @click="doPhone('18367908152')">18367908152</view>
<view class="w-10rpx" />
+7
View File
@@ -326,6 +326,13 @@ const onTabMenu = async (item: any) => {
case "扫一扫":
await appScanCode()
break
case "清除缓存":
uni.showLoading({ title: "清除中...", mask: true })
uni.clearStorageSync()
uni.hideLoading()
uni.showToast({ title: "清除缓存成功", icon: "success" })
uni.navigateTo({ url: "/pages/root/login" })
break
default:
router.jump(item.path)
}
+2 -1
View File
@@ -139,7 +139,8 @@ const toRechargeRecord = () => {
<view class="p-30rpx p-b-40rpx bg-white rounded-20rpx m-t-20rpx shadow-main">
<tm-radio-group direction="column" gap="40" v-model="state.currentPayType">
<tm-radio :disabled="v.disabled" v-for="(v, i) in data.payType" :key="i" :value="v.type"
:label="v.text" />
:label="v.text">
</tm-radio>
</tm-radio-group>
</view>
<view class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!"
+3 -3
View File
@@ -112,15 +112,15 @@ export default {
}),
// 设备认证
verifyDevice: (data: { mobilePhone: string; code: string }) =>
verifyDevice: (data: { mobilePhone: string; code: string, uniqueCode?: string, deviceJson?: string }) =>
request({
url: "/thirdparty/thirdPartyOAth2/verify-device",
url: "/thirdparty/thirdPartyOAth2/mini-app/verify-device",
method: "POST",
data,
}),
// 判断该设备是否被验证
isVerifyDevice: (data: { mobile: string; userId: string }) =>
isVerifyDevice: (data: { mobile: string; userId: string, uniqueCode?: string, deviceJson?: string }) =>
request({
url: "/thirdparty/thirdPartyOAth2/is-verify-device",
method: "POST",
@@ -64,7 +64,7 @@ function doToDashboard() {
.navbar {
position: absolute;
left: 20rpx;
height: 44px;
height: 56rpx;
display: flex;
justify-content: center;
align-items: center;
@@ -72,8 +72,8 @@ function doToDashboard() {
&_center {
display: flex;
align-items: center;
height: 56rpx;
border-radius: 28rpx;
height: 68rpx;
border-radius: 34rpx;
background: #fff;
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
z-index: 100;
@@ -88,7 +88,8 @@ function doToDashboard() {
display: flex;
justify-content: center;
align-items: center;
width: 56rpx;
font-size: 36rpx;
width: 68rpx;
}
}
}
@@ -67,7 +67,7 @@ function doToDashboard() {
<style lang="scss" scoped>
.navbar {
position: relative;
height: 44px;
height: 56rpx;
display: flex;
justify-content: center;
align-items: center;
@@ -77,8 +77,8 @@ function doToDashboard() {
left: 20rpx;
display: flex;
align-items: center;
height: 56rpx;
border-radius: 28rpx;
height: 68rpx;
border-radius: 34rpx;
background: #fff;
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
@@ -92,7 +92,8 @@ function doToDashboard() {
display: flex;
justify-content: center;
align-items: center;
width: 56rpx;
font-size: 36rpx;
width: 68rpx;
}
}
@@ -101,9 +102,9 @@ function doToDashboard() {
right: 20rpx;
background: #fff;
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
border-radius: 28rpx;
width: 56rpx;
height: 56rpx;
border-radius: 34rpx;
width: 68rpx;
height: 68rpx;
display: flex;
justify-content: center;
align-items: center;
@@ -16,6 +16,9 @@ function getHeight() {
return bottom + "px"
}
if (props.type && props.type === "top") {
if (top < 22) {
return "22px"
}
return top + "px"
}
return 22 + "px"
@@ -7,6 +7,7 @@ import { jump } from "@/sheep/router";
import { userApi } from "@@/api/index"
import VerifyDeviceImpModel from "../../verify-device-imp-model.vue"
import { getMiniUniqueCode, getDeviceKey } from "@@/utils/mini-tool"
const tabbarStore = useTabbarStore()
@@ -99,9 +100,11 @@ async function checkVerifyDevice() {
const userInfo = uni.getStorageSync(constant.storageConstant.USERINFO)
if (!userInfo || !userInfo.sysUser || !userInfo.sysUser.userId) return
// 是否在白名单中
if (constant.DEVICE_VERIFY_WRITE_LIST.includes(userInfo.sysUser.phone)) {
return
}
// if (constant.DEVICE_VERIFY_WRITE_LIST.includes(userInfo.sysUser.phone)) {
// return
// }
const uniqueCode = await getMiniUniqueCode()
const deviceKey = await getDeviceKey()
const _params = {
mobile: userInfo.sysUser.phone,
userId: userInfo.sysUser.userId,
@@ -154,7 +157,8 @@ defineExpose({
<view v-if="tabbarStore.isTabbar" class="layout_foot">
<tm-tabbar class="layout_foot" :activeIndex="tabbarStore.activeIndex" :list="tabbarStore.pageTabbarList"
:outIndex="-1" fontSize="12px" iconSize="24px" bgColor="#F6F5FA" :zIndex="99999" darkBgColor="#F6F5FA"
borderColor="#DBDDDE" color="#8E8E8E" selected-color="#0094ff" position="fixed" @change="tabbarChange">
borderColor="#DBDDDE" color="#8E8E8E" selected-color="#0094ff" position="relative"
@change="tabbarChange">
</tm-tabbar>
</view>
<VerifyDeviceImpModel ref="verifyDeviceImpModelRef" :close="verifyDeviceImpModelClose" />
@@ -27,7 +27,10 @@
<script setup lang="ts">
import { ref, reactive, watch, onUnmounted } from "vue"
import { constant } from "@@/index"
import { userApi } from "@@/api"
import { getMiniUniqueCode, getDeviceKey } from "@@/utils/mini-tool"
const props = withDefaults(
defineProps<{
@@ -106,7 +109,11 @@ const doSubmit = async () => {
if (!isPass) return
uni.showLoading({ title: "验证中...", mask: true })
const res: any = await userApi.verifyDevice({ mobilePhone: form.mobilePhone, code: form.code })
const params = {
mobilePhone: form.mobilePhone,
code: form.code,
}
const res: any = await userApi.verifyDevice(params)
if (res.code != 0) {
uni.showToast({ title: res.msg || "验证失败", icon: "none" })
return
+2
View File
@@ -7,4 +7,6 @@ export const SERVICE_IMAGE = {
blueBg: "/admin/sys-file/oss/file?fileName=ab8823b0abb045a7b9ce7a52d91eea8f.png",
// 生日海报
birthdayCoupon: "/admin/sys-file/oss/file?fileName=d723089fccec4f598e86459e8899b0de.jpg",
// 小程序logo
logo: "/admin/sys-file/oss/file?fileName=df4f93c63f3b431ea661e9c0031e6450.jpeg",
}
+16
View File
@@ -48,3 +48,19 @@ export const getUrlParamsObj = (url?: string): Record<string, any> => {
const params = getUrlParams(url)
return params
}
export const createCooldown = (cooldownMs: number = 300) => {
let locked = false
let timer: any = null
return (fn: () => void) => {
if (locked) return false
locked = true
clearTimeout(timer)
timer = setTimeout(() => {
locked = false
}, cooldownMs)
fn()
return true
}
}
+8 -3
View File
@@ -11,9 +11,7 @@ export async function getMiniUniqueCode(forceGet = false) {
}
const appEnv = await getAppInfo()
try {
const res = await uni.login({
provider: "weixin",
})
const res = await uni.login({ provider: "weixin" });
if (!res?.code) {
throw new Error("登录失败")
}
@@ -36,3 +34,10 @@ export async function getMiniUniqueCode(forceGet = false) {
console.log("获取小程序唯一编码失败", error)
}
}
// 获取设备唯一编码
export async function getDeviceKey() {
const sysInfo = await uni.getSystemInfo()
const sysKey = sysInfo.deviceId + "-" + sysInfo.brand + sysInfo.deviceModel + "-" + sysInfo.deviceType + "-" + sysInfo.platform
return sysKey
}
+16 -7
View File
@@ -2,7 +2,7 @@ import Request from "luch-request"
import { isEmpty } from "radash"
import { router, constant } from "@@/index"
import { getBaseApiUrl, getAppInfo, isDevEnv } from "@@/utils/app"
import { createCooldown, getBaseApiUrl, getAppInfo, isDevEnv } from "@@/utils/app"
import { userApi } from "../api"
import { APPENV } from "../constant"
@@ -19,12 +19,13 @@ const SKIP_AUTH_URLS = [
const SKIP_REFRESH_TOKEN = ["/sys/auth/refresh-token", "/auth/token/check_token"]
let refreshTokenLock = true
const runLoginRedirect = createCooldown(1500)
console.log("getBaseApiUrl:", getBaseApiUrl())
const https = new Request({
baseURL: getBaseApiUrl(),
timeout: 15000,
timeout: 10 * 1000,
method: "GET",
header: {
Accept: "*/*",
@@ -84,13 +85,21 @@ https.interceptors.response.use(
return Promise.resolve(response.data)
},
async (error) => {
uni.hideLoading()
// 处理 424 错误(token过期)
if (error.statusCode === 424) {
// 开发环境直接清除token并跳转登录
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
router.jump("/pages/root/login")
// 判断是否在登录页面
const currentPage = router.getCurrentPage()
const currentRoute = currentPage?.route
if (currentRoute === "pages/root/login" || currentRoute === "/pages/root/login") {
return
}
runLoginRedirect(() => {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
uni.navigateTo({ url: "/pages/root/login" })
})
}
// 显示错误信息
else if (error?.data?.msg) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

@@ -352,7 +352,7 @@ onMounted(() => {
</script>
<template>
<!-- @vue-ignore -->
<view :style="[{ position: position, zIndex: zIndex }]" class="xTabbar">
<view :style="[{ position: position == 'relative' ? 'none' : 'fixed', zIndex: zIndex }]" class="xTabbar">
<!-- 凸起占位 -->
<view v-if="_outIndex != -1 && _nowOutItem"
:style="{ top: '-18px', height: (100) + 'px', zIndex: (zIndex + 1).toString(), width: (100 / _list.length) + '%', left: (100 / _list.length * _outIndex) + '%' }"
+97 -97
View File
@@ -10,17 +10,17 @@ import { BasePluralRule, Tmui4xI18nTml, DateTimeFormatReal, I18nOptions, I18nOpt
// 日期时间名称格式类型
type DateTimeNames = {
long : string[],
short : string[],
narrow : string[]
long: string[],
short: string[],
narrow: string[]
}
type BestTimeTYpe = { unit : string, value : number }
type timeUnitsTYpe = { unit : string, ms : number, threshold : number }
export function mergeI18nOpts(args ?: I18nOptions) : I18nOptionsReally {
type BestTimeTYpe = { unit: string, value: number }
type timeUnitsTYpe = { unit: string, ms: number, threshold: number }
export function mergeI18nOpts(args?: I18nOptions): I18nOptionsReally {
let localLang = uni.getStorageSync("language")
// 提供默认配置
const currentLan = localLang != null && localLang != '' && typeof localLang == 'string' ? (localLang! as string) : "zh-Hans"
const defaultOptions : I18nOptionsReally = {
const defaultOptions: I18nOptionsReally = {
locale: currentLan,
fallbackLocale: "en",
messages: {},
@@ -41,7 +41,7 @@ export function mergeI18nOpts(args ?: I18nOptions) : I18nOptionsReally {
warnHandler: (v, e) => {
console.warn(`[tmui4x]:${v}`, e || '');
},
pluralRule: (choice : number, choicesLength : number, orgRule ?: BasePluralRule | null) : number => {
pluralRule: (choice: number, choicesLength: number, orgRule?: BasePluralRule | null): number => {
// 默认英语复数规则实现
// 参考 Vue I18n 标准实现:https://vue-i18n.intlify.dev/guide/essentials/pluralization
// 如果提供了原始规则函数,优先使用
@@ -110,37 +110,37 @@ export function mergeI18nOpts(args ?: I18nOptions) : I18nOptionsReally {
}
export class Tmui4xI18n implements Tmui4xI18nTml {
ops : I18nOptionsReally;
private static instance : Tmui4xI18n;
constructor(args ?: I18nOptions) {
ops: I18nOptionsReally;
private static instance: Tmui4xI18n;
constructor(args?: I18nOptions) {
this.ops = mergeI18nOpts(args)
}
/**
* 单例模式
*/
public static getInstance(args ?: I18nOptions) : Tmui4xI18n {
public static getInstance(args?: I18nOptions): Tmui4xI18n {
if (Tmui4xI18n.instance) {
return Tmui4xI18n.instance
}
return new Tmui4xI18n(args);
}
// 语言
setLocale(local : string) {
setLocale(local: string) {
uni.setStorageSync('language', local)
this.ops.locale = local
}
//设置语言
getLocale() : string {
getLocale(): string {
return this.ops.locale
}
getFallbackLocale() : string {
getFallbackLocale(): string {
return this.ops.fallbackLocale
}
setFallbackLocale(local : string) {
setFallbackLocale(local: string) {
this.ops.fallbackLocale = local
}
setOptions(args : I18nOptions) {
setOptions(args: I18nOptions) {
this.ops = mergeI18nOpts(args)
}
/**
@@ -150,14 +150,14 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param opts 可选的第二个参数当args为复数数字,key中函数 | 时,此opts起效且必须为utsjsonobject格式等同原vueI18n使用方式
* @returns 翻译后的字符串
*/
t(key : string, ...argsopts : Array<any>) : string {
let repeatCount : NumberOrNull = null;
let values : UTSJSONObjectOrArray = {};
let locale : string = this.ops?.locale??'zh-Hans';
t(key: string, ...argsopts: Array<any>): string {
let repeatCount: NumberOrNull = null;
let values: UTSJSONObjectOrArray = {};
let locale: string = this.ops?.locale ?? 'zh-Hans';
if (argsopts.length > 0) {
// 严格的类型检查和参数解析
let args : AnyOrNull = argsopts[0]
let opts : AnyOrNull = null;
let args: AnyOrNull = argsopts[0]
let opts: AnyOrNull = null;
if (argsopts.length > 1) {
opts = argsopts[1]
}
@@ -165,33 +165,33 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
if (args != null && opts == null) {
if (typeof args == 'number') {
// args 是数字类型
const numArgs : number = args;
const numArgs: number = args;
values = { count: numArgs, n: numArgs };
repeatCount = numArgs
} else if (typeof args == 'string') {
// args 是字符串类型(locale)
const strArgs : string = args;
const strArgs: string = args;
locale = strArgs;
} else if (args instanceof Object && args != null) {
const objArgs : Record<string, any> = args;
const objArgs: Record<string, any> = args;
values = objArgs;
} else if (Array.isArray(args)) {
const arrArgs : Array<any> = args;
const arrArgs: Array<any> = args;
values = arrArgs;
}
} else if (typeof args == 'number' && opts instanceof Object && opts != null) {
const objOpts : Record<string, any> = opts;
const objOpts: Record<string, any> = opts;
values = objOpts;
const numArgs : number = args;
const numArgs: number = args;
repeatCount = numArgs
}
}
// 类型安全的消息获取
let message : StringOrNull = this.getMessage(key, locale);
let message: StringOrNull = this.getMessage(key, locale);
// 如果没有找到消息,尝试回退语言
if (message == null && locale != this.ops.fallbackLocale) {
@@ -209,7 +209,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
// 类型安全的复数处理
if (repeatCount != null && typeof message == 'string' && message.includes('|')) {
const safeCount : number = repeatCount;
const safeCount: number = repeatCount;
message = this.handlePlural(message, safeCount, locale);
}
@@ -220,7 +220,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
// 类型安全的后处理
if (this.ops.postTranslation != null && typeof message == 'string') {
const processedMessage : string = this.ops.postTranslation(message, key);
const processedMessage: string = this.ops.postTranslation(message, key);
message = processedMessage;
}
@@ -235,9 +235,9 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param opts 格式化选项
* @returns 格式化后的字符串
*/
n(val : number, formatName ?: StringOrNull, opts ?: NumberFormatOrNull) : string {
n(val: number, formatName?: StringOrNull, opts?: NumberFormatOrNull): string {
let currentLocale = (opts?.local ?? this.ops.locale)!;
let formatOptions : NumberFormatOpts = {
let formatOptions: NumberFormatOpts = {
style: 'decimal',
currency: 'CNY',
local: currentLocale,
@@ -304,9 +304,9 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param opts 格式化选项
* @returns 格式化后的字符串
*/
d(val : DateOrNumberOrString, formatName ?: StringOrNull, opts ?: DateTimeFormatOrNull) : string {
d(val: DateOrNumberOrString, formatName?: StringOrNull, opts?: DateTimeFormatOrNull): string {
let currentLocale = (opts?.local ?? this.ops.locale)!;
let formatOptions : DateTimeFormatReal = {
let formatOptions: DateTimeFormatReal = {
localeMatcher: 'best fit',
local: currentLocale,
calendar: 'gregory',
@@ -407,13 +407,13 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param {string} local 语言
* @param {Record<string,any>} newMessage 键值及字段。
*/
mergeLocaleMessage(local : string, newMessage : Record<string, any>) {
let nowmessage = this.ops.messages?.[local]??null;
mergeLocaleMessage(local: string, newMessage: Record<string, any>) {
let nowmessage = this.ops.messages?.[local] ?? null;
if (nowmessage != null) {
nowmessage = { ...nowmessage, ...newMessage }
this.ops.messages[local] = {...this.ops.messages.local,...newMessage}
this.ops.messages[local] = { ...this.ops.messages.local, ...newMessage }
} else {
this.ops.messages[local] = newMessage
this.ops.messages[local] = newMessage
}
}
/**
@@ -422,7 +422,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param {string} locale 可选的语言代码,不提供则使用当前语言
* @returns {boolean} 存在返回true,不存在返回false
*/
te(key : string, locale ?: string | null) : boolean {
te(key: string, locale?: string | null): boolean {
// 确定要检查的语言
const targetLocale = (locale != null ? locale : this.ops.locale);
@@ -449,7 +449,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param {string} locale 可选语言,不提供以默认创建的设置为准。
* @returns {string} 格式化后的时间字符串
*/
rt(timeValues ?: number | Date | string | null, units ?: string | null, locale ?: string | null) : string {
rt(timeValues?: number | Date | string | null, units?: string | null, locale?: string | null): string {
const targetLocale = (locale != null ? locale : this.ops.locale)
const timeValue = (timeValues != null ? new Date() : timeValues!)
const unit = (units != null ? 'minute' : units!)
@@ -534,7 +534,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* 获取可用语言列表
* @returns {string[]}
*/
availableLocales() : string[] {
availableLocales(): string[] {
return Object.keys(this.ops.messages)
}
/**
@@ -543,11 +543,11 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param preferredUnit 首选单位
* @returns 包含最佳单位和数值的对象
*/
private getBestTimeUnit(timeDiff : number, preferredUnit : string) : BestTimeTYpe {
private getBestTimeUnit(timeDiff: number, preferredUnit: string): BestTimeTYpe {
const absTimeDiff = Math.abs(timeDiff);
// 定义时间单位的毫秒值和优先级
const timeUnits : timeUnitsTYpe[] = [
const timeUnits: timeUnitsTYpe[] = [
{ unit: 'second', ms: 1000, threshold: 60 },
{ unit: 'minute', ms: 1000 * 60, threshold: 60 },
{ unit: 'hour', ms: 1000 * 60 * 60, threshold: 24 },
@@ -589,7 +589,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的相对时间字符串
*/
private formatRelativeTimeFallback(value : number, unit : string, locale : string) : string {
private formatRelativeTimeFallback(value: number, unit: string, locale: string): string {
const absValue = Math.abs(value);
const isPast = value < 0;
const isFuture = value > 0;
@@ -656,7 +656,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param langCode 语言代码
* @returns 本地化的单位名称
*/
private getLocalizedUnitName(unit : string, value : number, langCode : string) : string {
private getLocalizedUnitName(unit: string, value: number, langCode: string): string {
const isPlural = value != 1;
switch (langCode) {
@@ -719,10 +719,10 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的字符串
*/
private formatDateTime(value : DateOrNumberOrString, options : DateTimeFormatReal, locale : string) : string {
private formatDateTime(value: DateOrNumberOrString, options: DateTimeFormatReal, locale: string): string {
try {
// 转换输入值为Date对象
let dateObj : Date = new Date();
let dateObj: Date = new Date();
if (value instanceof Date) {
dateObj = value;
} else if (typeof value == 'number') {
@@ -758,7 +758,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的字符串
*/
private formatWithPredefinedStyle(date : Date, options : DateTimeFormatReal, locale : string) : string {
private formatWithPredefinedStyle(date: Date, options: DateTimeFormatReal, locale: string): string {
let result = '';
// 处理日期样式
@@ -814,9 +814,9 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的字符串
*/
private formatWithCustomOptions(date : Date, options : DateTimeFormatReal, locale : string) : string {
const dateParts : string[] = [];
const timeParts : string[] = [];
private formatWithCustomOptions(date: Date, options: DateTimeFormatReal, locale: string): string {
const dateParts: string[] = [];
const timeParts: string[] = [];
// 根据语言环境确定日期组件的排列顺序
const langCode = locale.split('-')[0].toLowerCase();
@@ -900,7 +900,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
}
// 组合日期和时间
const result : string[] = [];
const result: string[] = [];
if (dateParts.length > 0) {
result.push(this.combineDateTimeParts(dateParts, locale, options.dateSeparator));
}
@@ -918,7 +918,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的字符串
*/
private formatNumber(value : number, options : NumberFormatOpts, locale : string) : string {
private formatNumber(value: number, options: NumberFormatOpts, locale: string): string {
try {
// 基础格式化逻辑
let result = value.toString();
@@ -991,7 +991,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param options 格式化选项
* @returns 格式化后的字符串
*/
private formatWithSignificantDigits(value : number, options : NumberFormatOpts) : string {
private formatWithSignificantDigits(value: number, options: NumberFormatOpts): string {
const minSigDigits = options.minimumSignificantDigits ?? 1;
const maxSigDigits = options.maximumSignificantDigits ?? 21;
@@ -1023,7 +1023,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param minIntegerDigits 最小整数位数
* @returns 格式化后的字符串
*/
private formatWithMinimumIntegerDigits(numStr : string, minIntegerDigits : number) : string {
private formatWithMinimumIntegerDigits(numStr: string, minIntegerDigits: number): string {
const parts = numStr.split('.');
const integerPart = parts[0];
const decimalPart = parts.length > 1 ? parts[1] : '';
@@ -1039,7 +1039,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取完整日期格式
*/
private getFullDateFormat(date : Date, locale : string) : string {
private getFullDateFormat(date: Date, locale: string): string {
const langCode = locale.split('-')[0].toLowerCase();
const isUSFormat = langCode == 'en' && (locale.toLowerCase().includes('us') || locale.toLowerCase() == 'en');
@@ -1061,7 +1061,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取长日期格式
*/
private getLongDateFormat(date : Date, locale : string) : string {
private getLongDateFormat(date: Date, locale: string): string {
const langCode = locale.split('-')[0].toLowerCase();
const isUSFormat = langCode == 'en' && (locale.toLowerCase().includes('us') || locale.toLowerCase() == 'en');
@@ -1083,7 +1083,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取中等日期格式
*/
private getMediumDateFormat(date : Date, locale : string) : string {
private getMediumDateFormat(date: Date, locale: string): string {
const langCode = locale.split('-')[0].toLowerCase();
const isUSFormat = langCode == 'en' && (locale.toLowerCase().includes('us') || locale.toLowerCase() == 'en');
@@ -1102,7 +1102,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取短日期格式
*/
private getShortDateFormat(date : Date, locale : string) : string {
private getShortDateFormat(date: Date, locale: string): string {
const langCode = locale.split('-')[0].toLowerCase();
const isUSFormat = langCode == 'en' && (locale.toLowerCase().includes('us') || locale.toLowerCase() == 'en');
const year = date.getFullYear().toString().slice(-2);
@@ -1124,7 +1124,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取完整时间格式
*/
private getFullTimeFormat(date : Date, options : DateTimeFormatReal, locale : string) : string {
private getFullTimeFormat(date: Date, options: DateTimeFormatReal, locale: string): string {
const timeStr = this.getMediumTimeFormat(date, options, locale);
const timeZone = this.formatTimeZone(date, 'long', options.timeZone);
return `${timeStr} ${timeZone}`;
@@ -1133,7 +1133,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取长时间格式
*/
private getLongTimeFormat(date : Date, options : DateTimeFormatReal, locale : string) : string {
private getLongTimeFormat(date: Date, options: DateTimeFormatReal, locale: string): string {
const timeStr = this.getMediumTimeFormat(date, options, locale);
const timeZone = this.formatTimeZone(date, 'short', options.timeZone);
return `${timeStr} ${timeZone}`;
@@ -1142,7 +1142,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取中等时间格式
*/
private getMediumTimeFormat(date : Date, options : DateTimeFormatReal, locale : string) : string {
private getMediumTimeFormat(date: Date, options: DateTimeFormatReal, locale: string): string {
const hour = this.formatHour(date, 'numeric', options.hour12, options.hourCycle);
const minute = this.formatMinute(date, '2-digit');
const second = this.formatSecond(date, '2-digit', 0);
@@ -1153,7 +1153,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取短时间格式
*/
private getShortTimeFormat(date : Date, options : DateTimeFormatReal, locale : string) : string {
private getShortTimeFormat(date: Date, options: DateTimeFormatReal, locale: string): string {
const hour = this.formatHour(date, 'numeric', options.hour12, options.hourCycle);
const minute = this.formatMinute(date, '2-digit');
const dayPeriod = options.hour12 ? ` ${this.formatDayPeriod(date, 'short', locale)}` : '';
@@ -1163,7 +1163,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化星期
*/
private formatWeekday(date : Date, style : string, locale : string) : string {
private formatWeekday(date: Date, style: string, locale: string): string {
const weekdays = this.getWeekdayNames(locale);
const dayIndex = date.getDay();
@@ -1182,7 +1182,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化纪元
*/
private formatEra(date : Date, style : string, locale : string) : string {
private formatEra(date: Date, style: string, locale: string): string {
const year = date.getFullYear();
const langCode = locale.split('-')[0].toLowerCase();
@@ -1206,7 +1206,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param numberingSystem 数字系统
* @returns 转换后的数字字符串
*/
private convertNumberingSystem(number : string, numberingSystem : string) : string {
private convertNumberingSystem(number: string, numberingSystem: string): string {
if (numberingSystem == 'latn' || numberingSystem == '') {
return number; // 拉丁数字(默认)
}
@@ -1243,7 +1243,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化年份
*/
private formatYear(date : Date, style : string, locale : string = '', numberingSystem : string = 'latn', dateSeparator : string = '') : string {
private formatYear(date: Date, style: string, locale: string = '', numberingSystem: string = 'latn', dateSeparator: string = ''): string {
const year = date.getFullYear();
const langCode = locale.split('-')[0].toLowerCase();
@@ -1266,7 +1266,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化月份
*/
private formatMonth(date : Date, style : string, locale : string, numberingSystem : string = 'latn', dateSeparator : string = '') : string {
private formatMonth(date: Date, style: string, locale: string, numberingSystem: string = 'latn', dateSeparator: string = ''): string {
const month = date.getMonth();
const monthNames = this.getMonthNames(locale);
const langCode = locale.split('-')[0].toLowerCase();
@@ -1295,7 +1295,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化日期
*/
private formatDay(date : Date, style : string, locale : string = '', numberingSystem : string = 'latn', dateSeparator : string = '') : string {
private formatDay(date: Date, style: string, locale: string = '', numberingSystem: string = 'latn', dateSeparator: string = ''): string {
const day = date.getDate();
const langCode = locale.split('-')[0].toLowerCase();
@@ -1317,7 +1317,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化小时
*/
private formatHour(date : Date, style : string, hour12 : boolean, hourCycle : string, numberingSystem : string = 'latn') : string {
private formatHour(date: Date, style: string, hour12: boolean, hourCycle: string, numberingSystem: string = 'latn'): string {
let hour = date.getHours();
if (hour12) {
@@ -1349,7 +1349,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化分钟
*/
private formatMinute(date : Date, style : string, numberingSystem : string = 'latn') : string {
private formatMinute(date: Date, style: string, numberingSystem: string = 'latn'): string {
const minute = date.getMinutes();
switch (style) {
case 'numeric':
@@ -1364,7 +1364,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化秒
*/
private formatSecond(date : Date, style : string, fractionalDigits : number, numberingSystem : string = 'latn') : string {
private formatSecond(date: Date, style: string, fractionalDigits: number, numberingSystem: string = 'latn'): string {
const second = date.getSeconds();
const millisecond = date.getMilliseconds();
@@ -1394,7 +1394,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化时段(AM/PM)
*/
private formatDayPeriod(date : Date, style : string, locale : string) : string {
private formatDayPeriod(date: Date, style: string, locale: string): string {
const hour = date.getHours();
const isAM = hour < 12;
const langCode = locale.split('-')[0].toLowerCase();
@@ -1423,7 +1423,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 格式化时区
*/
private formatTimeZone(date : Date, style : string, timeZone : string) : string {
private formatTimeZone(date: Date, style: string, timeZone: string): string {
// 简化实现,实际应用中可能需要更复杂的时区处理
switch (style) {
case 'long':
@@ -1437,7 +1437,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 组合日期时间各部分
*/
private combineDateTimeParts(parts : string[], locale : string, dateSeparator : string = '') : string {
private combineDateTimeParts(parts: string[], locale: string, dateSeparator: string = ''): string {
const langCode = locale.split('-')[0].toLowerCase();
// 如果设置了自定义分隔符,使用自定义分隔符连接
@@ -1457,7 +1457,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取星期名称
*/
private getWeekdayNames(locale : string) : DateTimeNames {
private getWeekdayNames(locale: string): DateTimeNames {
const langCode = locale.split('-')[0].toLowerCase();
if (langCode == 'zh') {
@@ -1478,7 +1478,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
/**
* 获取月份名称
*/
private getMonthNames(locale : string) : DateTimeNames {
private getMonthNames(locale: string): DateTimeNames {
const langCode = locale.split('-')[0].toLowerCase();
if (langCode == 'zh') {
@@ -1502,7 +1502,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 带千分位分隔符的字符串
*/
private addThousandsSeparator(numStr : string, locale : string) : string {
private addThousandsSeparator(numStr: string, locale: string): string {
const parts = numStr.split('.');
const integerPart = parts[0];
const decimalPart = parts.length > 1 ? parts[1] : '';
@@ -1560,7 +1560,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 分隔符字符
*/
private getThousandsSeparator(locale : string) : string {
private getThousandsSeparator(locale: string): string {
// 提取语言代码前缀(如 'en-US' -> 'en')
const langCode = locale.split('-')[0].toLowerCase();
@@ -1578,7 +1578,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的货币字符串
*/
private formatCurrency(numStr : string, options : NumberFormatOpts, locale : string) : string {
private formatCurrency(numStr: string, options: NumberFormatOpts, locale: string): string {
const currency = options.currency ?? 'USD';
const currencyDisplay = options.currencyDisplay ?? 'symbol';
@@ -1688,7 +1688,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 格式化后的货币字符串
*/
private getCurrencyPosition(symbol : string, numStr : string, currency : string, locale : string) : string {
private getCurrencyPosition(symbol: string, numStr: string, currency: string, locale: string): string {
const currencyCode = currency.toUpperCase();
// 符号在前面的货币(无空格)
@@ -1720,7 +1720,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param originalValue 原始数值
* @returns 格式化后的百分比字符串
*/
private formatPercent(numStr : string, originalValue : number) : string {
private formatPercent(numStr: string, originalValue: number): string {
// 将数值乘以100并添加百分号
const percentValue = originalValue * 100;
return `${percentValue}%`;
@@ -1731,7 +1731,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 翻译消息或null
*/
private getMessage(key : string, locale : string) : StringOrNull {
private getMessage(key: string, locale: string): StringOrNull {
const messages = this.ops.messages;
if (messages[locale] == null) {
return null;
@@ -1747,22 +1747,22 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param path 路径,如 'user.name'
* @returns 值或null
*/
private getNestedValue(obj : UTSJSONObjectOrNull, path : string) : StringOrNull {
private getNestedValue(obj: UTSJSONObjectOrNull, path: string): StringOrNull {
const keys = path.split('.');
let getAny : GetAnyTypeOrNull = (objdata : AnyOrNull) : AnyOrNull => {
let getAny: GetAnyTypeOrNull = (objdata: AnyOrNull): AnyOrNull => {
let realGetAny = getAny!;
let path : StringOrNull = keys.shift() || null
let path: StringOrNull = keys.shift() || null
if (objdata == null) return null;
if (
typeof objdata == 'string' ||
typeof objdata == 'number'
) return objdata
if (path == null) return null;
return realGetAny((objdata! as Record<string, any>)?.[path]??null);
return realGetAny((objdata! as Record<string, any>)?.[path] ?? null);
}
let current : AnyOrNull = getAny(obj);
let current: AnyOrNull = getAny(obj);
if (typeof current == 'number') return current.toString()
return typeof current == 'string' ? current : null;
@@ -1775,8 +1775,8 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param locale 语言代码
* @returns 选择的复数形式
*/
private handlePlural(message : string, count : number, locale : string) : string {
const choices = message.split('|').map((choice : string) : string => choice.trim());
private handlePlural(message: string, count: number, locale: string): string {
const choices = message.split('|').map((choice: string): string => choice.trim());
const choicesLength = choices.length;
// 获取复数规则
@@ -1811,12 +1811,12 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
* @param values 插值参数
* @returns 插值后的字符串
*/
private interpolate(message : string, values : UTSJSONObjectOrArray) : string {
private interpolate(message: string, values: UTSJSONObjectOrArray): string {
let msg = message;
if (Array.isArray(values)) {
// 处理位置插值 {0}, {1}, {2}...
// #ifndef APP-ANDROID
msg = message.replace(/\{(\d+)\}/g, (match : string, key : string, index : number) : string => {
msg = message.replace(/\{(\d+)\}/g, (match: string, key: string, index: number): string => {
const value = values[parseInt(key)];
if (value != null) {
@@ -1827,7 +1827,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
});
// #endif
// #ifdef APP-ANDROID
msg = message.replace(/\{(\d+)\}/g, (match : string, key : string, offset : number, str : string) : string => {
msg = message.replace(/\{(\d+)\}/g, (match: string, key: string, offset: number, str: string): string => {
const value = values[parseInt(key)];
if (value != null) {
let result = typeof value == 'number' ? value.toString() : value as string
@@ -1842,7 +1842,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
}
// 处理命名插值 {name}比如{count},{n}等
// #ifndef APP-ANDROID
msg = message.replace(/\{([^}]+)\}/g, (match : string, key : string, index : number) : string => {
msg = message.replace(/\{([^}]+)\}/g, (match: string, key: string, index: number): string => {
const value = values[key];
if (value != null) {
return value as string;
@@ -1851,7 +1851,7 @@ export class Tmui4xI18n implements Tmui4xI18nTml {
});
// #endif
// #ifdef APP-ANDROID
msg = message.replace(/\{([^}]+)\}/g, (match : string, key : string, offset : number, str : string) : string => {
msg = message.replace(/\{([^}]+)\}/g, (match: string, key: string, offset: number, str: string): string => {
const value = values[key];
if (value != null) {
if (typeof value == 'string') return value;
+2 -2
View File
@@ -44,8 +44,8 @@ export const i18n = (options?: I18nOptions) => {
}
});
}
return i18nInstance;
}