diff --git a/examples/sites/demos/apis/numeric.js b/examples/sites/demos/apis/numeric.js
index d50a399e8..50d844549 100644
--- a/examples/sites/demos/apis/numeric.js
+++ b/examples/sites/demos/apis/numeric.js
@@ -393,6 +393,18 @@ export default {
mode: ['mobile-first'],
mfDemo: ''
},
+ {
+ name: 'string-mode',
+ type: 'Boolean',
+ defaultValue: '',
+ desc: {
+ 'zh-CN': '使用字符串模式,精度超过JS限制时使用',
+ 'en-US': ''
+ },
+ mode: ['pc', 'mobile', 'mobile-first'],
+ pcDemo: 'string-mode',
+ mfDemo: ''
+ },
{
name: 'value',
type: 'Number',
diff --git a/examples/sites/demos/pc/app/button/ghost-composition-api.vue b/examples/sites/demos/pc/app/button/ghost-composition-api.vue
index 194e11942..db5cb58a1 100644
--- a/examples/sites/demos/pc/app/button/ghost-composition-api.vue
+++ b/examples/sites/demos/pc/app/button/ghost-composition-api.vue
@@ -1,11 +1,11 @@
- 幽灵按钮
- 主要按钮
- 成功按钮
- 信息按钮
- 告警按钮
- 危险按钮
+ 幽灵按钮
+ 主要按钮
+ 成功按钮
+ 信息按钮
+ 告警按钮
+ 危险按钮
diff --git a/examples/sites/demos/pc/app/button/ghost.spec.ts b/examples/sites/demos/pc/app/button/ghost.spec.ts
index fdbc036b3..425f0bc05 100644
--- a/examples/sites/demos/pc/app/button/ghost.spec.ts
+++ b/examples/sites/demos/pc/app/button/ghost.spec.ts
@@ -8,10 +8,12 @@ test('幽灵按钮', async ({ page }) => {
const getGhostBtn = (index: number) => demo.locator('.tiny-button').nth(index)
// 默认幽灵按钮
+ await page.waitForTimeout(1000)
await expect(getGhostBtn(0)).toHaveCSS('color', 'rgb(37, 43, 58)')
await expect(getGhostBtn(0)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(0)).toHaveCSS('border-bottom-color', 'rgb(173, 176, 184)')
- await getGhostBtn(0).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(0).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(0)).toHaveCSS('color', 'rgb(94, 124, 224)')
await expect(getGhostBtn(0)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
@@ -21,7 +23,8 @@ test('幽灵按钮', async ({ page }) => {
await expect(getGhostBtn(1)).toHaveCSS('color', 'rgb(94, 124, 224)')
await expect(getGhostBtn(1)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(1)).toHaveCSS('border-bottom-color', 'rgb(94, 124, 224)')
- await getGhostBtn(1).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(1).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(1)).toHaveCSS('color', 'rgb(118, 147, 245)')
await expect(getGhostBtn(1)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
@@ -31,7 +34,8 @@ test('幽灵按钮', async ({ page }) => {
await expect(getGhostBtn(2)).toHaveCSS('color', 'rgb(80, 212, 171)')
await expect(getGhostBtn(2)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(2)).toHaveCSS('border-bottom-color', 'rgb(80, 212, 171)')
- await getGhostBtn(2).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(2).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(2)).toHaveCSS('color', 'rgb(172, 242, 220)')
await expect(getGhostBtn(2)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
@@ -41,7 +45,8 @@ test('幽灵按钮', async ({ page }) => {
await expect(getGhostBtn(3)).toHaveCSS('color', 'rgb(37, 43, 58)')
await expect(getGhostBtn(3)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(3)).toHaveCSS('border-bottom-color', 'rgb(37, 43, 58)')
- await getGhostBtn(3).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(3).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(3)).toHaveCSS('color', 'rgb(92, 97, 115)')
await expect(getGhostBtn(3)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
@@ -51,7 +56,8 @@ test('幽灵按钮', async ({ page }) => {
await expect(getGhostBtn(4)).toHaveCSS('color', 'rgb(250, 152, 65)')
await expect(getGhostBtn(4)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(4)).toHaveCSS('border-bottom-color', 'rgb(250, 152, 65)')
- await getGhostBtn(4).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(4).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(4)).toHaveCSS('color', 'rgb(250, 194, 10)')
await expect(getGhostBtn(4)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
@@ -61,7 +67,8 @@ test('幽灵按钮', async ({ page }) => {
await expect(getGhostBtn(5)).toHaveCSS('color', 'rgb(199, 0, 11)')
await expect(getGhostBtn(5)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
await expect(getGhostBtn(5)).toHaveCSS('border-bottom-color', 'rgb(199, 0, 11)')
- await getGhostBtn(5).hover()
+ await page.waitForTimeout(100)
+ await getGhostBtn(5).click()
await page.waitForTimeout(100)
await expect(getGhostBtn(5)).toHaveCSS('color', 'rgb(214, 74, 82)')
await expect(getGhostBtn(5)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
diff --git a/examples/sites/demos/pc/app/button/ghost.vue b/examples/sites/demos/pc/app/button/ghost.vue
index 80998cada..73da3562f 100644
--- a/examples/sites/demos/pc/app/button/ghost.vue
+++ b/examples/sites/demos/pc/app/button/ghost.vue
@@ -1,11 +1,11 @@
- 幽灵按钮
- 主要按钮
- 成功按钮
- 信息按钮
- 告警按钮
- 危险按钮
+ 幽灵按钮
+ 主要按钮
+ 成功按钮
+ 信息按钮
+ 告警按钮
+ 危险按钮
diff --git a/examples/sites/demos/pc/app/numeric/mouse-wheel.spec.ts b/examples/sites/demos/pc/app/numeric/mouse-wheel.spec.ts
index d87a1af25..06b322576 100644
--- a/examples/sites/demos/pc/app/numeric/mouse-wheel.spec.ts
+++ b/examples/sites/demos/pc/app/numeric/mouse-wheel.spec.ts
@@ -7,7 +7,7 @@ test('鼠标滚轮事件', async ({ page }) => {
const numeric = page.getByRole('spinbutton')
const initVal = Number(await numeric.inputValue())
await numeric.click()
- await page.mouse.wheel(0, -100)
+ await page.mouse.wheel(0, -500)
const currentVal = Number(await numeric.inputValue())
- expect(currentVal).toBeGreaterThanOrEqual(initVal)
+ expect(currentVal).toBeLessThan(initVal)
})
diff --git a/examples/sites/demos/pc/app/numeric/string-mode-composition-api.vue b/examples/sites/demos/pc/app/numeric/string-mode-composition-api.vue
new file mode 100644
index 000000000..c9b08ae8c
--- /dev/null
+++ b/examples/sites/demos/pc/app/numeric/string-mode-composition-api.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/examples/sites/demos/pc/app/numeric/string-mode.spec.ts b/examples/sites/demos/pc/app/numeric/string-mode.spec.ts
new file mode 100644
index 000000000..2dc813089
--- /dev/null
+++ b/examples/sites/demos/pc/app/numeric/string-mode.spec.ts
@@ -0,0 +1,17 @@
+import { test, expect } from '@playwright/test'
+
+test('高精度', async ({ page }) => {
+ page.on('pageerror', (exception) => expect(exception).toBeNull())
+ await page.goto('numeric#string-mode')
+
+ const input = page.getByRole('spinbutton')
+ const increaseBtn = page.locator('.tiny-numeric__increase')
+ const decreaseBtn = page.locator('.tiny-numeric__decrease')
+ await increaseBtn.click()
+ const increasedVal = await input.inputValue()
+ expect(increasedVal).toContain('0.00000000000000000004')
+
+ await decreaseBtn.click()
+ const decreasedVal = await input.inputValue()
+ expect(decreasedVal).toContain('0.00000000000000000002')
+})
diff --git a/examples/sites/demos/pc/app/numeric/string-mode.vue b/examples/sites/demos/pc/app/numeric/string-mode.vue
new file mode 100644
index 000000000..925ad9b90
--- /dev/null
+++ b/examples/sites/demos/pc/app/numeric/string-mode.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/examples/sites/demos/pc/app/numeric/webdoc/numeric.js b/examples/sites/demos/pc/app/numeric/webdoc/numeric.js
index 709660ac4..0212aa419 100644
--- a/examples/sites/demos/pc/app/numeric/webdoc/numeric.js
+++ b/examples/sites/demos/pc/app/numeric/webdoc/numeric.js
@@ -171,6 +171,19 @@ export default {
},
codeFiles: ['blur-event.vue']
},
+ {
+ demoId: 'string-mode',
+ name: {
+ 'zh-CN': '高精度',
+ 'en-US': 'Out of Focus Event'
+ },
+ desc: {
+ 'zh-CN':
+ '
可通过 string-mode
设置高精度模式,当 JS 默认的 Number 不满足数字的长度与精度需求时。
\n',
+ 'en-US': 'The@blur
event is triggered when the text box loses focus.
\n'
+ },
+ codeFiles: ['string-mode.vue']
+ },
{
demoId: 'filter-mode',
name: {
diff --git a/examples/sites/demos/pc/app/select/disabled-composition-api.vue b/examples/sites/demos/pc/app/select/disabled-composition-api.vue
index 22fc976ba..5f8139c62 100644
--- a/examples/sites/demos/pc/app/select/disabled-composition-api.vue
+++ b/examples/sites/demos/pc/app/select/disabled-composition-api.vue
@@ -82,12 +82,12 @@ const options2 = ref([
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶', disabled: true },
{ value: '选项3', label: '蚵仔煎' },
- { value: '选项4', label: '龙须面' },
+ { value: '选项4', label: '龙须面', disabled: true },
{ value: '选项5', label: '北京烤鸭' }
])
const value1 = ref('')
-const value2 = ref([])
+const value2 = ref(['选项2'])
const value3 = ref('')
const value4 = ref(['选项2', '选项3'])
const value5 = ref(['选项1', '选项2', '选项3', '选项4', '选项5'])
diff --git a/examples/sites/demos/pc/app/select/disabled.spec.ts b/examples/sites/demos/pc/app/select/disabled.spec.ts
index 6d65657ac..f19b35661 100644
--- a/examples/sites/demos/pc/app/select/disabled.spec.ts
+++ b/examples/sites/demos/pc/app/select/disabled.spec.ts
@@ -19,15 +19,15 @@ test('多选某项禁用', async ({ page }) => {
const tag = select.locator('.tiny-tag')
const option = dropdown.locator('.tiny-option')
- await expect(tag).toHaveCount(0)
+ await expect(tag).toHaveCount(1)
await select.click()
await expect(option.filter({ hasText: '双皮奶' })).toHaveClass(/is-disabled/)
await option.filter({ hasText: '双皮奶' }).click()
- await expect(tag).toHaveCount(0)
+ await expect(tag).toHaveCount(1)
await option.filter({ hasText: '黄金糕' }).click()
- await expect(tag).toHaveCount(1)
+ await expect(tag).toHaveCount(2)
await expect(tag.filter({ hasText: '黄金糕' })).toHaveCount(1)
})
diff --git a/examples/sites/demos/pc/app/select/disabled.vue b/examples/sites/demos/pc/app/select/disabled.vue
index 3822862da..92f23fc35 100644
--- a/examples/sites/demos/pc/app/select/disabled.vue
+++ b/examples/sites/demos/pc/app/select/disabled.vue
@@ -87,11 +87,11 @@ export default {
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶', disabled: true },
{ value: '选项3', label: '蚵仔煎' },
- { value: '选项4', label: '龙须面' },
+ { value: '选项4', label: '龙须面', disabled: true },
{ value: '选项5', label: '北京烤鸭' }
],
value1: '',
- value2: [],
+ value2: ['选项2'],
value3: '',
value4: ['选项2', '选项3'],
value5: ['选项1', '选项2', '选项3', '选项4', '选项5']
diff --git a/examples/sites/demos/pc/app/select/events.spec.ts b/examples/sites/demos/pc/app/select/events.spec.ts
index cf990224e..9cb67d948 100644
--- a/examples/sites/demos/pc/app/select/events.spec.ts
+++ b/examples/sites/demos/pc/app/select/events.spec.ts
@@ -26,7 +26,7 @@ test('单选事件', async ({ page }) => {
await page.waitForTimeout(200)
await input.hover()
- await select.locator('.tiny-select__caret').click()
+ await select.locator('.tiny-select__caret.icon-close').click()
await page.waitForTimeout(500)
await expect(input).toHaveValue('')
await expect(model.filter({ hasText: '触发 clear 事件' })).toHaveCount(1)
@@ -42,6 +42,7 @@ test('多选事件', async ({ page }) => {
const option = dropdown.locator('.tiny-option')
const model = page.locator('.tiny-modal')
+ await page.waitForTimeout(500)
await select.click()
await expect(model.filter({ hasText: '触发 focus 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1)
@@ -56,7 +57,6 @@ test('多选事件', async ({ page }) => {
await page.waitForTimeout(500)
await tag.first().locator('.tiny-tag__close').click()
- await expect(model.filter({ hasText: '触发 blur 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 remove-tag 事件' })).toHaveCount(1)
await expect(tag).toHaveCount(4)
@@ -66,7 +66,7 @@ test('多选事件', async ({ page }) => {
await page.waitForTimeout(200)
await select.hover()
- await select.locator('.tiny-select__caret').click()
+ await select.locator('.tiny-select__caret.icon-close').click()
await expect(tag).toHaveCount(0)
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
diff --git a/examples/sites/demos/pc/app/select/multiple.vue b/examples/sites/demos/pc/app/select/multiple.vue
index c3f14ad17..84d230cfd 100644
--- a/examples/sites/demos/pc/app/select/multiple.vue
+++ b/examples/sites/demos/pc/app/select/multiple.vue
@@ -3,7 +3,7 @@
场景1:多选
-
+
diff --git a/examples/sites/demos/pc/app/tabs/tabs-events-close.spec.ts b/examples/sites/demos/pc/app/tabs/tabs-events-close.spec.ts
index f262e04c3..82b1b421c 100644
--- a/examples/sites/demos/pc/app/tabs/tabs-events-close.spec.ts
+++ b/examples/sites/demos/pc/app/tabs/tabs-events-close.spec.ts
@@ -5,7 +5,7 @@ test('删除事件', async ({ page }) => {
await page.goto('tabs#tabs-events-close')
const tabs = page.locator('.tiny-tabs')
- const tabItem = tabs.getByRole('tab', { name: '表单组件' })
+ const tabItem = tabs.getByRole('tab', { name: '其他组件' })
const close = tabItem.locator('.tiny-tabs__icon-close')
const modal = page.locator('.tiny-modal').first()
diff --git a/internals/cli/src/commands/build/build-entry-app.ts b/internals/cli/src/commands/build/build-entry-app.ts
index 1097cd120..0e94e13b0 100644
--- a/internals/cli/src/commands/build/build-entry-app.ts
+++ b/internals/cli/src/commands/build/build-entry-app.ts
@@ -10,7 +10,7 @@ import {
prettierFormat,
logGreen
} from '../../shared/utils'
-import { getComponents } from '../../shared/module-utils'
+import { getComponents, excludeComponents } from '../../shared/module-utils'
import handlebarsRender from './handlebars.render'
const version = getopentinyVersion({ key: 'version' })
@@ -61,7 +61,12 @@ const buildFullRuntime = () => {
})
components.forEach((item) => {
- if (item.inEntry !== false && !item.path.includes('river') && !item.path.includes('chart-beta')) {
+ if (
+ item.inEntry !== false &&
+ !item.path.includes('river') &&
+ !item.path.includes('chart-beta') &&
+ !excludeComponents.includes(item.name)
+ ) {
const component = capitalizeKebabCase(item.name)
componentsTemplate.push(` ${component}`)
diff --git a/internals/cli/src/commands/build/build-entry.ts b/internals/cli/src/commands/build/build-entry.ts
index d226c39c9..da2fbe368 100644
--- a/internals/cli/src/commands/build/build-entry.ts
+++ b/internals/cli/src/commands/build/build-entry.ts
@@ -10,7 +10,7 @@ import {
prettierFormat,
logGreen
} from '../../shared/utils'
-import { getComponents } from '../../shared/module-utils'
+import { getComponents, excludeComponents } from '../../shared/module-utils'
import handlebarsRender from './handlebars.render'
const version = getopentinyVersion({ key: 'version' })
@@ -79,7 +79,7 @@ const createEntry = (mode) => {
const PKGDeps = {}
components.forEach((item) => {
- if (item.inEntry !== false) {
+ if (item.inEntry !== false && !excludeComponents.includes(item.name)) {
const component = capitalizeKebabCase(item.name)
PKGDeps[item.importName] = 'workspace:~'
componentsTemplate.push(` ${component}`)
diff --git a/internals/cli/src/commands/build/build-ui.ts b/internals/cli/src/commands/build/build-ui.ts
index c7dd7ef0c..2df8f0ef3 100644
--- a/internals/cli/src/commands/build/build-ui.ts
+++ b/internals/cli/src/commands/build/build-ui.ts
@@ -160,6 +160,7 @@ export const getBaseConfig = ({ vueVersion, dtsInclude, dts, buildTarget, isRunt
}
},
include: [...dtsInclude, 'packages/vue/*.d.ts'],
+ exclude: ['**/__tests__'],
beforeWriteFile: (filePath, content) => {
return {
// "vue/src/alert/index.d.ts" ==> "alert/index.d.ts"
diff --git a/internals/cli/src/commands/build/rollup/inline-chunks.ts b/internals/cli/src/commands/build/rollup/inline-chunks.ts
index aa5d02658..3334217d5 100644
--- a/internals/cli/src/commands/build/rollup/inline-chunks.ts
+++ b/internals/cli/src/commands/build/rollup/inline-chunks.ts
@@ -1,13 +1,22 @@
-import type { Plugin, NormalizedOutputOptions, OutputBundle, OutputChunk } from 'rollup'
+import type { Plugin, NormalizedOutputOptions, OutputBundle } from 'rollup'
+import path from 'node:path'
+import fs from 'node:fs'
+import { rollup } from 'rollup'
import chalk from 'chalk'
+import commonjs from '@rollup/plugin-commonjs'
+import { external } from '../../../shared/config'
+
+const bundlesToMerge = new Set()
+const commonChunk = new Set()
+let buildFormat
+let isDelete
export default function ({ deleteInlinedFiles = true }): Plugin {
return {
name: 'opentiny-vue:inline-chunks',
- generateBundle: ({ format }: NormalizedOutputOptions, bundle: OutputBundle) => {
- const bundlesToDelete = new Set()
- const cache = {}
-
+ generateBundle: async ({ format, dir }: NormalizedOutputOptions, bundle: OutputBundle) => {
+ buildFormat = format
+ isDelete = deleteInlinedFiles
const jsAssets = Object.keys(bundle).filter((i) => /\.[mc]?js$/.test(i))
for (const jsName of jsAssets) {
const jsChunk = bundle[jsName]
@@ -16,51 +25,66 @@ export default function ({ deleteInlinedFiles = true }): Plugin {
if (!jsChunk.code) continue
if (format === 'es') {
- jsChunk.code = jsChunk.code.replace(
- // import { _ as _export_sfc } from "../../../../_plugin-vue_export-helper-1faf6727.mjs"
- /^import\s*.+\s*from\s+"[./]+(.+-[a-f0-9]{8}.+)".*$/gim,
- (_, chunkName) => {
- if (!cache[chunkName]) {
- cache[chunkName] = (bundle[chunkName] as OutputChunk).code
- .replace(/export {[\s\S]+$/, '')
- .replace(/_extends/g, '_extends_tiny')
- .replace(/_createForOfIteratorHelperLoose/g, '_createForOfIteratorHelperLoose_tiny')
- .replace(/_unsupportedIterableToArray/g, '_unsupportedIterableToArray_tiny')
- .replace(/_arrayLikeToArray/g, '_arrayLikeToArray_tiny')
- bundlesToDelete.add(chunkName)
- }
-
- return cache[chunkName]
- }
- )
+ const reg = /^import(\s*.+\s*from)?\s+"[./]+(.+-[a-f0-9]{8}.+)".*$/gim
+ const matchArr = jsChunk.code.match(reg)
+ if (matchArr) {
+ const filePath = path.join(dir, jsName)
+ bundlesToMerge.add(filePath)
+ matchArr.forEach((matchImport) => {
+ const sourceName = matchImport.match(/"(.+)"/)[1]
+ commonChunk.add(path.join(filePath, '../', sourceName))
+ })
+ }
}
if (format === 'cjs') {
- jsChunk.code = jsChunk.code.replace(
- // var _pluginVue_exportHelper = require("../../../../_plugin-vue_export-helper-65c7de93.js");
- /^var\s+(.+)\s+=\s+require\("[./]+(.+-[a-f0-9]{8}.+)".*$/gim,
- (_, localVarName, chunkName) => {
- if (!cache[chunkName]) {
- cache[chunkName] =
- 'var _pluginVue_exportHelper = {};\n' +
- (bundle[chunkName] as OutputChunk).code.replace(/exports\./g, `${localVarName}.`)
- bundlesToDelete.add(chunkName)
- }
-
- return cache[chunkName]
- }
- )
+ const reg = /require\("[./]+(.+-[a-f0-9]{8}.+)".*$/gim
+ const matchArr = jsChunk.code.match(reg)
+ if (matchArr) {
+ const filePath = path.join(dir, jsName)
+ bundlesToMerge.add(filePath)
+ matchArr.forEach((matchRequire) => {
+ const sourceName = matchRequire.match(/"(.+)"/)[1]
+ commonChunk.add(path.join(filePath, '../', sourceName))
+ })
+ }
}
}
-
- if (deleteInlinedFiles) {
- // 删除 chunks
- bundlesToDelete.forEach((name) => {
- delete bundle[name]
- // eslint-disable-next-line no-console
- console.log(`\n${chalk.red(name)} 已经被内联并删除`)
- })
- }
- }
+ },
+ closeBundle: async () =>
+ new Promise((resolve) => {
+ // eslint-disable-next-line no-console
+ console.log(`\n${chalk.green('开始内联公共依赖')}`)
+ let i = 0
+ if (bundlesToMerge.size > 0) {
+ bundlesToMerge.forEach(async (filePath) => {
+ if (commonChunk.has(filePath)) {
+ ++i
+ } else {
+ const bundle = await rollup({
+ input: filePath,
+ external: (source) => external(source),
+ plugins: buildFormat === 'cjs' ? [commonjs()] : []
+ })
+ await bundle.write({ dir: path.join(filePath, '../'), format: buildFormat })
+ await bundle.close()
+ ++i
+ }
+ if (i === bundlesToMerge.size) {
+ resolve()
+ }
+ })
+ } else {
+ resolve()
+ }
+ }).then(async () => {
+ if (isDelete) {
+ commonChunk.forEach((filePath) => {
+ fs.unlinkSync(filePath)
+ // eslint-disable-next-line no-console
+ console.log(`\n${chalk.red(filePath)} 已经被内联并删除`)
+ })
+ }
+ })
}
}
diff --git a/internals/cli/src/commands/release/releaseAurora.ts b/internals/cli/src/commands/release/releaseAurora.ts
index 71b4e63bd..66465462a 100644
--- a/internals/cli/src/commands/release/releaseAurora.ts
+++ b/internals/cli/src/commands/release/releaseAurora.ts
@@ -35,15 +35,14 @@ const findAllpage = (packagesPath) => {
// 解决TinyVue和AUI国际化键名不兼容问题
.replace(/zhCN/g, 'zh_CN')
.replace(/enUS/g, 'en_US')
+ .replace(/-openaui/g, '-opentiny')
// 解决在linkjs环境z-index无法统一导致下拉框被遮挡问题
.replace(/"(.*?\/popup-manager)"/g, '"@aurora/renderless/common/deps/popup-manager"')
// 解决当AUI只有一个mobile-first模板而TinyVue有多个模板,导致Linkjs加载不到多端模板的问题
if (
packagesPath.endsWith('index.js') &&
- onlyMobileFirstTemplateLists.some(
- (item) => packagesPath.includes(`${item}\\`) || packagesPath.includes(`${item}/`)
- )
+ onlyMobileFirstTemplateLists.some((item) => packagesPath.includes(`${path.sep}${item}${path.sep}`))
) {
result = result.replace(/pc.js/g, 'mobile-first.js')
}
diff --git a/internals/cli/src/shared/module-utils.ts b/internals/cli/src/shared/module-utils.ts
index bac94afb9..297b31e4a 100644
--- a/internals/cli/src/shared/module-utils.ts
+++ b/internals/cli/src/shared/module-utils.ts
@@ -13,6 +13,9 @@ const moduleMap = require(pathFromWorkspaceRoot('packages/modules.json'))
type mode = 'pc' | 'mobile' | 'mobile-first'
+// 需要在入口文件中排除的组件,比如:富文本
+export const excludeComponents = ['RichText']
+
export interface Module {
/** 源码路径,如 vue/src/button/index.ts */
path: string
@@ -452,8 +455,7 @@ const createModuleMapping = (componentName, isMobile = false) => {
parser: 'json',
printWidth: 10
}
- }),
-
+ })
)
}
diff --git a/packages/design/aurora/src/select/index.ts b/packages/design/aurora/src/select/index.ts
index 0aff271da..99f0bea43 100644
--- a/packages/design/aurora/src/select/index.ts
+++ b/packages/design/aurora/src/select/index.ts
@@ -14,7 +14,9 @@ export default {
medium: 42
},
spacingHeight: 2,
- initialInputHeight: 30
+ initialInputHeight: 30,
+ // 显示清除等图标时,不隐藏下拉箭头时
+ autoHideDownIcon: false
},
props: {
tagType: 'info'
diff --git a/packages/design/saas/src/select/index.ts b/packages/design/saas/src/select/index.ts
index ff3366231..322369106 100644
--- a/packages/design/saas/src/select/index.ts
+++ b/packages/design/saas/src/select/index.ts
@@ -14,7 +14,9 @@ export default {
medium: 32
},
spacingHeight: 4,
- initialInputHeight: 30
+ initialInputHeight: 30,
+ // 显示清除等图标时,不隐藏下拉箭头时
+ autoHideDownIcon: false
},
props: {
tagType: 'info'
diff --git a/packages/design/smb/src/tree-node/index.ts b/packages/design/smb/src/tree-node/index.ts
index 1bb8ba7ef..9795ce9c3 100644
--- a/packages/design/smb/src/tree-node/index.ts
+++ b/packages/design/smb/src/tree-node/index.ts
@@ -2,6 +2,7 @@ import { iconPutAway, iconExpand } from '@opentiny/vue-icon'
export default {
icons: {
+ // 在 showLine=true时,才要切换的图标。 并不是设置正常模式下的图标
expanded: iconExpand(),
collapse: iconPutAway()
}
diff --git a/packages/modules.json b/packages/modules.json
index a20e599ca..123c42e43 100644
--- a/packages/modules.json
+++ b/packages/modules.json
@@ -2360,6 +2360,14 @@
"type": "template",
"exclude": false
},
+ "RichText": {
+ "path": "vue/src/rich-text/index.ts",
+ "type": "component",
+ "exclude": false,
+ "mode": [
+ "pc"
+ ]
+ },
"RichTextEditor": {
"path": "vue/src/rich-text-editor/index.ts",
"type": "component",
diff --git a/packages/renderless/src/common/bigInt.ts b/packages/renderless/src/common/bigInt.ts
index f12676c09..5e4254141 100644
--- a/packages/renderless/src/common/bigInt.ts
+++ b/packages/renderless/src/common/bigInt.ts
@@ -180,7 +180,7 @@ export class BigIntDecimal {
const convertBigInt = (str) => {
// 将以多个零开头的整数前置零清空 '0000000000000003e+21' --> '3e+21' ,解决BigInt(0000000000000003e+21)报错问题
const validStr = str.replace(/^0+/, '') || '0'
- return f(`return BigInt(${validStr})`)()
+ return f(`return BigInt('${validStr}')`)()
}
if (validateNumber(mergedValue)) {
const trimRet = trimNumber(mergedValue)
@@ -188,7 +188,9 @@ export class BigIntDecimal {
const numbers = trimRet.trimStr.split('.')
this.integer = !numbers[0].includes('e') ? BigInt(numbers[0]) : numbers[0]
const decimalStr = numbers[1] || '0'
- this.decimal = convertBigInt(decimalStr)
+
+ // 如果小数点后有科学计数法,需要特殊处理,如果是正常数字则保留之前逻辑
+ this.decimal = decimalStr.includes('e') ? convertBigInt(decimalStr) : BigInt(decimalStr)
this.decimalLen = decimalStr.length
} else {
this.nan = true
diff --git a/packages/renderless/src/common/deps/date-util.ts b/packages/renderless/src/common/deps/date-util.ts
index 77573e32c..e4e92c0f8 100644
--- a/packages/renderless/src/common/deps/date-util.ts
+++ b/packages/renderless/src/common/deps/date-util.ts
@@ -91,11 +91,13 @@ export const prevDate = (date, amount = 1) => new Date(date.getFullYear(), date.
export const nextDate = (date, amount = 1) => new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount)
-export const getStartDateOfMonth = (year, month) => {
+export const getStartDateOfMonth = (year, month, offsetDay = 0) => {
const res = new Date(year, month, 1)
const day = res.getDay()
+ const _day = day === 0 ? 7 : day
- return day === 0 ? prevDate(res, 7) : prevDate(res, day)
+ const offset = _day + offsetDay <= 0 ? 7 + _day : _day
+ return prevDate(res, offset)
}
export const getWeekNumber = (src) => {
@@ -143,6 +145,7 @@ const setRangeData = (arr, start, end, value) => {
}
}
+// eslint-disable-next-line prefer-spread
export const range = (length) => Array.apply(null, { length }).map((_, n) => n)
export const getMonthDays = (date) => {
diff --git a/packages/renderless/src/date-range/index.ts b/packages/renderless/src/date-range/index.ts
index 97b58e47e..e148c02b3 100644
--- a/packages/renderless/src/date-range/index.ts
+++ b/packages/renderless/src/date-range/index.ts
@@ -207,6 +207,7 @@ export const handleClear =
state.leftDate = calcDefaultValue(state.defaultValue)[0]
state.rightDate = nextMonth(state.leftDate)
state.rangeState.selecting = false
+ // tiny 新增下面行
state.rangeState.endDate = null
emit('pick', null)
diff --git a/packages/renderless/src/date-range/vue.ts b/packages/renderless/src/date-range/vue.ts
index fa9369fb1..3cce39cec 100644
--- a/packages/renderless/src/date-range/vue.ts
+++ b/packages/renderless/src/date-range/vue.ts
@@ -127,6 +127,7 @@ const initState = ({ reactive, computed, api, constants, designConfig }) => {
dateFormat: computed(() => (state.format ? extractDateFormat(state.format) : 'yyyy-MM-dd')),
enableMonthArrow: computed(() => api.getEnableMonthArrow()),
enableYearArrow: computed(() => api.computerEnableYearArrow()),
+ // tiny 新增
confirmButtonProps: {
plain: true,
type: 'default',
diff --git a/packages/renderless/src/date-table/index.ts b/packages/renderless/src/date-table/index.ts
index 498a05c7b..ab7bede46 100644
--- a/packages/renderless/src/date-table/index.ts
+++ b/packages/renderless/src/date-table/index.ts
@@ -21,10 +21,9 @@ import {
clearTime
} from '../common/deps/date-util'
import { DATEPICKER } from '../common'
-import type { IDateTableRow } from '@/types'
const formatJudg = ({ day, offset, j, i, cell, count, dateCountOfLastMonth }) => {
- const nodfpm = day + offset < 0 ? 7 + day + offset : day + offset
+ const nodfpm = day + offset <= 0 ? 7 + day + offset : day + offset
if (j + i * 7 >= nodfpm) {
cell.text = count++
@@ -58,22 +57,8 @@ export const getDateTimestamp = (time) => {
return NaN
}
-export const arrayFindIndex = (arr, pred) => {
- for (let i = 0, len = arr.length; i !== len; ++i) {
- if (pred(arr[i])) {
- return i
- }
- }
-
- return -1
-}
-
-export const arrayFind = (arr, pred) => {
- const idx = arrayFindIndex(arr, pred)
- return ~idx ? arr[idx] : undefined
-}
-
-const getSelected = ({ props, cell, format, t, cellDate, selectedDate }) => {
+// tiny 新增: api参数多余,优化掉
+const getSelected = (props, cell, format, t, cellDate, selectedDate) => {
let selected = cell.selected
if (props.selectionMode === 'dates') {
@@ -92,14 +77,7 @@ export const getCell =
let cell = row[props.showWeekNumber ? j + 1 : j]
if (!cell) {
- cell = {
- row: i,
- column: j,
- inRange: false,
- start: false,
- end: false,
- type: DATEPICKER.Normal
- }
+ cell = { row: i, column: j, inRange: false, start: false, end: false, type: DATEPICKER.Normal }
}
cell.type = DATEPICKER.Normal
@@ -119,15 +97,7 @@ export const getCell =
const doCount = ({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth }) => {
if (i >= 0 && i <= 1) {
- const ret = formatJudg({
- day,
- offset,
- j,
- i,
- cell,
- count,
- dateCountOfLastMonth
- })
+ const ret = formatJudg({ day, offset, j, i, cell, count, dateCountOfLastMonth })
count = ret.count
} else {
if (count <= dateCountOfMonth) {
@@ -141,8 +111,6 @@ const doCount = ({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCou
return count
}
-export const coerceTruthyValueToArray = (val) => (Array.isArray(val) ? val : val ? [val] : [])
-
/**
* 获取日期表格二维数组,用于渲染日期表格。
*
@@ -160,7 +128,7 @@ export const coerceTruthyValueToArray = (val) => (Array.isArray(val) ? val : val
*/
export const getRows =
({ api, props, state, t, vm }) =>
- (): IDateTableRow[][] => {
+ () => {
const date = new Date(state.year, state.month, 1)
let day = getFirstDayOfMonth(date)
const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth())
@@ -172,7 +140,7 @@ export const getRows =
day = day === 0 ? 7 : day
const offset = state.offsetDay
- const rows: IDateTableRow[][] = state.tableRows
+ const rows = state.tableRows
const startDate = state.startDate
const disabledDate = props.disabledDate
const cellClassName = props.cellClassName
@@ -181,7 +149,7 @@ export const getRows =
const isFunction = props.formatWeeks instanceof Function
- const arr: Date[][] = []
+ const arr = []
// 日期表格行,从0开始,共6行,[0, 5]
for (let i = 0; i < 6; i++) {
@@ -207,15 +175,7 @@ export const getRows =
count = doCount({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth })
cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate)
- cell.selected = getSelected({
- props,
- cell,
- api,
- format: DATEPICKER.DateFormats.date,
- t,
- cellDate,
- selectedDate
- })
+ cell.selected = getSelected(props, cell, DATEPICKER.DateFormats.date, t, cellDate, selectedDate)
cell.customClass = typeof cellClassName === 'function' && cellClassName(cellDate)
// 更新日期表格的行数据,执行了这行代码,rows 中才会有数据
@@ -250,6 +210,23 @@ export const getRows =
return rows
}
+export const arrayFindIndex = (arr, pred) => {
+ for (let i = 0, len = arr.length; i !== len; ++i) {
+ if (pred(arr[i])) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+export const arrayFind = (arr, pred) => {
+ const idx = arrayFindIndex(arr, pred)
+ return ~idx ? arr[idx] : undefined
+}
+
+export const coerceTruthyValueToArray = (val) => (Array.isArray(val) ? val : val ? [val] : [])
+
export const watchMinDate =
({ api, props }) =>
(value, oldvalue) => {
@@ -398,15 +375,15 @@ export const markRange =
const row = rows[i]
for (let j = 0, l = row.length; j < l; j++) {
- if (!props.showWeekNumber || j !== 0) {
- const cell = row[j]
- const index = i * 7 + j + (props.showWeekNumber ? -1 : 0)
- const time = nextDate(startDate, index - state.offsetDay).getTime()
+ if (props.showWeekNumber && j === 0) continue
- cell.inRange = minDate && time >= minDate && time <= maxDate
- cell.start = minDate && time === minDate
- cell.end = maxDate && time === maxDate
- }
+ const cell = row[j]
+ const index = i * 7 + j + (props.showWeekNumber ? -1 : 0)
+ const time = nextDate(startDate, index - state.offsetDay).getTime()
+
+ cell.inRange = minDate && time >= minDate && time <= maxDate
+ cell.start = minDate && time === minDate
+ cell.end = maxDate && time === maxDate
}
}
}
@@ -468,12 +445,6 @@ const getTarget = (event) => {
return target
}
-export const removeFromArray = (arr, pred) => {
- const idx = typeof pred === 'function' ? arrayFindIndex(arr, pred) : arr.indexOf(pred)
-
- return idx >= 0 ? [...arr.slice(0, idx), ...arr.slice(idx + 1)] : arr
-}
-
export const handleClick =
({ api, emit, props, state }) =>
(event) => {
@@ -529,8 +500,16 @@ export const handleClick =
}
}
-export const getCssToken = ({ api }) => (cell, prexfix = '') => {
- const cssStr = api.getCellClasses(cell) || ''
+export const removeFromArray = (arr, pred) => {
+ const idx = typeof pred === 'function' ? arrayFindIndex(arr, pred) : arr.indexOf(pred)
- return cssStr.split(' ').map((className) => prexfix + className)
+ return idx >= 0 ? [...arr.slice(0, idx), ...arr.slice(idx + 1)] : arr
}
+
+export const getCssToken =
+ ({ api }) =>
+ (cell, prexfix = '') => {
+ const cssStr = api.getCellClasses(cell) || ''
+
+ return cssStr.split(' ').map((className) => prexfix + className)
+ }
diff --git a/packages/renderless/src/date-table/vue.ts b/packages/renderless/src/date-table/vue.ts
index 4d27f50dc..0b8fff133 100644
--- a/packages/renderless/src/date-table/vue.ts
+++ b/packages/renderless/src/date-table/vue.ts
@@ -42,7 +42,7 @@ const initState = ({ reactive, computed, api, props }) => {
month: computed(() => !Array.isArray(props.date) && props.date.getMonth()),
offsetDay: computed(() => api.getOffsetDay()),
year: computed(() => !Array.isArray(props.date) && props.date.getFullYear()),
- startDate: computed(() => getStartDateOfMonth(state.year, state.month)),
+ startDate: computed(() => getStartDateOfMonth(state.year, state.month, state.offsetDay)),
date: props.value
})
diff --git a/packages/renderless/src/numeric/index.ts b/packages/renderless/src/numeric/index.ts
index 9e1528bbe..aecc9aea0 100644
--- a/packages/renderless/src/numeric/index.ts
+++ b/packages/renderless/src/numeric/index.ts
@@ -116,7 +116,10 @@ export const increase =
return
}
- const value = (props.mouseWheel ? state.displayValue : Number(state.userInput)) || 0
+ // 处理高精度情况
+ const userInput = props.stringMode ? state.userInput : Number(state.userInput)
+
+ const value = (props.mouseWheel ? state.displayValue : userInput) || 0
if (value.toString().includes('e')) {
return
@@ -142,7 +145,11 @@ export const decrease =
if (state.inputDisabled || state.minDisabled) {
return
}
- const value = (props.mouseWheel ? state.displayValue : Number(state.userInput)) || 0
+
+ // 处理高精度情况
+ const userInput = props.stringMode ? state.userInput : Number(state.userInput)
+
+ const value = (props.mouseWheel ? state.displayValue : userInput) || 0
if (value.toString().includes('e')) {
return
diff --git a/packages/renderless/src/picker/index.ts b/packages/renderless/src/picker/index.ts
index f355319fc..dbce4779e 100644
--- a/packages/renderless/src/picker/index.ts
+++ b/packages/renderless/src/picker/index.ts
@@ -16,7 +16,6 @@ import userPopper from '../common/deps/vue-popper'
import { DATEPICKER } from '../common'
import { formatDate, parseDate, isDateObject, getWeekNumber, prevDate, nextDate } from '../common/deps/date-util'
import { extend } from '../common/object'
-import { isFunction } from '../common/type'
import globalTimezone from './timezone'
const iso8601Reg = /^\d{4}-\d{2}-\d{2}(.)\d{2}:\d{2}:\d{2}(.+)$/
@@ -41,8 +40,18 @@ export const getPanel =
return DatePanel
}
+export const watchMobileVisible =
+ ({ api, props, state }) =>
+ ([dateMobileVisible, timeMobileVisible]) => {
+ if (dateMobileVisible || timeMobileVisible) {
+ state.valueOnOpen = Array.isArray(props.modelValue) ? [...props.modelValue] : props.modelValue
+ } else {
+ api.emitChange(props.modelValue)
+ }
+ }
+
export const watchPickerVisible =
- ({ api, vm, dispatch, emit, props, state }) =>
+ ({ api, vm, dispatch, emit, props, state, nextTick }) =>
(value) => {
if (props.readonly || state.pickerDisabled || state.isMobileScreen) return
@@ -52,15 +61,18 @@ export const watchPickerVisible =
state.valueOnOpen = Array.isArray(props.modelValue) ? [...props.modelValue] : props.modelValue
} else {
api.hidePicker()
- api.emitChange(props.modelValue)
+ // tiny 新增: 解决vue3下,modelValue的值仍是旧值,误认为值不变,不触发change事件了。
+ nextTick(() => api.emitChange(props.modelValue))
state.userInput = null
if (props.validateEvent) {
dispatch('FormItem', 'form.blur')
}
+
if (props.changeOnConfirm && !valueEquals(props.modelValue, state.oldValue)) {
emit('update:modelValue', state.oldValue)
}
+
emit('blur', vm)
api.blur()
}
@@ -117,14 +129,8 @@ export const displayValue =
const formatObj = {
rangeSeparator: props.rangeSeparator
}
- const formattedValue = api.formatAsFormatAndType(
- state.parsedValue,
- state.format,
- state.type,
- props.rangeSeparator,
- formatObj
- )
+ const formattedValue = api.formatAsFormatAndType(state.parsedValue, state.format, state.type, formatObj)
if (Array.isArray(state.userInput)) {
return [
state.userInput[0] || (formattedValue && formattedValue[0]) || '',
@@ -176,12 +182,13 @@ export const parsedValue =
if (isServiceTimezone) {
if (Array.isArray(date)) {
- date = [].concat(date).map((item) => (isDate(item) ? formatDate(item, state.valueFormat, t) : item))
+ date = [].concat(date).map((item) => {
+ return isDate(item) ? formatDate(item, state.valueFormat, t) : item
+ })
} else {
date = formatDate(date, state.valueFormat, t)
}
}
-
const result = api.parseAsFormatAndType(date, state.valueFormat, state.type, props.rangeSeparator)
if (Array.isArray(result)) {
return result.map((date) => getDateWithNewTimezone(date, from, to, timezoneOffset))
@@ -194,7 +201,6 @@ export const parsedValue =
const values = []
.concat(props.modelValue)
.map((val) => getDateWithNewTimezone(trans(val), from, to, timezoneOffset))
-
return values.length > 1 ? values : values[0]
}
@@ -337,7 +343,6 @@ const getWeekOfTypeValueResolveMap = ({ t, props, api }) => ({
trueDate.setHours(0, 0, 0, 0)
trueDate.setDate(trueDate.getDate() + 3 - ((trueDate.getDay() + 6) % 7))
}
-
let date
if (type === 'format' && !/W/.test(format)) {
const { start, end } = getWeekRange(value, format, t, props.pickerOptions)
@@ -346,6 +351,7 @@ const getWeekOfTypeValueResolveMap = ({ t, props, api }) => ({
date = formatDate(trueDate, format, t)
date = /WW/.test(date) ? date.replace(/WW/, week < 10 ? '0' + week : week) : date.replace(/W/, week)
}
+
return date
},
parser(text, format) {
@@ -505,12 +511,211 @@ export const handleMouseEnter =
}
}
+// 这个是 input 组件的 input 事件,应该只有一个 event 参数,input 组件的具体值从 event.target.value 中获取。
+export const handleInput =
+ ({ state, props, api }) =>
+ (val, event) => {
+ // 兼容tiny-input传参不同导致的报错问题
+ event = val.target ? val : event
+ if (props.autoFormat) {
+ const value = api.formatInputValue({ event, prevValue: state.displayValue })
+ state.userInput = value
+ } else {
+ const val = event.target.value
+ state.userInput = val
+ }
+ }
+
+export const formatInputValue =
+ ({ props, state }) =>
+ ({ event, prevValue = '' }) => {
+ const val = event.target.value
+ const inputData = event.data
+ const format = state.type === 'time-select' ? 'HH:mm' : props.format || DATEPICKER.DateFormats[state.type]
+ if (inputData && inputData.charCodeAt() >= 48 && inputData.charCodeAt() <= 57) {
+ return formatText({ event, format, text: prevValue, needSelectionStart: true })
+ } else {
+ return val
+ }
+ }
+
+const getSelectionStart = ({ value, format, regx, event }) => {
+ const formatMatchArr = format.match(regx)
+ let selectionStart = getSelectionStartIndex(event)
+ let I = 0
+
+ if (value !== '') {
+ const match = value.match(/[0-9]/g)
+ I = match === null ? 0 : match.length
+
+ for (let i = 0; i < formatMatchArr.length; i++) {
+ I -= Math.max(formatMatchArr[i].length, 2)
+ }
+
+ I = I >= 0 ? 1 : 0
+ I === 1 && selectionStart >= value.length && (selectionStart = value.length - 1)
+ }
+
+ return { selectionStart, I }
+}
+
+const getNum = (value, format, regx) => {
+ let len = value.length
+ if (format && regx) {
+ const formatMatchArr = format.match(regx)
+ len = Math.max(len, formatMatchArr.join('').length)
+ }
+ let num = { str: '', arr: [] }
+ for (let i = 0; i < len; i++) {
+ let char = value.charAt(i) ? value.charAt(i) : '00'
+
+ if (/[0-9]/.test(char)) {
+ num.str += char
+ } else {
+ num.arr[i] = 1
+ }
+ }
+ return num
+}
+
+const getSelectionStartIndex = (event) => {
+ const inputElem = event.target
+ return inputElem.selectionStart - (event.data ? event.data.length : 0)
+}
+
+const moveStart = (inputElem, moveStartIndex) => {
+ if (inputElem.setSelectionRange) {
+ inputElem.focus()
+ setTimeout(() => {
+ inputElem.setSelectionRange(moveStartIndex, moveStartIndex)
+ }, 0)
+ }
+}
+
+export const formatText = ({ event, text, format, needSelectionStart = false }) => {
+ if (!format) return text
+ let cursorOffset = 0
+ let value = ''
+ let regx = /yyyy|yyy|yy|y|MM|M|dd|d|HH|hh|H|h|mm|m|ss|s|WW|W|w/g
+ let startIndex = 0
+ let { numStr, selectionStart } = getNumAndSelectionStart({
+ value: text,
+ format,
+ regx,
+ event,
+ needSelectionStart
+ })
+
+ let matchResult = regx.exec(format)
+ while (numStr.str !== '' && matchResult !== null) {
+ let subStr
+ let newNum
+ let subLen
+ const endIndex = matchResult.index
+ if (startIndex >= 0) {
+ value += format.substring(startIndex, endIndex)
+ }
+
+ selectionStart >= startIndex + cursorOffset &&
+ selectionStart <= endIndex + cursorOffset &&
+ (selectionStart = selectionStart + endIndex - startIndex)
+
+ startIndex = regx.lastIndex
+ subLen = startIndex - endIndex
+
+ subStr = numStr.str.substring(0, subLen)
+
+ const firstMatchChar = matchResult[0].charAt(0)
+ const firstChar = parseInt(subStr.charAt(0), 10)
+
+ if (numStr.str.length > 1) {
+ const secondChar = numStr.str.charAt(1)
+ newNum = 10 * firstChar + parseInt(secondChar, 10)
+ } else {
+ newNum = firstChar
+ }
+ if (
+ numStr.arr[endIndex + 1] ||
+ (firstMatchChar === 'M' && newNum > 12) ||
+ (firstMatchChar === 'd' && newNum > 31) ||
+ (['H', 'h'].includes(firstMatchChar) && newNum > 23) ||
+ ('ms'.includes(firstMatchChar) && newNum > 59)
+ ) {
+ subStr = matchResult[0].length === 2 ? '0' + firstChar : firstChar
+ selectionStart++
+ } else {
+ if (subLen === 1) {
+ subStr = String(newNum)
+ subLen++
+ cursorOffset++
+ }
+ }
+
+ value += subStr
+ numStr.str = numStr.str.substring(subLen)
+ matchResult = regx.exec(format)
+ }
+
+ const { value: val, selectionStart: cursorPos } = checkFormat({
+ value,
+ format,
+ startIndex,
+ selectionStart,
+ regx,
+ needSelectionStart
+ })
+ value = val
+ selectionStart = cursorPos
+
+ needSelectionStart && moveStart(event.target, selectionStart)
+
+ return value
+}
+
+const getNumAndSelectionStart = ({ value, format, regx, event, needSelectionStart }) => {
+ if (needSelectionStart) {
+ let { selectionStart, I } = getSelectionStart({ value, format, regx, event })
+ let valueStr
+
+ if (event.data) {
+ valueStr = value.substring(0, selectionStart) + event.data + value.substring(selectionStart + I)
+ selectionStart++
+ } else {
+ valueStr = value
+ }
+
+ const numStr = getNum(valueStr)
+
+ return { numStr, selectionStart }
+ } else {
+ const numStr = getNum(value, format, regx)
+ return { numStr }
+ }
+}
+
+const checkFormat = ({ value, format, startIndex, selectionStart, regx, needSelectionStart }) => {
+ if (
+ (!needSelectionStart && regx.lastIndex === 0) ||
+ (needSelectionStart && regx.lastIndex === 0 && selectionStart >= startIndex)
+ ) {
+ const subFormat = `(?<=${format.substring(0, startIndex)})(\\s*\\S*\\s*)+`
+ const pattern = new RegExp(subFormat, 'g')
+
+ const res = format.match(pattern)
+
+ if (res) {
+ value += res[0]
+ selectionStart = value.length
+ }
+ }
+ return { value, selectionStart }
+}
+
export const handleChange =
({ api, state }) =>
() => {
if (state.userInput) {
const value = api.parseString(state.displayValue)
-
if (value) {
state.picker.state.value = value
@@ -534,6 +739,7 @@ export const handleStartInput =
const value = props.autoFormat
? api.formatInputValue({ event, prevValue: state.displayValue[0] })
: event.target.value
+
if (state.userInput) {
state.userInput = [value, state.userInput[1]]
} else {
@@ -754,15 +960,12 @@ export const handleKeydown =
}
export const hidePicker =
- ({ state, doDestroy }) =>
+ ({ destroyPopper, state }) =>
() => {
if (state.picker) {
state.picker.resetView && state.picker.resetView()
state.pickerVisible = state.picker.visible = state.picker.state.visible = false
-
- if (isFunction(doDestroy)) {
- doDestroy()
- }
+ destroyPopper()
}
}
@@ -780,7 +983,7 @@ export const showPicker =
state.pickerVisible = state.picker.state.visible = true
state.picker.state.value = state.parsedValue
state.picker.resetView && state.picker.resetView()
- // 使用nextTick方法解决time-picker组件的demo"下拉框类名"点击input,时间选择框弹出位置错误的问题,
+
nextTick(() => {
updatePopper(state.picker.$el)
state.picker.adjustSpinners && state.picker.adjustSpinners()
@@ -791,6 +994,7 @@ export const handlePick =
({ state, api }) =>
(date = '', visible = false) => {
if (!state.picker) return
+
state.userInput = null
state.pickerVisible = state.picker.state.visible = visible
@@ -806,25 +1010,24 @@ export const handleSelectRange = (state) => (start, end, pos) => {
}
const adjust = (value, start, end) => {
- if (!value) {
- return { start, end }
- }
- const valueReg = /(\d+):(\d+):(\d+)(\s+.+)?/
+ if (value) {
+ const valueReg = /(\d+):(\d+):(\d+)(\s+.+)?/
- if (valueReg.test(value)) {
- const matched = valueReg.exec(value)
- const hourLength = matched[1].length
- const minuteLength = matched[2].length
- const secondLength = matched[3].length
+ if (valueReg.test(value)) {
+ const matched = valueReg.exec(value)
+ const hourLength = matched[1].length
+ const minuteLength = matched[2].length
+ const secondLength = matched[3].length
- if (start === 0) {
- end = hourLength
- } else if (start === 3) {
- start = hourLength + 1
- end = hourLength + minuteLength + 1
- } else {
- start = hourLength + minuteLength + 2
- end = hourLength + minuteLength + secondLength + 2
+ if (start === 0) {
+ end = hourLength
+ } else if (start === 3) {
+ start = hourLength + 1
+ end = hourLength + minuteLength + 1
+ } else {
+ start = hourLength + minuteLength + 2
+ end = hourLength + minuteLength + secondLength + 2
+ }
}
}
@@ -965,7 +1168,6 @@ export const emitInput =
}
const formatted = api.formatToValue(value) || val
-
if (!valueEquals(props.modelValue, formatted)) {
emit('update:modelValue', formatted)
}
@@ -1051,19 +1253,23 @@ export const computedFormat =
export const computedTriggerClass =
({ props, state }) =>
- () =>
- props.suffixIcon ||
- props.prefixIcon ||
- (state.type.includes(DATEPICKER.Time) ? DATEPICKER.IconTime : DATEPICKER.IconDate)
+ () => {
+ return (
+ props.suffixIcon ||
+ props.prefixIcon ||
+ (state.type.includes(DATEPICKER.Time) ? DATEPICKER.IconTime : DATEPICKER.IconDate)
+ )
+ }
export const computedHaveTrigger =
({ props }) =>
- () =>
- typeof props.showTrigger !== 'undefined' ? props.showTrigger : DATEPICKER.TriggerTypes.includes(props.type)
+ () => {
+ return typeof props.showTrigger !== 'undefined' ? props.showTrigger : DATEPICKER.TriggerTypes.includes(props.type)
+ }
export const initPopper = ({ props, hooks, vnode }) => {
const { reactive, watch, toRefs, onBeforeUnmount, onDeactivated } = hooks
- // vnode就是第3参,名字有误导性
+ // tiny提示: vnode就是第3参,名字有误导性
const { emit, vm, slots, nextTick } = vnode
const placementMap = DATEPICKER.PlacementMap
@@ -1073,7 +1279,7 @@ export const initPopper = ({ props, hooks, vnode }) => {
emit,
props: {
...props,
- popperOptions: { boundariesPadding: 0, gpuAcceleration: false },
+ popperOptions: Object.assign({ boundariesPadding: 0, gpuAcceleration: false }, props.popperOptions),
visibleArrow: true,
offset: 0,
boundariesPadding: 5,
@@ -1163,203 +1369,3 @@ export const setInputPaddingLeft =
})
}
}
-
-const getSelectionStart = ({ value, format, regx, event }) => {
- const formatMatchArr = format.match(regx)
- let selectionStart = getSelectionStartIndex(event)
- let I = 0
-
- if (value !== '') {
- const match = value.match(/[0-9]/g)
- I = match === null ? 0 : match.length
-
- for (let i = 0; i < formatMatchArr.length; i++) {
- I -= Math.max(formatMatchArr[i].length, 2)
- }
-
- I = I >= 0 ? 1 : 0
- I === 1 && selectionStart >= value.length && (selectionStart = value.length - 1)
- }
-
- return { selectionStart, I }
-}
-
-const getNum = (value, format, regx) => {
- let len = value.length
- if (format && regx) {
- const formatMatchArr = format.match(regx)
- len = Math.max(len, formatMatchArr.join('').length)
- }
- let num = { str: '', arr: [] }
- for (let i = 0; i < len; i++) {
- let char = value.charAt(i) ? value.charAt(i) : '00'
-
- if (/[0-9]/.test(char)) {
- num.str += char
- } else {
- num.arr[i] = 1
- }
- }
- return num
-}
-
-const getSelectionStartIndex = (event) => {
- const inputElem = event.target
- return inputElem.selectionStart - (event.data ? event.data.length : 0)
-}
-
-const getNumAndSelectionStart = ({ value, format, regx, event, needSelectionStart }) => {
- if (needSelectionStart) {
- let { selectionStart, I } = getSelectionStart({ value, format, regx, event })
- let valueStr
-
- if (event.data) {
- valueStr = value.substring(0, selectionStart) + event.data + value.substring(selectionStart + I)
- selectionStart++
- } else {
- valueStr = value
- }
-
- const numStr = getNum(valueStr)
-
- return { numStr, selectionStart }
- } else {
- const numStr = getNum(value, format, regx)
- return { numStr }
- }
-}
-
-const checkFormat = ({ value, format, startIndex, selectionStart, regx, needSelectionStart }) => {
- if (
- (!needSelectionStart && regx.lastIndex === 0) ||
- (needSelectionStart && regx.lastIndex === 0 && selectionStart >= startIndex)
- ) {
- const subFormat = `(?<=${format.substring(0, startIndex)})(\\s*\\S*\\s*)+`
- const pattern = new RegExp(subFormat, 'g')
-
- const res = format.match(pattern)
-
- if (res) {
- value += res[0]
- selectionStart = value.length
- }
- }
- return { value, selectionStart }
-}
-
-const moveStart = (inputElem, moveStartIndex) => {
- if (inputElem.setSelectionRange) {
- inputElem.focus()
- setTimeout(() => {
- inputElem.setSelectionRange(moveStartIndex, moveStartIndex)
- }, 0)
- }
-}
-
-// 这个是 input 组件的 input 事件,应该只有一个 event 参数,input 组件的具体值从 event.target.value 中获取。
-export const handleInput =
- ({ state, props, api }) =>
- (val, event) => {
- // 兼容tiny-input传参不同导致的报错问题
- event = val.target ? val : event
- if (props.autoFormat) {
- const value = api.formatInputValue({ event, prevValue: state.displayValue })
- state.userInput = value
- } else {
- const val = event.target.value
- state.userInput = val
- }
- }
-
-export const formatInputValue =
- ({ props, state }) =>
- ({ event, prevValue = '' }) => {
- const val = event.target.value
- const inputData = event.data
- const format = state.type === 'time-select' ? 'HH:mm' : props.format || DATEPICKER.DateFormats[state.type]
- if (inputData && inputData.charCodeAt() >= 48 && inputData.charCodeAt() <= 57) {
- return formatText({ event, format, text: prevValue, needSelectionStart: true })
- } else {
- return val
- }
- }
-
-export const formatText = ({ event, text, format, needSelectionStart = false }) => {
- if (!format) return text
- let cursorOffset = 0
- let value = ''
- let regx = /yyyy|yyy|yy|y|MM|M|dd|d|HH|hh|H|h|mm|m|ss|s|WW|W|w/g
- let startIndex = 0
- let { numStr, selectionStart } = getNumAndSelectionStart({
- value: text,
- format,
- regx,
- event,
- needSelectionStart
- })
-
- let matchResult = regx.exec(format)
- while (numStr.str !== '' && matchResult !== null) {
- let subStr
- let newNum
- let subLen
- const endIndex = matchResult.index
- if (startIndex >= 0) {
- value += format.substring(startIndex, endIndex)
- }
-
- selectionStart >= startIndex + cursorOffset &&
- selectionStart <= endIndex + cursorOffset &&
- (selectionStart = selectionStart + endIndex - startIndex)
-
- startIndex = regx.lastIndex
- subLen = startIndex - endIndex
-
- subStr = numStr.str.substring(0, subLen)
-
- const firstMatchChar = matchResult[0].charAt(0)
- const firstChar = parseInt(subStr.charAt(0), 10)
-
- if (numStr.str.length > 1) {
- const secondChar = numStr.str.charAt(1)
- newNum = 10 * firstChar + parseInt(secondChar, 10)
- } else {
- newNum = firstChar
- }
- if (
- numStr.arr[endIndex + 1] ||
- (firstMatchChar === 'M' && newNum > 12) ||
- (firstMatchChar === 'd' && newNum > 31) ||
- (['H', 'h'].includes(firstMatchChar) && newNum > 23) ||
- ('ms'.includes(firstMatchChar) && newNum > 59)
- ) {
- subStr = matchResult[0].length === 2 ? '0' + firstChar : firstChar
- selectionStart++
- } else {
- if (subLen === 1) {
- subStr = String(newNum)
- subLen++
- cursorOffset++
- }
- }
-
- value += subStr
- numStr.str = numStr.str.substring(subLen)
- matchResult = regx.exec(format)
- }
-
- const { value: val, selectionStart: cursorPos } = checkFormat({
- value,
- format,
- startIndex,
- selectionStart,
- regx,
- needSelectionStart
- })
- value = val
- selectionStart = cursorPos
-
- needSelectionStart && moveStart(event.target, selectionStart)
-
- return value
-}
diff --git a/packages/renderless/src/picker/vue.ts b/packages/renderless/src/picker/vue.ts
index 402f8e1a7..ef274e9d2 100644
--- a/packages/renderless/src/picker/vue.ts
+++ b/packages/renderless/src/picker/vue.ts
@@ -16,6 +16,7 @@ import {
watchIsRange,
parseAsFormatAndType,
watchPickerVisible,
+ watchMobileVisible,
getValueEmpty,
getMode,
displayValue,
@@ -177,7 +178,7 @@ const initState = ({ api, reactive, vm, computed, props, utils, parent, breakpoi
const initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) => {
const { t, emit, dispatch, nextTick, vm } = vnode
const { TimePanel, TimeRangePanel } = others
- const { destroyPopper, popperElm, updatePopper, doDestroy } = initPopper({ props, hooks, vnode })
+ const { destroyPopper, popperElm, updatePopper } = initPopper({ props, hooks, vnode })
state.popperElm = popperElm
state.picker = null
@@ -185,7 +186,7 @@ const initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) =>
Object.assign(api, {
destroyPopper,
emitDbTime: emitDbTime({ emit, state, t }),
- hidePicker: hidePicker({ state, doDestroy }),
+ hidePicker: hidePicker({ destroyPopper, state }),
handleSelectChange: ({ tz, date }) => !state.ranged && emit('select-change', { tz, date }),
getPanel: getPanel(others),
handleFocus: handleFocus({ emit, vm, state, api }),
@@ -211,7 +212,8 @@ const initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) =>
handleClose: handleClose({ api, props, state }),
displayValue: displayValue({ api, props, state }),
handlePick: handlePick({ api, state }),
- watchPickerVisible: watchPickerVisible({ api, vm, dispatch, emit, props, state }),
+ watchPickerVisible: watchPickerVisible({ api, vm, dispatch, emit, props, state, nextTick }),
+ watchMobileVisible: watchMobileVisible({ api, props, state }),
formatToString: formatToString({ api, state }),
watchIsRange: watchIsRange({ api, state, TimePanel, TimeRangePanel }),
mountPicker: mountPicker({ api, vm, props, state, updatePopper }),
@@ -226,6 +228,7 @@ const initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) =>
initApi2({ api, props, state, t, parent })
initMobileApi({ api, props, state, t, parent })
}
+
const initApi2 = ({ api, props, state, t, parent }) => {
Object.assign(api, {
t,
@@ -268,6 +271,8 @@ const initWatch = ({ api, state, props, watch, markRaw }) => {
{ immediate: true }
)
+ watch(() => [state.dateMobileOption.visible, state.timeMobileOption.visible], api.watchMobileVisible)
+
watch(() => state.pickerVisible, api.watchPickerVisible)
watch(
diff --git a/packages/renderless/src/popeditor/index.ts b/packages/renderless/src/popeditor/index.ts
index dbb6c51d5..5ec78cfe7 100644
--- a/packages/renderless/src/popeditor/index.ts
+++ b/packages/renderless/src/popeditor/index.ts
@@ -813,7 +813,7 @@ export const doSuggesst =
api.sourceGridSelectChange({ checked: false, row, confirm: false })
})
- if (addtions.length) {
+ if (!state.suggestList.length || addtions.length) {
doQuery(query)
}
} else {
@@ -829,7 +829,7 @@ export const closeSuggestPanel =
const popper = vm.$refs.popper
let keep = !event
- if (event.target) {
+ if (event.target && reference) {
keep = reference.$el.contains(event.target) || popper.contains(event.target)
}
diff --git a/packages/renderless/src/select/index.ts b/packages/renderless/src/select/index.ts
index 5b82dcafc..c5b90c4b2 100644
--- a/packages/renderless/src/select/index.ts
+++ b/packages/renderless/src/select/index.ts
@@ -494,38 +494,14 @@ export const getPluginOption =
return items
}
-// tiny 修改: aui的 toggleCheckAll 在designConfig, 同步时要更新
export const toggleCheckAll =
({ api, state, props }) =>
(filtered) => {
- // tiny 移入内部
- const getEnabledValues = (options) => {
- let values = []
-
- // tiny 新增 避免全不选时,将disabled的项目勾掉
- for (let i = 0; i < options.length; i++) {
- const isEnabled = !options[i].state.disabled && !options[i].state.groupDisabled
- const isRequired = options[i].required
- const isDisabledAndChecked = !isEnabled && options[i].state.selectCls === 'checked-sur'
-
- if (state.isSelectAll) {
- // 取消选中全部,必填和禁用且选中项不可取消
- if (isRequired || isDisabledAndChecked) {
- values.push(options[i].value)
- }
- } else {
- // 选中全部,非禁用项 和 必填项和 禁用且选中项 需选中
- if (isEnabled || isRequired || isDisabledAndChecked) {
- values.push(options[i].value)
- }
- }
- }
-
- return values
- }
-
- let value
- const enabledValues = getEnabledValues(state.options)
+ let value = []
+ // 1. 需要控制勾选或去勾选的项
+ const enabledValues = state.options
+ .filter((op) => !op.state.disabled && !op.state.groupDisabled && !op.required && op.state.visible)
+ .map((op) => op.value)
if (filtered) {
if (state.filteredSelectCls === 'check' || state.filteredSelectCls === 'halfselect') {
@@ -541,10 +517,18 @@ export const toggleCheckAll =
unchecked.length ? (value = enabledValues) : (value = [])
} else if (state.selectCls === 'checked-sur') {
- // tiny 新增
- value = getEnabledValues(state.options)
+ value = []
}
}
+ // 2. 必选项
+ const requiredValue = state.options.filter((op) => op.required).map((op) => op.value)
+
+ // 3. 禁用且已设置为勾选的项
+ const disabledSelectedValues = state.options
+ .filter((op) => (op.state.disabled || op.state.groupDisabled) && op.state.selectCls === 'checked-sur')
+ .map((op) => op.value)
+
+ value = [...value, ...requiredValue, ...disabledSelectedValues]
api.setSoftFocus()
@@ -1278,10 +1262,9 @@ export const watchValue =
}
if (props.filterable && !props.reserveKeyword) {
- const isChange = false
- const isInput = true
- props.renderType !== constants.TYPE.Grid && (state.query = '')
- api.handleQueryChange(state.query, isChange, isInput)
+ // tiny 优化: 多选且props.reserveKeyword为false时, aui此处会多请求一次
+ // searchable时,不清空query, 这样才能保持搜索结果
+ props.renderType !== constants.TYPE.Grid && !props.searchable && (state.query = '')
}
}
@@ -1549,7 +1532,7 @@ export const queryVisibleOptions =
if (props.optimization) {
return optmzApis.queryVisibleOptions(vm, isMobileFirstMode)
} else {
- return Array.from(vm.$refs.scrollbar.$el.querySelectorAll('[data-index]:not([style*="display: none"])'))
+ return Array.from(vm.$refs.scrollbar?.$el.querySelectorAll('[data-index]:not([style*="display: none"])') || [])
}
}
@@ -1839,11 +1822,16 @@ export const watchHoverIndex =
}
export const handleDropdownClick =
- ({ emit }) =>
+ ({ vm, state, props, emit }) =>
($event) => {
+ if (props.allowCopy && vm.$refs.reference) {
+ vm.$refs.reference.$el.querySelector('input').selectionEnd = 0
+ }
+
+ state.softFocus = false
+
emit('dropdown-click', $event)
}
-
export const handleEnterTag =
({ state }) =>
($event, key) => {
diff --git a/packages/renderless/src/select/vue.ts b/packages/renderless/src/select/vue.ts
index 58e5c7a51..28237caa4 100644
--- a/packages/renderless/src/select/vue.ts
+++ b/packages/renderless/src/select/vue.ts
@@ -169,7 +169,7 @@ export const api = [
'clearSearchText'
]
-const initState = ({ reactive, computed, props, api, emitter, parent, constants, useBreakpoint, vm }) => {
+const initState = ({ reactive, computed, props, api, emitter, parent, constants, useBreakpoint, vm, designConfig }) => {
const stateAdd = initStateAdd({ computed, props, api, parent })
const state = reactive({
...stateAdd,
@@ -226,7 +226,13 @@ const initState = ({ reactive, computed, props, api, emitter, parent, constants,
// tiny 新增
getIcon: computed(() => api.computedGetIcon()),
getTagType: computed(() => api.computedGetTagType()),
- isSelectAll: computed(() => state.selectCls === 'checked-sur')
+ isSelectAll: computed(() => state.selectCls === 'checked-sur'),
+ autoHideDownIcon: (() => {
+ if (designConfig?.state && 'autoHideDownIcon' in designConfig.state) {
+ return designConfig.state.autoHideDownIcon
+ }
+ return true // tiny 默认为true
+ })()
})
return state
@@ -431,7 +437,7 @@ const addApi = ({
mounted: mounted({ api, parent, state, props, vm, designConfig }),
unMount: unMount({ api, parent, vm, state }),
watchOptimizeOpts: watchOptimizeOpts({ props, state }),
- handleDropdownClick: handleDropdownClick({ emit }),
+ handleDropdownClick: handleDropdownClick({ props, vm, state, emit }),
handleEnterTag: handleEnterTag({ state }),
calcCollapseTags: calcCollapseTags({ state, vm }),
initValue: initValue({ state }),
@@ -555,8 +561,19 @@ export const renderless = (
{ computed, onBeforeUnmount, onMounted, reactive, watch, provide, inject },
{ vm, parent, emit, constants, nextTick, dispatch, t, emitter, isMobileFirstMode, useBreakpoint, designConfig }
) => {
- const api = {}
- const state = initState({ reactive, computed, props, api, emitter, parent, constants, useBreakpoint, vm })
+ const api: any = {}
+ const state = initState({
+ reactive,
+ computed,
+ props,
+ api,
+ emitter,
+ parent,
+ constants,
+ useBreakpoint,
+ vm,
+ designConfig
+ })
const dialog = inject('dialog', null)
provide('selectEmitter', state.selectEmitter)
@@ -579,9 +596,7 @@ export const renderless = (
isMobileFirstMode,
designConfig
})
- initWatch({ watch, props, api, state, nextTick })
- onMounted(api.mounted)
- onBeforeUnmount(api.unMount)
+
parent.$on('handle-clear', (event) => {
api.handleClearClick(event)
})
@@ -599,5 +614,14 @@ export const renderless = (
state.selectEmitter.on(constants.EVENT_NAME.setSelected, api.setSelected)
state.selectEmitter.on(constants.EVENT_NAME.initValue, api.initValue)
+ initWatch({ watch, props, api, state, nextTick })
+
+ onMounted(api.mounted)
+
+ onBeforeUnmount(() => {
+ api.unMount()
+ dialog && dialog.state.emitter.off('handleSelectClose', api.handleClose)
+ })
+
return api
}
diff --git a/packages/renderless/src/tree-node/index.ts b/packages/renderless/src/tree-node/index.ts
index 43c8a47b1..355e602f9 100644
--- a/packages/renderless/src/tree-node/index.ts
+++ b/packages/renderless/src/tree-node/index.ts
@@ -349,6 +349,7 @@ export const addNode =
state.tree.state.emitter.emit('tree-node-add', event, node)
}
+// tiny 新增
export const computedExpandIcon =
({ designConfig }) =>
(treeRoot, state) => {
@@ -356,6 +357,7 @@ export const computedExpandIcon =
return state.tree.icon
}
+ // tiny 新增的判断。 显示线时强制切换图标,仅smb定制了
if (treeRoot.showLine) {
const expandIcon = designConfig?.icons?.expanded || 'icon-minus-square'
const collapseIcon = designConfig?.icons?.collapse || 'icon-plus-square'
@@ -364,7 +366,7 @@ export const computedExpandIcon =
return 'icon-chevron-right'
}
-
+// tiny 新增
export const computedIndent =
() =>
({ node, showLine }, { tree }) => {
diff --git a/packages/renderless/src/tree-node/vue.ts b/packages/renderless/src/tree-node/vue.ts
index 17d553964..681c39690 100644
--- a/packages/renderless/src/tree-node/vue.ts
+++ b/packages/renderless/src/tree-node/vue.ts
@@ -36,6 +36,7 @@ import {
deleteNode,
onSiblingToggleExpand,
watchExpandedChange,
+ // tiny 新增
computedExpandIcon,
computedIndent
} from './index'
diff --git a/packages/renderless/src/user/index.ts b/packages/renderless/src/user/index.ts
index 1749bd942..6710e154a 100644
--- a/packages/renderless/src/user/index.ts
+++ b/packages/renderless/src/user/index.ts
@@ -250,7 +250,7 @@ export const updateOptions =
export const autoSelect =
({ props, state, nextTick }) =>
(usersList) => {
- if (!usersList.length) {
+ if (!usersList.length || (props.multiple && props.multipleLimit && state.user.length >= props.multipleLimit)) {
return nextTick()
}
diff --git a/packages/theme-saas/src/drawer/index.less b/packages/theme-saas/src/drawer/index.less
index 01b4d8721..5c9156f1a 100644
--- a/packages/theme-saas/src/drawer/index.less
+++ b/packages/theme-saas/src/drawer/index.less
@@ -138,11 +138,15 @@
@apply text-base;
@apply items-center;
- .@{drawer-prefix-cls}__title {
+ .@{drawer-prefix-cls}__header-left {
@apply ~'max-w-[80%]';
- @apply pr-4;
- @apply text-left;
- @apply truncate;
+
+ // 标题增加帮助提示, 勿覆盖
+ .@{drawer-prefix-cls}__title {
+ @apply pr-4;
+ @apply text-left;
+ @apply truncate;
+ }
}
.@{drawer-prefix-cls}__header-right {
diff --git a/packages/theme-saas/theme/theme.json b/packages/theme-saas/theme/theme.json
index 517e447d8..1d4cf1324 100644
--- a/packages/theme-saas/theme/theme.json
+++ b/packages/theme-saas/theme/theme.json
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
- "themeName": "华为SaaS设计系统主题",
+ "themeName": "SaaS设计系统主题",
"themeColor": [
{
"mode": "light",
diff --git a/packages/theme/src/drawer/index.less b/packages/theme/src/drawer/index.less
index 01a8018bf..fd3e2c38f 100644
--- a/packages/theme/src/drawer/index.less
+++ b/packages/theme/src/drawer/index.less
@@ -150,7 +150,6 @@
border-bottom: 1px solid var(--ti-drawer-divider-border-color);
.@{drawer-prefix-cls}__title {
- max-width: 80%;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
@@ -161,7 +160,7 @@
}
.@{drawer-prefix-cls}__header-left {
- flex: 1;
+ max-width: 80%;
display: flex;
align-items: center;
padding-right: 16px;
diff --git a/packages/theme/src/tree/index.less b/packages/theme/src/tree/index.less
index 2074d239e..f334bf80a 100644
--- a/packages/theme/src/tree/index.less
+++ b/packages/theme/src/tree/index.less
@@ -425,6 +425,10 @@
transform: rotate(0);
}
}
+
+ &.is-leaf {
+ visibility: hidden;
+ }
}
&__label {
diff --git a/packages/vue/src/button/src/mobile-first.vue b/packages/vue/src/button/src/mobile-first.vue
index 1035f7d84..1916688ca 100644
--- a/packages/vue/src/button/src/mobile-first.vue
+++ b/packages/vue/src/button/src/mobile-first.vue
@@ -23,7 +23,7 @@
)
"
:tabindex="tabindex"
- v-bind="a($attrs, ['class', 'style'], true)"
+ v-bind="a($attrs, ['class', 'style', 'id'], true)"
>
diff --git a/packages/vue/src/checkbox/src/pc.vue b/packages/vue/src/checkbox/src/pc.vue
index ab2c2b0fe..a84e38a55 100644
--- a/packages/vue/src/checkbox/src/pc.vue
+++ b/packages/vue/src/checkbox/src/pc.vue
@@ -100,7 +100,8 @@ import type { ICheckboxApi } from '@opentiny/vue-renderless/types/checkbox.type'
import Tooltip from '@opentiny/vue-tooltip'
export default defineComponent({
- emits: ['update:modelValue', 'change', 'complete', 'click'],
+ // tiny 新增。 renderless中,没有emit('click')的地方。 此处勿声明,否则会造成丢失click事件。
+ emits: ['update:modelValue', 'change', 'complete'],
props: [
...props,
'modelValue',
diff --git a/packages/vue/src/grid/src/cell/src/cell.ts b/packages/vue/src/grid/src/cell/src/cell.ts
index 68b96dc53..5d6e77cc6 100644
--- a/packages/vue/src/grid/src/cell/src/cell.ts
+++ b/packages/vue/src/grid/src/cell/src/cell.ts
@@ -773,7 +773,7 @@ export const Cell = {
renderEditHeader(h, params) {
let { $table, column } = params
let { editConfig, editRules, validOpts } = $table
- let { filter, remoteSort, sortable, type } = column
+ let { filter, remoteSort, sortable, type, own } = column
let icon = GLOBAL_CONFIG.icon
let isRequired
@@ -799,7 +799,7 @@ export const Cell = {
let vNodes = [
isRequired && showAsterisk ? h('i', { class: `tiny-icon ${icon.required}` }) : null,
- !editConfig || !column.showIcon ? null : h(icon.edit, { class: 'tiny-grid-edit-icon tiny-svg-size' })
+ !editConfig || !own.showIcon ? null : h(icon.edit, { class: 'tiny-grid-edit-icon tiny-svg-size' })
]
vNodes = vNodes.concat(Cell.renderHeader(h, params))
diff --git a/packages/vue/src/grid/src/dragger/src/rowDrop.ts b/packages/vue/src/grid/src/dragger/src/rowDrop.ts
index 3deacc1e3..3f160528b 100644
--- a/packages/vue/src/grid/src/dragger/src/rowDrop.ts
+++ b/packages/vue/src/grid/src/dragger/src/rowDrop.ts
@@ -57,7 +57,7 @@ export const createHandlerOnEnd = ({ _vm, refresh }) => {
if (insertRecords.length) {
return false
}
- const options = { children: 'children' }
+ const options = { children: (_vm.treeConfig || {}).children || 'children' }
const targetTrElem = event.item
const { parentNode: wrapperElem, previousElementSibling: prevTrElem } = targetTrElem
// 这里优先使用用户通过props传递过来的表格数据,所以拖拽后会改变原始数据
diff --git a/packages/vue/src/grid/src/footer/src/footer.ts b/packages/vue/src/grid/src/footer/src/footer.ts
index d44bef3e5..f168f5ed4 100644
--- a/packages/vue/src/grid/src/footer/src/footer.ts
+++ b/packages/vue/src/grid/src/footer/src/footer.ts
@@ -176,6 +176,7 @@ export default {
name: `${$prefix}GridFooter`,
props: {
fixedColumn: Array,
+ fixedType: String,
footerData: Array,
size: String,
tableColumn: Array,
@@ -193,7 +194,7 @@ export default {
elemStore[`${keyPrefix}x-space`] = $refs.xSpace
},
render() {
- let { $parent: $table, buildParamFunc, footerData, tableColumn } = this
+ let { $parent: $table, buildParamFunc, fixedColumn, fixedType, footerData, tableColumn } = this
let {
align: allAlign,
columnKey,
@@ -203,7 +204,7 @@ export default {
footerSpanMethod,
columnStore
} = $table
- let { overflowX, showOverflow: allColumnOverflow, tableLayout, tableListeners } = $table
+ let { overflowX, showOverflow: allColumnOverflow, tableLayout, tableListeners, renderFooter } = $table
let tableAttrs = { cellspacing: 0, cellpadding: 0, border: 0 }
let colgroupVNode = renderColgroup(tableColumn)
@@ -219,6 +220,8 @@ export default {
}
let tfootVNode = renderTfoot(Object.assign(arg1, arg2))
+ const renderParams = { $table, columns: tableColumn, footerData, fixedColumns: fixedColumn, fixedType }
+
return h(
'div',
{
@@ -227,21 +230,23 @@ export default {
},
[
h('div', { class: 'tiny-grid-body__x-space', ref: 'xSpace' }),
- h(
- 'table',
- {
- class: 'tiny-grid__footer',
- style: { tableLayout },
- attrs: tableAttrs,
- ref: 'table'
- },
- [
- // 列宽
- colgroupVNode,
- // 底部
- tfootVNode
- ]
- )
+ typeof renderFooter === 'function'
+ ? renderFooter(renderParams, h)
+ : h(
+ 'table',
+ {
+ class: 'tiny-grid__footer',
+ style: { tableLayout },
+ attrs: tableAttrs,
+ ref: 'table'
+ },
+ [
+ // 列宽
+ colgroupVNode,
+ // 底部
+ tfootVNode
+ ]
+ )
]
)
},
diff --git a/packages/vue/src/grid/src/resize/src/methods.ts b/packages/vue/src/grid/src/resize/src/methods.ts
index f43e646f3..93913e3c1 100644
--- a/packages/vue/src/grid/src/resize/src/methods.ts
+++ b/packages/vue/src/grid/src/resize/src/methods.ts
@@ -33,7 +33,9 @@ export default {
this.recalculate()
}, GlobalConfig.resizeInterval)
- resizeObserver.observe(this.getParentElem())
+ const parentElem = this.getParentElem()
+
+ parentElem && resizeObserver.observe(parentElem)
this.$resize = resizeObserver
},
unbindResize() {
diff --git a/packages/vue/src/grid/src/table/src/strategy.ts b/packages/vue/src/grid/src/table/src/strategy.ts
index 62f4c6c4c..0bc1e184b 100644
--- a/packages/vue/src/grid/src/table/src/strategy.ts
+++ b/packages/vue/src/grid/src/table/src/strategy.ts
@@ -141,7 +141,7 @@ const setTotalRows = (_vm) => {
}
const getTotalRows = (_vm) => {
- const { afterFullData, scrollYLoad, treeConfig } = _vm
+ const { afterFullData, scrollYLoad, scrollLoad, treeConfig } = _vm
let totalRows = afterFullData.length
if (scrollYLoad && treeConfig) {
@@ -152,6 +152,11 @@ const getTotalRows = (_vm) => {
totalRows = TOTALROWS_MAP.get(_vm)
}
+ // 滚动分页场景总行数由(afterFullData.length)调整为(scrollLoad.pageSize),解决最后一页数据不足时滚动条位置改变问题
+ if (scrollLoad) {
+ totalRows = scrollLoad.pageSize || 10
+ }
+
return totalRows
}
diff --git a/packages/vue/src/rich-text/index.ts b/packages/vue/src/rich-text/index.ts
new file mode 100644
index 000000000..75a7b4a5b
--- /dev/null
+++ b/packages/vue/src/rich-text/index.ts
@@ -0,0 +1,23 @@
+import RichText from './src/pc.vue'
+import '@opentiny/vue-theme/rich-text/index.css'
+
+RichText.model = {
+ prop: 'modelValue',
+ event: 'update:modelValue'
+}
+
+/* istanbul ignore next */
+RichText.install = function (Vue) {
+ Vue.component(RichText.name, RichText)
+}
+
+RichText.version = process.env.COMPONENT_VERSION
+
+/* istanbul ignore next */
+if (process.env.BUILD_TARGET === 'runtime') {
+ if (typeof window !== 'undefined' && window.Vue) {
+ RichText.install(window.Vue)
+ }
+}
+
+export default RichText
diff --git a/packages/vue/src/rich-text/package.json b/packages/vue/src/rich-text/package.json
new file mode 100644
index 000000000..6bce44639
--- /dev/null
+++ b/packages/vue/src/rich-text/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@opentiny/vue-rich-text",
+ "version": "3.14.0",
+ "description": "",
+ "main": "lib/index.js",
+ "module": "index.ts",
+ "sideEffects": false,
+ "type": "module",
+ "devDependencies": {
+ "@opentiny-internal/vue-test-utils": "workspace:*",
+ "vitest": "^0.31.0"
+ },
+ "scripts": {
+ "build": "pnpm -w build:ui $npm_package_name",
+ "//postversion": "pnpm build"
+ },
+ "dependencies": {
+ "@opentiny/vue-icon": "workspace:~",
+ "@opentiny/vue-common": "workspace:~",
+ "@opentiny/vue-locale": "workspace:~",
+ "quill": "^1.3.7"
+ },
+ "license": "MIT"
+}
\ No newline at end of file
diff --git a/packages/vue/src/rich-text/src/pc.vue b/packages/vue/src/rich-text/src/pc.vue
new file mode 100644
index 000000000..42df9fb20
--- /dev/null
+++ b/packages/vue/src/rich-text/src/pc.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
diff --git a/packages/vue/src/select/src/pc.vue b/packages/vue/src/select/src/pc.vue
index 436ac2f3f..a4977e007 100644
--- a/packages/vue/src/select/src/pc.vue
+++ b/packages/vue/src/select/src/pc.vue
@@ -190,10 +190,10 @@
-
+
-
+
0 && !loading"
>
-
{{ t('ui.base.all') }} -->
-
+
@@ -460,8 +462,7 @@
!state.multipleLimit &&
state.query &&
!state.emptyText &&
- !remote &&
- !filterable
+ !remote
"
class="tiny-option tiny-select-dropdown__item"
data-tag="tiny-select-dropdown-item"
@@ -477,9 +478,9 @@
>
-
+
diff --git a/packages/vue/src/tabs/src/index.ts b/packages/vue/src/tabs/src/index.ts
index b8de98950..423d239cf 100644
--- a/packages/vue/src/tabs/src/index.ts
+++ b/packages/vue/src/tabs/src/index.ts
@@ -70,6 +70,7 @@ export const tabsProps = {
export default defineComponent({
name: $prefix + 'Tabs',
+ emits: ['tab-nav-update'],
props: tabsProps,
setup(props, context) {
return $setup({ props, context, template })
diff --git a/packages/vue/src/tree/src/tree-node.vue b/packages/vue/src/tree/src/tree-node.vue
index 5b7345dfd..6dd07c501 100644
--- a/packages/vue/src/tree/src/tree-node.vue
+++ b/packages/vue/src/tree/src/tree-node.vue
@@ -67,7 +67,6 @@
diff --git a/packages/vue/src/upload-list/src/index.ts b/packages/vue/src/upload-list/src/index.ts
index bf7312783..232638a61 100644
--- a/packages/vue/src/upload-list/src/index.ts
+++ b/packages/vue/src/upload-list/src/index.ts
@@ -85,6 +85,10 @@ export const uploadListProps = {
lockScroll: {
type: Boolean,
default: true
+ },
+ compact: {
+ type: Boolean,
+ default: false
}
}
diff --git a/packages/vue/src/user/src/index.ts b/packages/vue/src/user/src/index.ts
index a38cfcb12..3a7d7f866 100644
--- a/packages/vue/src/user/src/index.ts
+++ b/packages/vue/src/user/src/index.ts
@@ -23,6 +23,10 @@ export default defineComponent({
type: Boolean,
default: false
},
+ multipleLimit: {
+ type: Number,
+ default: 0
+ },
disabled: {
type: Boolean,
default: false
diff --git a/packages/vue/src/user/src/pc.vue b/packages/vue/src/user/src/pc.vue
index fdb678843..f28a10099 100644
--- a/packages/vue/src/user/src/pc.vue
+++ b/packages/vue/src/user/src/pc.vue
@@ -17,6 +17,7 @@
:placeholder="placeholder"
:collapse-tags="collapseTags"
:multiple="multiple"
+ :multipleLimit="multipleLimit"
@change="userChange"
:loading="state.loading"
filterable
@@ -123,7 +124,8 @@ export default defineComponent({
'maxWidth',
'keepFocus',
'changeCompat',
- 'multiLineDrag'
+ 'multiLineDrag',
+ 'multipleLimit'
],
setup(props, context) {
return setup({ props, context, renderless, api })