feat: 支持国际化 (#168)

* feat:  支持国际化

* feat:  支持国际化

* fix: 🐛 支持国际化

* docs: ✏️  文档中增加国际化的介绍

* feat:  支持越南语

* docs: ✏️  文档中增加支持国际化的版本

* refactor: ♻️  调整防抖函数的实现

* docs: ✏️  文档中增加vite.config.ts国际化相关的配置

* docs: ✏️  增加支持越南文

---------

Co-authored-by: xuqingkai <xuqingkai@hd123.com>
This commit is contained in:
weisheng 2024-01-28 20:18:55 +08:00 committed by GitHub
parent 38465905e7
commit ce9f19244e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 987 additions and 444 deletions

View File

@ -48,7 +48,7 @@
- 🚀 支持 APP、H5、微信小程序 等平台. - 🚀 支持 APP、H5、微信小程序 等平台.
- 🚀 60+ 个高质量组件,覆盖移动端主流场景. - 🚀 60+ 个高质量组件,覆盖移动端主流场景.
- 💪 使用 Typescript 构建,提供良好的组件类型系统. - 💪 使用 Typescript 构建,提供良好的组件类型系统.
- 💪 采用 Vue3 最新特性,提升组件性能. - 🌍 支持国际化,内置 6 种语言包.
- 📖 提供丰富的文档和组件示例. - 📖 提供丰富的文档和组件示例.
- 🎨 支持修改 CSS 变量实现主题定制. - 🎨 支持修改 CSS 变量实现主题定制.
- 🍭 支持暗黑模式 - 🍭 支持暗黑模式

View File

@ -1,17 +1,17 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2023-07-27 10:26:09 * @Date: 2023-07-27 10:26:09
* @LastEditTime: 2024-01-06 22:20:13 * @LastEditTime: 2024-01-26 13:32:22
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/config.ts * @FilePath: \wot-design-uni\docs\.vitepress\config.ts
* *
*/ */
import { defineConfig } from 'vitepress'; import { defineConfig } from 'vitepress';
export default defineConfig({ export default defineConfig({
title: `Wot Design Uni`, title: `Wot Design Uni`,
description: '一个参照Wot-design打造的uni-app组件库', description: '一个参照wot-design打造的uni-app组件库',
head: [ head: [
['link', { rel: 'icon', href: '/favicon.ico' }], ['link', { rel: 'icon', href: '/favicon.ico' }],
['script', {}, ` ['script', {}, `
@ -69,15 +69,17 @@ export default defineConfig({
{ {
text: '字体', text: '字体',
link: '/guide/typography', link: '/guide/typography',
}, {
text: '常见问题',
link: '/guide/common-problems',
}, },
{ {
text: '国际化',
link: '/guide/locale',
}, {
text: '更新日志', text: '更新日志',
link: '/guide/changelog', link: '/guide/changelog',
}, },
{
text: '常见问题',
link: '/guide/common-problems',
}
] ]
}, },
{ {
@ -133,13 +135,17 @@ export default defineConfig({
link: '/guide/typography', link: '/guide/typography',
}, },
{ {
text: '更新日志', text: '国际化',
link: '/guide/changelog', link: '/guide/locale',
}, },
{ {
text: '常见问题', text: '常见问题',
link: '/guide/common-problems', link: '/guide/common-problems',
} },
{
text: '更新日志',
link: '/guide/changelog',
},
], ],
'/reward/': [ '/reward/': [
{ {
@ -151,7 +157,7 @@ export default defineConfig({
link: '/reward/donor', link: '/reward/donor',
}, },
], ],
'/component/': [ '/component/': [
{ {
text: '基础', text: '基础',
@ -184,7 +190,7 @@ export default defineConfig({
] ]
}, },
{ {
text: "导航", text: "导航",
items: [{ items: [{
link: "/component/pagination", link: "/component/pagination",
@ -209,7 +215,7 @@ export default defineConfig({
text: "Sidebar 侧边栏" text: "Sidebar 侧边栏"
}] }]
}, { }, {
text: "数据输入", text: "数据输入",
items: [{ items: [{
link: "/component/calendar", link: "/component/calendar",
@ -324,7 +330,7 @@ export default defineConfig({
text: "NumberKeyboard 数字键盘" text: "NumberKeyboard 数字键盘"
}] }]
}, { }, {
text: "数据展示", text: "数据展示",
items: [{ items: [{
link: "/component/badge", link: "/component/badge",
@ -384,7 +390,7 @@ export default defineConfig({
} }
] ]
} }
}, },
}) })

View File

@ -87,7 +87,7 @@ const columns = ref([
传入 `column-change` 属性,其类型为 `function`,接收 pickerView 实例、选中项、当前修改列的下标、resolve 作为入参,根据选中项和列下标进行判断,通过 pickerView 实例暴露出来的 `setColumnData` 方法修改其他列的数据源,当修改完成后需要执行 `resolve()` 告知组件修改完成以继续执行,如果 `column-change` 包含异步操作,也可以使组件按照异步顺序进行执行。 传入 `column-change` 属性,其类型为 `function`,接收 pickerView 实例、选中项、当前修改列的下标、resolve 作为入参,根据选中项和列下标进行判断,通过 pickerView 实例暴露出来的 `setColumnData` 方法修改其他列的数据源,当修改完成后需要执行 `resolve()` 告知组件修改完成以继续执行,如果 `column-change` 包含异步操作,也可以使组件按照异步顺序进行执行。
> resolve 参数为 1.4.0 添加,每次修改完后都需要调用 resolve() 通知组件。 > 每次修改完后都需要调用 resolve() 通知组件。
```html ```html
<wd-picker <wd-picker

View File

@ -23,7 +23,7 @@
- 🚀 支持 APP、H5、微信小程序 等平台. - 🚀 支持 APP、H5、微信小程序 等平台.
- 🚀 60+ 个高质量组件,覆盖移动端主流场景. - 🚀 60+ 个高质量组件,覆盖移动端主流场景.
- 💪 使用 Typescript 构建,提供良好的组件类型系统. - 💪 使用 Typescript 构建,提供良好的组件类型系统.
- 💪 采用 Vue3 最新特性,提升组件性能. - 🌍 支持国际化,内置 6 种语言包.
- 📖 提供丰富的文档和组件示例. - 📖 提供丰富的文档和组件示例.
- 🎨 支持修改 CSS 变量实现主题定制. - 🎨 支持修改 CSS 变量实现主题定制.
- 🍭 支持暗黑模式 - 🍭 支持暗黑模式

67
docs/guide/locale.md Normal file
View File

@ -0,0 +1,67 @@
# 国际化<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.20</el-tag>
Wot Design Uni 默认使用中文语言,同时支持多语言切换,如果你希望使用其他语言,你可以参考下面的方案。
:::warning 注意点
目前组件库发布到 npm 上的包是未经编译的`vue``ts`,而 Vite 会将[预构建](https://cn.vitejs.dev/guide/dep-pre-bundling.html)的依赖项缓存到 `node_modules/.vite`,组件库的国际化的实现是基于`reactive`实现的数据共享,在`dev`阶段就会出现页面使用预构建产物中的国际化数据,而组件库使用组件库内部的国际化数据,所以在非`uni_modules`模式引入时,需要在`vite.config.ts`中增加以下配置:
```ts
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
export default defineConfig({
...
optimizeDeps: {
exclude: process.env.UNI_PLATFORM === 'h5' && process.env.NODE_ENV === 'development' ? ['wot-design-uni'] : []
}
...
})
```
使用[optimizeDeps.exclude](https://cn.vitejs.dev/config/dep-optimization-options.html#optimizedeps-exclude)在预构建中强制排除`wot-design-uni`模块,在`uni_modules`模式下,不需要做任何处理。
:::
## 使用其他语言
我们通过 **Locale** 组件实现多语言支持,使用 **Locale.use** 方法可以切换当前使用的语言。
```typescript
import { Locale } from 'wot-design-uni'
// 引入英文语言包
import enUS from 'wot-design-uni/locale/lang/en-US'
Locale.use('en-US', enUS)
```
## 覆盖语言包
通过 **Locale.add** 方法可以实现文案的修改和扩展,示例如下:
```typescript
import { Locale } from 'wot-design-uni'
const messages = {
'zh-CN': {
calendar: {
title: '请选择日期' // 将'选择日期'修改为'请选择日期'
}
}
}
Locale.add(messages)
```
## 支持的语言
| 语言 | 文件名 | 版本 |
| ---------------- | --------- | --------- |
| 简体中文 | zh-CN | `v0.2.20` |
| 繁体中文(台湾) | zh-TW | `v0.2.20` |
| 繁体中文(香港) | zh-HK | `v0.2.20` |
| 英文 | en-US | `v0.2.20` |
| 泰文 | th-TH | `v0.2.20` |
| 越南文 | vi-VN | `v0.2.20` |
如果你需要使用其他的语言,欢迎贡献 [PR](https://github.com/Moonofweisheng/wot-design-uni/pulls),只需在[这里](https://github.com/Moonofweisheng/wot-design-uni/tree/master/src/uni_modules/wot-design-uni/locale/lang)添加一个语言配置文件即可。

View File

@ -2,12 +2,12 @@
layout: home layout: home
title: Wot Design Uni title: Wot Design Uni
titleTemplate: 一个参照Wot-design打造的uni-app组件库 titleTemplate: 基于Vue3+TS开发的uni-app组件库
hero: hero:
name: Wot Design Uni name: Wot Design Uni
text: text:
tagline: 一个参照Wot-design打造的uni-app组件库 tagline: 基于Vue3+TS开发的uni-app组件库
image: image:
src: /wot-design.png src: /wot-design.png
alt: Wot Design alt: Wot Design
@ -30,17 +30,17 @@ features:
title: 60+ 组件 title: 60+ 组件
details: 超过 60 个高质量组件,覆盖移动端主流场景。 details: 超过 60 个高质量组件,覆盖移动端主流场景。
- icon: 💪 - icon: 💪
title: 支持 TypeScript title: TypeScript 支持
details: 使用 Typescript 构建,提供良好的组件类型系统。 details: 使用 Typescript 构建,提供良好的组件类型系统。
- icon: 💪 - icon: 🌍
title: 支持 Vue3 title: 支持国际化
details: 采用 Vue3 最新特性,提升组件性能 details: 支持国际化,内置 6 种语言包
- icon: 📖 - icon: 📖
title: 提供丰富的文档和组件示例 title: 提供丰富的文档和组件示例
details: 文档和组件示例为开发者提供稳定的后勤保障。 details: 文档和组件示例为开发者提供稳定的后勤保障。
- icon: 🍭 - icon: 🍭
title: 支持暗黑模式和主题定制 title: 支持暗黑模式和主题定制
details: 可以定制scss变量以及组件的样式自定义。 details: 可以定制css变量以及组件的样式自定义。
footer: false footer: false
--- ---

View File

@ -1,7 +1,7 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2023-03-09 19:23:03 * @Date: 2023-03-09 19:23:03
* @LastEditTime: 2023-08-22 23:02:46 * @LastEditTime: 2024-01-26 14:08:43
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: \wot-design-uni\src\main.ts * @FilePath: \wot-design-uni\src\main.ts

View File

@ -1,206 +0,0 @@
import isObject from './isObject'
import root from './internal/root'
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked, or until the next browser frame is drawn. The debounced function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* debounced function. Subsequent calls to the debounced function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `debounce` and `throttle`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0]
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
* used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', debounce(calculateLayout, 150))
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }))
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
* const source = new EventSource('/stream')
* jQuery(source).on('message', debounced)
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel)
*
* // Check for pending invocations.
* const status = debounced.pending() ? "Pending..." : "Ready"
*/
function debounce(func, wait, options?) {
let lastArgs, lastThis, maxWait, result, timerId, lastCallTime
let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = !wait && wait !== 0 && typeof root.requestAnimationFrame === 'function'
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}
function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
function startTimer(pendingFunc, wait) {
if (useRAF) {
root.cancelAnimationFrame(timerId)
return root.requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}
function cancelTimer(id) {
if (useRAF) {
return root.cancelAnimationFrame(id)
}
clearTimeout(id)
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time
// Start the timer for the trailing edge.
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}
function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall
return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting
}
function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || (maxing && timeSinceLastInvoke >= maxWait)
}
function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
// Restart the timer.
timerId = startTimer(timerExpired, remainingWait(time))
}
function trailingEdge(time) {
timerId = undefined
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}
function pending() {
return timerId !== undefined
}
function debounced(this: any, ...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}
export default debounce

View File

@ -1,4 +0,0 @@
/** Detect free variable `global` from Node.js. */
const freeGlobal = typeof global === 'object' && global !== null && global.Object === Object && global
export default freeGlobal

View File

@ -1,23 +0,0 @@
/*
* @Author: weisheng
* @Date: 2023-09-01 21:42:32
* @LastEditTime: 2023-09-03 11:43:06
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\common\lodash\internal\root.ts
*
*/
import freeGlobal from './freeGlobal'
/** Detect free variable `globalThis` */
// eslint-disable-next-line eqeqeq
const freeGlobalThis = typeof globalThis === 'object' && globalThis !== null && globalThis.Object == Object && globalThis
/** Detect free variable `self`. */
const freeSelf = typeof self === 'object' && self !== null && self.Object === Object && self
/** Used as a reference to the global object. */
// eslint-disable-next-line no-new-func
const root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')()
export default root

View File

@ -1,29 +0,0 @@
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* isObject({})
* // => true
*
* isObject([1, 2, 3])
* // => true
*
* isObject(Function)
* // => true
*
* isObject(null)
* // => false
*/
function isObject(value) {
const type = typeof value
return value != null && (type === 'object' || type === 'function')
}
export default isObject

View File

@ -1,6 +1,3 @@
/* eslint-disable no-prototype-builtins */
import debounce from './lodash/debounce'
/** /**
* uuid * uuid
* @returns string * @returns string
@ -78,8 +75,6 @@ export const defaultFunction = <T>(value: T): T => value
*/ */
export const isDef = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null export const isDef = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null
export { debounce }
/** /**
* @description * @description
* @param {number} num * @param {number} num
@ -275,6 +270,15 @@ export function kebabCase(word: string): string {
return newWord return newWord
} }
/**
* 线
* @param word 线
* @returns
*/
export function camelCase(word: string): string {
return word.replace(/-(\w)/g, (_, c) => c.toUpperCase())
}
/** /**
* *
* @param {any} value * @param {any} value
@ -482,6 +486,7 @@ export function deepMerge<T extends Record<string, any>>(target: T, source: Reco
// 遍历源对象的属性 // 遍历源对象的属性
for (const prop in source) { for (const prop in source) {
// eslint-disable-next-line no-prototype-builtins
if (!source.hasOwnProperty(prop)) if (!source.hasOwnProperty(prop))
continue continue
// 使用类型断言,告诉 TypeScript 这是有效的属性 // 使用类型断言,告诉 TypeScript 这是有效的属性
@ -491,6 +496,25 @@ export function deepMerge<T extends Record<string, any>>(target: T, source: Reco
return target return target
} }
/**
*
* @param target
* @param source
* @returns
*/
export function deepAssign(target: Record<string, any>, source: Record<string, any>): Record<string, any> {
Object.keys(source).forEach((key) => {
const targetValue = target[key]
const newObjValue = source[key]
if (isObj(targetValue) && isObj(newObjValue)) {
deepAssign(targetValue, newObjValue)
} else {
target[key] = newObjValue
}
})
return target
}
/** /**
* URL * URL
* @param baseUrl URL * @param baseUrl URL
@ -510,10 +534,66 @@ export function buildUrlWithParams(baseUrl: string, params: Record<string, strin
return `${baseUrl}${separator}${queryString}` return `${baseUrl}${separator}${queryString}`
} }
type DebounceOptions = {
leading?: boolean // 是否在延迟时间开始时调用函数
trailing?: boolean // 是否在延迟时间结束时调用函数
}
export function debounce<T extends (...args: any[]) => any>(func: T, wait: number, options: DebounceOptions = {}): T {
let timeoutId: ReturnType<typeof setTimeout> | null = null
let lastArgs: any[] | undefined
let lastThis: any
let result: ReturnType<T> | undefined
const leading = options.leading ?? false
const trailing = options.trailing ?? true
function invokeFunc() {
if (lastArgs !== undefined) {
result = func.apply(lastThis, lastArgs)
lastArgs = undefined
}
}
function startTimer() {
timeoutId = setTimeout(() => {
timeoutId = null
if (trailing) {
invokeFunc()
}
}, wait)
}
function cancelTimer() {
if (timeoutId !== null) {
clearTimeout(timeoutId)
timeoutId = null
}
}
function debounced(this: any, ...args: Parameters<T>): ReturnType<T> | undefined {
lastArgs = args
lastThis = this
if (timeoutId === null) {
if (leading) {
invokeFunc()
}
startTimer()
} else if (trailing) {
cancelTimer()
startTimer()
}
return result
}
return debounced as T
}
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export function throttle(func: Function, wait: number): Function { export function throttle(func: Function, wait: number): Function {
let timeout: NodeJS.Timeout | null let timeout: ReturnType<typeof setTimeout> | null = null
let previous = 0 let previous: number = 0
const throttled = function (this: any, ...args: any[]) { const throttled = function (this: any, ...args: any[]) {
const now = Date.now() const now = Date.now()

View File

@ -0,0 +1,22 @@
/*
* @Author: weisheng
* @Date: 2024-01-25 23:06:48
* @LastEditTime: 2024-01-26 14:00:48
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\composables\useTranslate.ts
*
*/
import { camelCase, getPropByPath, isFunction } from '../common/util'
import Locale from '../../locale'
export const useTranslate = (name?: string) => {
const prefix = name ? camelCase(name) + '.' : ''
const translate = (key: string, ...args: unknown[]) => {
const currentMessages = Locale.messages()
const message = getPropByPath(currentMessages, prefix + key)
return isFunction(message) ? message(...args) : message
}
return { translate }
}

View File

@ -51,6 +51,7 @@ import {
} from '../utils' } from '../utils'
import { useToast } from '../../wd-toast' import { useToast } from '../../wd-toast'
import { deepClone, getType, isArray } from '../../common/util' import { deepClone, getType, isArray } from '../../common/util'
import { useTranslate } from '../../composables/useTranslate'
interface Props { interface Props {
type: string type: string
@ -70,6 +71,8 @@ const props = withDefaults(defineProps<Props>(), {
allowSameDay: false allowSameDay: false
}) })
const { translate } = useTranslate('calendar-view')
const days = ref<Array<Record<string, any>>>([]) const days = ref<Array<Record<string, any>>>([])
const itemClass = computed(() => { const itemClass = computed(() => {
@ -295,7 +298,7 @@ function handleDateRangeChange(date) {
const maxEndDate = getDayByOffset(startDate, props.maxRange - 1) const maxEndDate = getDayByOffset(startDate, props.maxRange - 1)
value = [startDate, getDate(maxEndDate, true)] value = [startDate, getDate(maxEndDate, true)]
toast.show({ toast.show({
msg: props.rangePrompt || `选择天数不能超过${props.maxRange}` msg: props.rangePrompt || translate('rangePrompt', props.maxRange)
}) })
} else { } else {
value = [startDate, getDate(date.date, true)] value = [startDate, getDate(date.date, true)]

View File

@ -32,7 +32,7 @@
</scroll-view> </scroll-view>
<view v-if="timeType" class="wd-month-panel__time"> <view v-if="timeType" class="wd-month-panel__time">
<view v-if="type === 'datetimerange'" class="wd-month-panel__time-label"> <view v-if="type === 'datetimerange'" class="wd-month-panel__time-label">
<view class="wd-month-panel__time-text">{{ timeType === 'start' ? '开始' : '结束' }}</view> <view class="wd-month-panel__time-text">{{ timeType === 'start' ? translate('startTime') : translate('endTime') }}</view>
</view> </view>
<view class="wd-month-panel__time-picker"> <view class="wd-month-panel__time-picker">
<wd-picker-view <wd-picker-view
@ -65,6 +65,7 @@ import { debounce, getType, isEqual } from '../../common/util'
import { compareMonth, formatMonthTitle, getMonthEndDay, getMonths, getTimeData, getWeekLabel } from '../utils' import { compareMonth, formatMonthTitle, getMonthEndDay, getMonths, getTimeData, getWeekLabel } from '../utils'
import Month from '../month/month.vue' import Month from '../month/month.vue'
import type { MonthInfo } from './type' import type { MonthInfo } from './type'
import { useTranslate } from '../../composables/useTranslate'
interface Props { interface Props {
type: string type: string
@ -89,6 +90,7 @@ const props = withDefaults(defineProps<Props>(), {
showPanelTitle: false, showPanelTitle: false,
hideSecond: false hideSecond: false
}) })
const { translate } = useTranslate('calendar-view')
const title = ref<string>('') const title = ref<string>('')
const scrollTop = ref<number>(0) // const scrollTop = ref<number>(0) //

View File

@ -1,6 +1,17 @@
import { dayjs } from '../common/dayjs'
import { getType, padZero } from '../common/util' import { getType, padZero } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('calendar-view')
const weeks: string[] = ['日', '一', '二', '三', '四', '五', '六'] const weeks: string[] = [
translate('weeks.sun'),
translate('weeks.mon'),
translate('weeks.tue'),
translate('weeks.wed'),
translate('weeks.thu'),
translate('weeks.fri'),
translate('weeks.sat')
]
/** /**
* *
@ -86,12 +97,7 @@ export function getMonthEndDay(year, month) {
* @param {timestamp} date * @param {timestamp} date
*/ */
export function formatMonthTitle(date) { export function formatMonthTitle(date) {
date = new Date(date) return dayjs(date).format(translate('monthTitle'))
const year = date.getFullYear()
const month = date.getMonth() + 1
return year + '年' + month + '月'
} }
/** /**
@ -129,9 +135,7 @@ export function getFirstDayStyle(index: number, date: number, firstDayOfWeek: nu
* @param {timestamp} date * @param {timestamp} date
*/ */
export function formatYearTitle(date: number) { export function formatYearTitle(date: number) {
const year = new Date(date).getFullYear() return dayjs(date).format(translate('yearTitle'))
return year + '年'
} }
/** /**
@ -350,14 +354,14 @@ export function getTimeData({ date, minDate, maxDate, isHideSecond, filter } = {
let columns: any[] = [] let columns: any[] = []
let hours = times(24, (index) => { let hours = times(24, (index) => {
return { return {
label: `${padZero(index)}`, label: translate('hour', padZero(index)),
value: index, value: index,
disabled: index < minHour || index > maxHour disabled: index < minHour || index > maxHour
} }
}) })
let minutes = times(60, (index) => { let minutes = times(60, (index) => {
return { return {
label: `${padZero(index)}`, label: translate('minute', padZero(index)),
value: index, value: index,
disabled: index < minMinute || index > maxMinute disabled: index < minMinute || index > maxMinute
} }
@ -377,7 +381,7 @@ export function getTimeData({ date, minDate, maxDate, isHideSecond, filter } = {
if (!isHideSecond) { if (!isHideSecond) {
seconds = times(60, (index) => { seconds = times(60, (index) => {
return { return {
label: `${padZero(index)}`, label: translate('second', padZero(index)),
value: index, value: index,
disabled: index < minSecond || index > maxSecond disabled: index < minSecond || index > maxSecond
} }

View File

@ -11,7 +11,7 @@
@click="handleDateClick(index)" @click="handleDateClick(index)"
> >
<view class="wd-year__month-top">{{ item.topInfo }}</view> <view class="wd-year__month-top">{{ item.topInfo }}</view>
<view class="wd-year__month-text">{{ item.text }}</view> <view class="wd-year__month-text">{{ getMonthLabel(item.date) }}</view>
<view class="wd-year__month-bottom">{{ item.bottomInfo }}</view> <view class="wd-year__month-bottom">{{ item.bottomInfo }}</view>
</view> </view>
</view> </view>
@ -29,9 +29,11 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { deepClone, getType } from '../../common/util' import { deepClone, getType, padZero } from '../../common/util'
import { compareMonth, formatYearTitle, getDateByDefaultTime, getItemClass, getMonthByOffset, getMonthOffset } from '../utils' import { compareMonth, formatYearTitle, getDateByDefaultTime, getItemClass, getMonthByOffset, getMonthOffset } from '../utils'
import { useToast } from '../../wd-toast' import { useToast } from '../../wd-toast'
import { useTranslate } from '../../composables/useTranslate'
import { dayjs } from '../../common/dayjs'
interface Props { interface Props {
type: string type: string
@ -50,6 +52,7 @@ const props = withDefaults(defineProps<Props>(), {
allowSameDay: false allowSameDay: false
}) })
const toast = useToast('wd-year') const toast = useToast('wd-year')
const { translate } = useTranslate('calendar-view')
const months = ref<Record<string, any>[]>([]) const months = ref<Record<string, any>[]>([])
@ -78,6 +81,10 @@ watch(
} }
) )
function getMonthLabel(date) {
return dayjs(date).format(translate('month', date))
}
function setMonths() { function setMonths() {
const monthList: Record<string, any>[] = [] const monthList: Record<string, any>[] = []
const date = new Date(props.date) const date = new Date(props.date)
@ -166,7 +173,7 @@ function handleMonthRangeChange(date) {
const maxEndDate = getMonthByOffset(startDate, props.maxRange - 1) const maxEndDate = getMonthByOffset(startDate, props.maxRange - 1)
value = [startDate, getDate(maxEndDate)] value = [startDate, getDate(maxEndDate)]
toast.show({ toast.show({
msg: props.rangePrompt || `选择月份不能超过${props.maxRange}个月` msg: props.rangePrompt || translate('rangePromptMonth', props.maxRange)
}) })
} else { } else {
value = [startDate, getDate(date.date)] value = [startDate, getDate(date.date)]

View File

@ -21,7 +21,7 @@
<view <view
:class="`wd-calendar__value ${ellipsis ? 'is-ellipsis' : ''} ${customValueClass} ${showValue ? '' : 'wd-calendar__value--placeholder'}`" :class="`wd-calendar__value ${ellipsis ? 'is-ellipsis' : ''} ${customValueClass} ${showValue ? '' : 'wd-calendar__value--placeholder'}`"
> >
{{ showValue || placeholder || '请选择' }} {{ showValue || placeholder || translate('placeholder') }}
</view> </view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-calendar__arrow" name="arrow-right" /> <wd-icon v-if="!disabled && !readonly" custom-class="wd-calendar__arrow" name="arrow-right" />
</view> </view>
@ -38,12 +38,12 @@
@close="close" @close="close"
> >
<view class="wd-calendar__header"> <view class="wd-calendar__header">
<view v-if="!showTypeSwitch && shortcuts.length === 0" class="wd-calendar__title">{{ title || '选择日期' }}</view> <view v-if="!showTypeSwitch && shortcuts.length === 0" class="wd-calendar__title">{{ title || translate('title') }}</view>
<view v-if="showTypeSwitch" class="wd-calendar__tabs"> <view v-if="showTypeSwitch" class="wd-calendar__tabs">
<wd-tabs ref="calendarTabs" v-model="currentTab" @change="handleTypeChange"> <wd-tabs ref="calendarTabs" v-model="currentTab" @change="handleTypeChange">
<wd-tab title="日" name="日" /> <wd-tab :title="translate('day')" :name="translate('day')" />
<wd-tab title="周" name="周" /> <wd-tab :title="translate('week')" :name="translate('week')" />
<wd-tab title="月" name="月" /> <wd-tab :title="translate('month')" :name="translate('month')" />
</wd-tabs> </wd-tabs>
</view> </view>
<view v-if="shortcuts.length > 0" class="wd-calendar__shortcuts"> <view v-if="shortcuts.length > 0" class="wd-calendar__shortcuts">
@ -94,7 +94,7 @@
/> />
</view> </view>
<view v-if="showConfirm" class="wd-calendar__confirm"> <view v-if="showConfirm" class="wd-calendar__confirm">
<wd-button block :disabled="confirmBtnDisabled" @click="handleConfirm">{{ confirmText || '确定' }}</wd-button> <wd-button block :disabled="confirmBtnDisabled" @click="handleConfirm">{{ confirmText || translate('confirm') }}</wd-button>
</view> </view>
</wd-action-sheet> </wd-action-sheet>
</view> </view>
@ -119,6 +119,8 @@ import { getWeekNumber, isRange } from '../wd-calendar-view/utils'
import { useCell } from '../composables/useCell' import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('calendar')
const defaultDisplayFormat = (value, type) => { const defaultDisplayFormat = (value, type) => {
switch (type) { switch (type) {
@ -131,29 +133,35 @@ const defaultDisplayFormat = (value, type) => {
}) })
.join(', ') .join(', ')
case 'daterange': case 'daterange':
return `${value[0] ? dayjs(value[0]).format('YYYY-MM-DD') : '开始时间'}${value[1] ? dayjs(value[1]).format('YYYY-MM-DD') : '结束时间'}` return `${value[0] ? dayjs(value[0]).format('YYYY-MM-DD') : translate('startTime')} ${translate('to')} ${
value[1] ? dayjs(value[1]).format('YYYY-MM-DD') : translate('endTime')
}`
case 'datetime': case 'datetime':
return dayjs(value).format('YYYY-MM-DD HH:mm:ss') return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
case 'datetimerange': case 'datetimerange':
return `${value[0] ? dayjs(value[0]).format('YY年MM月DD日 HH:mm:ss') : '开始时间'}\n${ return `${value[0] ? dayjs(value[0]).format(translate('timeFormat')) : translate('startTime')} ${translate('to')}\n${
value[1] ? dayjs(value[1]).format('YY年MM月DD日 HH:mm:ss') : '结束时间' value[1] ? dayjs(value[1]).format(translate('timeFormat')) : translate('endTime')
}` }`
case 'week': { case 'week': {
const year = new Date(value).getFullYear() const year = new Date(value).getFullYear()
const week = getWeekNumber(value) const week = getWeekNumber(value)
return `${year}${padZero(week)}` return translate('weekFormat', year, padZero(week))
} }
case 'weekrange': { case 'weekrange': {
const year1 = new Date(value[0]).getFullYear() const year1 = new Date(value[0]).getFullYear()
const week1 = getWeekNumber(value[0]) const week1 = getWeekNumber(value[0])
const year2 = new Date(value[1]).getFullYear() const year2 = new Date(value[1]).getFullYear()
const week2 = getWeekNumber(value[1]) const week2 = getWeekNumber(value[1])
return `${value[0] ? `${year1}${padZero(week1)}` : '开始周'} - ${value[1] ? `${year2}${padZero(week2)}` : '结束周'}` return `${value[0] ? translate('weekFormat', year1, padZero(week1)) : translate('startWeek')} - ${
value[1] ? translate('weekFormat', year2, padZero(week2)) : translate('endWeek')
}`
} }
case 'month': case 'month':
return dayjs(value).format('YYYY / MM') return dayjs(value).format('YYYY / MM')
case 'monthrange': case 'monthrange':
return `${value[0] ? dayjs(value[0]).format('YYYY / MM') : '开始月'}${value[1] ? dayjs(value[1]).format('YYYY / MM') : '结束月'}` return `${value[0] ? dayjs(value[0]).format('YYYY / MM') : translate('startMonth')} ${translate('to')} ${
value[1] ? dayjs(value[1]).format('YYYY / MM') : translate('endMonth')
}`
} }
} }
@ -161,28 +169,28 @@ const formatRange = (value, rangeType, type) => {
switch (type) { switch (type) {
case 'daterange': case 'daterange':
if (!value) { if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间' return rangeType === 'end' ? translate('endTime') : translate('startTime')
} }
return dayjs(value).format('YYYY年MM月DD日') return dayjs(value).format(translate('dateFormat'))
case 'datetimerange': case 'datetimerange':
if (!value) { if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间' return rangeType === 'end' ? translate('endTime') : translate('startTime')
} }
return dayjs(value).format('YY年MM月DD日 HH:mm:ss') return dayjs(value).format(translate('timeFormat'))
case 'weekrange': { case 'weekrange': {
if (!value) { if (!value) {
return rangeType === 'end' ? '结束周' : '开始周' return rangeType === 'end' ? translate('endWeek') : translate('startWeek')
} }
const date = new Date(value) const date = new Date(value)
const year = date.getFullYear() const year = date.getFullYear()
const week = getWeekNumber(value) const week = getWeekNumber(value)
return year + '年第' + week + '周' return translate('weekFormat', year, padZero(week))
} }
case 'monthrange': case 'monthrange':
if (!value) { if (!value) {
return rangeType === 'end' ? '结束月' : '开始月' return rangeType === 'end' ? translate('endMonth') : translate('startMonth')
} }
return dayjs(value).format('YYYY年MM月') return dayjs(value).format(translate('monthFormat'))
} }
} }

View File

@ -21,7 +21,7 @@
<view <view
:class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-col-picker__value--placeholder'}`" :class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-col-picker__value--placeholder'}`"
> >
{{ showValue || placeholder || '请选择' }} {{ showValue || placeholder || translate('placeholder') }}
</view> </view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" /> <wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" />
</view> </view>
@ -32,7 +32,7 @@
<wd-action-sheet <wd-action-sheet
v-model="pickerShow" v-model="pickerShow"
:duration="250" :duration="250"
:title="title || '请选择'" :title="title || translate('title')"
:close-on-click-modal="closeOnClickModal" :close-on-click-modal="closeOnClickModal"
:z-index="zIndex" :z-index="zIndex"
:safe-area-inset-bottom="safeAreaInsetBottom" :safe-area-inset-bottom="safeAreaInsetBottom"
@ -48,7 +48,7 @@
:class="`wd-col-picker__selected-item ${colIndex === currentCol && 'is-selected'}`" :class="`wd-col-picker__selected-item ${colIndex === currentCol && 'is-selected'}`"
@click="handleColClick(colIndex)" @click="handleColClick(colIndex)"
> >
{{ selectShowList[colIndex] || '请选择' }} {{ selectShowList[colIndex] || translate('select') }}
</view> </view>
<view class="wd-col-picker__selected-line" :style="lineStyle"></view> <view class="wd-col-picker__selected-line" :style="lineStyle"></view>
</view> </view>
@ -100,6 +100,9 @@ import { debounce, getRect, getType } from '../common/util'
import { useCell } from '../composables/useCell' import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('col-picker')
const $container = '.wd-col-picker__selected-container' const $container = '.wd-col-picker__selected-container'
const $item = '.wd-col-picker__selected-item' const $item = '.wd-col-picker__selected-item'
@ -154,7 +157,6 @@ const props = withDefaults(defineProps<Props>(), {
useDefaultSlot: false, useDefaultSlot: false,
disabled: false, disabled: false,
readonly: false, readonly: false,
placeholder: '请选择',
alignRight: false, alignRight: false,
error: false, error: false,
required: false, required: false,

View File

@ -1,10 +1,10 @@
<!-- <!--
* @Author: weisheng * @Author: weisheng
* @Date: 2023-06-13 11:34:35 * @Date: 2023-06-13 11:34:35
* @LastEditTime: 2024-01-03 21:59:39 * @LastEditTime: 2024-01-26 11:53:06
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-col/wd-col.vue * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-col\wd-col.vue
* 记得注释 * 记得注释
--> -->
<template> <template>
@ -25,7 +25,7 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, provide, watch } from 'vue' import { computed, watch } from 'vue'
import { ref } from 'vue' import { ref } from 'vue'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { ROW_KEY } from '../wd-row/types' import { ROW_KEY } from '../wd-row/types'

View File

@ -19,7 +19,7 @@
</view> </view>
<!-- 显示展开或折叠按钮 --> <!-- 显示展开或折叠按钮 -->
<block v-else> <block v-else>
<span class="wd-collapse__more-txt">{{ !modelValue ? '展开' : '折叠' }}</span> <span class="wd-collapse__more-txt">{{ !modelValue ? translate('expand') : translate('retract') }}</span>
<view :class="`wd-collapse__arrow ${modelValue ? 'is-retract' : ''}`"> <view :class="`wd-collapse__arrow ${modelValue ? 'is-retract' : ''}`">
<wd-icon name="arrow-down"></wd-icon> <wd-icon name="arrow-down"></wd-icon>
</view> </view>
@ -45,6 +45,7 @@ import { onBeforeMount, ref, watch } from 'vue'
import { COLLAPSE_KEY, type CollapseToggleAllOptions } from './types' import { COLLAPSE_KEY, type CollapseToggleAllOptions } from './types'
import { useChildren } from '../composables/useChildren' import { useChildren } from '../composables/useChildren'
import { isArray, isDef } from '../common/util' import { isArray, isDef } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
customClass?: string customClass?: string
@ -65,6 +66,7 @@ const props = withDefaults(defineProps<Props>(), {
lineNum: 2 lineNum: 2
}) })
const { translate } = useTranslate('collapse')
const contentLineNum = ref<number>(0) // const contentLineNum = ref<number>(0) //
const { linkChildren, children } = useChildren(COLLAPSE_KEY) const { linkChildren, children } = useChildren(COLLAPSE_KEY)

View File

@ -21,11 +21,11 @@
<view :class="`wd-picker__value ${customValueClass}`"> <view :class="`wd-picker__value ${customValueClass}`">
<view v-if="region"> <view v-if="region">
<text :class="showValue[0] ? '' : 'wd-picker__placeholder'">{{ showValue[0] ? showValue[0] : placeholder }}</text> <text :class="showValue[0] ? '' : 'wd-picker__placeholder'">{{ showValue[0] ? showValue[0] : placeholder }}</text>
{{ translate('to') }}
<text :class="showValue[1] ? '' : 'wd-picker__placeholder'">{{ showValue[1] ? showValue[1] : placeholder }}</text> <text :class="showValue[1] ? '' : 'wd-picker__placeholder'">{{ showValue[1] ? showValue[1] : placeholder }}</text>
</view> </view>
<view v-else :class="showValue ? '' : 'wd-picker__placeholder'"> <view v-else :class="showValue ? '' : 'wd-picker__placeholder'">
{{ showValue ? showValue : placeholder }} {{ showValue ? showValue : placeholder || translate('placeholder') }}
</view> </view>
</view> </view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" /> <wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
@ -50,23 +50,23 @@
<view class="wd-picker__toolbar" @touchmove="noop"> <view class="wd-picker__toolbar" @touchmove="noop">
<!--取消按钮--> <!--取消按钮-->
<view class="wd-picker__action wd-picker__action--cancel" @click="onCancel"> <view class="wd-picker__action wd-picker__action--cancel" @click="onCancel">
{{ cancelButtonText }} {{ cancelButtonText || translate('cancel') }}
</view> </view>
<!--标题--> <!--标题-->
<view v-if="title" class="wd-picker__title">{{ title }}</view> <view v-if="title" class="wd-picker__title">{{ title }}</view>
<!--确定按钮--> <!--确定按钮-->
<view :class="`wd-picker__action ${loading || isLoading ? 'is-loading' : ''}`" @click="onConfirm"> <view :class="`wd-picker__action ${loading || isLoading ? 'is-loading' : ''}`" @click="onConfirm">
{{ confirmButtonText }} {{ confirmButtonText || translate('confirm') }}
</view> </view>
</view> </view>
<!-- 区域选择tab展示 --> <!-- 区域选择tab展示 -->
<view v-if="region" class="wd-picker__region-tabs"> <view v-if="region" class="wd-picker__region-tabs">
<view :class="`wd-picker__region ${showStart ? 'is-active' : ''} `" @click="tabChange"> <view :class="`wd-picker__region ${showStart ? 'is-active' : ''} `" @click="tabChange">
<view>开始时间</view> <view>{{ translate('start') }}</view>
<view class="wd-picker__region-time">{{ showTabLabel[0] }}</view> <view class="wd-picker__region-time">{{ showTabLabel[0] }}</view>
</view> </view>
<view :class="`wd-picker__region ${showStart ? '' : 'is-active'}`" @click="tabChange"> <view :class="`wd-picker__region ${showStart ? '' : 'is-active'}`" @click="tabChange">
<view>结束时间</view> <view>{{ translate('end') }}</view>
<view class="wd-picker__region-time">{{ showTabLabel[1] }}</view> <view class="wd-picker__region-time">{{ showTabLabel[1] }}</view>
</view> </view>
</view> </view>
@ -146,6 +146,7 @@ import { useCell } from '../composables/useCell'
import { type DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type' import { type DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
customClass?: string customClass?: string
customViewClass?: string customViewClass?: string
@ -227,19 +228,13 @@ const props = withDefaults(defineProps<Props>(), {
customViewClass: '', customViewClass: '',
customLabelClass: '', customLabelClass: '',
customValueClass: '', customValueClass: '',
//
placeholder: '请选择',
// //
disabled: false, disabled: false,
// //
readonly: false, readonly: false,
loading: false, loading: false,
loadingColor: '#4D80F0', loadingColor: '#4D80F0',
/* popup */
//
cancelButtonText: '取消',
//
confirmButtonText: '完成',
// //
required: false, required: false,
labelWidth: '33%', labelWidth: '33%',
@ -274,6 +269,9 @@ const props = withDefaults(defineProps<Props>(), {
zIndex: 15, zIndex: 15,
rules: () => [] rules: () => []
}) })
const { translate } = useTranslate('datetime-picker')
const datetimePickerView = ref() const datetimePickerView = ref()
const datetimePickerView1 = ref() const datetimePickerView1 = ref()

View File

@ -58,8 +58,8 @@
<view class="wd-img-cropper__footer"> <view class="wd-img-cropper__footer">
<wd-icon v-if="!disabledRotate" name="rotate" size="24px" color="#fff" data-eventsync="true" @click="handleRotate"></wd-icon> <wd-icon v-if="!disabledRotate" name="rotate" size="24px" color="#fff" data-eventsync="true" @click="handleRotate"></wd-icon>
<view class="wd-img-cropper__footer--button"> <view class="wd-img-cropper__footer--button">
<view class="is-cancel" @click="handleCancel">{{ cancelButtonText }}</view> <view class="is-cancel" @click="handleCancel">{{ cancelButtonText || translate('cancel') }}</view>
<wd-button size="small" :custom-style="buttonStyle" @click="handleConfirm">{{ confirmButtonText }}</wd-button> <wd-button size="small" :custom-style="buttonStyle" @click="handleConfirm">{{ confirmButtonText || translate('confirm') }}</wd-button>
</view> </view>
</view> </view>
</view> </view>
@ -79,6 +79,7 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { computed, getCurrentInstance, ref, watch } from 'vue' import { computed, getCurrentInstance, ref, watch } from 'vue'
import { addUnit, objToStyle } from '../common/util' import { addUnit, objToStyle } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
// //
let CHANGE_TIME: any | null = null let CHANGE_TIME: any | null = null
@ -118,11 +119,11 @@ interface Props {
maxScale?: number maxScale?: number
} }
const { translate } = useTranslate('img-cropper')
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
customClass: '', customClass: '',
modelValue: false, modelValue: false,
cancelButtonText: '取消',
confirmButtonText: '完成',
// //
disabledRotate: false, disabledRotate: false,
/** canvas绘图参数 start **/ /** canvas绘图参数 start **/

View File

@ -28,7 +28,7 @@
:type="type" :type="type"
:password="showPassword && !isPwdVisible" :password="showPassword && !isPwdVisible"
v-model="inputValue" v-model="inputValue"
:placeholder="placeholder" :placeholder="placeholder || translate('placeholder')"
:disabled="disabled" :disabled="disabled"
:maxlength="maxlength" :maxlength="maxlength"
:focus="isFocus" :focus="isFocus"
@ -90,6 +90,7 @@ import { objToStyle, requestAnimationFrame } from '../common/util'
import { useCell } from '../composables/useCell' import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
customInputClass?: string customInputClass?: string
@ -136,6 +137,8 @@ interface Props {
rules?: FormItemRule[] rules?: FormItemRule[]
} }
const { translate } = useTranslate('input')
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
customInputClass: '', customInputClass: '',
customLabelClass: '', customLabelClass: '',
@ -144,7 +147,6 @@ const props = withDefaults(defineProps<Props>(), {
type: 'text', type: 'text',
maxlength: -1, maxlength: -1,
modelValue: '', modelValue: '',
placeholder: '请输入...',
clearable: false, clearable: false,
showPassword: false, showPassword: false,
disabled: false, disabled: false,

View File

@ -1,19 +1,19 @@
<template> <template>
<view :class="['wd-loadmore', customClass]" @click="reload"> <view :class="['wd-loadmore', customClass]" @click="reload">
<wd-divider v-if="state === 'finished'">{{ finishedText }}</wd-divider> <wd-divider v-if="state === 'finished'">{{ finishedText || translate('finished') }}</wd-divider>
<block v-if="state === 'error'"> <block v-if="state === 'error'">
<block v-if="errorText"> <block v-if="errorText">
{{ errorText }} {{ errorText }}
</block> </block>
<block v-else> <block v-else>
<text class="wd-loadmore__text">加载失败</text> <text class="wd-loadmore__text">{{ translate('error') }}</text>
<text class="wd-loadmore__text is-light">点击重试</text> <text class="wd-loadmore__text is-light">{{ translate('retry') }}</text>
<wd-icon name="refresh" size="16px" custom-class="wd-loadmore__refresh" /> <wd-icon name="refresh" size="16px" custom-class="wd-loadmore__refresh" />
</block> </block>
</block> </block>
<block v-if="state === 'loading'"> <block v-if="state === 'loading'">
<wd-loading size="16px" custom-class="wd-loadmore__loading" /> <wd-loading size="16px" custom-class="wd-loadmore__loading" />
<text class="wd-loadmore__text">{{ loadingText }}</text> <text class="wd-loadmore__text">{{ loadingText || translate('loading') }}</text>
</block> </block>
</view> </view>
</template> </template>
@ -30,7 +30,8 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from 'vue' import { ref } from 'vue'
import { useTranslate } from '../composables/useTranslate'
type LoadMoreState = 'loading' | 'error' | 'finished' type LoadMoreState = 'loading' | 'error' | 'finished'
@ -44,36 +45,12 @@ interface Props {
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
customClass: '', customClass: '',
loadingText: '正在努力加载中...',
finishedText: '已加载完毕',
errorText: '' errorText: ''
}) })
const showText = ref<string>('') const { translate } = useTranslate('loadmore')
const currentState = ref<LoadMoreState | null>(null)
watch( const currentState = ref<LoadMoreState | null>(null)
() => props.state,
(newValue) => {
if (!newValue) return
const state = ['loading', 'error', 'finished']
if (state.indexOf(newValue) === -1) console.error(`state must be one of ${state.toString()}`)
switch (newValue) {
case 'loading':
showText.value = props.loadingText
break
case 'error':
showText.value = props.errorText
break
case 'finished':
showText.value = props.finishedText
break
default:
break
}
},
{ deep: true, immediate: true }
)
const emit = defineEmits(['reload']) const emit = defineEmits(['reload'])

View File

@ -1,7 +1,7 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2022-12-14 17:33:21 * @Date: 2022-12-14 17:33:21
* @LastEditTime: 2023-09-07 00:29:51 * @LastEditTime: 2024-01-26 11:19:42
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-message-box\index.ts * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-message-box\index.ts
@ -10,6 +10,9 @@
import { provide, ref } from 'vue' import { provide, ref } from 'vue'
import type { Message, MessageOptions, MessageResult, MessageType } from './types' import type { Message, MessageOptions, MessageResult, MessageType } from './types'
import { deepMerge } from '../common/util' import { deepMerge } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('message-box')
/** /**
* useMessage key * useMessage key
@ -24,13 +27,13 @@ export const defaultOptions: MessageOptions = {
showCancelButton: false, showCancelButton: false,
show: false, show: false,
closeOnClickModal: true, closeOnClickModal: true,
confirmButtonText: '确定', confirmButtonText: translate('confirm'),
cancelButtonText: '取消', cancelButtonText: translate('cancel'),
msg: '', msg: '',
type: 'alert', type: 'alert',
inputType: 'text', inputType: 'text',
inputValue: '', inputValue: '',
inputPlaceholder: '请输入', inputPlaceholder: translate('inputPlaceholder'),
inputValidate: null, inputValidate: null,
showErr: false, showErr: false,
zIndex: 99, zIndex: 99,

View File

@ -25,7 +25,7 @@
<wd-input v-model="inputValue" :type="inputType" size="large" :placeholder="inputPlaceholder || '请输入'" @input="inputValChange" /> <wd-input v-model="inputValue" :type="inputType" size="large" :placeholder="inputPlaceholder || '请输入'" @input="inputValChange" />
<!--错误提示--> <!--错误提示-->
<view v-if="showErr" class="wd-message-box__input-error"> <view v-if="showErr" class="wd-message-box__input-error">
{{ inputError || '输入的数据不合法' }} {{ inputError || translate('inputNoValidate') }}
</view> </view>
</block> </block>
<!--使用插槽--> <!--使用插槽-->
@ -37,10 +37,10 @@
<!--action按钮组合--> <!--action按钮组合-->
<view :class="`wd-message-box__actions ${showCancelButton ? 'wd-message-box__flex' : 'wd-message-box__block'}`"> <view :class="`wd-message-box__actions ${showCancelButton ? 'wd-message-box__flex' : 'wd-message-box__block'}`">
<wd-button type="info" block v-if="showCancelButton" custom-style="margin-right: 16px;" @click="toggleModal('cancel')"> <wd-button type="info" block v-if="showCancelButton" custom-style="margin-right: 16px;" @click="toggleModal('cancel')">
{{ cancelButtonText || '取消' }} {{ cancelButtonText || translate('cancel') }}
</wd-button> </wd-button>
<wd-button block @click="toggleModal('confirm')"> <wd-button block @click="toggleModal('confirm')">
{{ confirmButtonText || '确定' }} {{ confirmButtonText || translate('confirm') }}
</wd-button> </wd-button>
</view> </view>
</view> </view>
@ -63,6 +63,7 @@ import { computed, inject, ref, watch } from 'vue'
import type { MessageOptions, MessageType } from './types' import type { MessageOptions, MessageType } from './types'
import { defaultOptions, messageDefaultOptionKey } from '.' import { defaultOptions, messageDefaultOptionKey } from '.'
import { isDef } from '../common/util' import { isDef } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
useSlot?: boolean useSlot?: boolean
@ -76,6 +77,8 @@ const props = withDefaults(defineProps<Props>(), {
selector: '' selector: ''
}) })
const { translate } = useTranslate('message-box')
const rootClass = computed(() => { const rootClass = computed(() => {
return `wd-message-box__container ${props.customClass}` return `wd-message-box__container ${props.customClass}`
}) })

View File

@ -2,7 +2,7 @@
<view :class="`wd-pager ${customClass}`" v-if="!(hideIfOnePage && totalPageNum === 1)"> <view :class="`wd-pager ${customClass}`" v-if="!(hideIfOnePage && totalPageNum === 1)">
<view class="wd-pager__content"> <view class="wd-pager__content">
<wd-button :plain="modelValue > 1" type="info" size="small" :disabled="modelValue <= 1" custom-class="wd-pager__nav" @click="sub"> <wd-button :plain="modelValue > 1" type="info" size="small" :disabled="modelValue <= 1" custom-class="wd-pager__nav" @click="sub">
<text v-if="!showIcon">{{ prevText }}</text> <text v-if="!showIcon">{{ prevText || translate('prev') }}</text>
<wd-icon <wd-icon
v-else v-else
size="14px" size="14px"
@ -23,7 +23,7 @@
custom-class="wd-pager__nav" custom-class="wd-pager__nav"
@click="add" @click="add"
> >
<text v-if="!showIcon">{{ nextText }}</text> <text v-if="!showIcon">{{ nextText || translate('next') }}</text>
<wd-icon <wd-icon
v-else v-else
size="14px" size="14px"
@ -33,9 +33,9 @@
</wd-button> </wd-button>
</view> </view>
<view class="wd-pager__message" v-if="showMessage"> <view class="wd-pager__message" v-if="showMessage">
<text>当前页{{ modelValue }}</text> <text>{{ translate('page', modelValue) }}</text>
<text v-if="total">当前数据{{ total }}</text> <text v-if="total">{{ translate('total', total) }}</text>
<text>分页大小{{ pageSize }}</text> <text>{{ translate('size', pageSize) }}</text>
</view> </view>
</view> </view>
</template> </template>
@ -53,6 +53,9 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('pagination')
interface Props { interface Props {
customClass?: string customClass?: string
@ -74,8 +77,6 @@ const props = withDefaults(defineProps<Props>(), {
showMessage: false, showMessage: false,
total: 0, total: 0,
pageSize: 10, // pageSize: 10, //
prevText: '上一页',
nextText: '下一页',
hideIfOnePage: true hideIfOnePage: true
}) })

View File

@ -19,7 +19,7 @@
<view class="wd-picker__body"> <view class="wd-picker__body">
<view class="wd-picker__value-wraper"> <view class="wd-picker__value-wraper">
<view :class="`wd-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-picker__placeholder'}`"> <view :class="`wd-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-picker__placeholder'}`">
{{ showValue ? showValue : placeholder }} {{ showValue ? showValue : placeholder || translate('placeholder') }}
</view> </view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" /> <wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
</view> </view>
@ -43,13 +43,13 @@
<view class="wd-picker__toolbar" @touchmove="noop"> <view class="wd-picker__toolbar" @touchmove="noop">
<!--取消按钮--> <!--取消按钮-->
<view class="wd-picker__action wd-picker__action--cancel" @click="onCancel"> <view class="wd-picker__action wd-picker__action--cancel" @click="onCancel">
{{ cancelButtonText }} {{ cancelButtonText || translate('cancel') }}
</view> </view>
<!--标题--> <!--标题-->
<view v-if="title" class="wd-picker__title">{{ title }}</view> <view v-if="title" class="wd-picker__title">{{ title }}</view>
<!--确定按钮--> <!--确定按钮-->
<view :class="`wd-picker__action ${isLoading ? 'is-loading' : ''}`" @click="onConfirm"> <view :class="`wd-picker__action ${isLoading ? 'is-loading' : ''}`" @click="onConfirm">
{{ confirmButtonText }} {{ confirmButtonText || translate('done') }}
</view> </view>
</view> </view>
<!--pickerView--> <!--pickerView-->
@ -91,6 +91,8 @@ import { useCell } from '../composables/useCell'
import { type ColumnItem, formatArray } from '../wd-picker-view/type' import { type ColumnItem, formatArray } from '../wd-picker-view/type'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
const { translate } = useTranslate('picker')
interface Props { interface Props {
customClass?: string customClass?: string
@ -157,17 +159,12 @@ const props = withDefaults(defineProps<Props>(), {
customLabelClass: '', customLabelClass: '',
customValueClass: '', customValueClass: '',
// //
placeholder: '请选择',
// //
disabled: false, disabled: false,
// //
readonly: false, readonly: false,
loading: false, loading: false,
loadingColor: '#4D80F0', loadingColor: '#4D80F0',
//
cancelButtonText: '取消',
//
confirmButtonText: '完成',
// //
required: false, required: false,
useDefaultSlot: false, useDefaultSlot: false,

View File

@ -7,14 +7,14 @@
<view class="wd-search__field"> <view class="wd-search__field">
<view v-if="!placeholderLeft" :style="coverStyle" class="wd-search__cover" @click="closeCover"> <view v-if="!placeholderLeft" :style="coverStyle" class="wd-search__cover" @click="closeCover">
<wd-icon name="search" size="18px" custom-class="wd-search__search-icon"></wd-icon> <wd-icon name="search" size="18px" custom-class="wd-search__search-icon"></wd-icon>
<text class="wd-search__placeholder-txt">{{ placeholder || '搜索' }}</text> <text class="wd-search__placeholder-txt">{{ placeholder || translate('search') }}</text>
</view> </view>
<!--icon:search--> <!--icon:search-->
<wd-icon v-if="showInput || str || placeholderLeft" name="search" size="18px" custom-class="wd-search__search-left-icon"></wd-icon> <wd-icon v-if="showInput || str || placeholderLeft" name="search" size="18px" custom-class="wd-search__search-left-icon"></wd-icon>
<!--搜索框--> <!--搜索框-->
<input <input
v-if="showInput || str || placeholderLeft" v-if="showInput || str || placeholderLeft"
:placeholder="placeholder || '搜索'" :placeholder="placeholder || translate('search')"
placeholder-class="wd-search__placeholder-txt" placeholder-class="wd-search__placeholder-txt"
confirm-type="search" confirm-type="search"
v-model="str" v-model="str"
@ -37,7 +37,7 @@
<slot v-if="userSuffixSlot" name="suffix"></slot> <slot v-if="userSuffixSlot" name="suffix"></slot>
<!--默认button--> <!--默认button-->
<view v-else class="wd-search__cancel" @click="handleCancel"> <view v-else class="wd-search__cancel" @click="handleCancel">
{{ cancelTxt || '取消' }} {{ cancelTxt || translate('cancel') }}
</view> </view>
</block> </block>
</view> </view>
@ -57,6 +57,7 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { type CSSProperties, computed, onMounted, ref, watch } from 'vue' import { type CSSProperties, computed, onMounted, ref, watch } from 'vue'
import { objToStyle, requestAnimationFrame } from '../common/util' import { objToStyle, requestAnimationFrame } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
userSuffixSlot?: boolean userSuffixSlot?: boolean
@ -74,13 +75,13 @@ interface Props {
customStyle?: string customStyle?: string
} }
const { translate } = useTranslate('search')
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: '', modelValue: '',
customClass: '', customClass: '',
customStyle: '', customStyle: '',
userSuffixSlot: false, userSuffixSlot: false,
placeholder: '搜索',
cancelTxt: '取消',
light: false, light: false,
focus: false, focus: false,
focusWhenClear: false, focusWhenClear: false,

View File

@ -23,7 +23,7 @@
showValue ? '' : 'wd-select-picker__value--placeholder' showValue ? '' : 'wd-select-picker__value--placeholder'
}`" }`"
> >
{{ showValue || placeholder || '请选择' }} {{ showValue || placeholder || translate('placeholder') }}
</view> </view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-select-picker__arrow" name="arrow-right" /> <wd-icon v-if="!disabled && !readonly" custom-class="wd-select-picker__arrow" name="arrow-right" />
</view> </view>
@ -35,7 +35,7 @@
<wd-action-sheet <wd-action-sheet
v-model="pickerShow" v-model="pickerShow"
:duration="250" :duration="250"
:title="title || '请选择'" :title="title || translate('title')"
:close-on-click-modal="closeOnClickModal" :close-on-click-modal="closeOnClickModal"
:z-index="zIndex" :z-index="zIndex"
:safe-area-inset-bottom="safeAreaInsetBottom" :safe-area-inset-bottom="safeAreaInsetBottom"
@ -43,7 +43,14 @@
@opened="scrollIntoView ? setScrollIntoView() : ''" @opened="scrollIntoView ? setScrollIntoView() : ''"
custom-header-class="wd-select-picker__header" custom-header-class="wd-select-picker__header"
> >
<wd-search v-if="filterable" v-model="filterVal" :placeholder="filterPlaceholder" hide-cancel placeholder-left @change="handleFilterChange" /> <wd-search
v-if="filterable"
v-model="filterVal"
:placeholder="filterPlaceholder || translate('filterPlaceholder')"
hide-cancel
placeholder-left
@change="handleFilterChange"
/>
<scroll-view <scroll-view
:class="`wd-select-picker__wrapper ${filterable ? 'is-filterable' : ''} ${loading ? 'is-loading' : ''} ${customContentClass}`" :class="`wd-select-picker__wrapper ${filterable ? 'is-filterable' : ''} ${loading ? 'is-loading' : ''} ${customContentClass}`"
:scroll-y="!loading" :scroll-y="!loading"
@ -91,7 +98,7 @@
</scroll-view> </scroll-view>
<!-- 确认按钮 --> <!-- 确认按钮 -->
<view class="wd-select-picker__footer"> <view class="wd-select-picker__footer">
<wd-button block size="large" @click="onConfirm" :disabled="loading">{{ confirmButtonText }}</wd-button> <wd-button block size="large" @click="onConfirm" :disabled="loading">{{ confirmButtonText || translate('confirm') }}</wd-button>
</view> </view>
</wd-action-sheet> </wd-action-sheet>
</view> </view>
@ -113,9 +120,12 @@ import { useCell } from '../composables/useCell'
import { getRect, getType, isArray, isDef, requestAnimationFrame } from '../common/util' import { getRect, getType, isArray, isDef, requestAnimationFrame } from '../common/util'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useTranslate } from '../composables/useTranslate'
type SelectPickerType = 'checkbox' | 'radio' type SelectPickerType = 'checkbox' | 'radio'
const { translate } = useTranslate('select-picker')
interface Props { interface Props {
customClass?: string customClass?: string
customContentClass?: string customContentClass?: string
@ -170,12 +180,10 @@ const props = withDefaults(defineProps<Props>(), {
type: 'checkbox', type: 'checkbox',
valueKey: 'value', valueKey: 'value',
labelKey: 'label', labelKey: 'label',
placeholder: '请选择',
disabled: false, disabled: false,
loading: false, loading: false,
loadingColor: '#4D80F0', loadingColor: '#4D80F0',
readonly: false, readonly: false,
confirmButtonText: '确认',
labelWidth: '33%', labelWidth: '33%',
error: false, error: false,
required: false, required: false,
@ -188,7 +196,6 @@ const props = withDefaults(defineProps<Props>(), {
zIndex: 15, zIndex: 15,
safeAreaInsetBottom: true, safeAreaInsetBottom: true,
filterable: false, filterable: false,
filterPlaceholder: '搜索',
ellipsis: false, ellipsis: false,
scrollIntoView: true, scrollIntoView: true,
rules: () => [] rules: () => []

View File

@ -1,7 +1,7 @@
<!-- <!--
* @Author: weisheng * @Author: weisheng
* @Date: 2023-06-12 10:04:19 * @Date: 2023-06-12 10:04:19
* @LastEditTime: 2023-09-02 15:12:03 * @LastEditTime: 2024-01-26 12:42:13
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-status-tip\wd-status-tip.vue * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-status-tip\wd-status-tip.vue
@ -26,7 +26,7 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from 'vue' import { computed, ref, watch, type CSSProperties } from 'vue'
import { addUnit, objToStyle } from '../common/util' import { addUnit, objToStyle } from '../common/util'
interface Props { interface Props {
@ -59,7 +59,7 @@ watch(
) )
const imgStyle = computed(() => { const imgStyle = computed(() => {
let style: Record<string, string> = {} let style: CSSProperties = {}
if (props.imageSize) { if (props.imageSize) {
style = { style = {
height: addUnit(props.imageSize), height: addUnit(props.imageSize),

View File

@ -46,6 +46,7 @@ import { computed } from 'vue'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { STEPS_KEY } from '../wd-steps/types' import { STEPS_KEY } from '../wd-steps/types'
import { isDef } from '../common/util' import { isDef } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
type StepStatus = 'finished' | 'process' | 'error' type StepStatus = 'finished' | 'process' | 'error'
@ -71,6 +72,8 @@ const props = withDefaults(defineProps<Props>(), {
const { parent: steps, index } = useParent(STEPS_KEY) const { parent: steps, index } = useParent(STEPS_KEY)
const { translate } = useTranslate('steps')
const currentStatus = computed(() => { const currentStatus = computed(() => {
return getCurrentStatus(index.value) return getCurrentStatus(index.value)
}) })
@ -149,14 +152,14 @@ function getCurrentTitle(currentStatus: string) {
switch (currentStatus) { switch (currentStatus) {
case 'finished': case 'finished':
return '已完成' return translate('finished')
case 'error': case 'error':
return '失败' return translate('failed')
case 'process': case 'process':
return '进行中' return translate('process')
case 'wait': case 'wait':
default: default:
return '未开始' return translate('wait')
} }
} }
</script> </script>

View File

@ -32,7 +32,9 @@
<wd-icon name="arrow-down" /> <wd-icon name="arrow-down" />
</view> </view>
</view> </view>
<view class="wd-tabs__map-header" :style="`${mapShow ? '' : 'display:none;'} ${animating ? 'opacity:1;' : ''}`">全部</view> <view class="wd-tabs__map-header" :style="`${mapShow ? '' : 'display:none;'} ${animating ? 'opacity:1;' : ''}`">
{{ translate('all') }}
</view>
<view :class="`wd-tabs__map-body ${animating ? 'is-open' : ''}`" :style="mapShow ? '' : 'display:none'"> <view :class="`wd-tabs__map-body ${animating ? 'is-open' : ''}`" :style="mapShow ? '' : 'display:none'">
<view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)"> <view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)">
<view <view
@ -97,7 +99,9 @@
<wd-icon name="arrow-down" /> <wd-icon name="arrow-down" />
</view> </view>
</view> </view>
<view class="wd-tabs__map-header" :style="`${mapShow ? '' : 'display:none;'} ${animating ? 'opacity:1;' : ''}`">全部</view> <view class="wd-tabs__map-header" :style="`${mapShow ? '' : 'display:none;'} ${animating ? 'opacity:1;' : ''}`">
{{ translate('all') }}
</view>
<view :class="`wd-tabs__map-body ${animating ? 'is-open' : ''}`" :style="mapShow ? '' : 'display:none'"> <view :class="`wd-tabs__map-body ${animating ? 'is-open' : ''}`" :style="mapShow ? '' : 'display:none'">
<view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)"> <view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)">
<view :class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"> <view :class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`">
@ -136,6 +140,7 @@ import { checkNumRange, debounce, getRect, getType, isDef, isNumber, isString, o
import { useTouch } from '../composables/useTouch' import { useTouch } from '../composables/useTouch'
import { TABS_KEY } from './types' import { TABS_KEY } from './types'
import { useChildren } from '../composables/useChildren' import { useChildren } from '../composables/useChildren'
import { useTranslate } from '../composables/useTranslate'
const $item = '.wd-tabs__nav-item' const $item = '.wd-tabs__nav-item'
const $container = '.wd-tabs__nav-container' const $container = '.wd-tabs__nav-container'
@ -180,6 +185,8 @@ const props = withDefaults(defineProps<Props>(), {
duration: 300 duration: 300
}) })
const { translate } = useTranslate('tabs')
// //
const state = reactive({ activeIndex: 0 }) const state = reactive({ activeIndex: 0 })
// navBar线 // navBar线
@ -227,7 +234,7 @@ const bodyStyle = computed(() => {
* @param {Boolean } init - 是否伴随初始化操作 * @param {Boolean } init - 是否伴随初始化操作
*/ */
const setActive = debounce( const setActive = debounce(
function (value: number = 0, init: boolean = false, setScroll: boolean = true) { function (value: number | string = 0, init: boolean = false, setScroll: boolean = true) {
// tab // tab
if (items.value.length === 0) return if (items.value.length === 0) return

View File

@ -11,7 +11,7 @@
<input <input
v-if="dynamicInput && dynamic" v-if="dynamicInput && dynamic"
class="wd-tag__add-text" class="wd-tag__add-text"
placeholder="请输入" :placeholder="translate('placeholder')"
type="text" type="text"
focus="true" focus="true"
v-model="dynamicValue" v-model="dynamicValue"
@ -22,7 +22,7 @@
<slot name="add" v-if="$slots.add"></slot> <slot name="add" v-if="$slots.add"></slot>
<template v-else> <template v-else>
<wd-icon name="add" custom-class="wd-tag__add wd-tag__icon" /> <wd-icon name="add" custom-class="wd-tag__add wd-tag__icon" />
<text>新增标签</text> <text>{{ translate('add') }}</text>
</template> </template>
</view> </view>
</view> </view>
@ -41,6 +41,7 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { objToStyle } from '../common/util' import { objToStyle } from '../common/util'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { useTranslate } from '../composables/useTranslate'
type TagType = 'default' | 'primary' | 'success' | 'warning' | 'danger' type TagType = 'default' | 'primary' | 'success' | 'warning' | 'danger'
@ -69,6 +70,8 @@ const props = withDefaults(defineProps<Props>(), {
mark: false mark: false
}) })
const { translate } = useTranslate('tag')
const tagClass = ref<string>('') const tagClass = ref<string>('')
const dynamicValue = ref<string>('') const dynamicValue = ref<string>('')
const dynamicInput = ref<boolean>(false) const dynamicInput = ref<boolean>(false)
@ -86,7 +89,7 @@ watch(
(newValue) => { (newValue) => {
if (!newValue) return if (!newValue) return
// type: 'primary', 'danger', 'warning', 'success' // type: 'primary', 'danger', 'warning', 'success'
const type = ['primary', 'danger', 'warning', 'success'] const type = ['primary', 'danger', 'warning', 'success', 'default']
if (type.indexOf(newValue) === -1) console.error(`type must be one of ${type.toString()}`) if (type.indexOf(newValue) === -1) console.error(`type must be one of ${type.toString()}`)
computeTagClass() computeTagClass()
}, },

View File

@ -16,7 +16,7 @@
:class="`wd-textarea__inner ${showClear ? 'is-suffix' : ''} ${customTextareaClass}`" :class="`wd-textarea__inner ${showClear ? 'is-suffix' : ''} ${customTextareaClass}`"
v-model="inputValue" v-model="inputValue"
:show-count="false" :show-count="false"
:placeholder="placeholder" :placeholder="placeholder || translate('placeholder')"
:disabled="disabled" :disabled="disabled"
:maxlength="maxlength" :maxlength="maxlength"
:focus="isFocus" :focus="isFocus"
@ -80,8 +80,11 @@ import { objToStyle, requestAnimationFrame } from '../common/util'
import { useCell } from '../composables/useCell' import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types' import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent' import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate'
type ConfirmType = 'send' | 'search' | 'next' | 'go' | 'done' type ConfirmType = 'send' | 'search' | 'next' | 'go' | 'done'
const { translate } = useTranslate('textarea')
interface Props { interface Props {
// //
placeholder?: string placeholder?: string
@ -136,7 +139,6 @@ const props = withDefaults(defineProps<Props>(), {
customStyle: '', customStyle: '',
maxlength: -1, maxlength: -1,
modelValue: '', modelValue: '',
placeholder: '请输入...',
autoHeight: false, autoHeight: false,
clearable: false, clearable: false,
showPassword: false, showPassword: false,

View File

@ -25,7 +25,7 @@ export const defaultOptions: ToastOptions = {
} }
export function useToast(selector: string = ''): Toast { export function useToast(selector: string = ''): Toast {
let timer: NodeJS.Timeout | null = null let timer: ReturnType<typeof setTimeout> | null = null
const toastOption = ref<ToastOptions>(defaultOptions) // Toast选项 const toastOption = ref<ToastOptions>(defaultOptions) // Toast选项
const toastOptionKey = selector ? toastDefaultOptionKey + selector : toastDefaultOptionKey const toastOptionKey = selector ? toastDefaultOptionKey + selector : toastDefaultOptionKey
provide(toastOptionKey, toastOption) provide(toastOptionKey, toastOption)

View File

@ -16,7 +16,7 @@
<!-- 失败时展示失败图标以及失败信息 --> <!-- 失败时展示失败图标以及失败信息 -->
<view v-if="file.status === 'fail'" class="wd-upload__status-content"> <view v-if="file.status === 'fail'" class="wd-upload__status-content">
<wd-icon name="close-outline" custom-class="wd-upload__icon"></wd-icon> <wd-icon name="close-outline" custom-class="wd-upload__icon"></wd-icon>
<text class="wd-upload__progress-txt">{{ file.error || '上传失败' }}</text> <text class="wd-upload__progress-txt">{{ file.error || translate('error') }}</text>
</view> </view>
</view> </view>
<!-- 上传状态为上传中时不展示移除按钮 --> <!-- 上传状态为上传中时不展示移除按钮 -->
@ -54,6 +54,7 @@ export default {
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { context, getType, isDef, isEqual } from '../common/util' import { context, getType, isDef, isEqual } from '../common/util'
import { chooseFile } from './utils' import { chooseFile } from './utils'
import { useTranslate } from '../composables/useTranslate'
interface Props { interface Props {
customClass?: string customClass?: string
@ -122,6 +123,8 @@ const props = withDefaults(defineProps<Props>(), {
maxSize: Number.MAX_VALUE maxSize: Number.MAX_VALUE
}) })
const { translate } = useTranslate('upload')
const uploadFiles = ref<Record<string, any>[]>([]) const uploadFiles = ref<Record<string, any>[]>([])
watch( watch(

View File

@ -1,7 +1,7 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2021-12-21 14:22:03 * @Date: 2021-12-21 14:22:03
* @LastEditTime: 2023-10-31 22:20:14 * @LastEditTime: 2024-01-26 13:46:52
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\index.ts * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\index.ts
@ -24,3 +24,5 @@ export { dayjs } from './components/common/dayjs'
export * as CommonUtil from './components/common/util' export * as CommonUtil from './components/common/util'
export * as clickOut from './components/common/clickoutside' export * as clickOut from './components/common/clickoutside'
export * from './locale'

View File

@ -0,0 +1,39 @@
/*
* @Author: weisheng
* @Date: 2024-01-25 23:06:48
* @LastEditTime: 2024-01-26 17:30:21
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\locale\index.ts
*
*/
import { reactive, ref } from 'vue'
import zhCN from './lang/zh-CN'
import { deepAssign } from '../components/common/util'
type Message = Record<string, any>
type Messages = Record<string, Message>
const lang = ref<string>('zh-CN')
const messages = reactive<Messages>({
'zh-CN': zhCN
})
export const Locale = {
messages(): Message {
return messages[lang.value]
},
use(newLang: string, newMessages?: Message) {
lang.value = newLang
this.add({ [newLang]: newMessages })
},
add(newMessages: Message = {}) {
deepAssign(messages, newMessages)
}
}
export const useCurrentLang = () => lang
export default Locale

View File

@ -0,0 +1,124 @@
export default {
calendar: {
placeholder: 'Select',
title: 'Select Date',
day: 'Date',
week: 'Week',
month: 'Month',
confirm: 'OK',
startTime: 'Start Date',
endTime: 'End Date',
to: 'To',
timeFormat: 'YY-MM-DD HH:mm:ss',
dateFormat: 'YYYY-MM-DD',
weekFormat: (year: number, week: number) => `${year} W${week}`,
startWeek: 'Start Week',
endWeek: 'End Week',
startMonth: 'Start Month',
endMonth: 'End Month',
monthFormat: 'YYYY-MM'
},
calendarView: {
startTime: 'Start Time',
endTime: 'End Time',
weeks: {
sun: 'Sun',
mon: 'Mon',
tue: 'Tue',
wed: 'Wed',
thu: 'Thu',
fri: 'Fri',
sat: 'Sat'
},
rangePrompt: (maxRange: number) => `The number of selected days cannot exceed ${maxRange} days`,
rangePromptWeek: (maxRange: number) => `The number of weeks selected cannot exceed ${maxRange} weeks`,
rangePromptMonth: (maxRange: number) => `The selected month cannot exceed ${maxRange} months`,
monthTitle: 'YYYY-MM',
yearTitle: 'YYYY',
month: 'MM',
hour: (value: number) => `${value}`,
minute: (value: number) => `${value}`,
second: (value: number) => `${value}`
},
datetimePicker: {
start: 'From',
end: 'To',
to: 'To',
placeholder: 'Select',
confirm: 'OK',
cancel: 'Cancel'
},
collapse: {
expand: 'Expand',
retract: 'Fold'
},
colPicker: {
title: 'Select',
placeholder: 'Select',
select: 'Select'
},
loadmore: {
loading: 'Loading...',
finished: 'Finished loading',
error: 'Failed to load...',
retry: 'Refresh'
},
imgCropper: {
confirm: 'OK',
cancel: 'Cancel'
},
messageBox: {
inputPlaceholder: 'Please input information',
confirm: 'OK',
cancel: 'Cancel',
inputNoValidate: 'Please ensure you input correct information'
},
numberKeyboard: {
confirm: 'done'
},
pagination: {
prev: 'Previous',
next: 'Next',
page: (value: number) => `Page${value}`,
total: (total: number) => `Total${total}`,
size: (size: number) => `${size}/page`
},
picker: {
cancel: 'Cancel',
done: 'Done',
placeholder: 'Select'
},
search: {
search: 'Search',
cancel: 'Cancel'
},
steps: {
wait: 'Not Started',
finished: 'Expired',
process: 'In Progress',
failed: 'Failed'
},
tabs: {
all: 'All'
},
upload: {
error: 'Failed to upload'
},
input: {
placeholder: 'Please input information...'
},
selectPicker: {
title: 'Select',
placeholder: 'Select',
select: 'Select',
confirm: 'Ok',
filterPlaceholder: 'Search'
},
tag: {
placeholder: 'Enter',
add: 'Add Tag'
},
textarea: {
placeholder: 'Please input information...'
}
}

View File

@ -0,0 +1,124 @@
export default {
calendar: {
placeholder: 'เลือก',
title: 'เลือกวันที่',
day: 'วันที่',
week: 'สัปดาห์',
month: 'เดือน',
confirm: 'ยืนยัน',
startTime: 'วันที่เริ่มต้น',
endTime: 'วันที่สิ้นสุด',
to: 'ถึง',
timeFormat: 'YY-MM-DD HH:mm:ss',
dateFormat: 'YYYY-MM-DD',
weekFormat: (year: number, week: number) => `${year} W${week}`,
startWeek: 'เริ่มต้นสัปดาห์',
endWeek: 'สิ้นสุดสัปดาห์',
startMonth: 'เดือนเริ่มต้น',
endMonth: 'สิ้นเดือน',
monthFormat: 'YYYY-MM'
},
calendarView: {
startTime: 'เวลาเริ่มต้น',
endTime: 'เวลาสิ้นสุด',
weeks: {
sun: 'อา',
mon: 'จ',
tue: 'อ',
wed: 'พ',
thu: 'พฤ',
fri: 'ศ',
sat: 'ส'
},
rangePrompt: (maxRange: number) => `จำนวนวันที่เลือกต้องไม่เกิน ${maxRange} วัน`,
rangePromptWeek: (maxRange: number) => `จำนวนสัปดาห์ที่เลือกต้องไม่เกิน ${maxRange} สัปดาห์`,
rangePromptMonth: (maxRange: number) => `เดือนที่เลือกต้องไม่เกิน ${maxRange} เดือน`,
monthTitle: 'YYYY-MM',
yearTitle: 'YYYY',
month: 'MM',
hour: (value: number) => `${value}`,
minute: (value: number) => `${value}`,
second: (value: number) => `${value}`
},
collapse: {
expand: 'ดูเพิ่มเติม',
retract: 'ย่อ'
},
colPicker: {
title: 'เลือก',
placeholder: 'เลือก',
select: 'เลือก'
},
datetimePicker: {
start: 'จาก',
end: 'ถึง',
to: 'ถึง',
placeholder: 'เลือก',
confirm: 'ยืนยัน',
cancel: 'ยกเลิก'
},
loadmore: {
loading: 'กำลังโหลด.. รอสักครู่..',
finished: 'โหลดสำเร็จ',
error: 'โหลดไม่สำเร็จ',
retry: 'รีเฟรช'
},
imgCropper: {
confirm: 'ยืนยัน',
cancel: 'ยกเลิก'
},
messageBox: {
inputPlaceholder: 'กรุณาใส่ข้อมูล',
confirm: 'ยืนยัน',
cancel: 'ยกเลิก',
inputNoValidate: 'กรุณาตรวจสอบว่าคุณได้ใส่ข้อมูลที่ถูกต้อง'
},
numberKeyboard: {
confir: 'ตกลง'
},
pagination: {
prev: 'ก่อนหน้า',
next: 'หน้าต่อไป',
page: (value: number) => `หน้า: ${value}`,
total: (total: number) => `ทั้งหมด: ${total} หน้า`,
size: (size: number) => `${size}/ ต่อหน้า`
},
picker: {
cancel: 'ยกเลิก',
done: 'ตกลง',
placeholder: 'เลือก'
},
search: {
search: 'ค้นหา',
cancel: 'ยกเลิก'
},
steps: {
wait: 'ยังไม่เริ่ม',
finished: 'เสร็จสิ้น',
process: 'กำลังดำเนินการ',
failed: 'ไม่ผ่าน'
},
tabs: {
all: 'ทั้งหมด'
},
upload: {
error: 'อัปโหลดไม่สำเร็จ'
},
input: {
placeholder: 'กรุณาใส่ข้อมูล...'
},
selectPicker: {
title: 'เลือก',
placeholder: 'เลือก',
select: 'เลือก',
confirm: 'ยืนยัน',
filterPlaceholder: 'ค้นหา'
},
tag: {
placeholder: 'กรุณาใส่',
add: 'ป้ายใหม่'
},
textarea: {
placeholder: 'กรุณาใส่ข้อมูล...'
}
}

View File

@ -0,0 +1,65 @@
export default {
calendar: {
placeholder: 'Vui lòng chọn',
title: 'Chọn ngày',
day: 'Ngày',
week: 'Tuần',
month: 'Tháng',
confirm: 'Xác nhận',
startTime: 'Thời gian bắt đầu',
endTime: 'Thời gian kết thúc',
to: 'đến',
timeFormat: 'YY-MM-DD HH:mm:ss',
dateFormat: 'YYYY-MM-DD',
weekFormat: (year: number, week: number) => `Tuần thứ ${week} năm ${year}`,
startWeek: 'Tuần bắt đầu',
endWeek: 'Tuần kết thúc',
startMonth: 'Tháng bắt đầu',
endMonth: 'Tháng kết thúc',
monthFormat: 'Tháng YYYY MM'
},
calendarView: {
startTime: 'Bắt đầu',
endTime: 'Kết thúc',
weeks: { sun: 'CN', mon: 'T2', tue: 'T3', wed: 'T4', thu: 'T5', fri: 'T6', sat: 'T7' },
rangePrompt: (maxRange: number) => `Không thể chọn quá ${maxRange} ngày`,
rangePromptWeek: (maxRange: number) => `Không thể chọn quá ${maxRange} tuần`,
rangePromptMonth: (maxRange: number) => `Không thể chọn quá ${maxRange} tháng`,
monthTitle: 'Tháng YYYY M',
yearTitle: 'Năm YYYY',
month: 'MM',
hour: (value: number) => `${value} giờ`,
minute: (value: number) => `${value} phút`,
second: (value: number) => `${value} giây`
},
collapse: { expand: 'Mở rộng', retract: 'Thu gọn' },
colPicker: { title: 'Vui lòng chọn', placeholder: 'Vui lòng chọn', select: 'Vui lòng chọn' },
datetimePicker: {
start: 'Thời gian bắt đầu',
end: 'Thời gian kết thúc',
to: 'đến',
placeholder: 'Vui lòng chọn',
confirm: 'Hoàn thành',
cancel: 'Hủy bỏ'
},
loadmore: { loading: 'Đang tải...', finished: 'Đã tải xong', error: 'Tải thất bại', retry: 'Nhấp để thử lại' },
messageBox: { inputPlaceholder: 'Vui lòng nhập', confirm: 'Xác nhận', cancel: 'Hủy bỏ', inputNoValidate: 'Dữ liệu không hợp lệ' },
numberKeyboard: { confirm: 'Hoàn thành' },
pagination: {
prev: 'Trang trước',
next: 'Trang sau',
page: (value: number) => `Trang hiện tại: ${value}`,
total: (total: number) => `Tổng số dữ liệu: ${total}`,
size: (size: number) => `Kích thước trang: ${size}`
},
picker: { cancel: 'Hủy bỏ', done: 'Hoàn thành', placeholder: 'Vui lòng chọn' },
imgCropper: { confirm: 'Hoàn thành', cancel: 'Hủy bỏ' },
search: { search: 'Tìm kiếm', cancel: 'Hủy bỏ' },
steps: { wait: 'Chưa bắt đầu', finished: 'Đã hoàn thành', process: 'Đang tiến hành', failed: 'Thất bại' },
tabs: { all: 'Tất cả' },
upload: { error: 'Tải lên thất bại' },
input: { placeholder: 'Vui lòng nhập...' },
selectPicker: { title: 'Vui lòng chọn', placeholder: 'Vui lòng chọn', select: 'Xác nhận', confirm: 'Xác nhận', filterPlaceholder: 'Tìm kiếm' },
tag: { placeholder: 'Vui lòng nhập', add: 'thêm' },
textarea: { placeholder: 'Vui lòng nhập...' }
}

View File

@ -0,0 +1,124 @@
export default {
calendar: {
placeholder: '请选择',
title: '选择日期',
day: '日',
week: '周',
month: '月',
confirm: '确定',
startTime: '开始时间',
endTime: '结束时间',
to: '至',
timeFormat: 'YY年MM月DD日 HH:mm:ss',
dateFormat: 'YYYY年MM月DD日',
weekFormat: (year: number, week: number) => `${year}${week}`,
startWeek: '开始周',
endWeek: '结束周',
startMonth: '开始月',
endMonth: '结束月',
monthFormat: 'YYYY年MM月'
},
calendarView: {
startTime: '开始',
endTime: '结束',
weeks: {
sun: '日',
mon: '一',
tue: '二',
wed: '三',
thu: '四',
fri: '五',
sat: '六'
},
rangePrompt: (maxRange: number) => `选择天数不能超过${maxRange}`,
rangePromptWeek: (maxRange: number) => `选择周数不能超过${maxRange}`,
rangePromptMonth: (maxRange: number) => `选择月份不能超过${maxRange}个月`,
monthTitle: 'YYYY年M月',
yearTitle: 'YYYY年',
month: 'M月',
hour: (value: number) => `${value}`,
minute: (value: number) => `${value}`,
second: (value: number) => `${value}`
},
collapse: {
expand: '展开',
retract: '收起'
},
colPicker: {
title: '请选择',
placeholder: '请选择',
select: '请选择'
},
datetimePicker: {
start: '开始时间',
end: '结束时间',
to: '至',
placeholder: '请选择',
confirm: '完成',
cancel: '取消'
},
loadmore: {
loading: '正在努力加载中...',
finished: '已加载完毕',
error: '加载失败',
retry: '点击重试'
},
messageBox: {
inputPlaceholder: '请输入',
confirm: '确定',
cancel: '取消',
inputNoValidate: '输入的数据不合法'
},
numberKeyboard: {
confirm: '完成'
},
pagination: {
prev: '上一页',
next: '下一页',
page: (value: number) => `当前页:${value}`,
total: (total: number) => `当前数据:${total}`,
size: (size: number) => `分页大小:${size}`
},
picker: {
cancel: '取消',
done: '完成',
placeholder: '请选择'
},
imgCropper: {
confirm: '完成',
cancel: '取消'
},
search: {
search: '搜索',
cancel: '取消'
},
steps: {
wait: '未开始',
finished: '已完成',
process: '进行中',
failed: '失败'
},
tabs: {
all: '全部'
},
upload: {
error: '上传失败'
},
input: {
placeholder: '请输入...'
},
selectPicker: {
title: '请选择',
placeholder: '请选择',
select: '请选择',
confirm: '确认',
filterPlaceholder: '搜索'
},
tag: {
placeholder: '请输入',
add: '新增标签'
},
textarea: {
placeholder: '请输入...'
}
}

View File

@ -0,0 +1,58 @@
export default {
calendar: {
placeholder: '請選擇',
title: '選擇日期',
day: '日',
week: '週',
month: '月',
confirm: '確定',
startTime: '開始時間',
endTime: '結束時間',
to: '至',
timeFormat: 'YY年MM月DD日 HH:mm:ss',
dateFormat: 'YYYY年MM月DD日',
weekFormat: (year: number, week: number) => `${year}${week}`,
startWeek: '開始週',
endWeek: '結束週',
startMonth: '開始月',
endMonth: '結束月',
monthFormat: 'YYYY年MM月'
},
calendarView: {
startTime: '開始',
endTime: '結束',
weeks: { sun: '日', mon: '一', tue: '二', wed: '三', thu: '四', fri: '五', sat: '六' },
rangePrompt: (maxRange: number) => `選擇天數不能超過${maxRange}`,
rangePromptWeek: (maxRange: number) => `選擇週數不能超過${maxRange}`,
rangePromptMonth: (maxRange: number) => `選擇月份不能超過${maxRange}個月`,
monthTitle: 'YYYY年M月',
yearTitle: 'YYYY年',
month: 'M月',
hour: (value: number) => `${value}`,
minute: (value: number) => `${value}`,
second: (value: number) => `${value}`
},
collapse: { expand: '展開', retract: '收起' },
colPicker: { title: '請選擇', placeholder: '請選擇', select: '請選擇' },
datetimePicker: { start: '開始時間', end: '結束時間', to: '至', placeholder: '請選擇', confirm: '完成', cancel: '取消' },
loadmore: { loading: '正在努力加載中...', finished: '已加載完畢', error: '加載失敗', retry: '點擊重試' },
messageBox: { inputPlaceholder: '請輸入', confirm: '確定', cancel: '取消', inputNoValidate: '輸入的數據不合法' },
numberKeyboard: { confirm: '完成' },
pagination: {
prev: '上一頁',
next: '下一頁',
page: (value: number) => `當前頁:${value}`,
total: (total: number) => `當前數據:${total}`,
size: (size: number) => `分頁大小:${size}`
},
picker: { cancel: '取消', done: '完成', placeholder: '請選擇' },
imgCropper: { confirm: '完成', cancel: '取消' },
search: { search: '搜索', cancel: '取消' },
steps: { wait: '未開始', finished: '已完成', process: '進行中', failed: '失敗' },
tabs: { all: '全部' },
upload: { error: '上傳失敗' },
input: { placeholder: '請輸入...' },
selectPicker: { title: '請選擇', placeholder: '請選擇', select: '確認', confirm: '確認', filterPlaceholder: '搜索' },
tag: { placeholder: '請輸入', add: '新增標籤' },
textarea: { placeholder: '請輸入...' }
}

View File

@ -0,0 +1,58 @@
export default {
calendar: {
placeholder: '請選擇',
title: '選擇日期',
day: '日',
week: '週',
month: '月',
confirm: '確定',
startTime: '開始時間',
endTime: '結束時間',
to: '至',
timeFormat: 'YY年MM月DD日 HH:mm:ss',
dateFormat: 'YYYY年MM月DD日',
weekFormat: (year: number, week: number) => `${year}${week}`,
startWeek: '開始週',
endWeek: '結束週',
startMonth: '開始月',
endMonth: '結束月',
monthFormat: 'YYYY年MM月'
},
calendarView: {
startTime: '開始',
endTime: '結束',
weeks: { sun: '日', mon: '一', tue: '二', wed: '三', thu: '四', fri: '五', sat: '六' },
rangePrompt: (maxRange: number) => `選擇天數不能超過${maxRange}`,
rangePromptWeek: (maxRange: number) => `選擇週數不能超過${maxRange}`,
rangePromptMonth: (maxRange: number) => `選擇月份不能超過${maxRange} 個月`,
monthTitle: 'YYYY年M月',
yearTitle: 'YYYY年',
month: 'M月',
hour: (value: number) => `${value}`,
minute: (value: number) => `${value}`,
second: (value: number) => `${value}`
},
collapse: { expand: '展開', retract: '收起' },
colPicker: { title: '請選擇', placeholder: '請選擇', select: '請選擇' },
datetimePicker: { start: '開始時間', end: '結束時間', to: '至', placeholder: '請選擇', confirm: '完成', cancel: '取消' },
loadmore: { loading: '正在努力加載中...', finished: '已加載完畢', error: '加載失敗', retry: '點擊重試' },
messageBox: { inputPlaceholder: '請輸入', confirm: '確定', cancel: '取消', inputNoValidate: '輸入的數據不合法' },
numberKeyboard: { confirm: '完成' },
pagination: {
prev: '上一頁',
next: '下一頁',
page: (value: number) => `當前頁:${value}`,
total: (total: number) => `當前數據:${total}`,
size: (size: number) => `分頁大小:${size}`
},
picker: { cancel: '取消', done: '完成', placeholder: '請選擇' },
imgCropper: { confirm: '完成', cancel: '取消' },
search: { search: '搜索', cancel: '取消' },
steps: { wait: '未開始', finished: '已完成', process: '進行中', failed: '失敗' },
tabs: { all: '全部' },
upload: { error: '上傳失敗' },
input: { placeholder: '請輸入...' },
selectPicker: { title: '請選擇', placeholder: '請選擇', select: '確認', confirm: '確認', filterPlaceholder: '搜索' },
tag: { placeholder: '請輸入', add: '新增標籤' },
textarea: { placeholder: '請輸入...' }
}

View File

@ -49,7 +49,7 @@
- 🚀 支持 APP、H5、微信小程序 等平台. - 🚀 支持 APP、H5、微信小程序 等平台.
- 🚀 60+ 个高质量组件,覆盖移动端主流场景. - 🚀 60+ 个高质量组件,覆盖移动端主流场景.
- 💪 使用 Typescript 构建,提供良好的组件类型系统. - 💪 使用 Typescript 构建,提供良好的组件类型系统.
- 💪 采用 Vue3 最新特性,提升组件性能. - 🌍 支持国际化,内置 6 种语言包.
- 📖 提供丰富的文档和组件示例. - 📖 提供丰富的文档和组件示例.
- 🎨 支持修改 CSS 变量实现主题定制. - 🎨 支持修改 CSS 变量实现主题定制.
- 🍭 支持暗黑模式 - 🍭 支持暗黑模式