fix(select): fix select/picker bugs (#1487)

This commit is contained in:
ajaxzheng 2024-03-14 16:43:46 +08:00 committed by GitHub
parent a70d32573b
commit 85ce304b76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
61 changed files with 817 additions and 511 deletions

View File

@ -393,6 +393,18 @@ export default {
mode: ['mobile-first'], mode: ['mobile-first'],
mfDemo: '' mfDemo: ''
}, },
{
name: 'string-mode',
type: 'Boolean',
defaultValue: '',
desc: {
'zh-CN': '使用字符串模式精度超过JS限制时使用',
'en-US': ''
},
mode: ['pc', 'mobile', 'mobile-first'],
pcDemo: 'string-mode',
mfDemo: ''
},
{ {
name: 'value', name: 'value',
type: 'Number', type: 'Number',

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="gray-bg"> <div class="gray-bg">
<tiny-button ghost>幽灵按钮</tiny-button> <tiny-button ghost reset-time="0">幽灵按钮</tiny-button>
<tiny-button ghost type="primary">主要按钮</tiny-button> <tiny-button ghost type="primary" reset-time="0">主要按钮</tiny-button>
<tiny-button ghost type="success">成功按钮</tiny-button> <tiny-button ghost type="success" reset-time="0">成功按钮</tiny-button>
<tiny-button ghost type="info">信息按钮</tiny-button> <tiny-button ghost type="info" reset-time="0">信息按钮</tiny-button>
<tiny-button ghost type="warning">告警按钮</tiny-button> <tiny-button ghost type="warning" reset-time="0">告警按钮</tiny-button>
<tiny-button ghost type="danger">危险按钮</tiny-button> <tiny-button ghost type="danger" reset-time="0">危险按钮</tiny-button>
</div> </div>
</template> </template>

View File

@ -8,10 +8,12 @@ test('幽灵按钮', async ({ page }) => {
const getGhostBtn = (index: number) => demo.locator('.tiny-button').nth(index) 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('color', 'rgb(37, 43, 58)')
await expect(getGhostBtn(0)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(0)).toHaveCSS('color', 'rgb(94, 124, 224)') await expect(getGhostBtn(0)).toHaveCSS('color', 'rgb(94, 124, 224)')
await expect(getGhostBtn(0)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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('color', 'rgb(94, 124, 224)')
await expect(getGhostBtn(1)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(1)).toHaveCSS('color', 'rgb(118, 147, 245)') await expect(getGhostBtn(1)).toHaveCSS('color', 'rgb(118, 147, 245)')
await expect(getGhostBtn(1)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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('color', 'rgb(80, 212, 171)')
await expect(getGhostBtn(2)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(2)).toHaveCSS('color', 'rgb(172, 242, 220)') await expect(getGhostBtn(2)).toHaveCSS('color', 'rgb(172, 242, 220)')
await expect(getGhostBtn(2)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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('color', 'rgb(37, 43, 58)')
await expect(getGhostBtn(3)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(3)).toHaveCSS('color', 'rgb(92, 97, 115)') await expect(getGhostBtn(3)).toHaveCSS('color', 'rgb(92, 97, 115)')
await expect(getGhostBtn(3)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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('color', 'rgb(250, 152, 65)')
await expect(getGhostBtn(4)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(4)).toHaveCSS('color', 'rgb(250, 194, 10)') await expect(getGhostBtn(4)).toHaveCSS('color', 'rgb(250, 194, 10)')
await expect(getGhostBtn(4)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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('color', 'rgb(199, 0, 11)')
await expect(getGhostBtn(5)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') 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 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 page.waitForTimeout(100)
await expect(getGhostBtn(5)).toHaveCSS('color', 'rgb(214, 74, 82)') await expect(getGhostBtn(5)).toHaveCSS('color', 'rgb(214, 74, 82)')
await expect(getGhostBtn(5)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)') await expect(getGhostBtn(5)).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="gray-bg"> <div class="gray-bg">
<tiny-button ghost>幽灵按钮</tiny-button> <tiny-button ghost reset-time="0">幽灵按钮</tiny-button>
<tiny-button ghost type="primary">主要按钮</tiny-button> <tiny-button ghost reset-time="0" type="primary">主要按钮</tiny-button>
<tiny-button ghost type="success">成功按钮</tiny-button> <tiny-button ghost reset-time="0" type="success">成功按钮</tiny-button>
<tiny-button ghost type="info">信息按钮</tiny-button> <tiny-button ghost reset-time="0" type="info">信息按钮</tiny-button>
<tiny-button ghost type="warning">告警按钮</tiny-button> <tiny-button ghost reset-time="0" type="warning">告警按钮</tiny-button>
<tiny-button ghost type="danger">危险按钮</tiny-button> <tiny-button ghost reset-time="0" type="danger">危险按钮</tiny-button>
</div> </div>
</template> </template>

View File

@ -7,7 +7,7 @@ test('鼠标滚轮事件', async ({ page }) => {
const numeric = page.getByRole('spinbutton') const numeric = page.getByRole('spinbutton')
const initVal = Number(await numeric.inputValue()) const initVal = Number(await numeric.inputValue())
await numeric.click() await numeric.click()
await page.mouse.wheel(0, -100) await page.mouse.wheel(0, -500)
const currentVal = Number(await numeric.inputValue()) const currentVal = Number(await numeric.inputValue())
expect(currentVal).toBeGreaterThanOrEqual(initVal) expect(currentVal).toBeLessThan(initVal)
}) })

View File

@ -0,0 +1,10 @@
<template>
<tiny-numeric style="width: 500px" v-model="stepNum" :step="step" string-mode :precision="20"></tiny-numeric>
</template>
<script setup>
import { Numeric as TinyNumeric } from '@opentiny/vue'
import { ref } from 'vue'
const step = ref('0.00000000000000000002')
const stepNum = ref('0.00000000000000000002')
</script>

View File

@ -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')
})

View File

@ -0,0 +1,19 @@
<template>
<tiny-numeric style="width: 500px" v-model="stepNum" :step="step" string-mode :precision="20"></tiny-numeric>
</template>
<script>
import { Numeric } from '@opentiny/vue'
export default {
components: {
TinyNumeric: Numeric
},
data() {
return {
step: '0.00000000000000000002',
stepNum: '0.00000000000000000002'
}
}
}
</script>

View File

@ -171,6 +171,19 @@ export default {
}, },
codeFiles: ['blur-event.vue'] codeFiles: ['blur-event.vue']
}, },
{
demoId: 'string-mode',
name: {
'zh-CN': '高精度',
'en-US': 'Out of Focus Event'
},
desc: {
'zh-CN':
'<p>可通过 <code>string-mode</code> 设置高精度模式,当 JS 默认的 Number 不满足数字的长度与精度需求时。</p>\n',
'en-US': '<p>The<code>@blur</code>event is triggered when the text box loses focus. </p>\n'
},
codeFiles: ['string-mode.vue']
},
{ {
demoId: 'filter-mode', demoId: 'filter-mode',
name: { name: {

View File

@ -82,12 +82,12 @@ const options2 = ref([
{ value: '选项1', label: '黄金糕' }, { value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶', disabled: true }, { value: '选项2', label: '双皮奶', disabled: true },
{ value: '选项3', label: '蚵仔煎' }, { value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' }, { value: '选项4', label: '龙须面', disabled: true },
{ value: '选项5', label: '北京烤鸭' } { value: '选项5', label: '北京烤鸭' }
]) ])
const value1 = ref('') const value1 = ref('')
const value2 = ref([]) const value2 = ref(['选项2'])
const value3 = ref('') const value3 = ref('')
const value4 = ref(['选项2', '选项3']) const value4 = ref(['选项2', '选项3'])
const value5 = ref(['选项1', '选项2', '选项3', '选项4', '选项5']) const value5 = ref(['选项1', '选项2', '选项3', '选项4', '选项5'])

View File

@ -19,15 +19,15 @@ test('多选某项禁用', async ({ page }) => {
const tag = select.locator('.tiny-tag') const tag = select.locator('.tiny-tag')
const option = dropdown.locator('.tiny-option') const option = dropdown.locator('.tiny-option')
await expect(tag).toHaveCount(0) await expect(tag).toHaveCount(1)
await select.click() await select.click()
await expect(option.filter({ hasText: '双皮奶' })).toHaveClass(/is-disabled/) await expect(option.filter({ hasText: '双皮奶' })).toHaveClass(/is-disabled/)
await option.filter({ hasText: '双皮奶' }).click() await option.filter({ hasText: '双皮奶' }).click()
await expect(tag).toHaveCount(0) await expect(tag).toHaveCount(1)
await option.filter({ hasText: '黄金糕' }).click() await option.filter({ hasText: '黄金糕' }).click()
await expect(tag).toHaveCount(1) await expect(tag).toHaveCount(2)
await expect(tag.filter({ hasText: '黄金糕' })).toHaveCount(1) await expect(tag.filter({ hasText: '黄金糕' })).toHaveCount(1)
}) })

View File

@ -87,11 +87,11 @@ export default {
{ value: '选项1', label: '黄金糕' }, { value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶', disabled: true }, { value: '选项2', label: '双皮奶', disabled: true },
{ value: '选项3', label: '蚵仔煎' }, { value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' }, { value: '选项4', label: '龙须面', disabled: true },
{ value: '选项5', label: '北京烤鸭' } { value: '选项5', label: '北京烤鸭' }
], ],
value1: '', value1: '',
value2: [], value2: ['选项2'],
value3: '', value3: '',
value4: ['选项2', '选项3'], value4: ['选项2', '选项3'],
value5: ['选项1', '选项2', '选项3', '选项4', '选项5'] value5: ['选项1', '选项2', '选项3', '选项4', '选项5']

View File

@ -26,7 +26,7 @@ test('单选事件', async ({ page }) => {
await page.waitForTimeout(200) await page.waitForTimeout(200)
await input.hover() await input.hover()
await select.locator('.tiny-select__caret').click() await select.locator('.tiny-select__caret.icon-close').click()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await expect(input).toHaveValue('') await expect(input).toHaveValue('')
await expect(model.filter({ hasText: '触发 clear 事件' })).toHaveCount(1) await expect(model.filter({ hasText: '触发 clear 事件' })).toHaveCount(1)
@ -42,6 +42,7 @@ test('多选事件', async ({ page }) => {
const option = dropdown.locator('.tiny-option') const option = dropdown.locator('.tiny-option')
const model = page.locator('.tiny-modal') const model = page.locator('.tiny-modal')
await page.waitForTimeout(500)
await select.click() await select.click()
await expect(model.filter({ hasText: '触发 focus 事件' })).toHaveCount(1) await expect(model.filter({ hasText: '触发 focus 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1) await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1)
@ -56,7 +57,6 @@ test('多选事件', async ({ page }) => {
await page.waitForTimeout(500) await page.waitForTimeout(500)
await tag.first().locator('.tiny-tag__close').click() 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: '触发 change 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 remove-tag 事件' })).toHaveCount(1) await expect(model.filter({ hasText: '触发 remove-tag 事件' })).toHaveCount(1)
await expect(tag).toHaveCount(4) await expect(tag).toHaveCount(4)
@ -66,7 +66,7 @@ test('多选事件', async ({ page }) => {
await page.waitForTimeout(200) await page.waitForTimeout(200)
await select.hover() 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(tag).toHaveCount(0)
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1) await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)

View File

@ -3,7 +3,7 @@
<br /> <br />
<div>场景1多选</div> <div>场景1多选</div>
<br /> <br />
<tiny-select v-model="value1" multiple> <tiny-select v-model="value1" multiple searchable>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option> <tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-select> </tiny-select>
<br /> <br />

View File

@ -5,7 +5,7 @@ test('删除事件', async ({ page }) => {
await page.goto('tabs#tabs-events-close') await page.goto('tabs#tabs-events-close')
const tabs = page.locator('.tiny-tabs') 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 close = tabItem.locator('.tiny-tabs__icon-close')
const modal = page.locator('.tiny-modal').first() const modal = page.locator('.tiny-modal').first()

View File

@ -10,7 +10,7 @@ import {
prettierFormat, prettierFormat,
logGreen logGreen
} from '../../shared/utils' } from '../../shared/utils'
import { getComponents } from '../../shared/module-utils' import { getComponents, excludeComponents } from '../../shared/module-utils'
import handlebarsRender from './handlebars.render' import handlebarsRender from './handlebars.render'
const version = getopentinyVersion({ key: 'version' }) const version = getopentinyVersion({ key: 'version' })
@ -61,7 +61,12 @@ const buildFullRuntime = () => {
}) })
components.forEach((item) => { 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) const component = capitalizeKebabCase(item.name)
componentsTemplate.push(` ${component}`) componentsTemplate.push(` ${component}`)

View File

@ -10,7 +10,7 @@ import {
prettierFormat, prettierFormat,
logGreen logGreen
} from '../../shared/utils' } from '../../shared/utils'
import { getComponents } from '../../shared/module-utils' import { getComponents, excludeComponents } from '../../shared/module-utils'
import handlebarsRender from './handlebars.render' import handlebarsRender from './handlebars.render'
const version = getopentinyVersion({ key: 'version' }) const version = getopentinyVersion({ key: 'version' })
@ -79,7 +79,7 @@ const createEntry = (mode) => {
const PKGDeps = {} const PKGDeps = {}
components.forEach((item) => { components.forEach((item) => {
if (item.inEntry !== false) { if (item.inEntry !== false && !excludeComponents.includes(item.name)) {
const component = capitalizeKebabCase(item.name) const component = capitalizeKebabCase(item.name)
PKGDeps[item.importName] = 'workspace:~' PKGDeps[item.importName] = 'workspace:~'
componentsTemplate.push(` ${component}`) componentsTemplate.push(` ${component}`)

View File

@ -160,6 +160,7 @@ export const getBaseConfig = ({ vueVersion, dtsInclude, dts, buildTarget, isRunt
} }
}, },
include: [...dtsInclude, 'packages/vue/*.d.ts'], include: [...dtsInclude, 'packages/vue/*.d.ts'],
exclude: ['**/__tests__'],
beforeWriteFile: (filePath, content) => { beforeWriteFile: (filePath, content) => {
return { return {
// "vue/src/alert/index.d.ts" ==> "alert/index.d.ts" // "vue/src/alert/index.d.ts" ==> "alert/index.d.ts"

View File

@ -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 chalk from 'chalk'
import commonjs from '@rollup/plugin-commonjs'
import { external } from '../../../shared/config'
const bundlesToMerge = new Set<string>()
const commonChunk = new Set<string>()
let buildFormat
let isDelete
export default function ({ deleteInlinedFiles = true }): Plugin { export default function ({ deleteInlinedFiles = true }): Plugin {
return { return {
name: 'opentiny-vue:inline-chunks', name: 'opentiny-vue:inline-chunks',
generateBundle: ({ format }: NormalizedOutputOptions, bundle: OutputBundle) => { generateBundle: async ({ format, dir }: NormalizedOutputOptions, bundle: OutputBundle) => {
const bundlesToDelete = new Set<string>() buildFormat = format
const cache = {} isDelete = deleteInlinedFiles
const jsAssets = Object.keys(bundle).filter((i) => /\.[mc]?js$/.test(i)) const jsAssets = Object.keys(bundle).filter((i) => /\.[mc]?js$/.test(i))
for (const jsName of jsAssets) { for (const jsName of jsAssets) {
const jsChunk = bundle[jsName] const jsChunk = bundle[jsName]
@ -16,51 +25,66 @@ export default function ({ deleteInlinedFiles = true }): Plugin {
if (!jsChunk.code) continue if (!jsChunk.code) continue
if (format === 'es') { if (format === 'es') {
jsChunk.code = jsChunk.code.replace( const reg = /^import(\s*.+\s*from)?\s+"[./]+(.+-[a-f0-9]{8}.+)".*$/gim
// import { _ as _export_sfc } from "../../../../_plugin-vue_export-helper-1faf6727.mjs" const matchArr = jsChunk.code.match(reg)
/^import\s*.+\s*from\s+"[./]+(.+-[a-f0-9]{8}.+)".*$/gim, if (matchArr) {
(_, chunkName) => { const filePath = path.join(dir, jsName)
if (!cache[chunkName]) { bundlesToMerge.add(filePath)
cache[chunkName] = (bundle[chunkName] as OutputChunk).code matchArr.forEach((matchImport) => {
.replace(/export {[\s\S]+$/, '') const sourceName = matchImport.match(/"(.+)"/)[1]
.replace(/_extends/g, '_extends_tiny') commonChunk.add(path.join(filePath, '../', sourceName))
.replace(/_createForOfIteratorHelperLoose/g, '_createForOfIteratorHelperLoose_tiny') })
.replace(/_unsupportedIterableToArray/g, '_unsupportedIterableToArray_tiny') }
.replace(/_arrayLikeToArray/g, '_arrayLikeToArray_tiny')
bundlesToDelete.add(chunkName)
}
return cache[chunkName]
}
)
} }
if (format === 'cjs') { if (format === 'cjs') {
jsChunk.code = jsChunk.code.replace( const reg = /require\("[./]+(.+-[a-f0-9]{8}.+)".*$/gim
// var _pluginVue_exportHelper = require("../../../../_plugin-vue_export-helper-65c7de93.js"); const matchArr = jsChunk.code.match(reg)
/^var\s+(.+)\s+=\s+require\("[./]+(.+-[a-f0-9]{8}.+)".*$/gim, if (matchArr) {
(_, localVarName, chunkName) => { const filePath = path.join(dir, jsName)
if (!cache[chunkName]) { bundlesToMerge.add(filePath)
cache[chunkName] = matchArr.forEach((matchRequire) => {
'var _pluginVue_exportHelper = {};\n' + const sourceName = matchRequire.match(/"(.+)"/)[1]
(bundle[chunkName] as OutputChunk).code.replace(/exports\./g, `${localVarName}.`) commonChunk.add(path.join(filePath, '../', sourceName))
bundlesToDelete.add(chunkName) })
} }
return cache[chunkName]
}
)
} }
} }
},
if (deleteInlinedFiles) { closeBundle: async () =>
// 删除 chunks new Promise((resolve) => {
bundlesToDelete.forEach((name) => { // eslint-disable-next-line no-console
delete bundle[name] console.log(`\n${chalk.green('开始内联公共依赖')}`)
// eslint-disable-next-line no-console let i = 0
console.log(`\n${chalk.red(name)} 已经被内联并删除`) 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)} 已经被内联并删除`)
})
}
})
} }
} }

View File

@ -35,15 +35,14 @@ const findAllpage = (packagesPath) => {
// 解决TinyVue和AUI国际化键名不兼容问题 // 解决TinyVue和AUI国际化键名不兼容问题
.replace(/zhCN/g, 'zh_CN') .replace(/zhCN/g, 'zh_CN')
.replace(/enUS/g, 'en_US') .replace(/enUS/g, 'en_US')
.replace(/-openaui/g, '-opentiny')
// 解决在linkjs环境z-index无法统一导致下拉框被遮挡问题 // 解决在linkjs环境z-index无法统一导致下拉框被遮挡问题
.replace(/"(.*?\/popup-manager)"/g, '"@aurora/renderless/common/deps/popup-manager"') .replace(/"(.*?\/popup-manager)"/g, '"@aurora/renderless/common/deps/popup-manager"')
// 解决当AUI只有一个mobile-first模板而TinyVue有多个模板导致Linkjs加载不到多端模板的问题 // 解决当AUI只有一个mobile-first模板而TinyVue有多个模板导致Linkjs加载不到多端模板的问题
if ( if (
packagesPath.endsWith('index.js') && packagesPath.endsWith('index.js') &&
onlyMobileFirstTemplateLists.some( onlyMobileFirstTemplateLists.some((item) => packagesPath.includes(`${path.sep}${item}${path.sep}`))
(item) => packagesPath.includes(`${item}\\`) || packagesPath.includes(`${item}/`)
)
) { ) {
result = result.replace(/pc.js/g, 'mobile-first.js') result = result.replace(/pc.js/g, 'mobile-first.js')
} }

View File

@ -13,6 +13,9 @@ const moduleMap = require(pathFromWorkspaceRoot('packages/modules.json'))
type mode = 'pc' | 'mobile' | 'mobile-first' type mode = 'pc' | 'mobile' | 'mobile-first'
// 需要在入口文件中排除的组件,比如:富文本
export const excludeComponents = ['RichText']
export interface Module { export interface Module {
/** 源码路径,如 vue/src/button/index.ts */ /** 源码路径,如 vue/src/button/index.ts */
path: string path: string
@ -452,8 +455,7 @@ const createModuleMapping = (componentName, isMobile = false) => {
parser: 'json', parser: 'json',
printWidth: 10 printWidth: 10
} }
}), })
) )
} }

View File

@ -14,7 +14,9 @@ export default {
medium: 42 medium: 42
}, },
spacingHeight: 2, spacingHeight: 2,
initialInputHeight: 30 initialInputHeight: 30,
// 显示清除等图标时,不隐藏下拉箭头时
autoHideDownIcon: false
}, },
props: { props: {
tagType: 'info' tagType: 'info'

View File

@ -14,7 +14,9 @@ export default {
medium: 32 medium: 32
}, },
spacingHeight: 4, spacingHeight: 4,
initialInputHeight: 30 initialInputHeight: 30,
// 显示清除等图标时,不隐藏下拉箭头时
autoHideDownIcon: false
}, },
props: { props: {
tagType: 'info' tagType: 'info'

View File

@ -2,6 +2,7 @@ import { iconPutAway, iconExpand } from '@opentiny/vue-icon'
export default { export default {
icons: { icons: {
// 在 showLine=true时才要切换的图标。 并不是设置正常模式下的图标
expanded: iconExpand(), expanded: iconExpand(),
collapse: iconPutAway() collapse: iconPutAway()
} }

View File

@ -2360,6 +2360,14 @@
"type": "template", "type": "template",
"exclude": false "exclude": false
}, },
"RichText": {
"path": "vue/src/rich-text/index.ts",
"type": "component",
"exclude": false,
"mode": [
"pc"
]
},
"RichTextEditor": { "RichTextEditor": {
"path": "vue/src/rich-text-editor/index.ts", "path": "vue/src/rich-text-editor/index.ts",
"type": "component", "type": "component",

View File

@ -180,7 +180,7 @@ export class BigIntDecimal {
const convertBigInt = (str) => { const convertBigInt = (str) => {
// 将以多个零开头的整数前置零清空 '0000000000000003e+21' --> '3e+21' ,解决BigInt(0000000000000003e+21)报错问题 // 将以多个零开头的整数前置零清空 '0000000000000003e+21' --> '3e+21' ,解决BigInt(0000000000000003e+21)报错问题
const validStr = str.replace(/^0+/, '') || '0' const validStr = str.replace(/^0+/, '') || '0'
return f(`return BigInt(${validStr})`)() return f(`return BigInt('${validStr}')`)()
} }
if (validateNumber(mergedValue)) { if (validateNumber(mergedValue)) {
const trimRet = trimNumber(mergedValue) const trimRet = trimNumber(mergedValue)
@ -188,7 +188,9 @@ export class BigIntDecimal {
const numbers = trimRet.trimStr.split('.') const numbers = trimRet.trimStr.split('.')
this.integer = !numbers[0].includes('e') ? BigInt(numbers[0]) : numbers[0] this.integer = !numbers[0].includes('e') ? BigInt(numbers[0]) : numbers[0]
const decimalStr = numbers[1] || '0' const decimalStr = numbers[1] || '0'
this.decimal = convertBigInt(decimalStr)
// 如果小数点后有科学计数法,需要特殊处理,如果是正常数字则保留之前逻辑
this.decimal = decimalStr.includes('e') ? convertBigInt(decimalStr) : BigInt(decimalStr)
this.decimalLen = decimalStr.length this.decimalLen = decimalStr.length
} else { } else {
this.nan = true this.nan = true

View File

@ -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 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 res = new Date(year, month, 1)
const day = res.getDay() 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) => { 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 range = (length) => Array.apply(null, { length }).map((_, n) => n)
export const getMonthDays = (date) => { export const getMonthDays = (date) => {

View File

@ -207,6 +207,7 @@ export const handleClear =
state.leftDate = calcDefaultValue(state.defaultValue)[0] state.leftDate = calcDefaultValue(state.defaultValue)[0]
state.rightDate = nextMonth(state.leftDate) state.rightDate = nextMonth(state.leftDate)
state.rangeState.selecting = false state.rangeState.selecting = false
// tiny 新增下面行
state.rangeState.endDate = null state.rangeState.endDate = null
emit('pick', null) emit('pick', null)

View File

@ -127,6 +127,7 @@ const initState = ({ reactive, computed, api, constants, designConfig }) => {
dateFormat: computed(() => (state.format ? extractDateFormat(state.format) : 'yyyy-MM-dd')), dateFormat: computed(() => (state.format ? extractDateFormat(state.format) : 'yyyy-MM-dd')),
enableMonthArrow: computed(() => api.getEnableMonthArrow()), enableMonthArrow: computed(() => api.getEnableMonthArrow()),
enableYearArrow: computed(() => api.computerEnableYearArrow()), enableYearArrow: computed(() => api.computerEnableYearArrow()),
// tiny 新增
confirmButtonProps: { confirmButtonProps: {
plain: true, plain: true,
type: 'default', type: 'default',

View File

@ -21,10 +21,9 @@ import {
clearTime clearTime
} from '../common/deps/date-util' } from '../common/deps/date-util'
import { DATEPICKER } from '../common' import { DATEPICKER } from '../common'
import type { IDateTableRow } from '@/types'
const formatJudg = ({ day, offset, j, i, cell, count, dateCountOfLastMonth }) => { 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) { if (j + i * 7 >= nodfpm) {
cell.text = count++ cell.text = count++
@ -58,22 +57,8 @@ export const getDateTimestamp = (time) => {
return NaN return NaN
} }
export const arrayFindIndex = (arr, pred) => { // tiny 新增: api参数多余优化掉
for (let i = 0, len = arr.length; i !== len; ++i) { const getSelected = (props, cell, format, t, cellDate, selectedDate) => {
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 }) => {
let selected = cell.selected let selected = cell.selected
if (props.selectionMode === 'dates') { if (props.selectionMode === 'dates') {
@ -92,14 +77,7 @@ export const getCell =
let cell = row[props.showWeekNumber ? j + 1 : j] let cell = row[props.showWeekNumber ? j + 1 : j]
if (!cell) { if (!cell) {
cell = { cell = { row: i, column: j, inRange: false, start: false, end: false, type: DATEPICKER.Normal }
row: i,
column: j,
inRange: false,
start: false,
end: false,
type: DATEPICKER.Normal
}
} }
cell.type = DATEPICKER.Normal cell.type = DATEPICKER.Normal
@ -119,15 +97,7 @@ export const getCell =
const doCount = ({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth }) => { const doCount = ({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth }) => {
if (i >= 0 && i <= 1) { if (i >= 0 && i <= 1) {
const ret = formatJudg({ const ret = formatJudg({ day, offset, j, i, cell, count, dateCountOfLastMonth })
day,
offset,
j,
i,
cell,
count,
dateCountOfLastMonth
})
count = ret.count count = ret.count
} else { } else {
if (count <= dateCountOfMonth) { if (count <= dateCountOfMonth) {
@ -141,8 +111,6 @@ const doCount = ({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCou
return count 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 = export const getRows =
({ api, props, state, t, vm }) => ({ api, props, state, t, vm }) =>
(): IDateTableRow[][] => { () => {
const date = new Date(state.year, state.month, 1) const date = new Date(state.year, state.month, 1)
let day = getFirstDayOfMonth(date) let day = getFirstDayOfMonth(date)
const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth()) const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth())
@ -172,7 +140,7 @@ export const getRows =
day = day === 0 ? 7 : day day = day === 0 ? 7 : day
const offset = state.offsetDay const offset = state.offsetDay
const rows: IDateTableRow[][] = state.tableRows const rows = state.tableRows
const startDate = state.startDate const startDate = state.startDate
const disabledDate = props.disabledDate const disabledDate = props.disabledDate
const cellClassName = props.cellClassName const cellClassName = props.cellClassName
@ -181,7 +149,7 @@ export const getRows =
const isFunction = props.formatWeeks instanceof Function const isFunction = props.formatWeeks instanceof Function
const arr: Date[][] = [] const arr = []
// 日期表格行从0开始共6行[0, 5] // 日期表格行从0开始共6行[0, 5]
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
@ -207,15 +175,7 @@ export const getRows =
count = doCount({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth }) count = doCount({ i, day, offset, j, cell, count, dateCountOfLastMonth, dateCountOfMonth })
cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate) cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate)
cell.selected = getSelected({ cell.selected = getSelected(props, cell, DATEPICKER.DateFormats.date, t, cellDate, selectedDate)
props,
cell,
api,
format: DATEPICKER.DateFormats.date,
t,
cellDate,
selectedDate
})
cell.customClass = typeof cellClassName === 'function' && cellClassName(cellDate) cell.customClass = typeof cellClassName === 'function' && cellClassName(cellDate)
// 更新日期表格的行数据执行了这行代码rows 中才会有数据 // 更新日期表格的行数据执行了这行代码rows 中才会有数据
@ -250,6 +210,23 @@ export const getRows =
return rows 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 = export const watchMinDate =
({ api, props }) => ({ api, props }) =>
(value, oldvalue) => { (value, oldvalue) => {
@ -398,15 +375,15 @@ export const markRange =
const row = rows[i] const row = rows[i]
for (let j = 0, l = row.length; j < l; j++) { for (let j = 0, l = row.length; j < l; j++) {
if (!props.showWeekNumber || j !== 0) { if (props.showWeekNumber && j === 0) continue
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 const cell = row[j]
cell.start = minDate && time === minDate const index = i * 7 + j + (props.showWeekNumber ? -1 : 0)
cell.end = maxDate && time === maxDate 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 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 = export const handleClick =
({ api, emit, props, state }) => ({ api, emit, props, state }) =>
(event) => { (event) => {
@ -529,8 +500,16 @@ export const handleClick =
} }
} }
export const getCssToken = ({ api }) => (cell, prexfix = '') => { export const removeFromArray = (arr, pred) => {
const cssStr = api.getCellClasses(cell) || '' 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)
}

View File

@ -42,7 +42,7 @@ const initState = ({ reactive, computed, api, props }) => {
month: computed(() => !Array.isArray(props.date) && props.date.getMonth()), month: computed(() => !Array.isArray(props.date) && props.date.getMonth()),
offsetDay: computed(() => api.getOffsetDay()), offsetDay: computed(() => api.getOffsetDay()),
year: computed(() => !Array.isArray(props.date) && props.date.getFullYear()), 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 date: props.value
}) })

View File

@ -116,7 +116,10 @@ export const increase =
return 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')) { if (value.toString().includes('e')) {
return return
@ -142,7 +145,11 @@ export const decrease =
if (state.inputDisabled || state.minDisabled) { if (state.inputDisabled || state.minDisabled) {
return 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')) { if (value.toString().includes('e')) {
return return

View File

@ -16,7 +16,6 @@ import userPopper from '../common/deps/vue-popper'
import { DATEPICKER } from '../common' import { DATEPICKER } from '../common'
import { formatDate, parseDate, isDateObject, getWeekNumber, prevDate, nextDate } from '../common/deps/date-util' import { formatDate, parseDate, isDateObject, getWeekNumber, prevDate, nextDate } from '../common/deps/date-util'
import { extend } from '../common/object' import { extend } from '../common/object'
import { isFunction } from '../common/type'
import globalTimezone from './timezone' import globalTimezone from './timezone'
const iso8601Reg = /^\d{4}-\d{2}-\d{2}(.)\d{2}:\d{2}:\d{2}(.+)$/ const iso8601Reg = /^\d{4}-\d{2}-\d{2}(.)\d{2}:\d{2}:\d{2}(.+)$/
@ -41,8 +40,18 @@ export const getPanel =
return DatePanel 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 = export const watchPickerVisible =
({ api, vm, dispatch, emit, props, state }) => ({ api, vm, dispatch, emit, props, state, nextTick }) =>
(value) => { (value) => {
if (props.readonly || state.pickerDisabled || state.isMobileScreen) return 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 state.valueOnOpen = Array.isArray(props.modelValue) ? [...props.modelValue] : props.modelValue
} else { } else {
api.hidePicker() api.hidePicker()
api.emitChange(props.modelValue) // tiny 新增: 解决vue3下modelValue的值仍是旧值误认为值不变不触发change事件了。
nextTick(() => api.emitChange(props.modelValue))
state.userInput = null state.userInput = null
if (props.validateEvent) { if (props.validateEvent) {
dispatch('FormItem', 'form.blur') dispatch('FormItem', 'form.blur')
} }
if (props.changeOnConfirm && !valueEquals(props.modelValue, state.oldValue)) { if (props.changeOnConfirm && !valueEquals(props.modelValue, state.oldValue)) {
emit('update:modelValue', state.oldValue) emit('update:modelValue', state.oldValue)
} }
emit('blur', vm) emit('blur', vm)
api.blur() api.blur()
} }
@ -117,14 +129,8 @@ export const displayValue =
const formatObj = { const formatObj = {
rangeSeparator: props.rangeSeparator 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)) { if (Array.isArray(state.userInput)) {
return [ return [
state.userInput[0] || (formattedValue && formattedValue[0]) || '', state.userInput[0] || (formattedValue && formattedValue[0]) || '',
@ -176,12 +182,13 @@ export const parsedValue =
if (isServiceTimezone) { if (isServiceTimezone) {
if (Array.isArray(date)) { 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 { } else {
date = formatDate(date, state.valueFormat, t) date = formatDate(date, state.valueFormat, t)
} }
} }
const result = api.parseAsFormatAndType(date, state.valueFormat, state.type, props.rangeSeparator) const result = api.parseAsFormatAndType(date, state.valueFormat, state.type, props.rangeSeparator)
if (Array.isArray(result)) { if (Array.isArray(result)) {
return result.map((date) => getDateWithNewTimezone(date, from, to, timezoneOffset)) return result.map((date) => getDateWithNewTimezone(date, from, to, timezoneOffset))
@ -194,7 +201,6 @@ export const parsedValue =
const values = [] const values = []
.concat(props.modelValue) .concat(props.modelValue)
.map((val) => getDateWithNewTimezone(trans(val), from, to, timezoneOffset)) .map((val) => getDateWithNewTimezone(trans(val), from, to, timezoneOffset))
return values.length > 1 ? values : values[0] return values.length > 1 ? values : values[0]
} }
@ -337,7 +343,6 @@ const getWeekOfTypeValueResolveMap = ({ t, props, api }) => ({
trueDate.setHours(0, 0, 0, 0) trueDate.setHours(0, 0, 0, 0)
trueDate.setDate(trueDate.getDate() + 3 - ((trueDate.getDay() + 6) % 7)) trueDate.setDate(trueDate.getDate() + 3 - ((trueDate.getDay() + 6) % 7))
} }
let date let date
if (type === 'format' && !/W/.test(format)) { if (type === 'format' && !/W/.test(format)) {
const { start, end } = getWeekRange(value, format, t, props.pickerOptions) const { start, end } = getWeekRange(value, format, t, props.pickerOptions)
@ -346,6 +351,7 @@ const getWeekOfTypeValueResolveMap = ({ t, props, api }) => ({
date = formatDate(trueDate, format, t) date = formatDate(trueDate, format, t)
date = /WW/.test(date) ? date.replace(/WW/, week < 10 ? '0' + week : week) : date.replace(/W/, week) date = /WW/.test(date) ? date.replace(/WW/, week < 10 ? '0' + week : week) : date.replace(/W/, week)
} }
return date return date
}, },
parser(text, format) { 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 = export const handleChange =
({ api, state }) => ({ api, state }) =>
() => { () => {
if (state.userInput) { if (state.userInput) {
const value = api.parseString(state.displayValue) const value = api.parseString(state.displayValue)
if (value) { if (value) {
state.picker.state.value = value state.picker.state.value = value
@ -534,6 +739,7 @@ export const handleStartInput =
const value = props.autoFormat const value = props.autoFormat
? api.formatInputValue({ event, prevValue: state.displayValue[0] }) ? api.formatInputValue({ event, prevValue: state.displayValue[0] })
: event.target.value : event.target.value
if (state.userInput) { if (state.userInput) {
state.userInput = [value, state.userInput[1]] state.userInput = [value, state.userInput[1]]
} else { } else {
@ -754,15 +960,12 @@ export const handleKeydown =
} }
export const hidePicker = export const hidePicker =
({ state, doDestroy }) => ({ destroyPopper, state }) =>
() => { () => {
if (state.picker) { if (state.picker) {
state.picker.resetView && state.picker.resetView() state.picker.resetView && state.picker.resetView()
state.pickerVisible = state.picker.visible = state.picker.state.visible = false state.pickerVisible = state.picker.visible = state.picker.state.visible = false
destroyPopper()
if (isFunction(doDestroy)) {
doDestroy()
}
} }
} }
@ -780,7 +983,7 @@ export const showPicker =
state.pickerVisible = state.picker.state.visible = true state.pickerVisible = state.picker.state.visible = true
state.picker.state.value = state.parsedValue state.picker.state.value = state.parsedValue
state.picker.resetView && state.picker.resetView() state.picker.resetView && state.picker.resetView()
// 使用nextTick方法解决time-picker组件的demo"下拉框类名"点击input时间选择框弹出位置错误的问题
nextTick(() => { nextTick(() => {
updatePopper(state.picker.$el) updatePopper(state.picker.$el)
state.picker.adjustSpinners && state.picker.adjustSpinners() state.picker.adjustSpinners && state.picker.adjustSpinners()
@ -791,6 +994,7 @@ export const handlePick =
({ state, api }) => ({ state, api }) =>
(date = '', visible = false) => { (date = '', visible = false) => {
if (!state.picker) return if (!state.picker) return
state.userInput = null state.userInput = null
state.pickerVisible = state.picker.state.visible = visible state.pickerVisible = state.picker.state.visible = visible
@ -806,25 +1010,24 @@ export const handleSelectRange = (state) => (start, end, pos) => {
} }
const adjust = (value, start, end) => { const adjust = (value, start, end) => {
if (!value) { if (value) {
return { start, end } const valueReg = /(\d+):(\d+):(\d+)(\s+.+)?/
}
const valueReg = /(\d+):(\d+):(\d+)(\s+.+)?/
if (valueReg.test(value)) { if (valueReg.test(value)) {
const matched = valueReg.exec(value) const matched = valueReg.exec(value)
const hourLength = matched[1].length const hourLength = matched[1].length
const minuteLength = matched[2].length const minuteLength = matched[2].length
const secondLength = matched[3].length const secondLength = matched[3].length
if (start === 0) { if (start === 0) {
end = hourLength end = hourLength
} else if (start === 3) { } else if (start === 3) {
start = hourLength + 1 start = hourLength + 1
end = hourLength + minuteLength + 1 end = hourLength + minuteLength + 1
} else { } else {
start = hourLength + minuteLength + 2 start = hourLength + minuteLength + 2
end = hourLength + minuteLength + secondLength + 2 end = hourLength + minuteLength + secondLength + 2
}
} }
} }
@ -965,7 +1168,6 @@ export const emitInput =
} }
const formatted = api.formatToValue(value) || val const formatted = api.formatToValue(value) || val
if (!valueEquals(props.modelValue, formatted)) { if (!valueEquals(props.modelValue, formatted)) {
emit('update:modelValue', formatted) emit('update:modelValue', formatted)
} }
@ -1051,19 +1253,23 @@ export const computedFormat =
export const computedTriggerClass = export const computedTriggerClass =
({ props, state }) => ({ props, state }) =>
() => () => {
props.suffixIcon || return (
props.prefixIcon || props.suffixIcon ||
(state.type.includes(DATEPICKER.Time) ? DATEPICKER.IconTime : DATEPICKER.IconDate) props.prefixIcon ||
(state.type.includes(DATEPICKER.Time) ? DATEPICKER.IconTime : DATEPICKER.IconDate)
)
}
export const computedHaveTrigger = export const computedHaveTrigger =
({ props }) => ({ 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 }) => { export const initPopper = ({ props, hooks, vnode }) => {
const { reactive, watch, toRefs, onBeforeUnmount, onDeactivated } = hooks const { reactive, watch, toRefs, onBeforeUnmount, onDeactivated } = hooks
// vnode就是第3参名字有误导性 // tiny提示 vnode就是第3参名字有误导性
const { emit, vm, slots, nextTick } = vnode const { emit, vm, slots, nextTick } = vnode
const placementMap = DATEPICKER.PlacementMap const placementMap = DATEPICKER.PlacementMap
@ -1073,7 +1279,7 @@ export const initPopper = ({ props, hooks, vnode }) => {
emit, emit,
props: { props: {
...props, ...props,
popperOptions: { boundariesPadding: 0, gpuAcceleration: false }, popperOptions: Object.assign({ boundariesPadding: 0, gpuAcceleration: false }, props.popperOptions),
visibleArrow: true, visibleArrow: true,
offset: 0, offset: 0,
boundariesPadding: 5, 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
}

View File

@ -16,6 +16,7 @@ import {
watchIsRange, watchIsRange,
parseAsFormatAndType, parseAsFormatAndType,
watchPickerVisible, watchPickerVisible,
watchMobileVisible,
getValueEmpty, getValueEmpty,
getMode, getMode,
displayValue, 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 initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) => {
const { t, emit, dispatch, nextTick, vm } = vnode const { t, emit, dispatch, nextTick, vm } = vnode
const { TimePanel, TimeRangePanel } = others 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.popperElm = popperElm
state.picker = null state.picker = null
@ -185,7 +186,7 @@ const initApi = ({ api, props, hooks, state, vnode, others, utils, parent }) =>
Object.assign(api, { Object.assign(api, {
destroyPopper, destroyPopper,
emitDbTime: emitDbTime({ emit, state, t }), emitDbTime: emitDbTime({ emit, state, t }),
hidePicker: hidePicker({ state, doDestroy }), hidePicker: hidePicker({ destroyPopper, state }),
handleSelectChange: ({ tz, date }) => !state.ranged && emit('select-change', { tz, date }), handleSelectChange: ({ tz, date }) => !state.ranged && emit('select-change', { tz, date }),
getPanel: getPanel(others), getPanel: getPanel(others),
handleFocus: handleFocus({ emit, vm, state, api }), 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 }), handleClose: handleClose({ api, props, state }),
displayValue: displayValue({ api, props, state }), displayValue: displayValue({ api, props, state }),
handlePick: handlePick({ api, 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 }), formatToString: formatToString({ api, state }),
watchIsRange: watchIsRange({ api, state, TimePanel, TimeRangePanel }), watchIsRange: watchIsRange({ api, state, TimePanel, TimeRangePanel }),
mountPicker: mountPicker({ api, vm, props, state, updatePopper }), 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 }) initApi2({ api, props, state, t, parent })
initMobileApi({ api, props, state, t, parent }) initMobileApi({ api, props, state, t, parent })
} }
const initApi2 = ({ api, props, state, t, parent }) => { const initApi2 = ({ api, props, state, t, parent }) => {
Object.assign(api, { Object.assign(api, {
t, t,
@ -268,6 +271,8 @@ const initWatch = ({ api, state, props, watch, markRaw }) => {
{ immediate: true } { immediate: true }
) )
watch(() => [state.dateMobileOption.visible, state.timeMobileOption.visible], api.watchMobileVisible)
watch(() => state.pickerVisible, api.watchPickerVisible) watch(() => state.pickerVisible, api.watchPickerVisible)
watch( watch(

View File

@ -813,7 +813,7 @@ export const doSuggesst =
api.sourceGridSelectChange({ checked: false, row, confirm: false }) api.sourceGridSelectChange({ checked: false, row, confirm: false })
}) })
if (addtions.length) { if (!state.suggestList.length || addtions.length) {
doQuery(query) doQuery(query)
} }
} else { } else {
@ -829,7 +829,7 @@ export const closeSuggestPanel =
const popper = vm.$refs.popper const popper = vm.$refs.popper
let keep = !event let keep = !event
if (event.target) { if (event.target && reference) {
keep = reference.$el.contains(event.target) || popper.contains(event.target) keep = reference.$el.contains(event.target) || popper.contains(event.target)
} }

View File

@ -494,38 +494,14 @@ export const getPluginOption =
return items return items
} }
// tiny 修改: aui的 toggleCheckAll 在designConfig, 同步时要更新
export const toggleCheckAll = export const toggleCheckAll =
({ api, state, props }) => ({ api, state, props }) =>
(filtered) => { (filtered) => {
// tiny 移入内部 let value = []
const getEnabledValues = (options) => { // 1. 需要控制勾选或去勾选的项
let values = [] const enabledValues = state.options
.filter((op) => !op.state.disabled && !op.state.groupDisabled && !op.required && op.state.visible)
// tiny 新增 避免全不选时将disabled的项目勾掉 .map((op) => op.value)
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)
if (filtered) { if (filtered) {
if (state.filteredSelectCls === 'check' || state.filteredSelectCls === 'halfselect') { if (state.filteredSelectCls === 'check' || state.filteredSelectCls === 'halfselect') {
@ -541,10 +517,18 @@ export const toggleCheckAll =
unchecked.length ? (value = enabledValues) : (value = []) unchecked.length ? (value = enabledValues) : (value = [])
} else if (state.selectCls === 'checked-sur') { } else if (state.selectCls === 'checked-sur') {
// tiny 新增 value = []
value = getEnabledValues(state.options)
} }
} }
// 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() api.setSoftFocus()
@ -1278,10 +1262,9 @@ export const watchValue =
} }
if (props.filterable && !props.reserveKeyword) { if (props.filterable && !props.reserveKeyword) {
const isChange = false // tiny 优化: 多选且props.reserveKeyword为false时 aui此处会多请求一次
const isInput = true // searchable时不清空query, 这样才能保持搜索结果
props.renderType !== constants.TYPE.Grid && (state.query = '') props.renderType !== constants.TYPE.Grid && !props.searchable && (state.query = '')
api.handleQueryChange(state.query, isChange, isInput)
} }
} }
@ -1549,7 +1532,7 @@ export const queryVisibleOptions =
if (props.optimization) { if (props.optimization) {
return optmzApis.queryVisibleOptions(vm, isMobileFirstMode) return optmzApis.queryVisibleOptions(vm, isMobileFirstMode)
} else { } 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 = export const handleDropdownClick =
({ emit }) => ({ vm, state, props, emit }) =>
($event) => { ($event) => {
if (props.allowCopy && vm.$refs.reference) {
vm.$refs.reference.$el.querySelector('input').selectionEnd = 0
}
state.softFocus = false
emit('dropdown-click', $event) emit('dropdown-click', $event)
} }
export const handleEnterTag = export const handleEnterTag =
({ state }) => ({ state }) =>
($event, key) => { ($event, key) => {

View File

@ -169,7 +169,7 @@ export const api = [
'clearSearchText' '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 stateAdd = initStateAdd({ computed, props, api, parent })
const state = reactive({ const state = reactive({
...stateAdd, ...stateAdd,
@ -226,7 +226,13 @@ const initState = ({ reactive, computed, props, api, emitter, parent, constants,
// tiny 新增 // tiny 新增
getIcon: computed(() => api.computedGetIcon()), getIcon: computed(() => api.computedGetIcon()),
getTagType: computed(() => api.computedGetTagType()), 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 return state
@ -431,7 +437,7 @@ const addApi = ({
mounted: mounted({ api, parent, state, props, vm, designConfig }), mounted: mounted({ api, parent, state, props, vm, designConfig }),
unMount: unMount({ api, parent, vm, state }), unMount: unMount({ api, parent, vm, state }),
watchOptimizeOpts: watchOptimizeOpts({ props, state }), watchOptimizeOpts: watchOptimizeOpts({ props, state }),
handleDropdownClick: handleDropdownClick({ emit }), handleDropdownClick: handleDropdownClick({ props, vm, state, emit }),
handleEnterTag: handleEnterTag({ state }), handleEnterTag: handleEnterTag({ state }),
calcCollapseTags: calcCollapseTags({ state, vm }), calcCollapseTags: calcCollapseTags({ state, vm }),
initValue: initValue({ state }), initValue: initValue({ state }),
@ -555,8 +561,19 @@ export const renderless = (
{ computed, onBeforeUnmount, onMounted, reactive, watch, provide, inject }, { computed, onBeforeUnmount, onMounted, reactive, watch, provide, inject },
{ vm, parent, emit, constants, nextTick, dispatch, t, emitter, isMobileFirstMode, useBreakpoint, designConfig } { vm, parent, emit, constants, nextTick, dispatch, t, emitter, isMobileFirstMode, useBreakpoint, designConfig }
) => { ) => {
const api = {} const api: any = {}
const state = initState({ reactive, computed, props, api, emitter, parent, constants, useBreakpoint, vm }) const state = initState({
reactive,
computed,
props,
api,
emitter,
parent,
constants,
useBreakpoint,
vm,
designConfig
})
const dialog = inject('dialog', null) const dialog = inject('dialog', null)
provide('selectEmitter', state.selectEmitter) provide('selectEmitter', state.selectEmitter)
@ -579,9 +596,7 @@ export const renderless = (
isMobileFirstMode, isMobileFirstMode,
designConfig designConfig
}) })
initWatch({ watch, props, api, state, nextTick })
onMounted(api.mounted)
onBeforeUnmount(api.unMount)
parent.$on('handle-clear', (event) => { parent.$on('handle-clear', (event) => {
api.handleClearClick(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.setSelected, api.setSelected)
state.selectEmitter.on(constants.EVENT_NAME.initValue, api.initValue) 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 return api
} }

View File

@ -349,6 +349,7 @@ export const addNode =
state.tree.state.emitter.emit('tree-node-add', event, node) state.tree.state.emitter.emit('tree-node-add', event, node)
} }
// tiny 新增
export const computedExpandIcon = export const computedExpandIcon =
({ designConfig }) => ({ designConfig }) =>
(treeRoot, state) => { (treeRoot, state) => {
@ -356,6 +357,7 @@ export const computedExpandIcon =
return state.tree.icon return state.tree.icon
} }
// tiny 新增的判断。 显示线时强制切换图标仅smb定制了
if (treeRoot.showLine) { if (treeRoot.showLine) {
const expandIcon = designConfig?.icons?.expanded || 'icon-minus-square' const expandIcon = designConfig?.icons?.expanded || 'icon-minus-square'
const collapseIcon = designConfig?.icons?.collapse || 'icon-plus-square' const collapseIcon = designConfig?.icons?.collapse || 'icon-plus-square'
@ -364,7 +366,7 @@ export const computedExpandIcon =
return 'icon-chevron-right' return 'icon-chevron-right'
} }
// tiny 新增
export const computedIndent = export const computedIndent =
() => () =>
({ node, showLine }, { tree }) => { ({ node, showLine }, { tree }) => {

View File

@ -36,6 +36,7 @@ import {
deleteNode, deleteNode,
onSiblingToggleExpand, onSiblingToggleExpand,
watchExpandedChange, watchExpandedChange,
// tiny 新增
computedExpandIcon, computedExpandIcon,
computedIndent computedIndent
} from './index' } from './index'

View File

@ -250,7 +250,7 @@ export const updateOptions =
export const autoSelect = export const autoSelect =
({ props, state, nextTick }) => ({ props, state, nextTick }) =>
(usersList) => { (usersList) => {
if (!usersList.length) { if (!usersList.length || (props.multiple && props.multipleLimit && state.user.length >= props.multipleLimit)) {
return nextTick() return nextTick()
} }

View File

@ -138,11 +138,15 @@
@apply text-base; @apply text-base;
@apply items-center; @apply items-center;
.@{drawer-prefix-cls}__title { .@{drawer-prefix-cls}__header-left {
@apply ~'max-w-[80%]'; @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 { .@{drawer-prefix-cls}__header-right {

View File

@ -1,6 +1,6 @@
{ {
"version": "1.0.0", "version": "1.0.0",
"themeName": "华为SaaS设计系统主题", "themeName": "SaaS设计系统主题",
"themeColor": [ "themeColor": [
{ {
"mode": "light", "mode": "light",

View File

@ -150,7 +150,6 @@
border-bottom: 1px solid var(--ti-drawer-divider-border-color); border-bottom: 1px solid var(--ti-drawer-divider-border-color);
.@{drawer-prefix-cls}__title { .@{drawer-prefix-cls}__title {
max-width: 80%;
text-align: left; text-align: left;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -161,7 +160,7 @@
} }
.@{drawer-prefix-cls}__header-left { .@{drawer-prefix-cls}__header-left {
flex: 1; max-width: 80%;
display: flex; display: flex;
align-items: center; align-items: center;
padding-right: 16px; padding-right: 16px;

View File

@ -425,6 +425,10 @@
transform: rotate(0); transform: rotate(0);
} }
} }
&.is-leaf {
visibility: hidden;
}
} }
&__label { &__label {

View File

@ -23,7 +23,7 @@
) )
" "
:tabindex="tabindex" :tabindex="tabindex"
v-bind="a($attrs, ['class', 'style'], true)" v-bind="a($attrs, ['class', 'style', 'id'], true)"
> >
<icon-loading v-if="loading" :class="gcls('loading-svg')" /> <icon-loading v-if="loading" :class="gcls('loading-svg')" />
<component <component

View File

@ -31,7 +31,7 @@
} }
]" ]"
:tabindex="tabindex" :tabindex="tabindex"
v-bind="a($attrs, ['class', 'style'], true)" v-bind="a($attrs, ['class', 'style', 'title', 'id'], true)"
> >
<icon-loading v-if="loading" class="tiny-icon-loading tiny-svg-size" /> <icon-loading v-if="loading" class="tiny-icon-loading tiny-svg-size" />
<component v-if="icon && !loading" :is="icon" :class="{ 'is-text': text || slots.default }" /> <component v-if="icon && !loading" :is="icon" :class="{ 'is-text': text || slots.default }" />

View File

@ -100,7 +100,8 @@ import type { ICheckboxApi } from '@opentiny/vue-renderless/types/checkbox.type'
import Tooltip from '@opentiny/vue-tooltip' import Tooltip from '@opentiny/vue-tooltip'
export default defineComponent({ export default defineComponent({
emits: ['update:modelValue', 'change', 'complete', 'click'], // tiny renderlessemit('click') click
emits: ['update:modelValue', 'change', 'complete'],
props: [ props: [
...props, ...props,
'modelValue', 'modelValue',

View File

@ -773,7 +773,7 @@ export const Cell = {
renderEditHeader(h, params) { renderEditHeader(h, params) {
let { $table, column } = params let { $table, column } = params
let { editConfig, editRules, validOpts } = $table let { editConfig, editRules, validOpts } = $table
let { filter, remoteSort, sortable, type } = column let { filter, remoteSort, sortable, type, own } = column
let icon = GLOBAL_CONFIG.icon let icon = GLOBAL_CONFIG.icon
let isRequired let isRequired
@ -799,7 +799,7 @@ export const Cell = {
let vNodes = [ let vNodes = [
isRequired && showAsterisk ? h('i', { class: `tiny-icon ${icon.required}` }) : null, 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)) vNodes = vNodes.concat(Cell.renderHeader(h, params))

View File

@ -57,7 +57,7 @@ export const createHandlerOnEnd = ({ _vm, refresh }) => {
if (insertRecords.length) { if (insertRecords.length) {
return false return false
} }
const options = { children: 'children' } const options = { children: (_vm.treeConfig || {}).children || 'children' }
const targetTrElem = event.item const targetTrElem = event.item
const { parentNode: wrapperElem, previousElementSibling: prevTrElem } = targetTrElem const { parentNode: wrapperElem, previousElementSibling: prevTrElem } = targetTrElem
// 这里优先使用用户通过props传递过来的表格数据所以拖拽后会改变原始数据 // 这里优先使用用户通过props传递过来的表格数据所以拖拽后会改变原始数据

View File

@ -176,6 +176,7 @@ export default {
name: `${$prefix}GridFooter`, name: `${$prefix}GridFooter`,
props: { props: {
fixedColumn: Array, fixedColumn: Array,
fixedType: String,
footerData: Array, footerData: Array,
size: String, size: String,
tableColumn: Array, tableColumn: Array,
@ -193,7 +194,7 @@ export default {
elemStore[`${keyPrefix}x-space`] = $refs.xSpace elemStore[`${keyPrefix}x-space`] = $refs.xSpace
}, },
render() { render() {
let { $parent: $table, buildParamFunc, footerData, tableColumn } = this let { $parent: $table, buildParamFunc, fixedColumn, fixedType, footerData, tableColumn } = this
let { let {
align: allAlign, align: allAlign,
columnKey, columnKey,
@ -203,7 +204,7 @@ export default {
footerSpanMethod, footerSpanMethod,
columnStore columnStore
} = $table } = $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 tableAttrs = { cellspacing: 0, cellpadding: 0, border: 0 }
let colgroupVNode = renderColgroup(tableColumn) let colgroupVNode = renderColgroup(tableColumn)
@ -219,6 +220,8 @@ export default {
} }
let tfootVNode = renderTfoot(Object.assign(arg1, arg2)) let tfootVNode = renderTfoot(Object.assign(arg1, arg2))
const renderParams = { $table, columns: tableColumn, footerData, fixedColumns: fixedColumn, fixedType }
return h( return h(
'div', 'div',
{ {
@ -227,21 +230,23 @@ export default {
}, },
[ [
h('div', { class: 'tiny-grid-body__x-space', ref: 'xSpace' }), h('div', { class: 'tiny-grid-body__x-space', ref: 'xSpace' }),
h( typeof renderFooter === 'function'
'table', ? renderFooter(renderParams, h)
{ : h(
class: 'tiny-grid__footer', 'table',
style: { tableLayout }, {
attrs: tableAttrs, class: 'tiny-grid__footer',
ref: 'table' style: { tableLayout },
}, attrs: tableAttrs,
[ ref: 'table'
// 列宽 },
colgroupVNode, [
// 底部 // 列宽
tfootVNode colgroupVNode,
] // 底部
) tfootVNode
]
)
] ]
) )
}, },

View File

@ -33,7 +33,9 @@ export default {
this.recalculate() this.recalculate()
}, GlobalConfig.resizeInterval) }, GlobalConfig.resizeInterval)
resizeObserver.observe(this.getParentElem()) const parentElem = this.getParentElem()
parentElem && resizeObserver.observe(parentElem)
this.$resize = resizeObserver this.$resize = resizeObserver
}, },
unbindResize() { unbindResize() {

View File

@ -141,7 +141,7 @@ const setTotalRows = (_vm) => {
} }
const getTotalRows = (_vm) => { const getTotalRows = (_vm) => {
const { afterFullData, scrollYLoad, treeConfig } = _vm const { afterFullData, scrollYLoad, scrollLoad, treeConfig } = _vm
let totalRows = afterFullData.length let totalRows = afterFullData.length
if (scrollYLoad && treeConfig) { if (scrollYLoad && treeConfig) {
@ -152,6 +152,11 @@ const getTotalRows = (_vm) => {
totalRows = TOTALROWS_MAP.get(_vm) totalRows = TOTALROWS_MAP.get(_vm)
} }
// 滚动分页场景总行数由afterFullData.length调整为scrollLoad.pageSize解决最后一页数据不足时滚动条位置改变问题
if (scrollLoad) {
totalRows = scrollLoad.pageSize || 10
}
return totalRows return totalRows
} }

View File

@ -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

View File

@ -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"
}

View File

@ -0,0 +1,87 @@
<template>
<div class="quill-editor" :class="{ 'is-display-only': state.isDisplayOnly }">
<slot name="toolbar"></slot>
<div ref="editor" @paste="handlePaste"></div>
<div class="toolbar-icon">
<icon-fileupload ref="iconFile"></icon-fileupload>
</div>
</div>
</template>
<script>
import { $prefix, setup, defineComponent } from '@opentiny/vue-common'
import Quill from 'quill'
import ImageDrop from '@opentiny/vue-renderless/rich-text/module/image-drop'
import ImageUpload from '@opentiny/vue-renderless/rich-text/module/image-upload'
import FileUpload from '@opentiny/vue-renderless/rich-text/module/file-upload'
import Modal from '@opentiny/vue-modal'
import { IconFileupload } from '@opentiny/vue-icon'
import { renderless, api } from '@opentiny/vue-renderless/rich-text/vue'
const $constants = {
MAX_LENGTH: Number.MAX_SAFE_INTEGER
}
export default defineComponent({
name: $prefix + 'RichText',
components: {
IconFileupload: IconFileupload()
},
props: {
_constants: {
type: Object,
default: () => $constants
},
content: String,
disabled: {
type: Boolean,
default: false
},
fileUpload: Object,
globalOptions: {
type: Object,
required: false,
default: () => ({})
},
imageDrop: {
type: Boolean,
default: true
},
imageUpload: Object,
keepClass: [String, Array],
modelValue: String,
options: {
type: Object,
required: false,
default: () => ({})
},
tableModule: {
type: Boolean,
default: true
},
maxLength: {
type: Number
},
displayOnly: {
type: Boolean,
default: false
}
},
setup(props, context) {
return setup({
props,
context,
renderless,
api,
mono: true,
extendOptions: {
Quill,
ImageDrop,
ImageUpload,
FileUpload,
Modal
}
})
}
})
</script>

View File

@ -190,10 +190,10 @@
</template> </template>
</tiny-tooltip> </tiny-tooltip>
</span> </span>
<!-- tiny 新增searchable时, 这里不显示 state.query -->
<input <input
ref="input" ref="input"
v-if="filterable && !state.selectDisabled" v-show="filterable && !searchable && !state.selectDisabled"
v-model="state.query" v-model="state.query"
type="text" type="text"
class="tiny-select__input" class="tiny-select__input"
@ -272,10 +272,15 @@
@click="handleClearClick" @click="handleClearClick"
@mouseenter="state.inputHovering = true" @mouseenter="state.inputHovering = true"
></icon-close> ></icon-close>
<!-- tiny 新增 自定义getIcon . tiny 显示 close时 不显示向下的箭头 aui是同时显示2个 待使用designConfig解决这个 --> <!-- tiny 新增 自定义getIcon .
tiny autoHideDownIcon=true, 显示 close时 不显示向下的箭头
aui是同时显示2个 -->
<component <component
v-else v-show="
v-show="!(remote && filterable && !remoteConfig.showIcon)" state.autoHideDownIcon
? !state.showClose && !(remote && filterable && !remoteConfig.showIcon)
: !(remote && filterable && !remoteConfig.showIcon)
"
:is="state.getIcon.icon" :is="state.getIcon.icon"
:class="[ :class="[
'tiny-svg-size', 'tiny-svg-size',
@ -424,11 +429,8 @@
v-show="state.options.length > 0 && !loading" v-show="state.options.length > 0 && !loading"
> >
<slot name="dropdown"></slot> <slot name="dropdown"></slot>
<!-- tiny 新增 !filterable 的判断 有过滤时不显示全部 aui没有该判断 -->
<li <li
v-if=" v-if="multiple && showCheck && showAlloption && !state.multipleLimit && !state.query && !remote"
multiple && showCheck && showAlloption && !state.multipleLimit && !state.query && !remote && !filterable
"
class="tiny-option tiny-select-dropdown__item" class="tiny-option tiny-select-dropdown__item"
data-tag="tiny-select-dropdown-item" data-tag="tiny-select-dropdown-item"
:class="[ :class="[
@ -443,9 +445,9 @@
> >
<!-- <component :is="`icon-${state.selectCls}`" :class="['tiny-svg-size', state.selectCls]" /> <!-- <component :is="`icon-${state.selectCls}`" :class="['tiny-svg-size', state.selectCls]" />
<span>{{ t('ui.base.all') }}</span> --> <span>{{ t('ui.base.all') }}</span> -->
<!-- tiny 新增 使用checkbox 代替 svg --> <!-- tiny 新增 使用checkbox 代替 svg , 列表模式 -->
<tiny-checkbox <tiny-checkbox
:model-value="state.isSelectAll" :model-value="state.selectCls === 'checked-sur'"
:indeterminate="state.selectCls === 'halfselect'" :indeterminate="state.selectCls === 'halfselect'"
:class="state.selectCls" :class="state.selectCls"
> >
@ -460,8 +462,7 @@
!state.multipleLimit && !state.multipleLimit &&
state.query && state.query &&
!state.emptyText && !state.emptyText &&
!remote && !remote
!filterable
" "
class="tiny-option tiny-select-dropdown__item" class="tiny-option tiny-select-dropdown__item"
data-tag="tiny-select-dropdown-item" data-tag="tiny-select-dropdown-item"
@ -477,9 +478,9 @@
> >
<!-- <component :is="`icon-${state.filteredSelectCls}`" :class="['tiny-svg-size', state.filteredSelectCls]" /> <!-- <component :is="`icon-${state.filteredSelectCls}`" :class="['tiny-svg-size', state.filteredSelectCls]" />
<span>{{ t('ui.base.all') }}</span> --> <span>{{ t('ui.base.all') }}</span> -->
<!-- tiny 新增 使用checkbox 代替 svg --> <!-- tiny 新增 使用checkbox 代替 svg过滤模式 -->
<tiny-checkbox <tiny-checkbox
:model-value="state.isSelectAll" :model-value="state.filteredSelectCls === 'checked-sur'"
:indeterminate="state.filteredSelectCls === 'halfselect'" :indeterminate="state.filteredSelectCls === 'halfselect'"
:class="state.selectCls" :class="state.selectCls"
> >

View File

@ -70,6 +70,7 @@ export const tabsProps = {
export default defineComponent({ export default defineComponent({
name: $prefix + 'Tabs', name: $prefix + 'Tabs',
emits: ['tab-nav-update'],
props: tabsProps, props: tabsProps,
setup(props, context) { setup(props, context) {
return $setup({ props, context, template }) return $setup({ props, context, template })

View File

@ -67,7 +67,6 @@
</template> </template>
<template v-else> <template v-else>
<span <span
v-if="!node.isLeaf"
:class="['tree-node-icon', { 'is-disabled': node.disabled }]" :class="['tree-node-icon', { 'is-disabled': node.disabled }]"
@click="handleExpandIconClick($event, node)" @click="handleExpandIconClick($event, node)"
> >

View File

@ -85,6 +85,10 @@ export const uploadListProps = {
lockScroll: { lockScroll: {
type: Boolean, type: Boolean,
default: true default: true
},
compact: {
type: Boolean,
default: false
} }
} }

View File

@ -23,6 +23,10 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: false default: false
}, },
multipleLimit: {
type: Number,
default: 0
},
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false

View File

@ -17,6 +17,7 @@
:placeholder="placeholder" :placeholder="placeholder"
:collapse-tags="collapseTags" :collapse-tags="collapseTags"
:multiple="multiple" :multiple="multiple"
:multipleLimit="multipleLimit"
@change="userChange" @change="userChange"
:loading="state.loading" :loading="state.loading"
filterable filterable
@ -123,7 +124,8 @@ export default defineComponent({
'maxWidth', 'maxWidth',
'keepFocus', 'keepFocus',
'changeCompat', 'changeCompat',
'multiLineDrag' 'multiLineDrag',
'multipleLimit'
], ],
setup(props, context) { setup(props, context) {
return setup({ props, context, renderless, api }) return setup({ props, context, renderless, api })