feat: update vue-docs (#1810)

This commit is contained in:
ajaxzheng 2024-08-01 17:45:27 +08:00 committed by GitHub
parent 04bf0aea3b
commit 0811cb12e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
305 changed files with 5013 additions and 1306 deletions

View File

@ -434,6 +434,20 @@ export default {
mode: ['pc'],
pcDemo: ''
},
{
name: 'footer-buttons',
type: 'Slot',
defaultValue: '',
desc: {
'zh-CN': '自定义弹窗底部按钮',
'en-US': 'Custom Pop Up Bottom buttons'
},
metaData: {
new: '3.18.0'
},
mode: ['pc'],
pcDemo: ''
},
{
name: 'option',
type: 'Slot',

View File

@ -228,6 +228,22 @@ export default {
},
mode: ['pc'],
pcDemo: 'visible-arrow'
},
{
name: 'lazy-show-popper',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否懒加载下拉菜单及内部的项,以优化性能,默认初始全加载菜单及内部项。',
'en-US':
'Indicates whether to lazily load the drop-down menu and internal items to optimize performance. The default value is false. The menu and internal items are loaded initially.'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'lazy-show-popper',
mfDemo: '',
metaData: {
experimental: '3.18.0'
}
}
],
events: [

View File

@ -262,7 +262,8 @@ export default {
'zh-CN': '限制文件大小,单位为 KB当为 Number 类型时,小于该值停止上传;为数组时[min,max] 设置上传范围',
'en-US': ''
},
mode: ['mobile-first'],
mode: ['pc', 'mobile-first'],
pcDemo: 'file-size',
mfDemo: ''
},
{

View File

@ -1073,6 +1073,19 @@ export default {
}
],
events: [
{
name: 'after-refresh-column',
type: '()=> void',
defaultValue: '',
desc: {
'zh-CN': '在新增或者删除列后,列配置是异步更新的,列配置刷新后触发的回调',
'en-US':
'After adding or deleting a column, the column configuration is updated asynchronously, and the callback is triggered after the column configuration is refreshed.'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'grid-dynamically-columns#column-switching-scroll',
mfDemo: ''
},
{
name: 'before-page-change',
typeAnchorName: 'IBeforePageChangeArgs',

View File

@ -175,7 +175,7 @@ interface IDomData {
//销毁的回调函数
destroy: () => void
// 完成的回调函数
completey: () => void
complete: () => void
// 需要设置的按钮组
button: {

View File

@ -396,6 +396,21 @@ export default {
mobileDemo: 'counter',
mfDemo: ''
},
{
name: 'show-tooltip',
type: 'boolean',
defaultValue: 'true',
metaData: {
new: '3.18.0'
},
desc: {
'zh-CN': '只读状态下,文本超出是否悬浮提示',
'en-US': 'In the read-only state, whether the text exceeds the floating prompt'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'size',
mfDemo: ''
},
{
name: 'size',
type: "'medium' | 'small' | 'mini'",

View File

@ -491,6 +491,17 @@ export default {
},
mode: ['pc'],
pcDemo: 'edit'
},
{
name: 'highlight-query',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '通过 <code> highlightQuery </code> 属性,是否在匹配的节点中,高亮搜索文字。<br>',
'en-US': 'Indicates whether to highlight the search text in the matched node.'
},
mode: ['pc'],
pcDemo: 'filter-view'
}
],
events: [

View File

@ -0,0 +1,19 @@
<template>
<tiny-file-upload ref="upload" :action="action" accept=".doc,.docx" :file-list="fileList" :file-size="[100, 200]" />
</template>
<script>
import { FileUpload } from '@opentiny/vue'
export default {
components: {
TinyFileUpload: FileUpload
},
data() {
return {
action: 'http://localhost:3000/api/upload',
fileList: []
}
}
}
</script>

View File

@ -0,0 +1,76 @@
<template>
<tiny-file-upload ref="upload" :action="action" list-type="text" :file-list="fileList" :file-size="100" />
</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>

View File

@ -38,6 +38,30 @@ export default {
},
codeFiles: ['file-list.vue']
},
{
demoId: 'file-size',
name: {
'zh-CN': '上传的文件大小限制',
'en-US': 'Uploaded file size limit'
},
desc: {
'zh-CN': '<p>通过 <code>file-size</code> 配置上传文件的大小。<p>',
'en-US': '<p>Use <code>file-size</code> to configure the size of the uploaded file.</p>'
},
codeFiles: ['file-size.vue']
},
{
demoId: 'file-size-array',
name: {
'zh-CN': '上传的文件大小范围',
'en-US': 'Uploaded file size range'
},
desc: {
'zh-CN': '<p>通过 <code>file-size</code> 配置为数组类型限制上传文件的大小范围。<p>',
'en-US': '<p>Set <code>file-size</code> to an array to limit the size of the file to be uploaded.</p>'
},
codeFiles: ['file-size-array.vue']
},
{
demoId: 'show-title',
name: {

View File

@ -5,6 +5,7 @@
<tiny-radio v-model="viewType" label="card">卡片视图</tiny-radio>
<tiny-radio v-model="viewType" label="list">列表视图</tiny-radio>
<tiny-radio v-model="viewType" label="gantt">甘特视图</tiny-radio>
<tiny-radio v-model="viewType" label="custom">custom视图</tiny-radio>
</div>
<tiny-grid
highlight-current-row
@ -68,7 +69,10 @@
</div>
</template>
<template #gantt="{ rows }">
<div class="gantt-container">{{ rows.length }}</div>
<div class="gantt-container">gantt视图表格行数{{ rows.length }}</div>
</template>
<template #custom="{ rows }">
<div class="custom-container">custom视图表格行数{{ rows.length }}</div>
</template>
</tiny-grid>
</div>

View File

@ -0,0 +1,226 @@
<template>
<div class="cascader-panel-demo-props">
<div>
选中值<span>{{ value }}</span>
</div>
<tiny-cascader-panel
v-model="value"
:options="optionsCascader"
:props="{
multiple: true
}"
></tiny-cascader-panel>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { CascaderPanel as TinyCascaderPanel } from '@opentiny/vue'
const value = ref([
['zhinan', 'anzhuang', 'xiangmudengji'],
['zhinan', 'anzhuang', 'huanjingzhunbei'],
['zhinan', 'anzhuang', 'anzhuangcli'],
['zhinan', 'kaifa', 'monishuju']
])
const optionsCascader = ref([
{
value: 'zhinan',
label: '指南',
children: [
{
value: 'anzhuang',
label: '安装',
children: [
{
value: 'xiangmudengji',
label: '项目登记'
},
{
value: 'huanjingzhunbei',
label: '环境准备'
},
{
value: 'anzhuangcli',
label: '安装 CLI'
},
{
value: 'chuangjianxiangmu',
label: '创建项目'
}
]
},
{
value: 'kaifa',
label: '开发',
children: [
{
value: 'yinruzujian',
label: '引入组件'
},
{
value: 'monishuju',
label: '模拟数据'
}
]
}
]
},
{
value: 'zujian',
label: '组件',
children: [
{
value: 'basic',
label: '框架风格',
children: [
{
value: 'layout',
label: 'Layout 布局'
},
{
value: 'color',
label: 'Color 色彩'
},
{
value: 'font',
label: 'Font 字体'
},
{
value: 'icon',
label: 'Icon 图标'
}
]
},
{
value: 'form',
label: '表单组件',
children: [
{
value: 'radio',
label: 'Radio 单选框'
},
{
value: 'checkbox',
label: 'Checkbox 多选框'
},
{
value: 'input',
label: 'Input 输入框'
},
{
value: 'number',
label: 'Numeric 计数器'
},
{
value: 'select',
label: 'Select 选择器'
},
{
value: 'cascader',
label: 'Cascader 级联选择器'
},
{
value: 'switch',
label: 'Switch 开关'
},
{
value: 'slider',
label: 'Slider 滑块'
},
{
value: 'time-picker',
label: 'TimePicker 时间选择器'
},
{
value: 'date-picker',
label: 'DatePicker 日期选择器'
},
{
value: 'form',
label: 'Form 表单'
}
]
},
{
value: 'data',
label: '数据组件',
children: [
{
value: 'tree',
label: 'Tree 树形控件'
},
{
value: 'pager',
label: 'Pager 分页'
}
]
},
{
value: 'notice',
label: '提示组件',
children: [
{
value: 'alert',
label: 'Alert 警告'
},
{
value: 'loading',
label: 'Loading 加载'
}
]
},
{
value: 'navigation',
label: '导航组件',
children: [
{
value: 'menu',
label: 'NavMenu 导航菜单'
},
{
value: 'tabs',
label: 'Tabs 标签页'
},
{
value: 'breadcrumb',
label: 'Breadcrumb 面包屑'
},
{
value: 'steps',
label: 'Steps 步骤条'
}
]
},
{
value: 'others',
label: '其他组件',
children: [
{
value: 'rate',
label: 'Rate 评分'
},
{
value: 'tag',
label: 'Tag 标签'
},
{
value: 'usercontact',
label: 'UserContact 联系人'
},
{
value: 'slidebar',
label: 'SlideBar 滚动块'
}
]
}
]
}
])
</script>
<style scoped>
.cascader-panel-demo-props > :not(:last-child) {
margin-bottom: 12px;
}
</style>

View File

@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test'
test('多选', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('cascader-panel#multiple')
const multipleText1 = page.getByText(
'选中值:[ [ "zhinan", "anzhuang", "xiangmudengji" ], [ "zhinan", "anzhuang", "huanjingzhunbei" ], [ "zhinan", "anzhuang", "anzhuangcli" ], [ "zhinan", "kaifa", "monishuju" ] ]'
)
await expect(multipleText1).toBeVisible()
await page.getByRole('menuitem', { name: '创建项目' }).locator('span').nth(1).click()
const multipleText2 = page.getByText(
'选中值:[ [ "zhinan", "anzhuang", "xiangmudengji" ], [ "zhinan", "anzhuang", "huanjingzhunbei" ], [ "zhinan", "anzhuang", "anzhuangcli" ], [ "zhinan", "anzhuang", "chuangjianxiangmu" ], [ "zhinan", "kaifa", "monishuju" ] ]'
)
await expect(multipleText2).toBeVisible()
})

View File

@ -0,0 +1,234 @@
<template>
<div class="cascader-panel-demo-props">
<div>
选中值<span>{{ value }}</span>
</div>
<tiny-cascader-panel
v-model="value"
:options="optionsCascader"
:props="{
multiple: true
}"
></tiny-cascader-panel>
</div>
</template>
<script>
import { CascaderPanel } from '@opentiny/vue'
export default {
components: {
TinyCascaderPanel: CascaderPanel
},
data() {
return {
value: [
['zhinan', 'anzhuang', 'xiangmudengji'],
['zhinan', 'anzhuang', 'huanjingzhunbei'],
['zhinan', 'anzhuang', 'anzhuangcli'],
['zhinan', 'kaifa', 'monishuju']
],
optionsCascader: [
{
value: 'zhinan',
label: '指南',
children: [
{
value: 'anzhuang',
label: '安装',
children: [
{
value: 'xiangmudengji',
label: '项目登记'
},
{
value: 'huanjingzhunbei',
label: '环境准备'
},
{
value: 'anzhuangcli',
label: '安装 CLI'
},
{
value: 'chuangjianxiangmu',
label: '创建项目'
}
]
},
{
value: 'kaifa',
label: '开发',
children: [
{
value: 'yinruzujian',
label: '引入组件'
},
{
value: 'monishuju',
label: '模拟数据'
}
]
}
]
},
{
value: 'zujian',
label: '组件',
children: [
{
value: 'basic',
label: '框架风格',
children: [
{
value: 'layout',
label: 'Layout 布局'
},
{
value: 'color',
label: 'Color 色彩'
},
{
value: 'font',
label: 'Font 字体'
},
{
value: 'icon',
label: 'Icon 图标'
}
]
},
{
value: 'form',
label: '表单组件',
children: [
{
value: 'radio',
label: 'Radio 单选框'
},
{
value: 'checkbox',
label: 'Checkbox 多选框'
},
{
value: 'input',
label: 'Input 输入框'
},
{
value: 'number',
label: 'Numeric 计数器'
},
{
value: 'select',
label: 'Select 选择器'
},
{
value: 'cascader',
label: 'Cascader 级联选择器'
},
{
value: 'switch',
label: 'Switch 开关'
},
{
value: 'slider',
label: 'Slider 滑块'
},
{
value: 'time-picker',
label: 'TimePicker 时间选择器'
},
{
value: 'date-picker',
label: 'DatePicker 日期选择器'
},
{
value: 'form',
label: 'Form 表单'
}
]
},
{
value: 'data',
label: '数据组件',
children: [
{
value: 'tree',
label: 'Tree 树形控件'
},
{
value: 'pager',
label: 'Pager 分页'
}
]
},
{
value: 'notice',
label: '提示组件',
children: [
{
value: 'alert',
label: 'Alert 警告'
},
{
value: 'loading',
label: 'Loading 加载'
}
]
},
{
value: 'navigation',
label: '导航组件',
children: [
{
value: 'menu',
label: 'NavMenu 导航菜单'
},
{
value: 'tabs',
label: 'Tabs 标签页'
},
{
value: 'breadcrumb',
label: 'Breadcrumb 面包屑'
},
{
value: 'steps',
label: 'Steps 步骤条'
}
]
},
{
value: 'others',
label: '其他组件',
children: [
{
value: 'rate',
label: 'Rate 评分'
},
{
value: 'tag',
label: 'Tag 标签'
},
{
value: 'usercontact',
label: 'UserContact 联系人'
},
{
value: 'slidebar',
label: 'SlideBar 滚动块'
}
]
}
]
}
]
}
}
}
</script>
<style scoped>
.cascader-panel-demo-props > :not(:last-child) {
margin-bottom: 12px;
}
</style>

View File

@ -30,6 +30,18 @@ export default {
},
codeFiles: ['custom-option-content.vue']
},
{
demoId: 'multiple',
name: {
'zh-CN': '多选',
'en-US': 'Multiple Choices'
},
desc: {
'zh-CN': '<p>通过 <code>props.multiple = true</code> 来开启多选模式。</p>\n',
'en-US': '<p>Use <code>props.multiple = true</code> to enable the multi-selection mode. </p>\n'
},
codeFiles: ['multiple.vue']
},
{
demoId: 'cascader-panel-props',
name: {

View File

@ -0,0 +1,52 @@
<template>
<div>
<div>
关键字<tiny-input v-model="query" placeholder="输入关键字,观察下面的高亮"></tiny-input> &nbsp;
<tiny-button @click="changeList">修改内容</tiny-button>
</div>
<div>避免场景1 直接包含文字节点</div>
<div v-highlight-query="query">
{{ list.join(',') }}
</div>
<br />
<div>避免场景2文字节点与其它组件混合</div>
<div v-highlight-query="query">
{{ list.join(',') }}
<tiny-input></tiny-input>
</div>
<br />
<div>正确的场景1</div>
<div v-highlight-query="query">
<span> {{ list.join(',') }}</span>
</div>
<br />
<div>正确的场景2</div>
<div v-highlight-query="query">
<span> {{ list.join(',') }}</span>
<tiny-input></tiny-input>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Input as TinyInput, Button as TinyButton } from '@opentiny/vue'
import { HighlightQuery as vHighlightQuery } from '@opentiny/vue-directive'
const query = ref('一片')
const list = ref(['一片一片又一片', '两片三片四五片', '六片七片八九片', '飞入芦花都不见'])
const changeList = () => (list.value = ['江上一笼统', '井上黑窟窿', '黄狗身上白', '白狗身上肿'])
</script>
<style scoped>
.tiny-input {
width: 280px;
margin-bottom: 20px;
}
div {
line-height: 2;
}
</style>

View File

@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test'
test('避免用法', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('directives-highlight-query#avoid')
const input = page.locator('.pc-demo-container .tiny-input-inner').first()
const hlNode = page.locator('.pc-demo-container .tiny-hl-query-node')
const button = page.locator('.pc-demo-container .tiny-button')
await expect(hlNode).toHaveCount(12)
await button.click()
await expect(hlNode).toHaveCount(6)
})

View File

@ -0,0 +1,65 @@
<template>
<div>
<div>
关键字<tiny-input v-model="query" placeholder="输入关键字,观察下面的高亮"></tiny-input> &nbsp;
<tiny-button @click="changeList">修改内容</tiny-button>
</div>
<div>避免场景1 直接包含文字节点</div>
<div v-highlight-query="query">
{{ list.join(',') }}
</div>
<br />
<div>避免场景2文字节点与其它组件混合</div>
<div v-highlight-query="query">
{{ list.join(',') }}
<tiny-input></tiny-input>
</div>
<br />
<div>正确的场景1</div>
<div v-highlight-query="query">
<span> {{ list.join(',') }}</span>
</div>
<br />
<div>正确的场景2</div>
<div v-highlight-query="query">
<span> {{ list.join(',') }}</span>
<tiny-input></tiny-input>
</div>
</div>
</template>
<script>
import { HighlightQuery } from '@opentiny/vue-directive'
import { Input, Button } from '@opentiny/vue'
export default {
directives: { HighlightQuery },
components: {
TinyInput: Input,
TinyButton: Button
},
data() {
return {
query: '一片',
list: ['一片一片又一片', '两片三片四五片', '六片七片八九片', '飞入芦花都不见']
}
},
methods: {
changeList() {
this.list = ['江上一笼统', '井上黑窟窿', '黄狗身上白', '白狗身上肿']
}
}
}
</script>
<style scoped>
.tiny-input {
width: 280px;
margin-bottom: 20px;
}
div {
line-height: 2;
}
</style>

View File

@ -0,0 +1,31 @@
<template>
<div>
<div>关键字<tiny-input v-model="query" placeholder="输入关键字,观察下面的高亮"></tiny-input></div>
<div v-highlight-query="query">
<ul>
<li v-for="line in list" :key="line">
{{ line }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Input as TinyInput } from '@opentiny/vue'
import { HighlightQuery as vHighlightQuery } from '@opentiny/vue-directive'
const query = ref('')
const list = ref(['一片一片又一片', '两片三片四五片', '六片七片八九片', '飞入芦花都不见'])
</script>
<style scoped>
.tiny-input {
width: 280px;
margin-bottom: 20px;
}
li {
line-height: 2;
}
</style>

View File

@ -0,0 +1,12 @@
import { test, expect } from '@playwright/test'
test('基本用法', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('directives-highlight-query#basic-usage')
const input = page.locator('.pc-demo-container .tiny-input__inner')
const hlNode = page.locator('.pc-demo-container .tiny-hl-query-node')
await input.fill('一片')
await expect(hlNode).toHaveCount(3)
})

View File

@ -0,0 +1,40 @@
<template>
<div>
<div>关键字<tiny-input v-model="query" placeholder="输入关键字,观察下面的高亮"></tiny-input></div>
<div v-highlight-query="query">
<ul>
<li v-for="line in list" :key="line">
{{ line }}
</li>
</ul>
</div>
</div>
</template>
<script>
import { HighlightQuery } from '@opentiny/vue-directive'
import { Input } from '@opentiny/vue'
export default {
directives: { HighlightQuery },
components: {
TinyInput: Input
},
data() {
return {
query: '',
list: ['一片一片又一片', '两片三片四五片', '六片七片八九片', '飞入芦花都不见']
}
}
}
</script>
<style scoped>
.tiny-input {
width: 280px;
margin-bottom: 20px;
}
li {
line-height: 2;
}
</style>

View File

@ -0,0 +1,7 @@
---
title: HighlightQuery 高亮搜索字
---
# HighlightQuery 高亮搜索字
<div>HighlightQuery 高亮搜索字指令,用于高亮区个区域的匹配字符。</div>

View File

@ -0,0 +1,7 @@
---
title: HighlightQuery Highlight search word
---
# HighlightQuery Highlight search word
<div>HighlightQuery Highlight search word command, which is used to match characters in each area of the highlight area. </div>

View File

@ -0,0 +1,39 @@
export default {
column: '2',
owner: '',
metaData: {
stable: '3.18.0'
},
versionTipOption: {
stages: ['stable']
},
demos: [
{
demoId: 'basic-usage',
name: {
'zh-CN': '基本用法',
'en-US': 'Basic Usage'
},
desc: {
'zh-CN': '通过自动高亮搜索字指令,可以自动高亮某个节点下,所有匹配的字符。',
'en-US':
'You can use the automatic highlight search word command to automatically highlight all matched characters under a node.'
},
codeFiles: ['highlight-query/basic-usage.vue']
},
{
demoId: 'avoid',
name: {
'zh-CN': '避免场景',
'en-US': 'Basic Usage'
},
desc: {
'zh-CN':
'纯文字节点在<code>Vue</code> 编译时有特殊处理。自动高亮搜索字的指令是直接处理<code>Dom</code>节点的内容所以要避免纯文本节点。以下2个场景会造成<code>Vue</code> 更新机制失败。',
'en-US':
'Plain text nodes are specially processed during <code>Vue</code> compilation. The instruction for automatically highlighting search words is to directly process the contents of the <code>Dom</code> node, so avoid plain text nodes. The <code>Vue</code> update mechanism fails in the following scenarios:'
},
codeFiles: ['highlight-query/avoid.vue']
}
]
}

View File

@ -0,0 +1,43 @@
<template>
<div>
<div>场景1初始全加载</div>
<br />
<tiny-dropdown>
<span>下拉菜单</span>
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item>黄金糕</tiny-dropdown-item>
<tiny-dropdown-item>狮子头</tiny-dropdown-item>
<tiny-dropdown-item>螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item disabled>双皮奶</tiny-dropdown-item>
<tiny-dropdown-item divided>蚵仔煎</tiny-dropdown-item>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
<br />
<br />
<div>场景2初始不加载菜单及子项适合表格等大数量的场景</div>
<br />
<tiny-dropdown lazy-show-popper>
<span>下拉菜单</span>
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item>黄金糕</tiny-dropdown-item>
<tiny-dropdown-item>狮子头</tiny-dropdown-item>
<tiny-dropdown-item>螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item disabled>双皮奶</tiny-dropdown-item>
<tiny-dropdown-item divided>蚵仔煎</tiny-dropdown-item>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
<br />
</div>
</template>
<script setup>
import {
Dropdown as TinyDropdown,
DropdownMenu as TinyDropdownMenu,
DropdownItem as TinyDropdownItem
} from '@opentiny/vue'
</script>

View File

@ -0,0 +1,20 @@
import { test, expect } from '@playwright/test'
test('基本用法', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('dropdown#lazy-show-popper')
const wrap = page.locator('#lazy-show-popper')
const dropDownLi = wrap.locator('.tiny-dropdown li')
const dropDownOnBody = page.locator('body .tiny-dropdown-menu li')
const dropDown2 = wrap.locator('.tiny-dropdown').nth(1)
// 2个示例应该是10个菜单项 懒加载成功的话应该只加载5个。
await expect(dropDownLi).toHaveCount(5)
// 点击后应该加载10个, 但展开的5项是 appendToBody的。
await dropDown2.click()
await expect(dropDownLi).toHaveCount(5)
await expect(dropDownOnBody).toHaveCount(5)
})

View File

@ -0,0 +1,47 @@
<template>
<div>
<div>场景1初始全加载</div>
<br />
<tiny-dropdown>
<span>下拉菜单</span>
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item>黄金糕</tiny-dropdown-item>
<tiny-dropdown-item>狮子头</tiny-dropdown-item>
<tiny-dropdown-item>螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item disabled>双皮奶</tiny-dropdown-item>
<tiny-dropdown-item divided>蚵仔煎</tiny-dropdown-item>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
<br />
<br />
<div>场景2初始不加载菜单及子项适合表格等大数量的场景</div>
<br />
<tiny-dropdown lazy-show-popper>
<span>下拉菜单</span>
<template #dropdown>
<tiny-dropdown-menu>
<tiny-dropdown-item>黄金糕</tiny-dropdown-item>
<tiny-dropdown-item>狮子头</tiny-dropdown-item>
<tiny-dropdown-item>螺蛳粉</tiny-dropdown-item>
<tiny-dropdown-item disabled>双皮奶</tiny-dropdown-item>
<tiny-dropdown-item divided>蚵仔煎</tiny-dropdown-item>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
<br />
</div>
</template>
<script>
import { Dropdown, DropdownMenu, DropdownItem } from '@opentiny/vue'
export default {
components: {
TinyDropdown: Dropdown,
TinyDropdownMenu: DropdownMenu,
TinyDropdownItem: DropdownItem
}
}
</script>

View File

@ -231,6 +231,19 @@ export default {
'<p><code>button-click</code>: When the button type is selected, listen for the left button click event.</p>\n<p><code>item-click</code>: Listens for menu item click events</p>\n<p><code>visible-change</code>: Listens for changes in the display and hiding of dropdown pop ups.</p>\n'
},
codeFiles: ['events.vue']
},
{
demoId: 'lazy-show-popper',
name: {
'zh-CN': '懒加载菜单和子项',
'en-US': 'Lazy Load Menus and Subitems'
},
desc: {
'zh-CN': '通过 <code>lazy-show-popper </code>属性,指定是否懒加载下拉菜单及内部的项',
'en-US':
'The <code>lazy-show-popper </code> property specifies whether to lazy load the drop-down menu and internal items.'
},
codeFiles: ['lazy-show-popper.vue']
}
]
}

View File

@ -0,0 +1,85 @@
<template>
<div style="height: 540px">
<tiny-file-upload
ref="upload"
: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>

View File

@ -0,0 +1,78 @@
<template>
<div style="height: 540px">
<tiny-file-upload ref="upload" :action="action" :file-list="fileList" list-type="saas" :file-size="100" />
</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>

View File

@ -189,6 +189,30 @@ export default {
},
codeFiles: ['upload-file-list.vue']
},
{
demoId: 'file-size',
name: {
'zh-CN': '文件大小限制',
'en-US': 'Uploaded file size limit'
},
desc: {
'zh-CN': '<p>通过 <code>file-size</code> 配置上传文件的大小。<p>',
'en-US': '<p>Use <code>file-size</code> to configure the size of the uploaded file.</p>'
},
codeFiles: ['file-size.vue']
},
{
demoId: 'file-size-array',
name: {
'zh-CN': '文件大小范围',
'en-US': 'Uploaded file size range'
},
desc: {
'zh-CN': '<p>通过 <code>file-size</code> 配置为数组类型限制上传文件的大小范围。<p>',
'en-US': '<p>Set <code>file-size</code> to an array to limit the size of the file to be uploaded.</p>'
},
codeFiles: ['file-size-array.vue']
},
{
demoId: 'upload-file-list-slot',
name: {

View File

@ -0,0 +1,81 @@
<template>
<div>
<tiny-button @click="changeFlag">{{ flag ? '隐藏' : '显示' }}最后一列</tiny-button>
<tiny-grid
ref="grid"
column-width="200"
:data="tableData"
:resizable="true"
:optimization="{ scrollX: { gt: 15, rSize: 20 } }"
@after-refresh-column="handleAfterRefresh"
>
<tiny-grid-column field="name" :title="(h) => h('div', '名称')"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市1"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名1" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本1" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围1"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数1" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址1"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名2"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间2" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市2"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名2" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本2" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围2"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数2" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址2"></tiny-grid-column>
<tiny-grid-column v-if="flag" field="introduction" title="公司简介(最后一列)" show-overflow></tiny-grid-column>
</tiny-grid>
</div>
</template>
<script setup>
import { Grid as TinyGrid, GridColumn as TinyGridColumn, Button as TinyButton } from '@opentiny/vue'
import { ref } from 'vue'
const flag = ref(false)
const tableData = ref([
{
id: '1',
name: 'GFD科技有限公司',
address: '福州',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。',
employees: 800,
englishName: 'GFD',
established: '2005',
listed: '是',
ranking: '23',
registeredCapital: '2000万',
businessScope: '食品'
}
])
const grid = ref()
function changeFlag() {
flag.value = !flag.value
}
function handleAfterRefresh() {
const fullColumn = grid.value.getTableColumn().fullColumn
console.log(fullColumn[fullColumn.length - 1])
}
</script>

View File

@ -0,0 +1,14 @@
import { test, expect } from '@playwright/test'
test('新增列滚动位置', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-dynamically-columns#column-switching-scroll')
const demo = page.locator('#column-switching-scroll')
await demo.locator('.tiny-grid__body-wrapper.body__wrapper').click()
await page.mouse.wheel(10000, 0)
await expect(demo.getByText('地址2')).toBeVisible()
await demo.getByText('显示最后一列').click()
await expect(demo.getByText('地址2')).toBeVisible()
})

View File

@ -0,0 +1,89 @@
<template>
<div>
<tiny-button @click="changeFlag">{{ flag ? '隐藏' : '显示' }}最后一列</tiny-button>
<tiny-grid
ref="grid"
column-width="200"
:data="tableData"
:resizable="true"
:optimization="{ scrollX: { gt: 15, rSize: 20 } }"
@after-refresh-column="handleAfterRefresh"
>
<tiny-grid-column field="name" :title="(h) => h('div', '名称')"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市1"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名1" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本1" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围1"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数1" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址1"></tiny-grid-column>
<tiny-grid-column field="englishName" title="英文名2"></tiny-grid-column>
<tiny-grid-column field="established" title="成立时间2" sortable></tiny-grid-column>
<tiny-grid-column field="listed" title="是否上市2"></tiny-grid-column>
<tiny-grid-column field="ranking" title="国内排名2" sortable></tiny-grid-column>
<tiny-grid-column field="registeredCapital" title="注册资本2" sortable></tiny-grid-column>
<tiny-grid-column field="businessScope" title="业务范围2"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数2" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址2"></tiny-grid-column>
<tiny-grid-column v-if="flag" field="introduction" title="公司简介(最后一列)" show-overflow></tiny-grid-column>
</tiny-grid>
</div>
</template>
<script>
import { Grid, GridColumn, Button } from '@opentiny/vue'
export default {
components: {
TinyButton: Button,
TinyGrid: Grid,
TinyGridColumn: GridColumn
},
data() {
return {
flag: false,
tableData: [
{
id: '1',
name: 'GFD科技有限公司',
address: '福州',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。',
employees: 800,
englishName: 'GFD',
established: '2005',
listed: '是',
ranking: '23',
registeredCapital: '2000万',
businessScope: '食品'
}
]
}
},
methods: {
changeFlag() {
this.flag = !this.flag
},
handleAfterRefresh() {
const fullColumn = this.$refs.grid.getTableColumn().fullColumn
console.log(fullColumn[fullColumn.length - 1])
}
}
}
</script>

View File

@ -14,6 +14,18 @@ export default {
},
'codeFiles': ['dynamically-columns/dynamically-columns.vue']
},
{
'demoId': 'column-switching-scroll',
'name': {
'zh-CN': '新增列滚动位置',
'en-US': 'New columns scroll positon'
},
'desc': {
'zh-CN': '根据日期范围选择框的日期范围动态的生成表格列。',
'en-US': 'For details, see the following example.'
},
'codeFiles': ['dynamically-columns/column-switching-scroll.vue']
},
{
'demoId': 'column-columns-dynamic',
'name': { 'zh-CN': '动态列数据', 'en-US': '' },

View File

@ -1,42 +1,249 @@
export const iconGroups = {
Arrow: [
'IconArrowDown',
Left: [
'IconAngleLeft',
'IconArrowLeft',
'IconArrowRight',
'IconArrowUp',
'IconAscending',
'IconChevronDown',
'IconChevronLeft',
'IconChevronRight',
'IconChevronUp',
'IconDefault',
'IconDeltaDownO',
'IconDeltaDown',
'IconDeltaLeftO',
'IconDeltaLeft',
'IconDeltaRightO',
'IconDeltaRight',
'IconDeltaUpO',
'IconDeltaUp',
'IconDoubleLeft',
'IconDoubleRight',
'IconDownO',
'IconDown',
'IconDownWard',
'IconEnd',
'IconLeft',
'IconLeftO',
'IconLeftWard',
'IconLeftWardArrow',
'IconLeft',
'IconPopup',
'IconRightO',
'IconDeltaLeft',
'IconPagerFirst',
'IconPagerPrev',
'IconRichTextAlignLeft',
'IconSplitLeft',
'IconUndo'
],
Right: [
'IconArrowRight',
'IconAngleRight',
'IconChevronRight',
'IconDeltaRight',
'IconDeltaRightO',
'IconDoubleRight',
'IconEnd',
'IconPagerLast',
'IconPagerNext',
'IconRedo',
'IconRight',
'IconRightO',
'IconRichTextRedo',
'IconRichTextAlignRight',
'IconRightward',
'IconTriangleDown',
'IconUpO',
'IconSplitRight',
'IconStart',
'IconTransform'
],
TextAlign: [
'IconAlignCenter',
'IconAlignLeft',
'IconAlignJustify',
'IconAlignRight',
'IconAlignStretch',
'IconStreamSolid',
'IconRichTextAddColumnAfter',
'IconRichTextAddColumnBefore',
'IconRichTextAddRowAfter',
'IconRichTextAddRowBefore',
'IconRichTextAlignCenter',
'IconRichTextDeleteColumn',
'IconRichTextDeleteRow',
'IconRichTextLineHeight',
'IconRichTextListOrdered',
'IconRichTextListUnordered',
'IconRichTextMergeCells',
'IconRichTextMergeCellsVertical',
'IconRichTextSplitCellsHorizontal',
'IconRichTextSplitCellsVertical',
'IconRichTextTable',
'IconRichTextTaskList',
'IconSplit'
],
Upward: [
'IconArrowUp',
'IconChevronUp',
'IconDeltaUp',
'IconDeltaUpO',
'IconFreeze',
'IconUp',
'IconUpO',
'IconUpWard'
],
Downward: [
'IconArrowBottom',
'IconArrowDown',
'IconChevronDown',
'IconDeltaDown',
'IconDeltaDownO',
'IconDown',
'IconDownO',
'IconDownWard',
'IconTriangleDown'
],
Back: ['IconEditorUndo', 'IconGoBack', 'IconImport', 'IconRichTextUndo'],
Cycle: [
'IconConmentRefresh',
'IconDownload',
'IconGenerating',
'IconGroupTransfer',
'IconReplace',
'IconRefres',
'IconRefresh',
'IconRepeat',
'IconUpload'
],
Sort: [
'IconAscending',
'IconDescending',
'IconSort',
'IconSortDefault',
'IconSortO',
'IconSortTriangle',
'IconSortTriangleAscending',
'IconSortTriangleDescending'
],
Filter: ['IconBefilter', 'IconClearFilter', 'IconDefinedFiltration', 'IconFilter', 'IconFiltered', 'IconUnfilter'],
Reduce: [
'IconExpand',
'IconMinus',
'IconMinusCircle',
'IconMinusSquare',
'IconNodeOpen',
'IconPanelMini',
'IconStop',
'IconZoomOut'
],
Add: [
'IconAdd',
'IconAddCircle',
'IconNew',
'IconNode',
'IconPlus',
'IconPlusSquare',
'IconPlusCircle',
'IconPutAway',
'IconView',
'IconZoomIn'
],
Check: [
'IconCheck',
'IconHalfchecked',
'IconHalfselect',
'IconMobileCheckbox',
'IconMobileCheckboxHalf',
'IconPreChecked'
],
Success: [
'IconCheckedLinear',
'IconCheckedSur',
'IconDone',
'IconFinish',
'IconMobileCheckboxSelected',
'IconSubScript',
'IconSuccess',
'IconSuccessful',
'IconYes'
],
Fail: [
'IconClose',
'IconCloseCircle',
'IconCloseSquare',
'IconDelete',
'IconError',
'IconOperationfaild',
'IconOperationfaildL',
'IconStepsError'
],
Warn: [
'IconCueL',
'IconCueLO',
'IconExceptionO',
'IconExclamation',
'IconWarn',
'IconWarning',
'IconWarningO',
'IconWarningTriangle'
],
Question: ['IconHelpCircle', 'IconHelpQuery', 'IconUnknow', 'IconUnknown'],
Prompt: [
'IconInfo',
'IconInfoCircle',
'IconInfoSolid',
'IconHelp',
'IconHelpSolid',
'IconPrompt',
'IconPromptExclamation'
],
Stretchable: [
'IconFullscreenLeft',
'IconMinscreenLeft',
'IconMinscreenRight',
'IconStretch',
'IconStretchCrosswise',
'IconStretchUpright'
],
More: ['IconEllipsis', 'IconMore', 'IconPopup'],
Dot: [
'IconDotIpv4',
'IconLeave',
'IconOffLine',
'IconOnLine',
'IconBusy',
'IconDoneMini',
'IconOnGoing',
'IconOnGoingMini',
'IconMobileRadio',
'IconExceptionMiniO',
'IconMobileRadioSelected',
'IconOperation',
'IconRadio',
'IconRadioselected'
],
Location: ['IconLocation', 'IconLocationNumber', 'IconMarkOn'],
Letter: [
'IconEditorBold',
'IconEditorDeleteline',
'IconEditorItalic',
'IconEditorSub',
'IconEditorSubtitle',
'IconEditorSuper',
'IconEditorTitle',
'IconEditorUnderline',
'IconFontColor',
'IconFontSize',
'IconFontStyle',
'IconFontWeight',
'IconRichTextBold',
'IconRichTextFontSize',
'IconRichTextFormatClear',
'IconRichTextH1',
'IconRichTextH2',
'IconRichTextH3',
'IconRichTextH4',
'IconRichTextH5',
'IconRichTextH6',
'IconRichTextHeading',
'IconRichTextItalic',
'IconRichTextStrikeThrough',
'IconRichTextSubscript',
'IconRichTextSuperscript',
'IconRichTextUnderline',
'IconTextDecoration',
'IconUnderline'
],
Mall: [
'IconPurchasePlannedOrder',
'IconShipped',
'IconShoppingCard',
'IconSoldOut',
'IconSurchargeSettled',
'IconSurchargeSettled',
'IconWaitForDeliveryO',
'IconWaitingToPick'
],
Brands: ['IconAngularjs', 'IconEspaceAuto', 'IconEspace', 'IconNodejs', 'IconReactjs', 'IconVuejs'],
Charts: ['IconAreaChart', 'IconDotChart', 'IconLineChart', 'IconPieChart', 'IconStatistics'],
Editor: [
@ -44,11 +251,8 @@ export const iconGroups = {
'IconEditorAlignLeft',
'IconEditorAlignRight',
'IconEditorBackground',
'IconEditorBold',
'IconEditorCode',
'IconEditorDeleteline',
'IconEditorEraser',
'IconEditorItalic',
'IconEditorLeftBorder',
'IconEditorList',
'IconEditorListDot',
@ -58,51 +262,64 @@ export const iconGroups = {
'IconEditorQuote',
'IconEditorRedo',
'IconEditorRightBorder',
'IconEditorSub',
'IconEditorSubtitle',
'IconEditorSuper',
'IconEditorTable',
'IconEditorTextcolor',
'IconEditorTitle',
'IconEditorUnderline',
'IconEditorUndo',
'IconExport',
'IconEditorVideo',
'IconOperationfaildL',
'IconFilletExternalLink',
'IconOuterLink',
'IconTextDecoration',
'IconUnderline',
'IconWriting'
],
IT: [
'IconAdministrator',
'IconDataSource',
'IconDesktopView',
'IconEmailAdd',
'IconEmailCircle',
'IconExcel',
'IconException',
'IconExclamation',
'IconLanguage',
'IconMailContent',
'IconMail',
'IconMarkOn',
'IconMobileView',
'IconMobile',
'IconPrintPreview',
'IconReplies',
'IconSent',
'IconShare',
'IconShareArrow',
'IconShoppingCard',
'IconTabletView',
'IconUnlock',
'IconUnsent',
'IconUser',
'IconVersiontree',
'IconWebPlus',
'IconJs'
],
Clock: ['IconClockWork', 'IconProcessing', 'IconTime'],
Email: ['IconEmailAdd', 'IconEmailCircle', 'IconMailContent', 'IconMail', 'IconReplies', 'IconSent', 'IconUnsent'],
Folder: ['IconFileCloudupload', 'IconFolder', 'IconFolderClosed', 'IconFolderOpened'],
File: [
'IconCloudUpload',
'IconDocument',
'IconExcel',
'IconExcelType',
'IconFileExcel',
'IconFile',
'IconFilesCircle',
'IconFiles',
'IconFiletext',
'IconFileType',
'IconFileupload',
'IconFinishO',
'IconHistoryRecord',
'IconOtherType',
'IconPdfType',
'IconPptType',
'IconTextType',
'IconText',
'IconTextAlign',
'IconTextTab',
'IconVideoType',
'IconWordType',
'IconXml',
'IconZipType'
],
Image: ['IconAddPicture', 'IconImageAdd', 'IconPicture', 'IconPictureType', 'IconRichTextImage'],
Media: [
'IconAudio',
'IconCourse',
'IconCustom',
'IconCustomerService',
@ -110,161 +327,89 @@ export const iconGroups = {
'IconDialog2',
'IconInformation',
'IconMessageCircle',
'IconPagerFirst',
'IconPagerLast',
'IconPagerNext',
'IconPagerPrev',
'IconMic',
'IconPauseCircle',
'IconPause',
'IconPicture',
'IconStartCircle',
'IconStart',
'IconTime'
'IconStartO',
'IconTurnOn'
],
Objects: [
Object: [
'IconBoat',
'IconCalculator',
'IconCalendar',
'IconClockWork',
'IconCloudDownload',
'IconCloudUpload',
'IconCoin',
'IconTaskCooperation',
'IconTelephoneCircle',
'IconTelephone'
],
Symbols: [
'IconBusy',
'IconCheck',
'IconCheckedLinear',
'IconCheckedSur',
'IconCloseCircle',
'IconCloseSquare',
Symbol: [
'IconCode',
'IconCommission',
'IconCueL',
'IconCrop',
'IconDotIpv4',
'IconEllipsis',
'IconError',
'IconEyeclose',
'IconEyeopen',
'IconFrownO',
'IconFrown',
'IconGroup',
'IconHeartempty',
'IconHelpCircle',
'IconHelpQuery',
'IconHelpSolid',
'IconHelp',
'IconHelpful',
'IconLeave',
'IconLoading',
'IconLock',
'IconMale',
'IconMeh',
'IconMore',
'IconPlus',
'IconNodeOpen',
'IconNode',
'IconNone',
'IconOffLine',
'IconOnLine',
'IconPanelMax',
'IconPanelMini',
'IconPanelNormal',
'IconRadio',
'IconRadioselected',
'IconSmileO',
'IconSmile',
'IconStarActive',
'IconStarDisable',
'IconStarO',
'IconStop',
'IconSuccess',
'IconSuccessful',
'IconInfoSolid',
'IconInfoCircle',
'IconInfo',
'IconOperationfaild',
'IconUnknow',
'IconUnknown',
'IconWarning',
'IconWarningTriangle',
'IconClose',
'IconYes'
'IconStarO'
],
Tools: [
'IconAdd',
'IconAddPicture',
Tool: [
'IconConfig',
'IconSetting',
'IconAlignBaseline',
'IconAlignCenter',
'IconAlignFlexCenter',
'IconAlignFlexEnd',
'IconAlignFlexStart',
'IconAlignJustify',
'IconAlignLeft',
'IconAlignRight',
'IconAlignStretch',
'IconApp',
'IconApplication',
'IconAssociation',
'IconAttachment',
'IconBarChart',
'IconBefilter',
'IconBoxSolid',
'IconCheckOut',
'IconCheckedTrue',
'IconClearFilter',
'IconConmentRefresh',
'IconCopySolid',
'IconCopy',
'IconDefinedFiltration',
'IconDel',
'IconDeleteL',
'IconDeleted',
'IconDerive',
'IconDeletePage',
'IconDescending',
'IconColReverse',
'IconDirectionCol',
'IconDirectionRow',
'IconRowReverse',
'IconDownloadCloud',
'IconDownloadLink',
'IconDownload',
'IconDraft',
'IconEdit',
'IconEditor',
'IconEditorTab',
'IconExport',
'IconExpressSearch',
'IconFileCloudupload',
'IconFileExcel',
'IconFile',
'IconFilesCircle',
'IconFiles',
'IconFiletext',
'IconFileupload',
'IconFiltered',
'IconFlag',
'IconFolderClosed',
'IconFolderOpened',
'IconFontColor',
'IconFontFamily',
'IconFontSize',
'IconFontStyle',
'IconFontWeight',
'IconFreezeLeft',
'IconFreezeRight',
'IconFullscreen',
'IconFullscreenLeft',
'IconGrade',
'IconGroupTransfer',
'IconHalfchecked',
'IconHalfselect',
'IconHideLeft',
'IconHideRight',
'IconHideTopleft',
'IconImport',
'IconJusitfyCenter',
'IconJusitfyFlexEnd',
'IconJusitfySpaceAround',
@ -274,24 +419,13 @@ export const iconGroups = {
'IconLineHeight',
'IconLineThrought',
'IconLink',
'IconMinscreenLeft',
'IconMinscreen',
'IconMinusCircle',
'IconMinusSquare',
'IconMinus',
'IconNew',
'IconNoPremission',
'IconPagelink',
'IconPlusCircle',
'IconPlusSquare',
'IconPreChecked',
'IconPushpin',
'IconRedo',
'IconRefres',
'IconRenew',
'IconRepeat',
'IconReplace',
'IconRightFrozen',
'IconRichTextNodeDelete',
'IconSandwichCollapse',
'IconSandwichExpand',
'IconSave',
@ -299,21 +433,8 @@ export const iconGroups = {
'IconSearch',
'IconSelect',
'IconSeparate',
'IconSetting',
'IconSortDefault',
'IconSort',
'IconStreamSolid',
'IconTextAlign',
'IconTextTab',
'IconText',
'IconTotal',
'IconUndelete',
'IconUndo',
'IconUnfilter',
'IconUnfreeze',
'IconUpload',
'IconView',
'IconZoomIn',
'IconZoomOut'
'IconUnfreeze'
]
}

View File

@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'
test('[Input]basic-usage: placeholder, focus-style, v-model', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('input#basic-usage')
const input = await page.locator('.demo-input > .tiny-input > .tiny-input-display-only > input')
const input = page.locator('.demo-input > .tiny-input > .tiny-input-display-only > input')
await expect(input).toBeVisible()
await expect(input).toHaveAttribute('placeholder', 'Please input')

View File

@ -0,0 +1,27 @@
<template>
<div class="demo-input">
是否悬浮提示<tiny-switch v-model="showTooltip"></tiny-switch>
<tiny-input v-model="input" display-only :show-tooltip="showTooltip"></tiny-input>
</div>
</template>
<script setup>
import { Input as TinyInput, Switch as TinySwitch } from '@opentiny/vue'
import { ref } from 'vue'
const showTooltip = ref(false)
const input = ref(
'内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多'
)
</script>
<style scoped>
.demo-input {
width: 200px;
display: inline-block;
}
.tiny-input {
margin-top: 12px;
}
</style>

View File

@ -0,0 +1,10 @@
import { test, expect } from '@playwright/test'
test('只读态悬浮提示', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('input#show-tooltip')
const demo = page.locator('#show-tooltip')
await demo.locator('.tiny-input .tiny-input-display-only__content').hover()
await expect(page.locator('.tiny-tooltip.tiny-tooltip__popper')).not.toBeVisible()
})

View File

@ -0,0 +1,34 @@
<template>
<div class="demo-input">
是否悬浮提示<tiny-switch v-model="showTooltip"></tiny-switch>
<tiny-input v-model="input" display-only :show-tooltip="showTooltip"></tiny-input>
</div>
</template>
<script>
import { Input, Switch } from '@opentiny/vue'
export default {
components: {
TinyInput: Input,
TinySwitch: Switch
},
data() {
return {
showTooltip: false,
input:
'内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多内容很多'
}
}
}
</script>
<style scoped>
.demo-input {
width: 200px;
display: inline-block;
}
.tiny-input {
margin-top: 12px;
}
</style>

View File

@ -41,6 +41,19 @@ export default {
},
codeFiles: ['show-password.vue']
},
{
demoId: 'show-tooltip',
name: {
'zh-CN': '只读态悬浮提示',
'en-US': 'Show Tooltip'
},
desc: {
'zh-CN': '<p>通过 <code>show-tooltip</code> 配置当文本超长时,是否显示悬浮提示。</p>\n',
'en-US':
'<p>Use <code>show-tooltip</code> to configure whether to display a floating tip when the text is too long. </p>\n'
},
codeFiles: ['show-tooltip.vue']
},
{
demoId: 'size',
name: {

View File

@ -12,8 +12,7 @@ const options = reactive([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
{ value: '选项4', label: '重庆' }
])
const value = ref('')
</script>

View File

@ -18,8 +18,7 @@ export default {
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
{ value: '选项4', label: '重庆' }
],
value: ''
}

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(20)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -15,3 +15,9 @@ function setValue() {
value.value = 50
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 32px;
}
</style>

View File

@ -25,3 +25,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 32px;
}
</style>

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(40)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -12,3 +12,9 @@ function format(value) {
return '当前值为:' + value
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -21,3 +21,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -1,9 +1,5 @@
<template>
<div>
<div>
<tiny-slider v-model="value" :marks="marks"></tiny-slider>
</div>
</div>
<tiny-slider v-model="value" :marks="marks"></tiny-slider>
</template>
<script setup>
@ -18,3 +14,9 @@ const marks = ref({
const value = ref(20)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -1,9 +1,5 @@
<template>
<div>
<div>
<tiny-slider v-model="value" :marks="marks"></tiny-slider>
</div>
</div>
<tiny-slider v-model="value" :marks="marks"></tiny-slider>
</template>
<script>
@ -25,3 +21,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(30)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref([20, 40])
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(40)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -10,3 +10,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(40)
const value2 = ref([40, 60])
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -18,3 +18,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -12,3 +12,9 @@ function format(value) {
return '当前值为:' + value
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -21,3 +21,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -32,3 +32,9 @@ function stop(val) {
})
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -39,3 +39,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -12,3 +12,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(40)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -20,3 +20,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -8,3 +8,9 @@ import { Slider as TinySlider } from '@opentiny/vue'
const value = ref(30)
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -16,3 +16,9 @@ export default {
}
}
</script>
<style scoped>
.tiny-slider-container {
margin-top: 50px;
}
</style>

View File

@ -9,7 +9,7 @@ test('选择器打开时默认时间设置', async ({ page }) => {
const minute = page.getByRole('listitem').filter({ hasText: '40' }).first()
const second = page.getByRole('listitem').filter({ hasText: '00' }).nth(2)
await page.locator('#default-value input[type="text"]').click()
await page.locator('#default-value input[type="text"]').nth(0).click()
await expect(hour).toHaveClass(/active/)
await expect(minute).toHaveClass(/active/)
await expect(second).toHaveClass(/active/)

View File

@ -5,6 +5,7 @@ test('固定时间范围', async ({ page }) => {
await page.goto('time-picker#picker-options')
const selectTime = page.getByRole('textbox', { name: '18:40:00' })
const selectTime1 = page.getByRole('textbox', { name: '19:40:00' })
// 点击17点点击确定关闭选择框后查看input 时间是否仍是184000
await selectTime.click()
await page.waitForTimeout(100)
@ -12,11 +13,11 @@ test('固定时间范围', async ({ page }) => {
await page.getByRole('button', { name: '确定' }).click()
await page.waitForTimeout(100)
await expect(selectTime).toBeVisible()
// 点击21点点击确定关闭选择框后查看input 时间是否仍是184000
// 点击19点击确定关闭选择框后查看input 时间是194000
await selectTime.click()
await page.waitForTimeout(100)
await page.getByRole('listitem').filter({ hasText: '21' }).first().click()
await page.getByRole('listitem').filter({ hasText: '19' }).first().click()
await page.getByRole('button', { name: '确定' }).click()
await page.waitForTimeout(100)
await expect(selectTime).toBeVisible()
await expect(selectTime1).toBeVisible()
})

View File

@ -14,6 +14,13 @@
<tiny-radio label="hide"> 隐藏 </tiny-radio>
</tiny-radio-group>
</div>
<div class="option-row">
<span> 搜索后是否高亮关键字</span>
<tiny-radio-group v-model="highlightQuery">
<tiny-radio label="show"> 高亮 </tiny-radio>
<tiny-radio label="hide"> 默认不变 </tiny-radio>
</tiny-radio-group>
</div>
<div class="option-row">
<span> 搜索值</span>
<tiny-input v-model="filterText" @input="inputChange"></tiny-input>
@ -23,6 +30,7 @@
ref="treeRef"
:data="data"
:view-type="viewType"
:highlight-query="highlightQuery === 'show'"
:filter-node-method="filterNodeMethod"
:show-auxi="showAuxi === 'show'"
default-expand-all
@ -41,6 +49,7 @@ import { Tree as TinyTree, RadioGroup as TinyRadioGroup, Radio as TinyRadio, Inp
const treeRef = ref()
const viewType = ref('tree')
const highlightQuery = ref('hide')
const showAuxi = ref('hide')
const filterText = ref('')
const data = ref([

View File

@ -7,15 +7,23 @@ test('测试过滤视图', async ({ page }) => {
const preview = page.locator('.pc-demo-container')
const tree = preview.locator('.tiny-tree').nth(0)
const btnPlain = preview.getByText('平铺视图 plain')
const btnHighlight = preview.getByRole('radio', { name: '高亮' })
const checkboxs = tree.locator('.tiny-tree__plain-node .tiny-checkbox')
const highlightNodes = tree.locator('.tiny-tree__plain-node .tiny-hl-query-node')
const input = preview.locator('.tiny-input input')
await expect(tree.getByText('数据 1-1-1')).toHaveCount(1)
// 测试平铺视图
await btnPlain.click()
await expect(checkboxs).toHaveCount(9)
await page.waitForTimeout(20)
// 测试过滤
await input.fill('1-1')
await expect(checkboxs).toHaveCount(3)
// 测试高亮
await btnHighlight.click()
await expect(highlightNodes).toHaveCount(2)
})

View File

@ -14,6 +14,13 @@
<tiny-radio label="hide"> 隐藏 </tiny-radio>
</tiny-radio-group>
</div>
<div class="option-row">
<span> 搜索后是否高亮关键字</span>
<tiny-radio-group v-model="highlightQuery">
<tiny-radio label="show"> 高亮 </tiny-radio>
<tiny-radio label="hide"> 默认不变 </tiny-radio>
</tiny-radio-group>
</div>
<div class="option-row">
<span> 搜索值</span>
<tiny-input v-model="filterText" @input="inputChange"></tiny-input>
@ -23,6 +30,7 @@
ref="treeRef"
:data="data"
:view-type="viewType"
:highlight-query="highlightQuery === 'show'"
:filter-node-method="filterNodeMethod"
:show-auxi="showAuxi === 'show'"
default-expand-all
@ -49,6 +57,7 @@ export default {
return {
viewType: 'tree',
showAuxi: 'hide',
highlightQuery: 'hide',
filterText: '',
data: [
{

View File

@ -312,6 +312,7 @@ export default {
'zh-CN': `
通过 <code> filter-node-method </code> , <code>true</code><br>
通过 <code> filter </code> <br>
通过 <code> highlightQuery </code> <br>
通过 <code> view-type </code> <code> tree </code> <code> plain </code>,<code> tree </code><br>
通过 <code> show-auxi </code> <code>true</code><br>
<div class="tip custom-block">
@ -321,6 +322,7 @@ export default {
`,
'en-US': `The <code> filter-node-method </code> property is used to specify the function for filtering nodes. The function returns <code>true</code> to display the nodes. <br>
Run the <code> filter </code> component method to trigger the component to filter. <br>
Use the <code>highlightQuery </code> attribute to determine whether to highlight the search text in the matched node.<br>
Use the <code> view-type </code> property to set the component view mode. The optional values are <code> tree </code> and <code> plain </code>. The default value is <code> tree </code>. <br>
The <code> show-auxi </code> property is used to set whether to display auxiliary node information in tiled view. The default is <code>true</code>. <br>
<div class="tip custom-block">

View File

@ -320,7 +320,10 @@ export const cmpMenus = [
'label': '自定义指令',
'labelEn': 'Custom Instruction',
'key': 'directives-custom-instruction',
'children': [{ 'nameCn': '超出隐藏', 'name': 'AutoTip', 'key': 'directives-auto-tip' }]
'children': [
{ 'nameCn': '超出隐藏', 'name': 'AutoTip', 'key': 'directives-auto-tip' },
{ 'nameCn': '高亮搜索字', 'name': 'HighlightQuery', 'key': 'directives-highlight-query' }
]
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,5 +1,7 @@
{
"language": "EN",
"zh-cn": "Chinese",
"en-us": "English",
"localeType": "Language Selection",
"dark": "Dark",
"light": "Light",
"searchPlaceholder": "Search",
@ -39,4 +41,4 @@
"demoModeMultiple": "Multiple",
"contributor": "Contributors",
"noData": "No Data"
}
}

View File

@ -1,5 +1,7 @@
{
"language": "中",
"zh-cn": "中文",
"en-us": "英文",
"localeType": "语言选择",
"dark": "深色",
"light": "浅色",
"searchPlaceholder": "搜索",
@ -39,4 +41,4 @@
"demoModeMultiple": "多示例",
"contributor": "贡献者",
"noData": "暂无数据"
}
}

View File

@ -11,17 +11,14 @@ const appData = reactive({
bpState: useMediaQuery([640, 1024, 1280]).matches // 3点4区间 bp0,bp1,bp2,bp3
})
const isZhCn = computed(() => appData.lang === ZH_CN_LANG)
let appFn = {
toggleLang() {
let url = location.href
if (appData.lang === ZH_CN_LANG) {
url = location.href.replace(zhPath, enPath)
} else {
url = location.href.replace(enPath, zhPath)
const appFn = {
toggleLang(name) {
if (name !== appData.lang) {
let url = location.href
url = location.href.replace(LANG_PATH_MAP[appData.lang], LANG_PATH_MAP[name])
appData.lang = name
location.replace(url)
}
appData.lang = appData.lang === ZH_CN_LANG ? EN_US_LANG : ZH_CN_LANG
// router.push(url)
location.replace(url)
},
toggleTheme() {
appData.theme = appData.theme === 'light' ? 'dark' : 'light'

View File

@ -1,9 +1,11 @@
import { reactive } from 'vue'
import { $local } from './storage'
import { appFn } from './appData'
const _modeKey = 'tiny-vue-api-mode'
const _demoModeKey = 'tiny-vue-demo-mode'
const apiModeState = reactive({
localeMode: location.href.includes('en-US') ? 'enUS' : 'zhCN',
apiMode: $local[_modeKey] || 'Composition', // 示例风格: Options: 组合式; Composition: 选项式
demoMode: $local[_demoModeKey] || 'default' // 示例展示: default:多示例, single:单示例
})
@ -12,6 +14,9 @@ const apiModeFn = {
getDemoName: (name) => {
return name.replace(/\.vue$/, `${apiModeState.apiMode === 'Options' ? '' : '-composition-api'}.vue`)
},
changeLocaleMode: (name) => {
appFn.toggleLang(name)
},
changeApiMode: (name) => {
$local[_modeKey] = name
},

View File

@ -1,6 +1,22 @@
/** 文档显示风格设置 */
const getStyleSettings = (i18nByKey) => {
const styleSettings = [
{
// 语言选择
name: 'localeMode',
defaultValue: 'zhCN',
title: i18nByKey('localeType'),
options: [
{
value: 'zhCN',
text: i18nByKey('zh-cn')
},
{
value: 'enUS',
text: i18nByKey('en-us')
}
]
},
{
// 示例代码风格
name: 'apiMode',

View File

@ -16,6 +16,17 @@ import {
INFINITY_THEME,
getKeyByValue
} from '../const'
import glaciers from '@/assets/images/glaciers.png'
import glaciersIcon from '@/assets/images/glaciers-icon.png'
import infinitely from '@/assets/images/Infinitely.png'
import infinitelyIcon from '@/assets/images/Infinitely-icon.png'
import oceanic from '@/assets/images/oceanic.png'
import oceanicIcon from '@/assets/images/oceanic-icon.png'
import starrySky from '@/assets/images/starry-sky.png'
import starrySkyIcon from '@/assets/images/starry-sky-icon.png'
const themeMap = {
[DEFAULT_THEME]: null,
@ -27,10 +38,34 @@ const themeMap = {
const isEn = appData.lang === 'enUS'
const themeData = [
{ value: [DEFAULT_THEME], label: isEn ? 'Default Theme' : '默认主题' },
{ value: [INFINITY_THEME], label: isEn ? 'Infinity Theme' : '无限主题' },
{ value: [AURORA_THEME], label: isEn ? 'Aurora Theme' : 'Aurora 主题' },
{ value: [SMB_THEME], label: isEn ? 'SMB Theme' : 'SMB 主题' }
{
value: [DEFAULT_THEME],
label: isEn ? 'Default Theme' : '冰川主题',
tips: isEn ? 'Accurate, Efficient, Distinct' : '精准、高效、清晰',
icon: glaciersIcon,
bgImage: glaciers
},
{
value: [SMB_THEME],
label: isEn ? 'Star Theme' : '星空主题',
tips: isEn ? 'Leading, Innovative, Reliable' : '领先、创新、信赖',
icon: starrySkyIcon,
bgImage: starrySky
},
{
value: [AURORA_THEME],
label: isEn ? 'Ocean Theme' : '海洋主题',
tips: isEn ? 'Simple, Agile, Delightful' : '简约、敏捷、愉悦',
icon: oceanicIcon,
bgImage: oceanic
},
{
value: [INFINITY_THEME],
label: isEn ? 'Infinity Theme' : '无限主题',
tips: isEn ? 'Creative, Scientific, Efficient' : '创造、科学、高效',
icon: infinitelyIcon,
bgImage: infinitely
}
]
const designConfigMap = {

View File

@ -5,69 +5,70 @@
:style="settingsStyle"
>
<!-- 切换主题样式 -->
<tiny-dropdown trigger="click" :show-icon="false" @visible-change="(visible) => toggleMenuShow(visible, 'theme')">
<tiny-tooltip
:content="i18nByKey('changeTheme')"
:placement="isSettingsAside ? 'left' : 'right'"
popper-class="docs-tooltip"
effect="light"
>
<div class="settings-btn theme-change-button">
<theme-settings-icon
:class="['settings-icon theme-settings-icon', { 'is-active': showThemeMenu }]"
></theme-settings-icon>
</div>
</tiny-tooltip>
<template #dropdown>
<tiny-dropdown-menu popper-class="opt-menu theme-settings-menu">
<div v-for="(item, index) in themeData" :key="index" class="theme-option-list">
<tiny-button
ghost
:class="['theme-option', { 'is-active': item.value === currentThemeKey }]"
@click="themeItemClick(item)"
>{{ item.text }}</tiny-button
>
<tiny-popover
width="404"
placement="left-end"
trigger="click"
:visible-arrow="false"
popper-class="theme-settings-popover"
>
<div class="theme-settings-menu">
<div class="theme-settings-title">{{ i18nByKey('changeTheme') }}</div>
<div
v-for="(item, index) in themeData"
:key="item.text + index"
:class="['theme-option-list', { 'is-active-popover': item.value === currentThemeKey }]"
:style="{ backgroundImage: 'url(' + item.bgImage + ')' }"
@click="themeItemClick(item)"
>
<div class="theme-option-list-icon" :style="{ backgroundImage: 'url(' + item.icon + ')' }"></div>
<div>
<div class="theme-option-list-text">
{{ item.text }}
</div>
<div class="theme-option-list-tips">
{{ item.tips }}
</div>
</div>
</tiny-dropdown-menu>
</div>
</div>
<template #reference>
<div class="settings-btn theme-change-button">
<theme-settings-icon class="settings-icon theme-settings-icon"></theme-settings-icon>
</div>
</template>
</tiny-dropdown>
</tiny-popover>
<!-- demo风格设置 -->
<tiny-dropdown
<tiny-popover
v-if="!templateModeState.isSaas"
trigger="click"
:show-icon="false"
@visible-change="(visible) => toggleMenuShow(visible, 'style')"
width="180"
placement="left-end"
trigger="manual"
:visible-arrow="false"
v-model="demoStyleVisible"
popper-class="opt-menu style-settings-menu theme-settings-popover"
>
<tiny-tooltip
:content="i18nByKey('changeApiType')"
:placement="isSettingsAside ? 'left' : 'right'"
popper-class="docs-tooltip"
effect="light"
>
<div class="settings-btn style-settings-btn">
<style-settings-icon
:class="['settings-icon style-settings-icon', { 'is-active': showStyleMenu }]"
></style-settings-icon>
<div v-for="(item, index) in styleSettings" :key="index" class="style-settings-item">
<p class="style-settings-title">{{ item.title }}</p>
<tiny-radio-group
v-model="apiModeState[item.name]"
class="style-settings-options-group"
@change="onSettingsChange(item.name)"
>
<tiny-radio v-for="option in item.options" :key="option" :label="option.value">{{ option.text }}</tiny-radio>
</tiny-radio-group>
</div>
<template #reference>
<div
class="settings-btn style-settings-btn"
@click="demoStyleVisible = !demoStyleVisible"
@blur="demoStyleVisible = false"
>
<style-settings-icon class="settings-icon style-settings-icon"></style-settings-icon>
</div>
</tiny-tooltip>
<template #dropdown>
<tiny-dropdown-menu popper-class="opt-menu style-settings-menu">
<div v-for="(item, index) in styleSettings" :key="index" class="style-settings-item">
<p class="style-settings-title">{{ item.title }}</p>
<tiny-radio-group
v-model="apiModeState[item.name]"
class="style-settings-options-group"
@change="onSettingsChange(item.name)"
>
<tiny-radio v-for="option in item.options" :key="option" :label="option.value">{{
option.text
}}</tiny-radio>
</tiny-radio-group>
</div>
</tiny-dropdown-menu>
</template>
</tiny-dropdown>
</tiny-popover>
<!-- 返回顶部 -->
<div v-show="showBackTop" @click="onBackTop">
@ -87,7 +88,7 @@
<script>
import { defineComponent, reactive, toRefs, onMounted, onUnmounted, watch, nextTick, ref } from 'vue'
import { Dropdown, DropdownMenu, Tooltip, Radio, RadioGroup, Button } from '@opentiny/vue'
import { Tooltip, Radio, RadioGroup, Popover } from '@opentiny/vue'
import { iconUpWard } from '@opentiny/vue-icon'
import debounce from '@opentiny/vue-renderless/common/deps/debounce'
import { i18nByKey, useApiMode, useTemplateMode } from '@/tools'
@ -100,13 +101,11 @@ import StyleSettingsIcon from '@/assets/images/style-settings.svg'
export default defineComponent({
name: 'FloatSettings',
components: {
TinyDropdown: Dropdown,
TinyDropdownMenu: DropdownMenu,
TinyTooltip: Tooltip,
TinyRadio: Radio,
TinyRadioGroup: RadioGroup,
TinyButton: Button,
IconUpWard: iconUpWard(),
TinyPopover: Popover,
ThemeSettingsIcon,
StyleSettingsIcon
},
@ -120,9 +119,8 @@ export default defineComponent({
const isPlus = import.meta.env.VITE_APP_MODE === 'plus'
const state = reactive({
demoStyleVisible: false,
themeData: [],
showThemeMenu: false,
showStyleMenu: false,
styleSettings: getStyleSettings(i18nByKey),
settingsStyle: {
bottom: DEFAULT_BOTTOM_VAL
@ -133,7 +131,7 @@ export default defineComponent({
})
if (isPlus) {
state.styleSettings = state.styleSettings.filter((item) => item.defaultValue === 'default')
state.styleSettings = state.styleSettings.filter((item) => item.name !== 'apiMode')
apiModeState.apiMode = 'Options'
}
@ -147,6 +145,10 @@ export default defineComponent({
},
onSettingsChange(modeType) {
switch (modeType) {
case 'localeMode': {
apiModeFn.changeLocaleMode(apiModeState[modeType])
break
}
case 'apiMode': {
apiModeFn.changeApiMode(apiModeState[modeType])
break
@ -162,21 +164,16 @@ export default defineComponent({
themeItemClick(node) {
const val = node?.value || 'tiny-smb-theme'
changeTheme(val)
},
toggleMenuShow(visible, type) {
if (type === 'theme') {
state.showThemeMenu = visible
} else if (type === 'style') {
state.showStyleMenu = visible
}
}
}
const getThemeOptions = () => {
const themeData = getThemeData().map((item) => ({
value: item.value[0],
text: item.label
text: item.label,
tips: item.tips,
icon: item.icon,
bgImage: item.bgImage
}))
state.themeData = themeData
}
@ -306,10 +303,6 @@ export default defineComponent({
width: var(--ti-common-size-4x);
height: var(--ti-common-size-4x);
color: #595959;
&.is-active {
color: var(--ti-common-color-primary-normal);
}
}
}
}
@ -320,13 +313,6 @@ export default defineComponent({
border-radius: var(--ti-common-space-3x);
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.15);
&.theme-settings-menu {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
&.style-settings-menu {
padding: 14px;
@ -338,6 +324,7 @@ export default defineComponent({
font-size: var(--ti-common-font-size-base);
line-height: var(--ti-common-line-height-6);
font-weight: 600;
color: #000000;
}
.style-settings-options-group {
@ -357,25 +344,64 @@ export default defineComponent({
}
}
}
}
.tiny-popover.tiny-popper.theme-settings-popover {
border-radius: 12px;
background-color: #ffffff;
}
.theme-option-list {
display: flex;
flex-direction: column;
align-items: center;
width: 154px;
.theme-option {
display: inline-block;
width: 126px;
border: none;
border-radius: var(--ti-common-space-2x);
&.is-active {
background-color: rgba(0, 0, 0, 0.05);
}
}
.theme-settings-menu {
width: 404px;
display: flex;
padding: 15px 32px;
flex-direction: column;
margin: 0;
.theme-settings-title {
height: 20px;
line-height: 20px;
color: #000000;
font-weight: 700;
}
}
.theme-option-list {
display: flex;
align-items: center;
justify-content: start;
background-color: aliceblue;
cursor: pointer;
margin-top: 20px;
width: 308px;
height: 64px;
border: 1px solid #ffffff;
border-radius: 8px;
background-repeat: no-repeat;
.theme-option-list-icon {
width: 32px;
height: 32px;
margin: 16px 24px;
}
.theme-option-list-text {
height: 22px;
line-height: 22px;
font-size: 14px;
color: #202e54;
font-weight: 700;
}
.theme-option-list-tips {
height: 20px;
line-height: 20px;
font-size: 14px;
color: #808080;
font-weight: 500;
}
}
.theme-option-list:hover {
border-color: rgba(0, 0, 0, 0.05);
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.15);
}
.is-active-popover {
border-color: #1476ff;
}
@media (max-width: 1279px) {
.float-settings {

View File

@ -10,6 +10,8 @@ import Guide from './src/guide'
import Pager from './src/pager'
import Select from './src/select'
import TreeNode from './src/tree-node'
import TimeSpinner from './src/time-spinner'
import Time from './src/time-spinner'
import UploadList from './src/upload-list'
import BreadcrumbItem from './src/breadcrumb-item'
import { version } from './package.json'
@ -30,6 +32,8 @@ export default {
Pager,
Select,
TreeNode,
TimeSpinner,
Time,
BreadcrumbItem,
UploadList
}

View File

@ -0,0 +1,6 @@
export default {
// 控制time-picker组件button显示和样式
showTimePickerButton: true,
// smb规范下时间选择器单个item的margin为12px.
itemMarginSpace: 12
}

View File

@ -3024,11 +3024,6 @@
"pc"
]
},
"TreeSelectPc": {
"path": "vue/src/tree-select/src/pc.vue",
"type": "template",
"exclude": false
},
"Upload": {
"path": "vue/src/upload/index.ts",
"type": "component",

View File

@ -29,7 +29,7 @@ if (!isServer) {
on(document, 'mouseup', (event) => {
nodeList
.filter((node) => !node[nameSpace].mousedownTrigger)
.forEach((node) => node[nameSpace].documentHandler(event, startClick))
.forEach((node) => node[nameSpace].documentHandler(event, node[nameSpace]?.mouseupTrigger ? event : startClick))
startClick = null
})
}
@ -60,8 +60,12 @@ const createDocumentHandler = (el, binding, vnode) =>
* v-clickoutside
* @desc
* @example
* mousedownmouseup
*
* ```html
* <div v-clickoutside="handleClose">
* <div v-clickoutside="handleClose"> // 在元素外部点击时触发
* <div v-clickoutside.mousedown="handleClose"> // 在元素外部按下鼠标时触发
* <div v-clickoutside.mouseup="handleClose"> // 在元素外部松开鼠标时触发
* ```
*/
export default {
@ -69,22 +73,25 @@ export default {
nodeList.push(el)
const id = seed++
const { modifiers, expression, value } = binding
const { mousedown = false, mouseup = false } = modifiers || {}
el[nameSpace] = {
id,
documentHandler: createDocumentHandler(el, binding, vnode),
methodName: expression,
bindingFn: value,
mousedownTrigger: modifiers.mousedown
mousedownTrigger: mousedown,
mouseupTrigger: mouseup
}
},
update: (el, binding, vnode) => {
const { modifiers, expression, value } = binding
const { mousedown = false, mouseup = false } = modifiers || {}
el[nameSpace].documentHandler = createDocumentHandler(el, binding, vnode)
el[nameSpace].methodName = expression
el[nameSpace].bindingFn = value
el[nameSpace].mousedownTrigger = modifiers.mousedown
el[nameSpace].mousedownTrigger = mousedown
el[nameSpace].mouseupTrigger = mouseup
},
unbind: (el) => {

View File

@ -7,27 +7,41 @@ import { onMountedOrActivated as createHook } from './useEventListener'
* relationContainer 使
* onChange 使
* childrenKey instanceChildren
* delivery
*/
export const useRelation =
({ getCurrentInstance, inject, markRaw, nextTick, onMounted, onActivated, onUnmounted, provide, reactive, toRef }) =>
({ relationKey, relationContainer, onChange, childrenKey } = {}) => {
({
computed,
getCurrentInstance,
inject,
markRaw,
nextTick,
onMounted,
onActivated,
onUnmounted,
provide,
reactive,
toRef
}) =>
({ relationKey, relationContainer, onChange, childrenKey, delivery } = {}) => {
if (!relationKey) {
throw new Error('[TINY Error]<relationKey> must exist.')
}
const instance = getCurrentInstance()
const state = reactive({ children: [] })
const state = reactive({ children: [], indexInParent: -1 })
const injectValue = inject(relationKey, null)
// 收集所有的子组件刷新回调
let callbacks = []
if (injectValue) {
const { link, unlink, callbacks: injectCbs, childrenKey: injectKey } = injectValue
const { link, unlink, callbacks: injectCbs, childrenKey: injectKey, delivery: injectDelivery } = injectValue
callbacks = injectCbs
childrenKey = childrenKey || injectKey || 'instanceChildren'
delivery = injectDelivery
link(instance)
state.indexInParent = link(instance)
onUnmounted(() => unlink(instance))
} else {
@ -69,7 +83,13 @@ export const useRelation =
})
}
const link = (child) => state.children.push(markRaw(child.proxy))
const link = (child) => {
const childPublic = child.proxy
state.children.push(markRaw(childPublic))
return computed(() => state.children.indexOf(childPublic))
}
const unlink = (child) => {
const index = state.children.indexOf(child.proxy)
@ -82,15 +102,15 @@ export const useRelation =
// 刷新子组件顺序
callbacks.push((flattenNodes) => sortPublicInstances(state.children, flattenNodes))
provide(relationKey, { link, unlink, callbacks, childrenKey })
provide(relationKey, { link, unlink, callbacks, childrenKey, delivery })
// 在 Public Instance 上定义子组件数组,并且在组件卸载时移除
Object.defineProperty(instance.proxy, childrenKey, { configurable: true, get: () => state.children })
onUnmounted(() => delete instance.proxy[childrenKey])
// 返回子组件数组 ref
return { children: toRef(state, 'children') }
// 返回子组件数组 ref、在父级中的位置索引 ref 和接收到的分发内容
return { children: toRef(state, 'children'), index: toRef(state, 'indexInParent'), delivery }
}
const flattenChildNodes = (childNodes, result) => {

View File

@ -348,8 +348,8 @@ export const handleDrag =
document.onmouseup = () => {
document.onmousemove = demMousemove
document.onmouseup = demMouseup
props.draggable && state.move && emit('drag-end', event)
state.move = false
props.draggable && emit('drag-end', event)
}
}

View File

@ -73,8 +73,16 @@ export const multiGridSelectAll =
}
})
} else {
state.selectedValues = []
state.selectedDatas = []
// 同步勿删,跨页取消全选时,仅删除当前页数据
const { data = [] } = props.gridOp || {}
data.forEach((item) => {
const selectedItem = state.selectedValues.find((i) => i === item[props.valueField])
if (selectedItem) {
const index = state.selectedValues.indexOf(selectedItem)
state.selectedValues.splice(index, 1)
state.selectedDatas.splice(index, 1)
}
})
state.selectedChanged = true
}
@ -82,20 +90,41 @@ export const multiGridSelectAll =
}
export const multiGridSelectChange =
({ api, props, state }) =>
({ api, props, state, vm }) =>
({ row, checked }) => {
const index = findIndexOf(state.selectedValues, (val) => val === row[props.valueField])
const property = props.valueField
const grid = vm.$refs?.multiGrid
const selectedRows = grid.getSelectRecords()
if (checked) {
if (!~index) {
state.selectedValues = [...state.selectedValues, row[props.valueField]]
state.selectedDatas = [...state.selectedDatas, row]
// 获取新勾选的行
const addSelectedRows = selectedRows.filter((row) => !state.selectedValues.includes(row[property]))
if (addSelectedRows.length > 0) {
state.selectedValues = [...state.selectedValues, ...addSelectedRows.map((row) => row[property])]
state.selectedDatas = [...state.selectedDatas, ...addSelectedRows]
state.selectedChanged = true
}
} else {
if (~index) {
state.selectedValues = [...state.selectedValues.slice(0, index), ...state.selectedValues.slice(index + 1)]
state.selectedDatas = [...state.selectedDatas.slice(0, index), ...state.selectedDatas.slice(index + 1)]
const childrenKey = props.gridOp?.treeConfig?.children
const checkStrictly = props.gridOp?.selectConfig?.checkStrictly
const getCancelRows = (row, arr) => {
arr.push(row)
if (row[childrenKey]?.length > 0) {
row[childrenKey].forEach((childRow) => getCancelRows(childRow, arr))
}
return arr
}
// 获取取消勾选的列
let cancelRows = checkStrictly ? [row] : getCancelRows(row, [])
cancelRows = cancelRows.filter((row) => state.selectedValues.includes(row[property]))
if (cancelRows.length > 0) {
cancelRows.forEach((row) => {
const index = state.selectedValues.indexOf(row[property])
state.selectedValues.splice(index, 1)
state.selectedDatas.splice(index, 1)
})
state.selectedChanged = true
}
}
@ -385,16 +414,18 @@ export const setChecked =
export const computedConfig =
({ props, state }) =>
() => {
(type) => {
const { multi, popseletor, gridOp } = props
const { selectConfig = {}, radioConfig = {} } = gridOp || {}
const { selectedValues } = state
let config = {}
if (popseletor === 'grid') {
if (multi) {
// 单选和多选同时使用computedConfig来获取配置因此需要加type区分否则单选和多选返回的是一模一样的配置
if (multi && type === 'select') {
config = Object.assign(config, selectConfig, { checkRowKeys: selectedValues })
} else {
}
if (!multi && type === 'radio') {
config = Object.assign(config, radioConfig, { checkRowKey: selectedValues[0] })
}
}

View File

@ -56,8 +56,8 @@ export const renderless = (props, { reactive, computed, watch }, { vm, nextTick,
selectedDatas: [],
selectedValues: [],
multiGridStore: {
selectConfig: computed(() => api.computedConfig()),
radioConfig: computed(() => api.computedConfig()),
selectConfig: computed(() => api.computedConfig('select')),
radioConfig: computed(() => api.computedConfig('radio')),
inited: false,
loading: false
},
@ -96,7 +96,7 @@ export const renderless = (props, { reactive, computed, watch }, { vm, nextTick,
Object.assign(api, {
multiGridSelectAll: multiGridSelectAll({ api, props, state }),
multiGridSelectChange: multiGridSelectChange({ api, props, state }),
multiGridSelectChange: multiGridSelectChange({ api, props, state, vm }),
multiTreeAfterLoad: multiTreeAfterLoad({ api, props, state, vm }),
multiTreeCheck: multiTreeCheck({ api, props, state, vm, nextTick }),
multiTreeFilterPlain: multiTreeFilterPlain({ api, props, state }),

Some files were not shown because too many files have changed in this diff Show More