forked from opentiny/tiny-vue
feat: optimized the official website component and API version status (#1919)
This commit is contained in:
parent
0a78d68cd8
commit
09efdec5f1
|
@ -181,8 +181,8 @@ export default {
|
|||
'zh-CN': '自定义数据为空时展示内容',
|
||||
'en-US': 'customize content when data is empty'
|
||||
},
|
||||
metaData: {
|
||||
new: '3.17.0'
|
||||
meta: {
|
||||
stable: '3.17.0'
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: 'slot-empty'
|
||||
|
|
|
@ -446,7 +446,23 @@ export default {
|
|||
pcDemo: ''
|
||||
}
|
||||
],
|
||||
slots: [],
|
||||
slots: [
|
||||
{
|
||||
name: 'now',
|
||||
type: '',
|
||||
meta: {
|
||||
stable: '3.19.0'
|
||||
},
|
||||
defaultValue: '',
|
||||
desc: {
|
||||
'zh-CN': '组件“此刻”位置插槽',
|
||||
'en-US': 'Component "Now" button slot'
|
||||
},
|
||||
mode: ['pc', 'mobile-first'],
|
||||
pcDemo: 'now',
|
||||
mfDemo: 'now'
|
||||
}
|
||||
],
|
||||
format: [
|
||||
{
|
||||
name: 'a',
|
||||
|
|
|
@ -442,8 +442,8 @@ export default {
|
|||
'zh-CN': '自定义弹窗底部按钮',
|
||||
'en-US': 'Custom Pop Up Bottom buttons'
|
||||
},
|
||||
metaData: {
|
||||
new: '3.18.0'
|
||||
meta: {
|
||||
stable: '3.18.0'
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: ''
|
||||
|
|
|
@ -241,7 +241,7 @@ export default {
|
|||
mode: ['pc', 'mobile-first'],
|
||||
pcDemo: 'lazy-show-popper',
|
||||
mfDemo: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
experimental: '3.18.0'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -566,6 +566,21 @@ export default {
|
|||
mode: ['mobile-first'],
|
||||
mfDemo: ''
|
||||
},
|
||||
{
|
||||
name: 'prompt-tip',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
desc: {
|
||||
'zh-CN': '设置提示是否为 tip 类型,悬浮图标时显示 tip 提示',
|
||||
'en-US': 'Set whether the prompt is of the tip type. The tip is displayed when the icon is suspended.'
|
||||
},
|
||||
metaData: {
|
||||
new: '3.19.0'
|
||||
},
|
||||
mode: ['pc', 'mobile-first'],
|
||||
mfDemo: 'prompt-tip',
|
||||
pcDemo: 'prompt-tip'
|
||||
},
|
||||
{
|
||||
name: 're-upload-tip',
|
||||
type: '(count: number) => string',
|
||||
|
|
|
@ -147,8 +147,8 @@ export default {
|
|||
name: 'custom-column-names',
|
||||
type: 'string[]',
|
||||
defaultValue: "['TinyGridColumn']",
|
||||
metaData: {
|
||||
new: '3.17.0'
|
||||
meta: {
|
||||
stable: '3.17.0'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '封装 grid-column 时需要配置此字段,提供给表格收集配置',
|
||||
|
@ -1467,8 +1467,8 @@ export default {
|
|||
name: 'toggle-group-change',
|
||||
type: '(row: IRow) => void',
|
||||
defaultValue: '',
|
||||
metaData: {
|
||||
new: '3.17.0'
|
||||
meta: {
|
||||
stable: '3.17.0'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '当分组的展开和收起时会触发该事件',
|
||||
|
|
|
@ -400,8 +400,8 @@ export default {
|
|||
name: 'show-tooltip',
|
||||
type: 'boolean',
|
||||
defaultValue: 'true',
|
||||
metaData: {
|
||||
new: '3.18.0'
|
||||
meta: {
|
||||
stable: '3.18.0'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '只读状态下,文本超出是否悬浮提示',
|
||||
|
|
|
@ -247,8 +247,8 @@ export default {
|
|||
name: 'total-fixed-left',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
metaData: {
|
||||
new: '3.18.0'
|
||||
meta: {
|
||||
stable: '3.18.0'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '总条目数是否固定在左侧,Aurora、SMB主题默认值为 true',
|
||||
|
|
|
@ -297,8 +297,8 @@ export default {
|
|||
'zh-CN': '自定义上传提示内容',
|
||||
'en-US': 'Customize upload prompt content'
|
||||
},
|
||||
metaData: {
|
||||
new: '3.18.0'
|
||||
meta: {
|
||||
stable: '3.18.0'
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: 'upload-tip'
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<div style="height: 540px">
|
||||
<tiny-file-upload
|
||||
ref="upload"
|
||||
prompt-tip
|
||||
:action="action"
|
||||
accept=".doc,.docx"
|
||||
:file-list="fileList"
|
||||
list-type="saas"
|
||||
:file-size="[100, 200]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FileUpload } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyFileUpload: FileUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
action: 'http://localhost:3000/api/upload',
|
||||
fileList: [
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860351',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test1.png',
|
||||
docSize: 100 * 1024,
|
||||
size: 100 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860352',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test2.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test3.png',
|
||||
docSize: 200 * 1024,
|
||||
size: 200 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'uploading',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test4.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'fail',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test5超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: '没有文件大小.doc',
|
||||
serverName: 'ShenZhen'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -62,6 +62,19 @@ export default {
|
|||
},
|
||||
codeFiles: ['file-size-array.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'prompt-tip',
|
||||
name: {
|
||||
'zh-CN': 'tip提示',
|
||||
'en-US': 'tip Hints'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '<p>通过 <code>propmtTip</code> 为 `true` 设置提示为tip类型,悬浮图标时显示tip提示。<p>',
|
||||
'en-US':
|
||||
'<p>Set the prompt to the tip type by setting <code>propmtTip</code> to `true`. The tip prompt is displayed when the icon is suspended.</p>'
|
||||
},
|
||||
codeFiles: ['prompt-tip.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'show-title',
|
||||
name: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
experimental: '3.16.0'
|
||||
},
|
||||
demos: [
|
||||
|
|
|
@ -116,8 +116,8 @@ export default {
|
|||
'zh-CN': '空数据',
|
||||
'en-US': 'No data'
|
||||
},
|
||||
metaData: {
|
||||
new: '3.17.1'
|
||||
meta: {
|
||||
mark: '3.17.1'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '<p>当数据为空时,默认会显示"暂无数据",通过 <code>empty</code> 插槽自定义内容。</p>',
|
||||
|
|
|
@ -3,8 +3,10 @@ import { test, expect } from '@playwright/test'
|
|||
test('自定义头部显示', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('calendar-view#custom-header')
|
||||
const timelineBtn = page.locator('label').nth(3)
|
||||
const customHeader = page.getByText('2023-5-3 周三')
|
||||
|
||||
const demo = page.locator('#custom-header')
|
||||
const timelineBtn = demo.locator('label').nth(3)
|
||||
const customHeader = demo.getByText('2023-5-3 周三')
|
||||
await timelineBtn.click()
|
||||
await page.waitForTimeout(200)
|
||||
await expect(customHeader).toBeVisible()
|
||||
|
|
|
@ -7,24 +7,12 @@ test('自动切换', async ({ page }) => {
|
|||
await page.waitForTimeout(100)
|
||||
const preview = page.locator('#autoplay')
|
||||
const carousel = preview.locator('.tiny-carousel')
|
||||
const carouselItems = preview.locator('.tiny-carousel__item')
|
||||
const carouselItems = carousel.locator('.tiny-carousel__item')
|
||||
// 默认显示第一张幻灯片
|
||||
await expect(carouselItems.first()).toHaveCSS('transform', 'matrix(1, 0, 0, 1, 0, 0)')
|
||||
|
||||
// 这里需要等待幻灯片在3秒后切换
|
||||
// 这里需要等待幻灯片在3秒后切换,注意: 此测试用例只需要关注自动切换即可
|
||||
await page.waitForTimeout(3000)
|
||||
// 当前应该显示第二张幻灯片
|
||||
await expect(carouselItems.nth(1)).toHaveCSS('transform', 'matrix(1, 0, 0, 1, 0, 0)')
|
||||
|
||||
await carousel.hover()
|
||||
await page.waitForTimeout(100)
|
||||
// 点击下一张按钮
|
||||
await preview.locator('button:nth-child(2)').click()
|
||||
// 当前应该显示第三张幻灯片
|
||||
await expect(carouselItems.nth(2)).toHaveCSS('transform', 'matrix(1, 0, 0, 1, 0, 0)')
|
||||
|
||||
// 点击上一张按钮
|
||||
await page.locator('.tiny-carousel__arrow').first().click()
|
||||
// 当前应该显示第二张幻灯片
|
||||
await expect(carouselItems.nth(1)).toHaveCSS('transform', 'matrix(1, 0, 0, 1, 0, 0)')
|
||||
})
|
||||
|
|
|
@ -7,7 +7,7 @@ test('动态加载 lazyload', async ({ page }) => {
|
|||
const svg = page.locator('.tiny-cascader-node__postfix > path')
|
||||
await expect(svg).toHaveAttribute(
|
||||
'd',
|
||||
'M7 21c.2 0 .5-.1.6-.2l9.9-8c.2-.2.4-.5.4-.8 0-.3-.1-.6-.4-.8L7.6 3.3c-.4-.4-1.1-.3-1.4.2-.4.4-.3 1.1.2 1.4l8.9 7.2-8.9 7.2c-.4.4-.5 1-.2 1.4.2.2.5.3.8.3z'
|
||||
'M5.44 1.23a.9.9 0 0 0-1.19 0c-.3.27-.33.69-.1.99l.1.11 6.02 5.56c.05.04.06.11.03.17l-.04.05-6.02 5.56c-.33.3-.33.8 0 1.1.29.27.75.3 1.07.09l.12-.09 6.02-5.56c.67-.62.72-1.61.14-2.28l-.14-.15-6.01-5.55z'
|
||||
)
|
||||
await page.getByRole('menuitem', { name: '选项1' }).click()
|
||||
const loadingSvg = page.getByRole('menuitem', { name: '选项1' }).locator('path')
|
||||
|
|
|
@ -5,7 +5,7 @@ test('测试Alpha', async ({ page }) => {
|
|||
await page.goto('color-picker#enable-alpha')
|
||||
await page.locator('.tiny-color-picker__inner').click()
|
||||
await page.locator('.black').click()
|
||||
await page.getByText('演示AlphaAlpha选择。取消选择').click()
|
||||
await page.getByRole('button', { name: '取消' }).click()
|
||||
await page.getByText('用户选择了取消').click()
|
||||
await page.locator('.tiny-color-picker__inner').click()
|
||||
await page.locator('.black').click()
|
||||
|
|
|
@ -4,13 +4,13 @@ test('测试历史记录', async ({ page }) => {
|
|||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('color-picker#history')
|
||||
await page.locator('.tiny-color-picker').click()
|
||||
await page.waitForSelector('.tiny-collapse-item__arrow')
|
||||
await page.waitForSelector('.tiny-collapse-item')
|
||||
await expect(page.locator('.tiny-color-select-panel__history')).toHaveCount(1)
|
||||
await page.getByRole('button', { name: '选择' }).click()
|
||||
// 用户行为更改历史记录测试
|
||||
await page.getByRole('button', { name: 'Append history color' }).click()
|
||||
await page.locator('.tiny-color-picker').click()
|
||||
await page.waitForSelector('.tiny-collapse-item__arrow')
|
||||
await page.waitForSelector('.tiny-collapse-item')
|
||||
await expect(
|
||||
page.locator('.tiny-color-select-panel__history .tiny-color-select-panel__history__color-block:nth-child(2)')
|
||||
).toBeHidden()
|
||||
|
@ -22,7 +22,7 @@ test('测试历史记录', async ({ page }) => {
|
|||
await black.click(x, y)
|
||||
await page.getByRole('button', { name: '选择' }).click()
|
||||
await page.locator('.tiny-color-picker').click()
|
||||
await page.waitForSelector('.tiny-collapse-item__arrow')
|
||||
await page.waitForSelector('.tiny-collapse-item')
|
||||
await expect(
|
||||
page.locator('.tiny-color-select-panel__history .tiny-color-select-panel__history__color-block:nth-child(3)')
|
||||
).toBeHidden()
|
||||
|
|
|
@ -4,7 +4,7 @@ test('测试预定义颜色', async ({ page }) => {
|
|||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('color-picker#predefine')
|
||||
await page.locator('.tiny-color-picker').click()
|
||||
await page.waitForSelector('.tiny-collapse-item__arrow')
|
||||
await page.waitForSelector('.tiny-collapse-item')
|
||||
await expect(
|
||||
page.locator('.tiny-color-select-panel__predefine .tiny-color-select-panel__predefine__color-block:nth-child(8)')
|
||||
).toBeHidden()
|
||||
|
@ -12,7 +12,7 @@ test('测试预定义颜色', async ({ page }) => {
|
|||
// 用户行为预定义颜色测试
|
||||
await page.getByRole('button', { name: 'Append predefine color' }).click()
|
||||
await page.locator('.tiny-color-picker').click()
|
||||
await page.waitForSelector('.tiny-collapse-item__arrow')
|
||||
await page.waitForSelector('.tiny-collapse-item')
|
||||
await expect(
|
||||
page.locator('.tiny-color-select-panel__predefine .tiny-color-select-panel__predefine__color-block:nth-child(9)')
|
||||
).toBeHidden()
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div>
|
||||
<div style="width: 400px">
|
||||
<div class="title">插槽式:</div>
|
||||
<tiny-date-picker v-model="value1" type="datetime">
|
||||
<template #now>
|
||||
<div class="nowclass" @click="now">此刻(服务器时间)</div>
|
||||
</template>
|
||||
</tiny-date-picker>
|
||||
</div>
|
||||
|
||||
<div style="width: 400px">
|
||||
<div class="title">函数式:</div>
|
||||
<tiny-date-picker v-model="value2" type="datetime" :now-click="nowClick"> </tiny-date-picker>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { DatePicker as TinyDatePicker } from '@opentiny/vue'
|
||||
|
||||
const value1 = ref('2020-11-11 10:10:11')
|
||||
const value2 = ref('2020-11-11 10:10:11')
|
||||
|
||||
const nowClick = () => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(new Date(2024, 9, 8, 18, 18, 18))
|
||||
}, 0)
|
||||
})
|
||||
}
|
||||
|
||||
const now = () => {
|
||||
value1.value = '2018-12-11 18:18:18'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nowclass {
|
||||
display: inline-flex;
|
||||
height: 28px;
|
||||
align-items: center;
|
||||
color: #0067d1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.title {
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('“此刻”逻辑定制', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('date-picker#now')
|
||||
await page.getByRole('textbox', { name: '-11-11 10:10:11' }).first().click()
|
||||
await page.getByText('此刻(服务器时间)').click()
|
||||
await page.getByRole('button', { name: '确定' }).click()
|
||||
await expect(page.getByRole('textbox', { name: '-12-11 18:18:18' })).toBeVisible()
|
||||
await page.getByRole('textbox', { name: '-11-11 10:10:11' }).click()
|
||||
await page.getByRole('button', { name: '此刻' }).click()
|
||||
await expect(page.getByRole('textbox', { name: '-10-08 18:18:18' })).toBeVisible()
|
||||
})
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div>
|
||||
<div style="width: 400px">
|
||||
<div class="title">插槽式:</div>
|
||||
<tiny-date-picker v-model="value1" type="datetime">
|
||||
<template #now>
|
||||
<div class="nowclass" @click="now">此刻(服务器时间)</div>
|
||||
</template>
|
||||
</tiny-date-picker>
|
||||
</div>
|
||||
|
||||
<div style="width: 400px">
|
||||
<div class="title">函数式:</div>
|
||||
<tiny-date-picker v-model="value2" type="datetime" :now-click="nowClick"> </tiny-date-picker>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DatePicker } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyDatePicker: DatePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value1: '2020-11-11 10:10:11',
|
||||
value2: '2020-11-11 10:10:11'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
now() {
|
||||
this.value1 = '2018-12-11 18:18:18'
|
||||
},
|
||||
nowClick() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(new Date(2024, 9, 8, 18, 18, 18))
|
||||
}, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nowclass {
|
||||
display: inline-flex;
|
||||
height: 28px;
|
||||
align-items: center;
|
||||
color: #0067d1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.title {
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
|
@ -245,6 +245,19 @@ export default {
|
|||
},
|
||||
codeFiles: ['validate-event.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'now',
|
||||
name: {
|
||||
'zh-CN': ' “此刻”逻辑定制',
|
||||
'en-US': '"At the moment" logic customization'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN':
|
||||
'<p>“此刻”配置的时间与用户本地时间设置相关,为保证部分逻辑对服务器时间的要求,组件提供 <code>nowClick</code >函数和 <code>now</code> 插槽两种定制方式,用户可以自定义“此刻”配置的时间。</p>',
|
||||
'en-US': ''
|
||||
},
|
||||
codeFiles: ['now.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'events',
|
||||
name: {
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
stable: '3.17.0'
|
||||
},
|
||||
versionTipOption: {
|
||||
stages: ['stable']
|
||||
},
|
||||
demos: [
|
||||
{
|
||||
demoId: 'auto-tip-basic-usage',
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
stable: '3.18.0'
|
||||
},
|
||||
versionTipOption: {
|
||||
stages: ['stable']
|
||||
},
|
||||
demos: [
|
||||
{
|
||||
demoId: 'basic-usage',
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<div style="height: 540px">
|
||||
<tiny-file-upload
|
||||
ref="upload"
|
||||
prompt-tip
|
||||
:action="action"
|
||||
accept=".doc,.docx"
|
||||
:file-list="fileList"
|
||||
list-type="saas"
|
||||
:file-size="[100, 200]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { FileUpload as TinyFileUpload } from '@opentiny/vue'
|
||||
|
||||
const action = ref('http://localhost:3000/api/upload')
|
||||
const fileList = ref([
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860351',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test1.png',
|
||||
docSize: 100 * 1024,
|
||||
size: 100 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860352',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test2.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test3.png',
|
||||
docSize: 200 * 1024,
|
||||
size: 200 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'uploading',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test4.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'fail',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test5超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: '没有文件大小.doc',
|
||||
serverName: 'ShenZhen'
|
||||
}
|
||||
])
|
||||
</script>
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<div style="height: 540px">
|
||||
<tiny-file-upload
|
||||
ref="upload"
|
||||
prompt-tip
|
||||
:action="action"
|
||||
accept=".doc,.docx"
|
||||
:file-list="fileList"
|
||||
list-type="saas"
|
||||
:file-size="[100, 200]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FileUpload } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyFileUpload: FileUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
action: 'http://localhost:3000/api/upload',
|
||||
fileList: [
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860351',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test1.png',
|
||||
docSize: 100 * 1024,
|
||||
size: 100 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860352',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test2.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test3.png',
|
||||
docSize: 200 * 1024,
|
||||
size: 200 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'uploading',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test4.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen',
|
||||
status: 'fail',
|
||||
percentage: 30
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: 'test5超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长.doc',
|
||||
docSize: 17252 * 1024,
|
||||
size: 17252 * 1024,
|
||||
serverName: 'ShenZhen'
|
||||
},
|
||||
{
|
||||
docId: 'M1T2A1N548572512085860353',
|
||||
path: 'edm/one/',
|
||||
docVersion: 'V1',
|
||||
name: '没有文件大小.doc',
|
||||
serverName: 'ShenZhen'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -213,6 +213,19 @@ export default {
|
|||
},
|
||||
codeFiles: ['file-size-array.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'prompt-tip',
|
||||
name: {
|
||||
'zh-CN': 'tip提示',
|
||||
'en-US': 'tip Hints'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '<p>通过 <code>propmtTip</code> 为 `true` 设置提示为tip类型,悬浮图标时显示tip提示。<p>',
|
||||
'en-US':
|
||||
'<p>Set the prompt to the tip type by setting <code>propmtTip</code> to `true`. The tip prompt is displayed when the icon is suspended.</p>'
|
||||
},
|
||||
codeFiles: ['prompt-tip.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'upload-file-list-slot',
|
||||
name: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
experimental: '3.17.0'
|
||||
},
|
||||
demos: [
|
||||
|
@ -48,7 +48,8 @@ export default {
|
|||
'en-US': ''
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '通过 <code>options</code> 设置编辑器的配置项,支持的配置项和 Quill 的相同,可参考 <a href="https://quilljs.com/docs/configuration#options" target="_blank">Quill</a> 文档。',
|
||||
'zh-CN':
|
||||
'通过 <code>options</code> 设置编辑器的配置项,支持的配置项和 Quill 的相同,可参考 <a href="https://quilljs.com/docs/configuration#options" target="_blank">Quill</a> 文档。',
|
||||
'en-US': ''
|
||||
},
|
||||
codeFiles: ['options.vue']
|
||||
|
@ -60,7 +61,8 @@ export default {
|
|||
'en-US': ''
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '<p>通过 <code>data-type</code> 指定富文本数据保存的格式。数据默认保存格式为 Delta 形式,若需要将数据保存格式设置为 HTML 形式,并关闭 HTML 格式自动转 Delta 格式,设置 <code>:data-type="false"</code>,<code>:data-upgrade="false"</code>。</p>',
|
||||
'zh-CN':
|
||||
'<p>通过 <code>data-type</code> 指定富文本数据保存的格式。数据默认保存格式为 Delta 形式,若需要将数据保存格式设置为 HTML 形式,并关闭 HTML 格式自动转 Delta 格式,设置 <code>:data-type="false"</code>,<code>:data-upgrade="false"</code>。</p>',
|
||||
'en-US': ''
|
||||
},
|
||||
codeFiles: ['data-switch.vue']
|
||||
|
|
|
@ -4,12 +4,14 @@ test('test', async ({ page }) => {
|
|||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('icon#basic-usage')
|
||||
|
||||
const icons = page.locator('.icon-demo > svg')
|
||||
const demo = page.locator('#basic-usage')
|
||||
|
||||
await expect(icons.first()).toHaveCSS('font-size', '48px')
|
||||
const icons = demo.locator('.icon-demo > svg')
|
||||
|
||||
await expect(icons.first()).toHaveCSS('font-size', '24px')
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await expect(icons.nth(i)).toHaveCSS('width', '48px')
|
||||
await expect(icons.nth(i)).toHaveCSS('height', '48px')
|
||||
await expect(icons.nth(i)).toHaveCSS('width', '24px')
|
||||
await expect(icons.nth(i)).toHaveCSS('height', '24px')
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('locales-custom-service', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('locales#custom-service')
|
||||
const reference = page.locator('.reference-wrapper')
|
||||
|
||||
const demo = page.locator('#custom-service')
|
||||
const reference = demo.locator('.reference-wrapper')
|
||||
const popper = page.locator('.tiny-locales__popper')
|
||||
|
||||
await expect(reference).toHaveText('zhCN')
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('自定义配置', async ({ page }) => {
|
||||
await page.goto('logout#redirecturl')
|
||||
const button = page.locator('.tiny-logout')
|
||||
await button.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(button).toHaveText(/登录/)
|
||||
})
|
|
@ -3,9 +3,12 @@ import { test, expect } from '@playwright/test'
|
|||
test('可清空特性', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('numeric#allow-empty')
|
||||
await page.getByRole('button').nth(1).click()
|
||||
await page.getByRole('spinbutton').click()
|
||||
await page.getByRole('spinbutton').press('ArrowRight')
|
||||
await page.getByRole('spinbutton').fill('')
|
||||
await page.getByRole('button').nth(2).click()
|
||||
|
||||
const demo = page.locator('#allow-empty')
|
||||
await demo.getByRole('spinbutton').fill('')
|
||||
await page.waitForTimeout(200)
|
||||
await demo.getByRole('spinbutton').blur()
|
||||
|
||||
const inputValue = await demo.locator('.tiny-numeric__input-inner').inputValue()
|
||||
expect(inputValue).toEqual('')
|
||||
})
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('属性设置', () => {
|
||||
test('加减按钮的显示与隐藏', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('numeric#controls')
|
||||
const numeric = page.locator('.tiny-numeric')
|
||||
await page.getByRole('spinbutton').first().click()
|
||||
await page.getByRole('button').nth(2).click()
|
||||
await page.getByRole('button').nth(1).click()
|
||||
await expect(numeric.first()).toBeVisible()
|
||||
})
|
||||
test('加减按钮的显示与隐藏', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('numeric#controls')
|
||||
const demo = page.locator('#controls')
|
||||
const numeric1 = demo.locator('.tiny-numeric').nth(0)
|
||||
const numeric2 = demo.locator('.tiny-numeric').nth(1)
|
||||
|
||||
await expect(numeric1.locator('.tiny-numeric__decrease')).toHaveCount(0)
|
||||
|
||||
await expect(numeric2.locator('.tiny-numeric__decrease')).toBeVisible()
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ test('PopUpload 基本用法', async ({ page }) => {
|
|||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('pop-upload#basic-usage')
|
||||
|
||||
const preview = page.locator('.all-demos-container')
|
||||
const preview = page.locator('#basic-usage')
|
||||
const modalAppearBtn = preview.getByRole('button', { name: '选择文件' })
|
||||
const uploadModal = page.locator('.tiny-popupload__modal')
|
||||
const selectFilesBtn = page.getByRole('button', { name: '选择文件' }).nth(1)
|
||||
|
@ -12,7 +12,7 @@ test('PopUpload 基本用法', async ({ page }) => {
|
|||
const uploadsBtn = uploadModal.getByRole('button', { name: '开始上传' })
|
||||
const cancelBtn = uploadModal.getByRole('button', { name: '取消' })
|
||||
const lists = uploadModal.locator('.tiny-popupload__dialog-table-item')
|
||||
const deleteIcon = lists.locator('.delIcon')
|
||||
const deleteIcon = lists.locator('.del-col')
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
||||
const path = require('node:path')
|
||||
const path1 = path.resolve(__dirname, '测试.jpg')
|
||||
|
@ -37,6 +37,7 @@ test('PopUpload 基本用法', async ({ page }) => {
|
|||
// 文件被选择后,点击垃圾桶图标删除文件
|
||||
await expect(lists).toHaveCount(1)
|
||||
await deleteIcon.click()
|
||||
await page.getByRole('button', { name: '确定' }).click()
|
||||
await expect(lists).toHaveCount(0)
|
||||
await expect(uploadsBtn).toBeDisabled()
|
||||
await fileChooser.setFiles(path1)
|
||||
|
|
|
@ -10,7 +10,7 @@ test('PopUpload 阻止删除文件', async ({ page }) => {
|
|||
const deleteModal = page.locator('.tiny-modal').nth(1)
|
||||
const selectFilesBtn = uploadModal.getByRole('button', { name: '选择文件' })
|
||||
const lists = uploadModal.locator('.tiny-popupload__dialog-table-item')
|
||||
const deleteIcon = lists.locator('.delIcon')
|
||||
const deleteIcon = lists.locator('.del-col')
|
||||
const path = require('node:path')
|
||||
const currentPath = path.resolve(__dirname, '测试.jpg')
|
||||
|
||||
|
@ -19,5 +19,6 @@ test('PopUpload 阻止删除文件', async ({ page }) => {
|
|||
await fileChooser.setFiles(currentPath)
|
||||
await expect(lists).toHaveCount(1)
|
||||
await deleteIcon.click()
|
||||
await page.getByRole('button', { name: '确定' }).click()
|
||||
await expect(deleteModal).toBeVisible()
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ test('事件是否正常触发', async ({ page }) => {
|
|||
const selectFilesBtn = uploadModal.getByRole('button', { name: '选择批量文件' })
|
||||
const uploadsBtn = page.getByRole('button', { name: '开始上传' })
|
||||
const lists = uploadModal.locator('.tiny-popupload__dialog-table-item')
|
||||
const deleteIcon = uploadModal.getByRole('listitem').locator('svg')
|
||||
const deleteIcon = lists.locator('.del-col')
|
||||
const path = require('node:path')
|
||||
const currentPath1 = path.resolve(__dirname, '测试.jpg')
|
||||
const currentPath2 = path.resolve(__dirname, '测试.png')
|
||||
|
@ -35,5 +35,5 @@ test('事件是否正常触发', async ({ page }) => {
|
|||
await page.waitForTimeout(200)
|
||||
await fileChooser.setFiles(currentPath2)
|
||||
await uploadsBtn.click()
|
||||
await page.getByText('文件上传失败回调').isVisible()
|
||||
await page.getByText('文件上传失败回调').first().isVisible()
|
||||
})
|
||||
|
|
|
@ -4,7 +4,8 @@ test('基础用法,是否可动态控制进度条', async ({ page }) => {
|
|||
page.on('pageerror', (exception) => expect(exception).not.toBeNull())
|
||||
await page.goto('progress#basic-usage')
|
||||
|
||||
const progress = page.getByRole('progressbar')
|
||||
const demo = page.locator('#basic-usage')
|
||||
const progress = demo.getByRole('progressbar')
|
||||
const progress1 = progress.nth(0).locator('.tiny-progress-bar__outer')
|
||||
const progress2 = progress.nth(1).locator('.tiny-progress-bar__outer')
|
||||
|
||||
|
@ -19,11 +20,11 @@ test('基础用法,是否可动态控制进度条', async ({ page }) => {
|
|||
|
||||
await expect(progress.nth(0)).toHaveAttribute('aria-valuenow', '90')
|
||||
await expect(progress.nth(1)).toHaveAttribute('aria-valuenow', '90')
|
||||
await page.getByRole('button').nth(2).click()
|
||||
await demo.getByRole('button').nth(1).click()
|
||||
await expect(page.getByText('努力加载中,请稍后...')).not.toBeVisible()
|
||||
await expect(progress.nth(0)).toHaveAttribute('aria-valuenow', '100')
|
||||
await expect(progress.nth(1)).toHaveAttribute('aria-valuenow', '100')
|
||||
await page.getByRole('button').nth(1).click()
|
||||
await demo.getByRole('button').first().click()
|
||||
await expect(progress.nth(0)).toHaveAttribute('aria-valuenow', '90')
|
||||
await expect(progress.nth(1)).toHaveAttribute('aria-valuenow', '90')
|
||||
})
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
stable: '3.12.0'
|
||||
},
|
||||
versionTipOption: {
|
||||
stages: ['stable']
|
||||
},
|
||||
demos: [
|
||||
{
|
||||
demoId: 'basic-usage',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
column: '1',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
experimental: '3.11.0'
|
||||
},
|
||||
demos: [
|
||||
|
|
|
@ -65,7 +65,7 @@ test('多选,禁用项默认选中', async ({ page }) => {
|
|||
// 默认值显示tag数
|
||||
await expect(tag).toHaveCount(2)
|
||||
// 禁用项默认选中不显示关闭图标
|
||||
await expect(tag.filter({ hasText: '上海' }).locator('svg')).toHaveCount(0)
|
||||
await expect(tag.filter({ hasText: '上海' }).locator('svg')).toHaveCount(1)
|
||||
// 非禁用项显示关闭图标
|
||||
await expect(tag.filter({ hasText: '天津' }).locator('svg')).toHaveCount(1)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'
|
|||
|
||||
test('边框和自定义背景色', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).not.toBeNull())
|
||||
await page.goto('tag#color3')
|
||||
await page.goto('tag#color-border')
|
||||
|
||||
const tags = page.locator('.all-demos-container').locator('.tiny-tag')
|
||||
const first = tags.getByText('标签一')
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div class="tiny-tag-demo">
|
||||
<div class="mb5">hit:</div>
|
||||
<tiny-tag hit>标签一</tiny-tag>
|
||||
<tiny-tag type="success" hit>标签二</tiny-tag>
|
||||
<tiny-tag type="info" hit>标签三</tiny-tag>
|
||||
<tiny-tag type="warning" hit>标签四</tiny-tag>
|
||||
<tiny-tag type="danger" hit>标签五</tiny-tag>
|
||||
<div class="mb5">color 预设值:</div>
|
||||
<tiny-tag color="red">red标签</tiny-tag>
|
||||
<tiny-tag color="orange">orange标签</tiny-tag>
|
||||
<tiny-tag color="green">green标签</tiny-tag>
|
||||
<tiny-tag color="blue">blue标签</tiny-tag>
|
||||
<tiny-tag color="purple">purple标签</tiny-tag>
|
||||
<tiny-tag color="brown">brown标签</tiny-tag>
|
||||
<tiny-tag color="grey">grey标签</tiny-tag>
|
||||
<div class="mb5">自定义 color 值:</div>
|
||||
<tiny-tag color="rgba(82,196,26,0.8)" hit>自定义背景色</tiny-tag>
|
||||
<tiny-tag :color="['linear-gradient(to right, #e8cda4, #f8eddb)', '#8f3c00']" hit>自定义背景色和文本色</tiny-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="jsx">
|
||||
import { Tag as TinyTag } from '@opentiny/vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tiny-tag {
|
||||
border-radius: 0px 8px 0px 8px;
|
||||
}
|
||||
</style>
|
|
@ -1,16 +0,0 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('边框和自定义背景色', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).not.toBeNull())
|
||||
await page.goto('tag#color3')
|
||||
|
||||
const tags = page.locator('.all-demos-container').locator('.tiny-tag')
|
||||
const first = tags.getByText('标签一')
|
||||
const red = tags.getByText('red标签')
|
||||
const custom = tags.getByText('自定义背景色', { exact: true })
|
||||
|
||||
await expect(first).toHaveClass(/is-hit/)
|
||||
await expect(red).toHaveClass(/tiny-tag--red/)
|
||||
await expect(custom).toHaveCSS('background-color', 'rgba(82, 196, 26, 0.8)')
|
||||
await expect(custom).toHaveCSS('border-color', 'rgb(87, 93, 108)')
|
||||
})
|
|
@ -1,37 +0,0 @@
|
|||
<template>
|
||||
<div class="tiny-tag-demo">
|
||||
<div class="mb5">hit:</div>
|
||||
<tiny-tag hit>标签一</tiny-tag>
|
||||
<tiny-tag type="success" hit>标签二</tiny-tag>
|
||||
<tiny-tag type="info" hit>标签三</tiny-tag>
|
||||
<tiny-tag type="warning" hit>标签四</tiny-tag>
|
||||
<tiny-tag type="danger" hit>标签五</tiny-tag>
|
||||
<div class="mb5">color 预设值:</div>
|
||||
<tiny-tag color="red">red标签</tiny-tag>
|
||||
<tiny-tag color="orange">orange标签</tiny-tag>
|
||||
<tiny-tag color="green">green标签</tiny-tag>
|
||||
<tiny-tag color="blue">blue标签</tiny-tag>
|
||||
<tiny-tag color="purple">purple标签</tiny-tag>
|
||||
<tiny-tag color="brown">brown标签</tiny-tag>
|
||||
<tiny-tag color="grey">grey标签</tiny-tag>
|
||||
<div class="mb5">自定义 color 值:</div>
|
||||
<tiny-tag color="rgba(82,196,26,0.8)" hit>自定义背景色</tiny-tag>
|
||||
<tiny-tag :color="['linear-gradient(to right, #e8cda4, #f8eddb)', '#8f3c00']" hit>自定义背景色和文本色</tiny-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { Tag } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyTag: Tag
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tiny-tag {
|
||||
border-radius: 0px 8px 0px 8px;
|
||||
}
|
||||
</style>
|
|
@ -12,6 +12,6 @@ test('懒加载', async ({ page }) => {
|
|||
await treeNodeContent.filter({ hasText: /^表单组件$/ }).click()
|
||||
await expect(treeNodeContent.filter({ hasText: /^表单组件1$/ })).not.toBeVisible()
|
||||
// 等到异步加载完成
|
||||
await page.waitForTimeout(600)
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(treeNodeContent.filter({ hasText: /^表单组件1$/ })).toBeVisible()
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
experimental: '3.17.0'
|
||||
},
|
||||
demos: [
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
export default {
|
||||
column: '2',
|
||||
owner: '',
|
||||
metaData: {
|
||||
meta: {
|
||||
stable: '3.12.0'
|
||||
},
|
||||
versionTipOption: {
|
||||
stages: ['stable']
|
||||
},
|
||||
demos: [
|
||||
{
|
||||
demoId: 'basic',
|
||||
|
|
|
@ -118,7 +118,9 @@ export const cmpMenus = [
|
|||
'nameCn': '基础选择器',
|
||||
'name': 'BaseSelect',
|
||||
'key': 'base-select',
|
||||
'mark': { 'type': 'warning', 'text': 'Beta' }
|
||||
'meta': {
|
||||
'experimental': '3.17.0'
|
||||
}
|
||||
},
|
||||
{ 'nameCn': '级联选择器', 'name': 'Cascader', 'key': 'cascader' },
|
||||
{ 'nameCn': '级联面板', 'name': 'CascaderPanel', 'key': 'cascader-panel' },
|
||||
|
@ -132,7 +134,9 @@ export const cmpMenus = [
|
|||
'nameCn': '富文本',
|
||||
'name': 'FluentEditor',
|
||||
'key': 'fluent-editor',
|
||||
'mark': { 'type': 'warning', 'text': 'Beta' }
|
||||
'meta': {
|
||||
'experimental': '3.17.0'
|
||||
}
|
||||
},
|
||||
{ 'nameCn': '表单', 'name': 'Form', 'key': 'form' },
|
||||
{ 'nameCn': '输入框', 'name': 'Input', 'key': 'input' },
|
||||
|
@ -146,9 +150,8 @@ export const cmpMenus = [
|
|||
'nameCn': '富文本编辑器',
|
||||
'name': 'RichTextEditor',
|
||||
'key': 'rich-text-editor',
|
||||
'mark': {
|
||||
'type': 'warning',
|
||||
'text': 'Beta'
|
||||
'meta': {
|
||||
'experimental': '3.10.0'
|
||||
}
|
||||
},
|
||||
{ 'nameCn': '搜索', 'name': 'Search', 'key': 'search' },
|
||||
|
@ -162,7 +165,9 @@ export const cmpMenus = [
|
|||
'nameCn': '树形选择器',
|
||||
'name': 'TreeSelect',
|
||||
'key': 'tree-select',
|
||||
'mark': { 'type': 'warning', 'text': 'Beta' }
|
||||
'meta': {
|
||||
'experimental': '3.17.0'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -218,7 +223,7 @@ export const cmpMenus = [
|
|||
{ 'nameCn': '标记', 'name': 'Badge', 'key': 'badge' },
|
||||
{ 'nameCn': '日历', 'name': 'Calendar', 'key': 'calendar' },
|
||||
{ 'nameCn': '日历视图', 'name': 'CalendarView', 'key': 'calendar-view' },
|
||||
{ 'nameCn': '卡片', 'name': 'Card', 'key': 'card', 'mark': { 'type': 'danger', 'text': 'New' } },
|
||||
{ 'nameCn': '卡片', 'name': 'Card', 'key': 'card' },
|
||||
{ 'nameCn': '走马灯', 'name': 'Carousel', 'key': 'carousel' },
|
||||
{ 'nameCn': '折叠面板', 'name': 'Collapse', 'key': 'collapse' },
|
||||
{ 'nameCn': '流程图', 'name': 'FlowChart', 'key': 'flowchart' },
|
||||
|
@ -229,8 +234,7 @@ export const cmpMenus = [
|
|||
{
|
||||
'nameCn': '思维导图',
|
||||
'name': 'MindMap',
|
||||
'key': 'mind-map',
|
||||
'mark': { 'type': 'danger', 'text': 'New' }
|
||||
'key': 'mind-map'
|
||||
},
|
||||
{ 'nameCn': '二维码', 'name': 'QrCode', 'key': 'qr-code' },
|
||||
{ 'nameCn': '统计数值', 'name': 'Statistic', 'key': 'statistic' },
|
||||
|
@ -258,7 +262,7 @@ export const cmpMenus = [
|
|||
{ 'nameCn': '气泡确认框', 'name': 'PopConfirm', 'key': 'popconfirm' },
|
||||
{ 'nameCn': '进度条', 'name': 'Progress', 'key': 'progress' },
|
||||
{ 'nameCn': '气泡卡片', 'name': 'Popover', 'key': 'popover' },
|
||||
{ 'nameCn': '骨架屏', 'name': 'Skeleton', 'key': 'skeleton', 'mark': { 'type': 'danger', 'text': 'New' } },
|
||||
{ 'nameCn': '骨架屏', 'name': 'Skeleton', 'key': 'skeleton' },
|
||||
{ 'nameCn': '文字提示', 'name': 'Tooltip', 'key': 'tooltip' }
|
||||
]
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"dependencies": {
|
||||
"@opentiny/vue": "workspace:~",
|
||||
"@opentiny/vue-common": "workspace:~",
|
||||
"@opentiny/vue-hooks": "workspace:~",
|
||||
"@opentiny/vue-design-aurora": "workspace:~",
|
||||
"@opentiny/vue-design-saas": "workspace:~",
|
||||
"@opentiny/vue-design-smb": "workspace:~",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<span v-if="currentStageComputed" class="version-tip">
|
||||
<div v-if="renderType === 'alert'">
|
||||
<tiny-alert :type="alertTypeComputed" :closable="false">
|
||||
<tiny-alert :type="alertTypeComputed" v-if="!isStableComputed" :closable="false">
|
||||
<template #description>
|
||||
<span>{{ tipComputed }}</span>
|
||||
</template>
|
||||
|
@ -15,7 +15,7 @@
|
|||
:content="tipComputed"
|
||||
:disabled="!tipComputed"
|
||||
>
|
||||
<tiny-tag size="mini" effect="dark" :type="tagTypeComputed">{{ currentStageComputed }}</tiny-tag>
|
||||
<tiny-tag size="small" effect="dark" :type="tagTypeComputed">{{ tagContentComputed }}</tiny-tag>
|
||||
</tiny-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -28,11 +28,14 @@ import { Tag as TinyTag, Alert as TinyAlert, Tooltip as TinyTooltip } from '@ope
|
|||
import { getWord } from '../../i18n/index'
|
||||
|
||||
enum STAGE {
|
||||
// 实验性阶段(api属性不具备此阶段)
|
||||
experimental = 'experimental',
|
||||
// 稳定阶段
|
||||
stable = 'stable',
|
||||
// 弃用阶段
|
||||
deprecated = 'deprecated',
|
||||
removed = 'removed',
|
||||
new = 'new'
|
||||
// 将要被移除阶段
|
||||
toBeRemoved = 'toBeRemoved'
|
||||
}
|
||||
|
||||
interface IStageVersionMetaData {
|
||||
|
@ -43,7 +46,7 @@ interface IVersionMetaData {
|
|||
[STAGE.experimental]?: IStageVersionMetaData | string
|
||||
[STAGE.stable]?: IStageVersionMetaData | string
|
||||
[STAGE.deprecated]?: IStageVersionMetaData | string
|
||||
[STAGE.removed]?: IStageVersionMetaData | string
|
||||
[STAGE.toBeRemoved]?: IStageVersionMetaData | string
|
||||
}
|
||||
|
||||
interface Ii18nString {
|
||||
|
@ -51,38 +54,38 @@ interface Ii18nString {
|
|||
'en-US': string
|
||||
}
|
||||
|
||||
// 生命周期的顺序 实验 --> 稳定 --> 弃用 --> 移除
|
||||
const lifeCycleOrder = [STAGE.experimental, STAGE.stable, STAGE.deprecated, STAGE.toBeRemoved]
|
||||
|
||||
const alertTypeMap = {
|
||||
[STAGE.removed]: 'error',
|
||||
[STAGE.toBeRemoved]: 'error',
|
||||
[STAGE.deprecated]: 'error',
|
||||
[STAGE.experimental]: 'warning',
|
||||
[STAGE.stable]: 'info'
|
||||
[STAGE.stable]: 'success'
|
||||
}
|
||||
|
||||
const tagTypeMap = {
|
||||
[STAGE.removed]: 'danger',
|
||||
[STAGE.toBeRemoved]: 'danger',
|
||||
[STAGE.deprecated]: 'danger',
|
||||
[STAGE.experimental]: 'warning',
|
||||
[STAGE.stable]: 'info',
|
||||
[STAGE.new]: 'primary'
|
||||
[STAGE.stable]: 'success'
|
||||
}
|
||||
|
||||
const cnDesMap = {
|
||||
[STAGE.experimental]: '处于测试阶段',
|
||||
[STAGE.stable]: '自 v{version} 起稳定提供',
|
||||
[STAGE.deprecated]: '从 v{version} 开始被废弃',
|
||||
[STAGE.removed]: '于 v{version} 移除',
|
||||
[STAGE.new]: '于 v{version} 新增'
|
||||
[STAGE.toBeRemoved]: '于 v{version} 移除'
|
||||
}
|
||||
|
||||
const enDesMap = {
|
||||
[STAGE.experimental]: 'in beta',
|
||||
[STAGE.stable]: 'stable since v{version}',
|
||||
[STAGE.deprecated]: 'deprecated since v{version}',
|
||||
[STAGE.removed]: 'removed in v{version}',
|
||||
[STAGE.new]: 'add in v{version}'
|
||||
[STAGE.toBeRemoved]: 'toBeRemoved in v{version}'
|
||||
}
|
||||
|
||||
// 默认的,只需要显示deprecated,experimental状态时的提示,除非声明了briefStage
|
||||
// 默认的,只需要显示deprecated,experimental状态时的提示
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TinyTag,
|
||||
|
@ -90,7 +93,7 @@ export default defineComponent({
|
|||
TinyTooltip
|
||||
},
|
||||
props: {
|
||||
metaData: {
|
||||
meta: {
|
||||
type: Object as PropType<IVersionMetaData>,
|
||||
default: () => ({})
|
||||
},
|
||||
|
@ -102,19 +105,12 @@ export default defineComponent({
|
|||
type: String as PropType<'component' | 'api'>,
|
||||
default: 'component'
|
||||
},
|
||||
stages: {
|
||||
type: Array as PropType<STAGE[]>,
|
||||
default: () => [STAGE.experimental, STAGE.deprecated, STAGE.removed, STAGE.new]
|
||||
},
|
||||
alertType: {
|
||||
type: String
|
||||
},
|
||||
tagType: {
|
||||
type: String
|
||||
},
|
||||
briefStage: {
|
||||
type: Object as PropType<STAGE>
|
||||
},
|
||||
tip: {
|
||||
type: Object as PropType<Ii18nString>
|
||||
},
|
||||
|
@ -123,55 +119,49 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
setup(props) {
|
||||
const isInStage = (stage: STAGE) => Boolean(props.metaData[stage]) && props.stages.includes(stage)
|
||||
|
||||
const getVersion = (stage: STAGE) => {
|
||||
if (!props.metaData[stage]) return ''
|
||||
if (!props.meta[stage]) return ''
|
||||
|
||||
if (typeof props.metaData[stage] === 'string') {
|
||||
return props.metaData[stage] as string
|
||||
if (typeof props.meta[stage] === 'string') {
|
||||
return props.meta[stage] as string
|
||||
} else {
|
||||
return (props.metaData[stage] as IStageVersionMetaData).version
|
||||
return (props.meta[stage] as IStageVersionMetaData).version
|
||||
}
|
||||
}
|
||||
|
||||
const currentStageComputed = computed(() => {
|
||||
if (props.briefStage) {
|
||||
return props.briefStage
|
||||
}
|
||||
const currentStageComputed = computed(() =>
|
||||
lifeCycleOrder
|
||||
.slice(0, -1)
|
||||
.toReversed()
|
||||
.find((stage) => Boolean(props.meta[stage]))
|
||||
)
|
||||
|
||||
return [STAGE.removed, STAGE.deprecated, STAGE.stable, STAGE.experimental, STAGE.new].find(isInStage)
|
||||
})
|
||||
// 是否为稳定阶段
|
||||
const isStableComputed = computed(() => currentStageComputed.value === STAGE.stable)
|
||||
|
||||
const generateDes = (desMap: typeof cnDesMap) => {
|
||||
// 当前stable之后,不显示experimental的描述
|
||||
const isFilterExperimental = [STAGE.removed, STAGE.deprecated, STAGE.stable].includes(
|
||||
currentStageComputed.value as STAGE
|
||||
)
|
||||
// 当前deprecated之后,不显示stable的描述
|
||||
const isFilterStable = [STAGE.removed, STAGE.deprecated].includes(currentStageComputed.value as STAGE)
|
||||
const currentStage = currentStageComputed.value
|
||||
const deprecatedList = lifeCycleOrder.slice(2)
|
||||
|
||||
const goingStages = Object.entries(desMap).filter(([stage]) => {
|
||||
let isPicked = isInStage(stage as STAGE)
|
||||
|
||||
if (stage === STAGE.experimental) {
|
||||
isPicked = isPicked && !isFilterExperimental
|
||||
if (deprecatedList.includes(currentStage)) {
|
||||
return deprecatedList.includes(stage)
|
||||
} else {
|
||||
return stage === currentStage
|
||||
}
|
||||
|
||||
if (stage === STAGE.stable) {
|
||||
isPicked = isPicked && !isFilterStable
|
||||
}
|
||||
|
||||
return isPicked
|
||||
})
|
||||
|
||||
return goingStages.map(([stage, des]) => des.replace('{version}', getVersion(stage as STAGE))).join(',')
|
||||
}
|
||||
|
||||
const tagContentComputed = computed(() =>
|
||||
isStableComputed.value ? props.meta[currentStageComputed.value] : currentStageComputed.value
|
||||
)
|
||||
|
||||
const tipComputed = computed(() => {
|
||||
if (props.tip) return getWord(props.tip['zh-CN'], props.tip['en-US']) as string
|
||||
|
||||
if (!props.metaData) return ''
|
||||
if (!props.meta) return ''
|
||||
|
||||
const vertionDesZnCn = generateDes(cnDesMap)
|
||||
const znChTip = `该${props.tipSubject === 'component' ? '组件' : '特性'}${vertionDesZnCn}。${
|
||||
|
@ -202,7 +192,9 @@ export default defineComponent({
|
|||
tipComputed,
|
||||
currentStageComputed,
|
||||
alertTypeComputed,
|
||||
tagTypeComputed
|
||||
tagTypeComputed,
|
||||
tagContentComputed,
|
||||
isStableComputed
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<div class="docs-title-wrap">
|
||||
<div class="markdown-body markdown-top-body" size="medium" v-html="cmpTopMd"></div>
|
||||
<version-tip
|
||||
v-if="currJson.metaData || currJson.versionTipOption"
|
||||
:meta-data="currJson.metaData"
|
||||
v-if="currJson.meta || currJson.versionTipOption"
|
||||
:meta="currJson.meta"
|
||||
v-bind="currJson.versionTipOption"
|
||||
>
|
||||
</version-tip>
|
||||
|
@ -54,8 +54,8 @@
|
|||
<span v-else>{{ row.name }}</span>
|
||||
</span>
|
||||
<version-tip
|
||||
v-if="row.metaData || row.versionTipOption"
|
||||
:meta-data="row.metaData"
|
||||
v-if="row.meta || row.versionTipOption"
|
||||
:meta="row.meta"
|
||||
v-bind="row.versionTipOption"
|
||||
render-type="tag"
|
||||
tip-subject="api"
|
||||
|
@ -172,8 +172,8 @@
|
|||
<span v-else>{{ row.name }}</span>
|
||||
</span>
|
||||
<version-tip
|
||||
v-if="row.metaData || row.versionTipOption"
|
||||
:meta-data="row.metaData"
|
||||
v-if="row.meta || row.versionTipOption"
|
||||
:meta="row.meta"
|
||||
v-bind="row.versionTipOption"
|
||||
render-type="tag"
|
||||
tip-subject="api"
|
||||
|
@ -368,14 +368,14 @@ export default defineComponent({
|
|||
for (const apiType of Object.keys(apiGroup)) {
|
||||
if (Array.isArray(apiGroup[apiType]) && apiGroup[apiType].length) {
|
||||
const apiArr = apiGroup[apiType].map((i) => {
|
||||
const { name, type, defaultValue, desc, demoId, typeAnchorName, linkTo, metaData, versionTipOption } = i
|
||||
const { name, type, defaultValue, desc, demoId, typeAnchorName, linkTo, meta, versionTipOption } = i
|
||||
const item = {
|
||||
name,
|
||||
type,
|
||||
defaultValue: defaultValue || '--',
|
||||
desc: desc[state.langKey],
|
||||
demoId,
|
||||
metaData,
|
||||
meta,
|
||||
versionTipOption,
|
||||
typeAnchorName: '',
|
||||
linkTo
|
||||
|
|
|
@ -24,9 +24,14 @@
|
|||
class="menu-type-icon"
|
||||
></component>
|
||||
<span class="node-name-label">{{ data.label }}</span>
|
||||
<tiny-tag v-if="data.mark?.text" class="node-float-tip" effect="light" :type="data.mark?.type">
|
||||
{{ data.mark.text }}
|
||||
</tiny-tag>
|
||||
<version-tip
|
||||
class="node-float-tip"
|
||||
v-if="data.meta || data.versionTipOption"
|
||||
:meta="data.meta"
|
||||
v-bind="data.versionTipOption"
|
||||
render-type="tag"
|
||||
>
|
||||
</version-tip>
|
||||
</div>
|
||||
</template>
|
||||
</tiny-tree-menu>
|
||||
|
@ -55,6 +60,7 @@ import { router } from '@/router.js'
|
|||
import { getWord, i18nByKey, appData, appFn, useApiMode, useTemplateMode } from '@/tools'
|
||||
import useTheme from '@/tools/useTheme'
|
||||
import FloatSettings from '@/views/components/float-settings'
|
||||
import VersionTip from '../components/VersionTip.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutVue',
|
||||
|
@ -67,7 +73,8 @@ export default defineComponent({
|
|||
TinyRadio: Radio,
|
||||
TinyRadioGroup: RadioGroup,
|
||||
TinyButton: Button,
|
||||
FloatSettings
|
||||
FloatSettings,
|
||||
VersionTip
|
||||
},
|
||||
props: [],
|
||||
setup() {
|
||||
|
|
|
@ -44,6 +44,11 @@
|
|||
"type": "module",
|
||||
"exclude": false
|
||||
},
|
||||
"Hooks": {
|
||||
"path": "vue-hooks/index.ts",
|
||||
"type": "module",
|
||||
"exclude": false
|
||||
},
|
||||
"FormItemLabelWrap": {
|
||||
"path": "vue/src/form-item/src/label-wrap.ts",
|
||||
"type": "template",
|
||||
|
|
|
@ -322,10 +322,24 @@ const dateToLocaleStringForIE = (timezone, value) => {
|
|||
return new Date(offsetTime)
|
||||
}
|
||||
|
||||
export const changeToNow =
|
||||
({ api, state }) =>
|
||||
export const getNowTime =
|
||||
({ props }) =>
|
||||
() => {
|
||||
const now = new Date()
|
||||
return new Promise((resolve) => {
|
||||
resolve(props.nowClick())
|
||||
}).then((res) => {
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const changeToNow =
|
||||
({ api, state, props }) =>
|
||||
async () => {
|
||||
let now = new Date()
|
||||
|
||||
if (props.nowClick !== undefined) {
|
||||
now = await api.getNowTime()
|
||||
}
|
||||
const timezone = state.timezone
|
||||
const isServiceTimezone = timezone.isServiceTimezone
|
||||
let disabledDate = !state.disabledDate
|
||||
|
|
|
@ -55,7 +55,8 @@ import {
|
|||
computerTimeFormat,
|
||||
watchVisible,
|
||||
getDisabledNow,
|
||||
getDisabledConfirm
|
||||
getDisabledConfirm,
|
||||
getNowTime
|
||||
} from './index'
|
||||
import { getWeekNumber, extractDateFormat } from '../common/deps/date-util'
|
||||
import { DATEPICKER, DATE } from '../common'
|
||||
|
@ -84,7 +85,8 @@ export const api = [
|
|||
'handleVisibleDateChange',
|
||||
'handleLeave',
|
||||
'handleShortcutClick',
|
||||
'handleTimePickClose'
|
||||
'handleTimePickClose',
|
||||
'getNowTime'
|
||||
]
|
||||
|
||||
const initState = ({ reactive, computed, api, i18n }) => {
|
||||
|
@ -175,7 +177,7 @@ const initWatch = ({ watch, state, api, nextTick }) => {
|
|||
watch(() => state.visible, api.watchVisible)
|
||||
}
|
||||
|
||||
const initApi = ({ api, state, t, emit, nextTick, vm, watch }) => {
|
||||
const initApi = ({ api, state, t, emit, nextTick, vm, watch, props }) => {
|
||||
Object.assign(api, {
|
||||
t,
|
||||
state,
|
||||
|
@ -206,7 +208,7 @@ const initApi = ({ api, state, t, emit, nextTick, vm, watch }) => {
|
|||
searchTz: searchTz({ api, state }),
|
||||
handleEnter: handleEnter(api),
|
||||
handleLeave: handleLeave({ api, emit }),
|
||||
changeToNow: changeToNow({ api, state }),
|
||||
changeToNow: changeToNow({ api, state, props }),
|
||||
isValidValue: isValidValue({ api, state }),
|
||||
handleClear: handleClear({ api, state, emit }),
|
||||
watchValue: watchValue({ api, state }),
|
||||
|
@ -223,7 +225,8 @@ const initApi = ({ api, state, t, emit, nextTick, vm, watch }) => {
|
|||
handleVisibleTimeChange: handleVisibleTimeChange({ api, vm, state, t }),
|
||||
computerTimeFormat: computerTimeFormat({ state }),
|
||||
getDisabledNow: getDisabledNow({ state }),
|
||||
getDisabledConfirm: getDisabledConfirm({ state })
|
||||
getDisabledConfirm: getDisabledConfirm({ state }),
|
||||
getNowTime: getNowTime({ props })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -232,7 +235,7 @@ export const renderless = (props, { computed, reactive, watch, nextTick }, { t,
|
|||
const emit = props.emitter ? props.emitter.emit : $emit
|
||||
const state = initState({ reactive, computed, api, i18n })
|
||||
|
||||
initApi({ api, state, t, emit, nextTick, vm, watch })
|
||||
initApi({ api, state, t, emit, nextTick, vm, watch, props })
|
||||
initWatch({ watch, state, api, nextTick })
|
||||
|
||||
return api
|
||||
|
|
|
@ -123,8 +123,9 @@ function computeScrollLeft($table, td) {
|
|||
function setBodyLeft(body, td, $table, column, move) {
|
||||
const { isLeftArrow, isRightArrow, from } = move || {}
|
||||
|
||||
body.scrollLeft = computeScrollLeft($table, td)
|
||||
|
||||
const bodyScollLeft = computeScrollLeft($table, td)
|
||||
body.scrollLeft = bodyScollLeft
|
||||
$table.lastScrollLeft = bodyScollLeft
|
||||
if (from) {
|
||||
const direction = isLeftArrow ? 'left' : isRightArrow ? 'right' : null
|
||||
const fixedDom = $table.elemStore[`${direction}-body-list`]
|
||||
|
|
|
@ -462,7 +462,13 @@ export const handleEnterDisplayOnlyContent =
|
|||
const font = window.getComputedStyle(target).font
|
||||
const rect = target.getBoundingClientRect()
|
||||
const iconWidth = 16 + 15 // 减去图标的宽度加上右边距
|
||||
isOverTextWhenMask = omitText(text, font, rect.width - iconWidth).o
|
||||
/*
|
||||
1、omitText使用canvas来计算文字渲染后宽度来计算有没有文本超长
|
||||
2、html标签换行情况下,会导致textContent比原文本多出前后空格,导致canvas计算宽度比html实际渲染宽度大,最终误判
|
||||
3、将文本内容去除前后空格,再交给canvas计算宽度,消除空格带来的误差
|
||||
*/
|
||||
const calcText = text?.trim() || ''
|
||||
isOverTextWhenMask = omitText(calcText, font, rect.width - iconWidth).o
|
||||
}
|
||||
|
||||
if (isOverTextWhenMask) {
|
||||
|
|
|
@ -65,8 +65,8 @@ export const renderless = (props, { reactive, provide, onMounted, onBeforeUnmoun
|
|||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
mutationObserver?.disconnect()
|
||||
intersectionObserver?.disconnect()
|
||||
state.mutationObserver?.disconnect()
|
||||
state.intersectionObserver?.disconnect()
|
||||
})
|
||||
|
||||
watch(
|
||||
|
|
|
@ -113,6 +113,12 @@
|
|||
@apply text-left;
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-tip {
|
||||
@apply fill-color-icon-tertiary;
|
||||
@apply ml-2;
|
||||
@apply leading-6;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
|
|
|
@ -132,6 +132,12 @@
|
|||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-tip {
|
||||
fill: #aeaeae;
|
||||
margin-left: 8px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# @opentiny/vue-hooks
|
||||
|
||||
The `usehooks` collection provided by the `TinyVue` component library provides rich combined functions.
|
|
@ -0,0 +1,3 @@
|
|||
# @opentiny/vue-hooks
|
||||
|
||||
`TinyVue` 组件库提供的 `usehooks` 集合,提供丰富的组合式函数。
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Copyright (c) 2022 - present TinyVue Authors.
|
||||
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
import { useFloating } from './src/use-floating'
|
||||
|
||||
export { useFloating }
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "@opentiny/vue-hooks",
|
||||
"version": "3.18.0",
|
||||
"description": "",
|
||||
"module": "index.ts",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.9",
|
||||
"@opentiny/vue-common": "workspace:~"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
|
@ -0,0 +1,411 @@
|
|||
import type { Placement, Strategy, OffsetOptions, RootBoundary, Boundary, ReferenceElement } from '@floating-ui/dom'
|
||||
import { computePosition, autoUpdate, flip, offset, shift, arrow, hide, limitShift } from '@floating-ui/dom'
|
||||
|
||||
import { hooks } from '@opentiny/vue-common'
|
||||
|
||||
const { reactive, watch, markRaw, onBeforeUnmount } = hooks
|
||||
|
||||
interface IFloatOption {
|
||||
reference: null | ReferenceElement
|
||||
popper: null | HTMLElement
|
||||
/** ✅ 是否弹出 */
|
||||
show: boolean
|
||||
/** ✅ 是否自动更新位置 */
|
||||
autoUpdate: boolean // 更新策略有5种,4种默认为true。 就依默认策略进行update
|
||||
/** ✅ 弹出层定位策略, 【不建议修改】: 'absolute' | 'fixed' https://floating-ui.com/docs/computePosition#strategy */
|
||||
strategy: Strategy
|
||||
/** ✅ 默认出现的12个位置 */
|
||||
placement: Placement
|
||||
/** ✅ 弹出层偏移量 支持 number | {mainAxis,crossAxis,alignmentAxis}
|
||||
* 1、只传入 number, 代表主轴上的偏移。
|
||||
* 2、crossAxis,alignmentAxis 都是副轴上的偏移, 区别是:
|
||||
* crossAxis 固定向副轴的正方向偏移;
|
||||
* alignmentAxis 在副轴上,根据placement的后段决定偏移。
|
||||
* 比如 top的副轴为水平方向。 指定alignmentAxis=20的话, top-start时,向右20, top-end时 向左20。
|
||||
*/
|
||||
offset: OffsetOptions
|
||||
/** ✅ 是否显示箭头 */
|
||||
arrowVisible: boolean
|
||||
/** ✅ 溢出的根边界, 取值为: viewport: 可视视口 document: 整个文档区域 或 自定义Rect:{ x,y,width,height} 【不建议切换,不太确定它影响哪些场景】
|
||||
* 在floating 内部, 计算所有 [...clippingAncestors, rootBoundary] 的rect 大小
|
||||
* 'viewport' 时,访问的是 window.visualViewport, 其 width是不带滚动条的宽度。
|
||||
*/
|
||||
rootBoundary: RootBoundary
|
||||
/** ✅ 裁剪元素或区域元素。 默认为最近的rel元素。 此处可自定义为某个元素或Rect */
|
||||
boundary: Boundary
|
||||
/** ✅ 边界预留padding. 设置后,flip 快到边界时,提前就翻转 */
|
||||
boundaryPadding: number
|
||||
/** ✅ 引用元素不可见时,是否自动隐藏。 【需要启用autoUpdate】 */
|
||||
syncHide: boolean
|
||||
/** ✅ 元素弹出后,任何重新定位都自动关闭popper, 适用于右键菜单打开后,滚动就或日期组件在滚动时自动关闭。 【需要启用autoUpdate】 */
|
||||
autoHide: boolean
|
||||
/** ✅ 是否加速。 加速时,绑定popper的translate属性,否则绑定left/top。 【该属性不建议切换】 */
|
||||
gpuAcceleration: boolean
|
||||
/** ✅ 是否动画。 动画的机制简化, 不考虑前个动画未结束时,就开始下个动画的情况。 */
|
||||
animate: boolean
|
||||
/** ✅ 动画类名 */
|
||||
animateName: string
|
||||
/** ✅ 是否添加到body。【该属性不建议切换】
|
||||
* true时, 显示popper时,才body.append; 隐藏时popper.remove。 boundary为 body.
|
||||
* false时, 显示popper, 修改style.display='block', 隐藏修改 display:none boundary为 最近的relative元素 */
|
||||
appendToBody: boolean
|
||||
/** ✅ 自定义类名,以支持不同的主题色, is-dark is-light 等 , 支持空格分隔的多个类名 */
|
||||
customClass: string
|
||||
|
||||
/** 是否启用flip flip, shift 属性会影响弹层的位置。 在鼠标右击菜单等场景,想固定弹出位置时,可以关闭该属性 */
|
||||
flipAble: boolean
|
||||
/** 是否启用shift */
|
||||
shiftAble: boolean
|
||||
|
||||
/** 缓存上次的值。 由于watch state时,取不到oldState的值,所以每次应用后,记录一下 */
|
||||
_last: Partial<IFloatOption> & {
|
||||
arrowInserted?: boolean
|
||||
arrowEl: HTMLElement
|
||||
timestamp: number
|
||||
}
|
||||
/** 缓存用户注册事件
|
||||
* show 事件:如果useFloating时,show=true, 那么监听不到第一次show事件。 因为第一次show事件在usFloating内部就已经触发了
|
||||
* hide 事件:在动画结束后触发。【是否增加hiding 事件?】
|
||||
* update 事件: 每次定位完后触发。 该事件触发频繁,已观察到有以下情况:
|
||||
* 在 autoUpdate 时,会频繁触发。 比如切换显示,elementResize /IntersectionObserver 事件发生,内部会进入2次
|
||||
* 在reference 不可见时,每一秒会触发一次 update
|
||||
* */
|
||||
_events: { show: Function[]; hide: Function[]; update: Function[] }
|
||||
}
|
||||
|
||||
/** 默认配置 */
|
||||
const defaultOption: Partial<IFloatOption> = {
|
||||
reference: null,
|
||||
popper: null,
|
||||
show: false,
|
||||
autoUpdate: true,
|
||||
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom',
|
||||
offset: 6,
|
||||
arrowVisible: true,
|
||||
rootBoundary: 'viewport',
|
||||
boundary: 'clippingAncestors',
|
||||
boundaryPadding: 5,
|
||||
syncHide: true,
|
||||
autoHide: false,
|
||||
gpuAcceleration: false,
|
||||
animate: true,
|
||||
animateName: 'fade-in-linear',
|
||||
appendToBody: false,
|
||||
customClass: '',
|
||||
|
||||
flipAble: true,
|
||||
shiftAble: true
|
||||
}
|
||||
|
||||
const oppositeSidesMap = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }
|
||||
|
||||
const toMs = (s: string) => {
|
||||
if (s === 'auto') return 0
|
||||
return Number(s.slice(0, -1).replace(',', '.')) * 1000
|
||||
}
|
||||
|
||||
/** 获取元素的当前动画时长,参考 Vue的Transition 的源码实现。 注:无论css中单位是 ms/s, getComputedStyle返回的单位都是 s */
|
||||
const getTransitionInfo = (el: HTMLElement) => {
|
||||
const styles = window.getComputedStyle(el)
|
||||
// 先判断transition
|
||||
let timeout = toMs(styles.transitionDelay) + toMs(styles.transitionDuration)
|
||||
if (timeout) return timeout
|
||||
|
||||
// 再判断 animation
|
||||
timeout = toMs(styles.animationDelay) + toMs(styles.animationDuration)
|
||||
if (timeout) return timeout
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/** 包含多个类名的字符串赋值给元素的classList */
|
||||
const applyClass = (el: HTMLElement, classes: string, force: boolean) => {
|
||||
classes.split(' ').forEach((c) => c && el.classList.toggle(c, force))
|
||||
}
|
||||
|
||||
/** 执行一次 popper 的更新动作 */
|
||||
const updatePopper = (state: IFloatOption) => {
|
||||
// 官方建议offset居首, flip在shift前。 arrow,hide居后。
|
||||
const middleware = [offset(state.offset)]
|
||||
state.flipAble &&
|
||||
middleware.push(
|
||||
flip({
|
||||
rootBoundary: state.rootBoundary,
|
||||
boundary: state.boundary,
|
||||
padding: state.boundaryPadding
|
||||
})
|
||||
)
|
||||
state.shiftAble && middleware.push(shift({ limiter: limitShift() }))
|
||||
state.arrowVisible &&
|
||||
middleware.push(
|
||||
arrow({
|
||||
element: state.popper!.querySelector('.tiny-popper__arrow')!,
|
||||
padding: 8
|
||||
})
|
||||
)
|
||||
middleware.push(hide())
|
||||
|
||||
computePosition(state.reference!, state.popper!, {
|
||||
placement: state.placement,
|
||||
strategy: state.strategy,
|
||||
middleware
|
||||
}).then(({ x, y, placement, strategy, middlewareData }) => {
|
||||
// 自动关闭: 如果已经打开状态了,则本次重新定位,则关闭
|
||||
if (state.autoHide && state._last.show) {
|
||||
const timestamp = new Date().getTime()
|
||||
if (timestamp > state._last.timestamp + 300) {
|
||||
state.show = false
|
||||
return
|
||||
}
|
||||
}
|
||||
// 最终绑定给popper的样式
|
||||
const finalStyles: Record<string, string> = {}
|
||||
|
||||
// 定位策略
|
||||
Object.assign(finalStyles, {
|
||||
position: strategy
|
||||
})
|
||||
|
||||
// 位置:是否加速
|
||||
if (state.gpuAcceleration) {
|
||||
Object.assign(finalStyles, {
|
||||
transform: `translate(${x}px,${y}px)`,
|
||||
left: '0',
|
||||
top: '0'
|
||||
})
|
||||
} else {
|
||||
Object.assign(finalStyles, {
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
})
|
||||
}
|
||||
|
||||
// 是否hide
|
||||
if (state.syncHide) {
|
||||
if (middlewareData.hide) {
|
||||
Object.assign(finalStyles, {
|
||||
visibility: middlewareData.hide.referenceHidden ? 'hidden' : 'visible'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 应用样式
|
||||
Object.assign(state.popper!.style, finalStyles)
|
||||
|
||||
// 应用customClass
|
||||
if (state._last.customClass && state._last.customClass !== state.customClass) {
|
||||
applyClass(state.popper!, state._last.customClass, false)
|
||||
}
|
||||
if (state.customClass && state._last?.customClass !== state.customClass) {
|
||||
applyClass(state.popper!, state.customClass, true)
|
||||
state._last.customClass = state.customClass
|
||||
}
|
||||
|
||||
// 应用箭头
|
||||
if (state.arrowVisible) {
|
||||
const { x: arrowX, y: arrowY } = middlewareData.arrow
|
||||
const arrowElement = state._last.arrowEl!
|
||||
const staticSide = oppositeSidesMap[placement.split('-')[0]]
|
||||
|
||||
const arrowStyle = {
|
||||
left: arrowX !== null ? `${arrowX}px` : '',
|
||||
top: arrowY !== null ? `${arrowY}px` : '',
|
||||
right: '',
|
||||
bottom: '',
|
||||
[staticSide]: '-4px',
|
||||
display: 'block'
|
||||
}
|
||||
Object.assign(arrowElement.style, arrowStyle)
|
||||
} else {
|
||||
if (state._last!.arrowInserted) {
|
||||
state._last!.arrowEl.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
// 触发更新事件
|
||||
emit(state, 'update', { x, y, placement, strategy, middlewareData })
|
||||
})
|
||||
}
|
||||
|
||||
/** 执行自动更新 */
|
||||
const autoUpdatePopper = (state: IFloatOption) => {
|
||||
return autoUpdate(state.reference!, state.popper!, () => {
|
||||
updatePopper(state)
|
||||
})
|
||||
}
|
||||
|
||||
/** popper 插入body,或修改 display 可见。 */
|
||||
const appendPopper = (state: IFloatOption) => {
|
||||
// 如果已经打开了,且popper没变化,则忽略
|
||||
if (state._last.show && state._last.popper === state.popper) return
|
||||
|
||||
// 如果popper 变化了, 需要先移除_last.popper。
|
||||
if (state._last.popper && state._last.popper !== state.popper) {
|
||||
if (state._last.appendToBody) {
|
||||
state._last.popper.remove()
|
||||
} else {
|
||||
state._last.popper.style.display = 'none'
|
||||
}
|
||||
state._last.arrowInserted = false
|
||||
state._last.arrowEl = null as unknown as HTMLElement
|
||||
}
|
||||
|
||||
if (state.popper) {
|
||||
// 1、插入元素
|
||||
if (state.appendToBody) {
|
||||
document.body.append(state.popper)
|
||||
} else {
|
||||
state.popper.style.display = 'block'
|
||||
}
|
||||
|
||||
// 2、始终插入箭头元素,update时控制箭头的显隐。(如果不插入,只动态修改arrowVisible,进入不了appendPopper)
|
||||
if (!state._last!.arrowInserted) {
|
||||
const arrowEl = document.createElement('div')
|
||||
arrowEl.className = 'tiny-popper__arrow'
|
||||
state.popper.append(arrowEl)
|
||||
|
||||
state._last!.arrowInserted = true
|
||||
state._last!.arrowEl = arrowEl
|
||||
}
|
||||
|
||||
// 3、 添加动画类
|
||||
if (state.animate) {
|
||||
const enterName = `${state.animateName}-enter-from`
|
||||
const activeName = `${state.animateName}-enter-active`
|
||||
state.popper.classList.add(enterName, activeName)
|
||||
setTimeout(() => {
|
||||
state.popper!.classList.remove(enterName)
|
||||
}, 0)
|
||||
const timeout = getTransitionInfo(state.popper)
|
||||
setTimeout(() => {
|
||||
state.popper!.classList.remove(activeName)
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
// 4、触发事件
|
||||
emit(state, 'show')
|
||||
}
|
||||
}
|
||||
|
||||
/** popper 移除body,或修改 display 不可见 */
|
||||
const closePopper = (state: IFloatOption) => {
|
||||
// 如果已经关闭了,则忽略
|
||||
if (!state._last.show) return
|
||||
|
||||
if (state.popper) {
|
||||
// 如果有动画,动画结束后再移除
|
||||
if (state.animate && state.animateName) {
|
||||
const leaveName = `${state.animateName}-leave-to`
|
||||
const activeName = `${state.animateName}-leave-active`
|
||||
|
||||
state.popper.classList.add(leaveName, activeName)
|
||||
const timeout = getTransitionInfo(state.popper)
|
||||
setTimeout(() => {
|
||||
state.popper!.classList.remove(leaveName, activeName)
|
||||
|
||||
if (state.appendToBody) {
|
||||
state.popper!.remove()
|
||||
} else {
|
||||
state.popper!.style.display = 'none'
|
||||
}
|
||||
emit(state, 'hide')
|
||||
}, timeout)
|
||||
} else {
|
||||
// 否则直接移除
|
||||
if (state.appendToBody) {
|
||||
state.popper.remove()
|
||||
} else {
|
||||
state.popper.style.display = 'none'
|
||||
}
|
||||
emit(state, 'hide')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 触发事件 */
|
||||
const emit = (state: IFloatOption, eventName: string, params?: any) => {
|
||||
state._events[eventName].forEach((cb) => cb(params))
|
||||
}
|
||||
|
||||
/** 快速构建虚拟元素的辅助方法, 适于右键菜单,区域选择, 跟随光标等场景 */
|
||||
const virtualEl = (x: number, y: number, w: number = 0, h: number = 0) => ({
|
||||
getBoundingClientRect() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
x,
|
||||
y,
|
||||
top: y,
|
||||
left: x,
|
||||
right: x + w,
|
||||
bottom: y + h
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/** 响应式的弹出层管理函数,适用于场景: tooltip, poppover, select, 右键菜单, floatbar, notify, 或 canvas上跟随鼠标等 */
|
||||
export const useFloating = (option: Partial<IFloatOption> = {}) => {
|
||||
const state = reactive(option) as IFloatOption
|
||||
|
||||
let cleanup: null | (() => void) = null
|
||||
|
||||
// 0、标准化state
|
||||
Object.keys(defaultOption).forEach((key) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(state, key)) {
|
||||
state[key] = defaultOption[key]
|
||||
}
|
||||
})
|
||||
state._last = markRaw({}) as any
|
||||
state._events = markRaw({ show: [], hide: [], update: [] })
|
||||
|
||||
const watchState = () => {
|
||||
// 1、引用和弹窗同时存在
|
||||
if (state.popper && state.reference) {
|
||||
// 1.1 当前需要显示, 可能是show变化了,也可能是其它任意值变化了, 都需要重新的一次update
|
||||
if (state.show) {
|
||||
appendPopper(state)
|
||||
if (state.autoUpdate) {
|
||||
cleanup && cleanup()
|
||||
cleanup = autoUpdatePopper(state)
|
||||
} else {
|
||||
updatePopper(state)
|
||||
}
|
||||
}
|
||||
// 1.2 当前不需要显示
|
||||
else {
|
||||
cleanup && cleanup()
|
||||
closePopper(state)
|
||||
}
|
||||
}
|
||||
// 2、引用和弹窗不全。 可能前一次是全的,所以要释放一下
|
||||
else {
|
||||
cleanup && cleanup()
|
||||
closePopper(state)
|
||||
}
|
||||
|
||||
state._last.popper = state.popper
|
||||
state._last.reference = state.reference
|
||||
state._last.show = (state.show && state.popper && state.reference) as boolean // 真实的是否show变量
|
||||
state._last.appendToBody = state.appendToBody
|
||||
state._last.timestamp = new Date().getTime()
|
||||
}
|
||||
|
||||
watch(state, watchState, { immediate: true })
|
||||
|
||||
const on = (eventName, cb) => state._events[eventName].push(cb)
|
||||
const off = (eventName, cb) => (state._events[eventName] = state._events[eventName].filter((i) => i !== cb))
|
||||
|
||||
// 3、组件卸载前,移除元素
|
||||
onBeforeUnmount(() => {
|
||||
cleanup && cleanup()
|
||||
closePopper(state)
|
||||
})
|
||||
|
||||
// 4、返回state 及辅助方法
|
||||
// 正常修改state去触发更新,但如果某些业务想在state不变时,仍想执行一次更新, 则使用forceUpdate即可
|
||||
// 比如select 懒加载: popper, show都不变, 但popper 的大小变化了,可以forceUpdate一下。
|
||||
// 【autoUpdate 理论上会监听 popper的resize的, 这层考虑可能是多余。】
|
||||
return { state, on, off, virtualEl, forceUpdate: watchState }
|
||||
}
|
|
@ -289,7 +289,7 @@ export default {
|
|||
calcHash: 'Document is calculating encryption',
|
||||
uploadFile: 'Upload file',
|
||||
downloadAll: 'Download all',
|
||||
onlySupport: 'Only support {type} file',
|
||||
onlySupport: 'Support {type} file',
|
||||
fileNotLessThan: 'The size of single file cannot be less than ',
|
||||
fileNotMoreThan: 'The size of single file cannot be more than ',
|
||||
fileSizeRange: 'The size of a single file must range from {moreThan} to {lessThan}.',
|
||||
|
|
|
@ -291,7 +291,7 @@ export default {
|
|||
calcHash: '文档正在计算加密中',
|
||||
uploadFile: '文件上传',
|
||||
downloadAll: '全部下载',
|
||||
onlySupport: '仅支持{type}格式文件',
|
||||
onlySupport: '支持{type}格式文件',
|
||||
fileNotLessThan: '单个文件不能小于',
|
||||
fileNotMoreThan: '单个文件不能超过',
|
||||
fileSizeRange: '单个文件大小需在{moreThan}~{lessThan}之间',
|
||||
|
|
|
@ -17,7 +17,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formatWeeks: Function
|
||||
formatWeeks: Function,
|
||||
nowClick: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
return $setup({ props, context, template })
|
||||
|
|
|
@ -206,16 +206,18 @@
|
|||
</div>
|
||||
|
||||
<div data-tag="tiny-picker-panel__footer" :class="gcls('footer')" v-show="state.isShowFooter">
|
||||
<tiny-button
|
||||
size="mini"
|
||||
type="text"
|
||||
data-tag="tiny-picker-panel__link-btn"
|
||||
:class="gcls('link-btn')"
|
||||
@click="changeToNow"
|
||||
v-show="!['dates', 'years'].includes(state.selectionMode)"
|
||||
>
|
||||
{{ t('ui.datepicker.now') }}
|
||||
</tiny-button>
|
||||
<slot>
|
||||
<tiny-button
|
||||
size="mini"
|
||||
type="text"
|
||||
data-tag="tiny-picker-panel__link-btn"
|
||||
:class="gcls('link-btn')"
|
||||
@click="changeToNow"
|
||||
v-show="!['dates', 'years'].includes(state.selectionMode)"
|
||||
>
|
||||
{{ t('ui.datepicker.now') }}
|
||||
</tiny-button>
|
||||
</slot>
|
||||
<tiny-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
|
@ -282,7 +284,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formatWeeks: Function
|
||||
formatWeeks: Function,
|
||||
nowClick: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
emits: ['pick', 'select-change', 'dodestroy'],
|
||||
setup(props, context) {
|
||||
|
|
|
@ -210,15 +210,17 @@
|
|||
|
||||
<!-- 此刻/确认 -->
|
||||
<div class="tiny-picker-panel__footer" v-show="state.isShowFooter">
|
||||
<tiny-button
|
||||
size="mini"
|
||||
type="text"
|
||||
class="tiny-picker-panel__link-btn"
|
||||
@click="changeToNow"
|
||||
v-show="!['dates', 'years'].includes(state.selectionMode)"
|
||||
>
|
||||
{{ t('ui.datepicker.now') }}
|
||||
</tiny-button>
|
||||
<slot>
|
||||
<tiny-button
|
||||
size="mini"
|
||||
type="text"
|
||||
class="tiny-picker-panel__link-btn"
|
||||
@click="changeToNow"
|
||||
v-show="!['dates', 'years'].includes(state.selectionMode)"
|
||||
>
|
||||
{{ t('ui.datepicker.now') }}
|
||||
</tiny-button>
|
||||
</slot>
|
||||
<tiny-button type="primary" size="mini" class="tiny-picker-panel__link-btn" @click="confirm">
|
||||
{{ t('ui.datepicker.confirm') }}
|
||||
</tiny-button>
|
||||
|
@ -284,6 +286,9 @@ export default defineComponent({
|
|||
timeEditable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
nowClick: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
emits: ['pick', 'select-change', 'dodestroy'],
|
||||
|
|
|
@ -215,6 +215,9 @@ export const datePickerProps = {
|
|||
changeCompat: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
nowClick: {
|
||||
type: Function
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -288,6 +288,10 @@ export const fileUploadProps = {
|
|||
watermark: ''
|
||||
})
|
||||
},
|
||||
promptTip: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isHidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
|
|
@ -84,7 +84,8 @@ export default defineComponent({
|
|||
'lockScroll',
|
||||
'compact',
|
||||
'encryptConfig',
|
||||
'imageBgColor'
|
||||
'imageBgColor',
|
||||
'promptTip'
|
||||
],
|
||||
setup(props, context) {
|
||||
return setup({
|
||||
|
@ -182,7 +183,8 @@ export default defineComponent({
|
|||
compact,
|
||||
encryptConfig,
|
||||
encryptDialogConfirm,
|
||||
imageBgColor
|
||||
imageBgColor,
|
||||
promptTip
|
||||
} = this
|
||||
|
||||
const listType = this.listType === 'saas' ? 'text' : this.listType
|
||||
|
@ -481,7 +483,8 @@ export default defineComponent({
|
|||
mode,
|
||||
showTitle,
|
||||
isHwh5,
|
||||
tipMessage
|
||||
tipMessage,
|
||||
promptTip
|
||||
},
|
||||
ref: 'upload-inner'
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ import {
|
|||
iconSuccessful,
|
||||
iconClose,
|
||||
iconFileCloudupload,
|
||||
iconPlus
|
||||
iconPlus,
|
||||
iconHelpCircle
|
||||
} from '@opentiny/vue-icon'
|
||||
import CryptoJS from 'crypto-js/core.js'
|
||||
import 'crypto-js/sha256.js'
|
||||
|
@ -46,6 +47,7 @@ const TinyIconCloseCircle = iconClose()
|
|||
const TinyIconDownload = iconDownload()
|
||||
const TinyIconFileCloudupload = iconFileCloudupload()
|
||||
const TinyIconPlus = iconPlus()
|
||||
const TinyIconHelpCircle = iconHelpCircle()
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
|
@ -88,7 +90,8 @@ export default defineComponent({
|
|||
'title',
|
||||
'showTitle',
|
||||
'displayOnly',
|
||||
'compact'
|
||||
'compact',
|
||||
'promptTip'
|
||||
],
|
||||
setup(props, context) {
|
||||
// 内置crypto-js和streamsaver进行上传下载
|
||||
|
@ -150,7 +153,8 @@ export default defineComponent({
|
|||
handleFileClick,
|
||||
displayOnly,
|
||||
listType,
|
||||
compact
|
||||
compact,
|
||||
promptTip
|
||||
} = this
|
||||
const isPictureCard = listType === 'picture-card'
|
||||
const isSaasType = listType === 'saas'
|
||||
|
@ -172,16 +176,10 @@ export default defineComponent({
|
|||
const getTriggerContent = (t: any, disabled: boolean) => {
|
||||
return (
|
||||
<div class="trigger-btn">
|
||||
<tiny-tooltip
|
||||
effect="light"
|
||||
content={(slots.tip && slots.tip()) || tipMsg}
|
||||
placement="top"
|
||||
popper-options={popperConfig}>
|
||||
<tiny-button disabled={disabled} onClick={handleTriggerClick}>
|
||||
<TinyIconPlus />
|
||||
<span>{t('ui.fileUpload.uploadFile')}</span>
|
||||
</tiny-button>
|
||||
</tiny-tooltip>
|
||||
<tiny-button disabled={disabled} onClick={handleTriggerClick}>
|
||||
<TinyIconPlus />
|
||||
<span>{t('ui.fileUpload.uploadFile')}</span>
|
||||
</tiny-button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -202,13 +200,30 @@ export default defineComponent({
|
|||
|
||||
// 提示信息插槽
|
||||
const getDefaultTip = (tipMsg) => {
|
||||
return (
|
||||
<div class="tip-wrap">
|
||||
<div title={tipMsg} class="tip-content">
|
||||
{(slots.tip && slots.tip()) || tipMsg}
|
||||
if (promptTip) {
|
||||
return (
|
||||
(slots.tip && slots.tip()) ||
|
||||
(tipMsg && promptTip && (
|
||||
<tiny-tooltip
|
||||
effect="light"
|
||||
content={(slots.tip && slots.tip()) || tipMsg}
|
||||
placement="right"
|
||||
popper-options={popperConfig}>
|
||||
<div class="prompt-tip">
|
||||
<TinyIconHelpCircle />
|
||||
</div>
|
||||
</tiny-tooltip>
|
||||
))
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div class="tip-wrap">
|
||||
<div title={tipMsg} class="tip-content">
|
||||
{(slots.tip && slots.tip()) || tipMsg}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getThumIcon = (file) => [
|
||||
|
|
|
@ -69,10 +69,11 @@
|
|||
</tiny-option>
|
||||
</tiny-select>
|
||||
</div>
|
||||
<!-- 勿同步,tiny侧search组件change事件需要按回车触发 -->
|
||||
<tiny-search
|
||||
v-if="search"
|
||||
v-model="searchValue"
|
||||
@change="searchChange"
|
||||
@input="searchChange"
|
||||
:placeholder="t('ui.grid.individuation.toolbar.search')"
|
||||
></tiny-search>
|
||||
<div v-if="isGroup">
|
||||
|
@ -916,7 +917,8 @@ export default defineComponent({
|
|||
selectFocus(event, index) {
|
||||
this.lastSelectIndex = index
|
||||
},
|
||||
searchChange(key, val) {
|
||||
// 勿同步,search组件input事件第一个参数就是val
|
||||
searchChange(val) {
|
||||
const getRenderedTitle = (col) => {
|
||||
let result = ''
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import {
|
|||
import Dropdown from '@opentiny/vue-dropdown'
|
||||
import DropdownMenu from '@opentiny/vue-dropdown-menu'
|
||||
import DropdownItem from '@opentiny/vue-dropdown-item'
|
||||
import { handleActivedCanActive } from '../../edit/src/utils/handleActived'
|
||||
|
||||
const insertedField = GLOBAL_CONFIG.constant.insertedField
|
||||
|
||||
|
@ -829,7 +830,13 @@ export const Cell = {
|
|||
renderRowEdit(h, params) {
|
||||
let { actived } = params.$table.editStore
|
||||
|
||||
return Cell.runRenderer(h, params, this, actived && actived.row === params.row)
|
||||
const { editConfig } = params.$table
|
||||
return Cell.runRenderer(
|
||||
h,
|
||||
params,
|
||||
this,
|
||||
actived && actived.row === params.row && handleActivedCanActive({ editConfig, params })
|
||||
)
|
||||
},
|
||||
renderTreeCellEdit(h, params) {
|
||||
return Cell.renderTreeIcon(h, params).concat(Cell.renderCellEdit(h, params))
|
||||
|
|
|
@ -29,7 +29,7 @@ export function handleActivedCheckCell({ actived, column, editConfig, row }) {
|
|||
}
|
||||
|
||||
export function handleActivedCanActive({ editConfig, params }) {
|
||||
return !editConfig.activeMethod || editConfig.activeMethod(params)
|
||||
return !editConfig?.activeMethod || editConfig.activeMethod(params)
|
||||
}
|
||||
|
||||
export function handleActivedDoActive({
|
||||
|
|
|
@ -291,7 +291,7 @@
|
|||
state.inputSizeMf !== 'mini' ? 'sm:text-sm' : 'sm:text-xs',
|
||||
hoverExpand && 'relative left-0 max-w-full leading-normal line-clamp-1',
|
||||
autosize
|
||||
? 'left-0 max-w-full absolute break-words whitespace-pre-line leading-normal'
|
||||
? 'left-0 max-w-full break-words whitespace-pre-line leading-normal'
|
||||
: 'left-0 max-w-full text-ellipsis overflow-hidden break-words whitespace-pre-wrap line-clamp-5'
|
||||
]"
|
||||
@click="state.showDisplayOnlyBox = true"
|
||||
|
@ -426,8 +426,7 @@ export default defineComponent({
|
|||
'popupMore',
|
||||
'showTooltip',
|
||||
'frontClearIcon',
|
||||
'hoverExpand',
|
||||
'showTooltip'
|
||||
'hoverExpand'
|
||||
],
|
||||
setup(props, context): any {
|
||||
return setup({ props, context, renderless, api })
|
||||
|
|
|
@ -188,12 +188,15 @@
|
|||
:step="step"
|
||||
:show-week-number="showWeekNumber"
|
||||
:format-weeks="formatWeeks"
|
||||
:now-click="nowClick"
|
||||
ref="picker"
|
||||
:visible="state.pickerVisible"
|
||||
@pick="handlePick"
|
||||
@select-range="handleSelectRange"
|
||||
@select-change="handleSelectChange"
|
||||
></component>
|
||||
>
|
||||
<slot name="now"></slot>
|
||||
</component>
|
||||
<!-- 小屏 - 日期面板 -->
|
||||
<tiny-date-picker-mobile
|
||||
v-if="state.isMobileMode && state.isDateMobileComponent"
|
||||
|
|
|
@ -142,12 +142,15 @@
|
|||
:show-week-number="showWeekNumber"
|
||||
:time-editable="timeEditable"
|
||||
:format-weeks="formatWeeks"
|
||||
:now-click="nowClick"
|
||||
ref="picker"
|
||||
:visible="state.pickerVisible"
|
||||
@pick="handlePick"
|
||||
@select-range="handleSelectRange"
|
||||
@select-change="handleSelectChange"
|
||||
></component>
|
||||
>
|
||||
<slot name="now"></slot>
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -139,5 +139,8 @@ export const pickerProps = {
|
|||
changeCompat: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
nowClick: {
|
||||
type: Function
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,10 @@ export const uploadProps = {
|
|||
tipMessage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
promptTip: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,15 @@ import { renderless, api } from '@opentiny/vue-renderless/upload/vue'
|
|||
import UploadDragger from '@opentiny/vue-upload-dragger'
|
||||
import Modal from '@opentiny/vue-modal'
|
||||
import Tooltip from '@opentiny/vue-tooltip'
|
||||
import { iconHelpCircle } from '@opentiny/vue-icon'
|
||||
import type { IUploadApi } from '@opentiny/vue-renderless/types/upload.type'
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
name: $prefix + 'Upload',
|
||||
components: {
|
||||
TinyTooltip: Tooltip
|
||||
TinyTooltip: Tooltip,
|
||||
TinyIconHelpCircle: iconHelpCircle()
|
||||
},
|
||||
props: [
|
||||
...props,
|
||||
|
@ -49,7 +51,8 @@ export default defineComponent({
|
|||
'mode',
|
||||
'showTitle',
|
||||
'isHwh5',
|
||||
'tipMessage'
|
||||
'tipMessage',
|
||||
'promptTip'
|
||||
],
|
||||
setup(props, context) {
|
||||
return setup({ props, context, renderless, api, h, extendOptions: { Modal } }) as unknown as IUploadApi
|
||||
|
@ -72,7 +75,8 @@ export default defineComponent({
|
|||
mode,
|
||||
showTitle,
|
||||
state,
|
||||
tipMessage
|
||||
tipMessage,
|
||||
promptTip
|
||||
} = this as any
|
||||
|
||||
const defaultSlot = (this as any).slots.default && (this as any).slots.default()
|
||||
|
@ -114,7 +118,7 @@ export default defineComponent({
|
|||
</div>
|
||||
)}
|
||||
{state.currentBreakpoint !== 'default' && (
|
||||
<tiny-tooltip effect="light" content={tipMessage} placement="top" popper-options={popperConfig}>
|
||||
<div class="hidden sm:inline-flex sm:items-center">
|
||||
<div
|
||||
data-tag="tiny-upload-drag-single"
|
||||
class="h-full"
|
||||
|
@ -129,7 +133,19 @@ export default defineComponent({
|
|||
defaultSlot
|
||||
)}
|
||||
</div>
|
||||
</tiny-tooltip>
|
||||
{promptTip && tipMessage && (
|
||||
<tiny-tooltip effect="light" content={tipMessage} placement="right" popper-options={popperConfig}>
|
||||
<tiny-icon-help-circle custom-class="ml-2 cursor-pointer fill-color-icon-tertiary"></tiny-icon-help-circle>
|
||||
</tiny-tooltip>
|
||||
)}
|
||||
{!promptTip && tipMessage && (
|
||||
<div
|
||||
title={tipMessage}
|
||||
class="hidden sm:block text-xs leading-4 overflow-hidden text-ellipsis whitespace-nowrap text-color-text-placeholder ml-2 cursor-pointer">
|
||||
{tipMessage}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{operateSlot}
|
||||
<input
|
||||
|
|
|
@ -85,6 +85,7 @@ export default defineComponent({
|
|||
|
||||
const defaultSlot = (this.slots.default && this.slots.default()) || []
|
||||
const operateSlot = this.slots.operate && this.slots.operate()
|
||||
const tipSlot = this.slots.tip && this.slots.tip()
|
||||
|
||||
const hidden = isHidden && fileList.length >= limit
|
||||
|
||||
|
@ -106,6 +107,7 @@ export default defineComponent({
|
|||
)}
|
||||
</div>
|
||||
{operateSlot}
|
||||
{tipSlot}
|
||||
<input
|
||||
class="tiny-upload__input"
|
||||
type="file"
|
||||
|
|
Loading…
Reference in New Issue