fix(mobile-first): fix mobile-first components bugs (#1426)

This commit is contained in:
ajaxzheng 2024-02-26 09:30:53 +08:00 committed by GitHub
parent f08fb98c17
commit c7618314f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
605 changed files with 11140 additions and 5540 deletions

View File

@ -25,6 +25,7 @@
<!-- 预览 -->
<!-- modeState.demoId === 'preview-in-dialog' 修复preview-in-dialog demo弹窗内容被遮罩层遮挡 -->
<div
:id="state.currDemo?.demoId"
class="rel px20 minh200"
:style="{ transform: modeState.demoId === 'preview-in-dialog' ? '' : 'translateX(0)' }"
>
@ -34,8 +35,7 @@
</div>
</div>
<!-- API表格 -->
<div v-if="state.currApi.length" class="mt20 f24 fw-bold">组件API</div>
<div v-if="state.currApi?.length" class="mt20 f24 fw-bold">组件API</div>
<div v-for="(oneGroup, idx) in state.currApi" :key="idx">
<div class="mt20 f-r f-pos-start fw-bold">
<div :id="oneGroup.name" class="f18">
@ -53,8 +53,8 @@
<table class="api-table">
<thead>
<tr>
<th width="15%">名称</th>
<th width="20%">类型</th>
<th width="20%">名称</th>
<th width="15%">类型</th>
<th width="20%">默认值</th>
<th width="55%">说明</th>
</tr>
@ -102,10 +102,11 @@
import { hooks } from '@opentiny/vue-common'
import { Floatbar, TreeMenu, Button, Tooltip, ConfigProvider } from '@opentiny/vue'
import { iconStarActive, iconSelect } from '@opentiny/vue-icon'
import { menuData, apis, demoStr, demoVue, mds } from './resourceMobileFirst.js'
import { useModeCtx } from './uses'
import designAuroraConfig from '@opentiny/vue-design-aurora'
import designSaasConfig from '@opentiny/vue-design-saas'
import { menuData, demos, demoStr, demoVue, mds } from './resourceMobileFirst.js'
import { useModeCtx } from './uses'
import { getDemosConfig, getApisConfig } from './utils/componentsDoc'
const isSaasMode = process.env.VITE_TINY_THEME === 'saas'
@ -165,18 +166,12 @@ export default {
// vue
async function _switchPath() {
// API
const apiModule = apis[`../../sites/demos/mobile-first/app/${modeState.pathName}/webdoc/${modeState.pathName}.js`]
if (apiModule) {
const module = await apiModule()
const apiRoot = module.default
state.currApi = apiRoot.apis
state.demos = apiRoot.demos || []
const demosModule =
demos[`../../sites/demos/mobile-first/app/${modeState.pathName}/webdoc/${modeState.pathName}.js`]
const demosConfig = await getDemosConfig(demosModule)
state.demos = demosConfig.demos
state.currDemo = state.demos.find((d) => d.demoId === modeState.demoId) || state.demos?.[0]
} else {
state.currApi = null
state.currDemos = []
}
state.currApi = (await getApisConfig(modeState.pathName, 'mobile-first')).apis
await _switchDemo()
}
async function _switchDemo() {

View File

@ -22,7 +22,7 @@
<div v-html="state.currDemo?.desc['zh-CN']"></div>
</div>
<!-- 预览 -->
<div class="rel px20">
<div class="rel px20" :id="state.currDemo?.demoId">
<div class="phone-container" @dblclick="fn.openInVscode(state.currDemo)">
<div class="mobile-view-container">
<component :is="state.comp"></component>
@ -31,8 +31,7 @@
</div>
</div>
<!-- API表格 -->
<div v-if="state.currApi.length" class="mt20 f24 fw-bold">组件API</div>
<div v-if="state.currApi?.length" class="mt20 f24 fw-bold">组件API</div>
<div v-for="(oneGroup, idx) in state.currApi" :key="idx">
<div class="mt20 f-r f-pos-start fw-bold">
<div :id="oneGroup.name" class="f18">
@ -47,12 +46,11 @@
<div class="f18 py28">
{{ key }}
</div>
<table class="api-table">
<thead>
<tr>
<th width="15%">名称</th>
<th width="20%">类型</th>
<th width="20%">名称</th>
<th width="15%">类型</th>
<th width="20%">默认值</th>
<th width="55%">说明</th>
</tr>
@ -76,7 +74,7 @@
</div>
</div>
<!-- 右边浮动所有的demos -->
<tiny-floatbar v-if="state.demos.length > 0" class="!top120 !z1 !right25">
<tiny-floatbar v-if="state.demos?.length > 0" class="!top120 !z1 !right25">
<div class="f12 ofy-auto">
<div
v-for="demo in state.demos"
@ -100,8 +98,9 @@
import { hooks } from '@opentiny/vue-common'
import { Floatbar, TreeMenu, Button, Tooltip } from '@opentiny/vue'
import { iconStarActive, iconSelect } from '@opentiny/vue-icon'
import { menuData, apis, demoStr, demoVue, mds } from './resourceMobile.js'
import { menuData, demos, demoStr, demoVue, mds } from './resourceMobile.js'
import { useModeCtx } from './uses'
import { getDemosConfig, getApisConfig } from './utils/componentsDoc'
export default {
props: {
@ -157,18 +156,11 @@ export default {
// vue
async function _switchPath() {
// API
const apiModule = apis[`../../sites/demos/mobile/app/${modeState.pathName}/webdoc/${modeState.pathName}.js`]
if (apiModule) {
const module = await apiModule()
const apiRoot = module.default
state.currApi = apiRoot.apis
state.demos = apiRoot.demos || []
const demosModule = demos[`../../sites/demos/mobile/app/${modeState.pathName}/webdoc/${modeState.pathName}.js`]
const demosConfig = await getDemosConfig(demosModule)
state.demos = demosConfig.demos
state.currDemo = state.demos.find((d) => d.demoId === modeState.demoId) || state.demos?.[0]
} else {
state.currApi = null
state.currDemos = []
}
state.currApi = (await getApisConfig(modeState.pathName, 'mobile')).apis
await _switchDemo()
}
async function _switchDemo() {

View File

@ -24,7 +24,7 @@
<div v-html="state.currDemo?.desc['zh-CN']"></div>
</div>
<!-- 预览 -->
<div class="rel px20 py60 b-a bs-dotted">
<div class="rel px20 py60 b-a bs-dotted" :id="state.currDemo?.demoId">
<div class="abs top10 right10">
<span title="点击在vscode中打开">
<IconOpeninVscode @click="fn.openInVscode(state.currDemo)" class="ml12 cur-hand" />
@ -154,21 +154,13 @@ import Loading from '@opentiny/vue-loading'
import designSmbConfig from '@opentiny/vue-design-smb'
import designAuroraConfig from '@opentiny/vue-design-aurora'
import designSaasConfig from '@opentiny/vue-design-saas'
import { menuData, apis, demoStr, demoVue, mds } from './resourcePc.js'
import { menuData, demoStr, demoVue, mds, demos } from './resourcePc.js'
import { useTheme, useModeCtx } from './uses'
import { getDemosConfig, getPath, getApisConfig } from './utils/componentsDoc'
import SvgTheme from './assets/theme.svg'
const isSaasMode = process.env.VITE_TINY_THEME === 'saas'
const getPath = (path) => {
if (path.startsWith('grid-')) {
return 'grid'
} else if (path.startsWith('chart-')) {
return 'chart'
}
return path
}
export default {
props: {
showFixedMenu: Boolean
@ -235,20 +227,14 @@ export default {
// vue
async function _switchPath() {
state.demoLoading = true
// API
const apiModule = apis[`../../sites/demos/pc/app/${getPath(modeState.pathName)}/webdoc/${modeState.pathName}.js`]
const componentName = getPath(modeState.pathName)
// demos
const demosModule = demos[`../../sites/demos/pc/app/${componentName}/webdoc/${modeState.pathName}.js`]
if (apiModule) {
const module = await apiModule()
const apiRoot = module.default
state.currApi = apiRoot.apis
state.demos = apiRoot.demos || []
const demosConfig = await getDemosConfig(demosModule)
state.demos = demosConfig.demos
state.currDemo = state.demos.find((d) => d.demoId === modeState.demoId) || state.demos?.[0]
} else {
state.currApi = null
state.currDemos = []
}
state.currApi = (await getApisConfig(componentName, 'pc')).apis
await _switchDemo()
}
async function _switchDemo() {

View File

@ -8,8 +8,8 @@ import { cmpMenus } from '../../sites/demos/mobile/menus.js'
export const demoStr = import.meta.glob('../../sites/demos/mobile/app/**/*.vue', { eager: false, as: 'raw' })
export const demoVue = import.meta.glob('../../sites/demos/mobile/app/**/*.vue', { eager: false })
// api属性
export const apis = import.meta.glob('../../sites/demos/mobile/app/*/webdoc/*.js', { eager: false })
// demos配置
export const demos = import.meta.glob('../../sites/demos/mobile/app/*/webdoc/*.js', { eager: false })
// 组件的md
const allMD = import.meta.glob('../../sites/demos/mobile/app/*/webdoc/*.cn.md', { eager: true })

View File

@ -3,8 +3,8 @@ import { cmpMenus } from '../../sites/demos/mobile-first/menus.js'
export const demoStr = import.meta.glob('../../sites/demos/mobile-first/app/**/*.vue', { eager: false, as: 'raw' })
export const demoVue = import.meta.glob('../../sites/demos/mobile-first/app/**/*.vue', { eager: false })
// api属性
export const apis = import.meta.glob('../../sites/demos/mobile-first/app/*/webdoc/*.js', { eager: false })
// demos配置
export const demos = import.meta.glob('../../sites/demos/mobile-first/app/*/webdoc/*.js', { eager: false })
// 组件的md
const allMD = import.meta.glob('../../sites/demos/mobile-first/app/*/webdoc/*.cn.md', { eager: true })

View File

@ -8,8 +8,8 @@ import { cmpMenus } from '../../sites/demos/pc/menus.js'
export const demoStr = import.meta.glob('../../sites/demos/pc/app/**/*.vue', { eager: false, as: 'raw' })
export const demoVue = import.meta.glob('../../sites/demos/pc/app/**/*.vue', { eager: false })
// api属性
export const apis = import.meta.glob('../../sites/demos/pc/app/*/webdoc/*.js', { eager: false })
// demos配置
export const demos = import.meta.glob('../../sites/demos/pc/app/*/webdoc/*.js', { eager: false })
// 组件的md
const allMD = import.meta.glob('../../sites/demos/pc/app/*/webdoc/*.cn.md', { eager: true })

View File

@ -0,0 +1,40 @@
export const apis = import.meta.glob('../../../sites/demos/apis/*.js', { eager: false })
export const getPath = (path) => {
if (path.startsWith('grid-')) {
return 'grid'
} else if (path.startsWith('chart-')) {
return 'chart'
}
return path
}
export const getApisConfig = async (component, mode) => {
const apisModule = apis[`../../../sites/demos/apis/${component}.js`]
if (apisModule) {
const apisConfig = (await apisModule()).default
const demoKey = mode === 'mobile-first' ? 'mfDemo' : `${mode}Demo`
const apis = apisConfig.apis.map((item) => {
Object.keys(item).forEach((key) => {
const apiItem = item[key]
if (Array.isArray(apiItem)) {
item[key] = apiItem
.filter((i) => !i.mode || i.mode.includes(mode))
.map((filterItem) => ({ ...filterItem, demoId: filterItem[demoKey] }))
}
})
return item
})
return { ...apisConfig, apis }
}
return {}
}
export const getDemosConfig = async (module) => {
if (module) {
const demosModule = await module()
const demosConfig = demosModule.default
return demosConfig
}
return { demos: [] }
}

View File

@ -200,8 +200,7 @@ export default {
defaultValue: '',
desc: {
'zh-CN': '弹出框标题',
'en-US':
'Height between the pop-up box and the top of the window. The default value is 15% of the screen height'
'en-US': 'Pop-up Box Title'
},
mode: ['pc'],
pcDemo: 'custom-dialog-title'
@ -212,7 +211,7 @@ export default {
defaultValue: '15vh',
desc: {
'zh-CN': '设置弹出框距离窗口顶部的高度',
'en-US': 'Display and close pop-up boxes'
'en-US': 'Set the height of the popup from the top of the window'
},
mode: ['pc'],
pcDemo: 'dialog-top-height'

View File

@ -565,7 +565,7 @@ export default {
type: 'number | string',
defaultValue: '',
desc: {
'zh-CN': '设置表格内容区域(不含表格头部,底部)的最小高度',
'zh-CN': '设置表格内容区域(不含表格头部,底部)的最小高度',
'en-US': 'Set the minimum height of the table content area (excluding the table header and bottom).'
},
mode: ['pc', 'mobile-first'],
@ -1019,6 +1019,7 @@ export default {
{
name: 'tooltip-config',
type: 'IToolTipConfig',
typeAnchorName: 'IToolTipConfig',
defaultValue: '',
desc: {
'zh-CN': 'Grid 内置 tooltip 配置项,请参考 Tooltip 组件属性说明',
@ -2497,7 +2498,7 @@ export default {
type: '',
defaultValue: '',
desc: {
'zh-CN': '在表格中新增数据',
'zh-CN': '在表格中新增数据',
'en-US': ''
},
mode: ['mobile-first'],
@ -3330,7 +3331,7 @@ export default {
type: "'left' | 'center' | 'right'",
defaultValue: "'left'",
desc: {
'zh-CN': '列对方式',
'zh-CN': '列对方式',
'en-US': 'Column pair mode; The optional values for this property are left, center, right'
},
mode: ['pc'],
@ -3416,7 +3417,7 @@ export default {
type: 'IFormatConfig',
defaultValue: '',
desc: {
'zh-CN': '开启该列数据异步渲染',
'zh-CN': '开启该列数据异步渲染',
'en-US': 'Enable the asynchronous rendering of the column data'
},
mode: ['pc'],
@ -3824,7 +3825,7 @@ export default {
type: 'boolean',
defaultValue: '',
desc: {
'zh-CN': '工具栏组件开启表格刷新功能',
'zh-CN': '工具栏组件开启表格刷新功能',
'en-US': 'The table refresh function is enabled for the toolbar component.'
},
mode: ['pc'],
@ -3898,7 +3899,7 @@ export default {
type: '()=> void',
defaultValue: '',
desc: {
'zh-CN': '点击个性化面板的重置按钮触发该事件',
'zh-CN': '点击个性化面板的重置按钮触发该事件',
'en-US': 'Click the Reset button on the personalized panel to trigger the event.'
},
mode: ['pc'],
@ -4030,6 +4031,21 @@ interface IToolbarConfig {
code: string
name: string
}[]
}
`
},
{
name: 'IToolTipConfig',
type: 'type',
code: `
interface IToolTipConfig {
placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end'
visibleArrow?: boolean
enterable?: boolean
type?: 'normal' | 'warning' | 'error' | 'info' | 'success'
effect?: 'dark' | 'light'
// 自定义提示内容
contentMethod?: ()=> string | VNode
}
`
},
@ -4379,27 +4395,11 @@ interface IToolbarButtonClickArgs {
code: `
interface ICellClickArgs {
// 当前行
row: object,
row: IRow,
// 当前行的下标
rowIndex: number
// 当前列
column: object
// 当前列的下标
columnIndex: number
}
`
},
{
name: 'ICellClickArgs',
type: 'type',
code: `
interface ICellClickArgs {
// 当前行
row: object
// 当前行的下标
rowIndex: number
// 当前列
column: object
column: IColumnConfig
// 当前列的下标
columnIndex: number
}
@ -4411,7 +4411,7 @@ interface ICellClickArgs {
code: `
interface ICellContextMenuArgs {
// 当前行
row: object
row: IRow
}
`
},
@ -4421,11 +4421,11 @@ interface ICellContextMenuArgs {
code: `
interface ICellArgs {
//当前行
row: object
row: IRow
//当前行的下标
rowIndex: number
// 当前列
column: object
column: IColumnConfig
// 当前列的下标
columnIndex: number
}
@ -4437,7 +4437,7 @@ interface ICellArgs {
code: `
interface ICurrentChangeArgs {
// 当前行
row: object
row: IRow
}
`
},
@ -4447,9 +4447,9 @@ interface ICurrentChangeArgs {
code: `
interface IEditActivedArgs {
// 当前行
row: object
row: IRow
// 当前列
column: object
column: IColumnConfig
}
`
},
@ -4459,9 +4459,9 @@ interface IEditActivedArgs {
code: `
interface IEditClosedArgs {
// 当前行
row: object
row: IRow
// 当前列
column: object
column: IColumnConfig
}
`
},
@ -4471,9 +4471,9 @@ interface IEditClosedArgs {
code: `
interface IEditDisabledArgs {
//当前行
row: object
row: IRow
// 当前列
column: object
column: IColumnConfig
}
`
},
@ -4501,7 +4501,7 @@ interface IFooterCellClickArgs {
// 当前单元格节点
cell: HTMLElement
// 当前列信息
column: object
column: IColumnConfig
columnIndex: number
}
`
@ -4518,7 +4518,7 @@ interface IContextMenuArgs {
// 当前单元格节点
cell: HTMLElement
// 当前列信息
column: object
column: IColumnConfig
columnIndex: number
// 配置清除等功能信息
options: object[]
@ -4539,7 +4539,7 @@ interface IFooterCellDblClickArgs {
// 当前单元格节点
cell: HTMLElement
// 当前列信息
column: object
column: IColumnConfig
columnIndex: number
}
`
@ -4556,7 +4556,7 @@ interface IHeaderCellClickArgs {
// 点击表头单元格
cell: HTMLElement
// 当前列信息
column: object
column: IColumnConfig
columnIndex: number
// 当前点击节点过滤标识
triggerFilter: boolean
@ -4571,7 +4571,7 @@ interface IHeaderCellClickArgs {
code: `
interface IHeaderCellDblClickArgs {
// 列数据
column: object
column: IColumnConfig
// 列索引
columnIndex: number
// table组件 vue实例
@ -4589,7 +4589,7 @@ interface IResizableChangeArgs {
// table组件的vue 实例
$table: Component,
// 列配置信息
column: object
column: IColumnConfig
// 拖动列的索引
columnIndex: number
// 是否固定列

View File

@ -10,7 +10,7 @@ export default {
type: 'Component',
defaultValue: '',
desc: {
'zh-CN': '设置IP段之间的分隔符默认图标为IconDotIpv4',
'zh-CN': '设置 ip 段之间的分隔符,默认图标为 IconDotIpv4 ',
'en-US': 'Set the separator between IP segments, default icon is icon-dot-ipv4'
},
mode: ['pc'],

View File

@ -369,6 +369,19 @@ export default {
pcDemo: 'basic-usage',
mfDemo: ''
},
{
name: 'change-compat',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '设置除加减按钮及直接输入数值之外值改变后是否触发change事件',
'en-US':
'Set whether to trigger the change event after the value is changed, except for the plus and minus buttons and direct input of values.'
},
mode: ['pc', 'mobile-first', 'mobile'],
pcDemo: 'change-event',
mfDemo: ''
},
{
name: 'validate-event',
type: 'Boolean',

View File

@ -1,7 +1,10 @@
<template>
<div>
<div>场景1单选 + 可搜索</div>
<br />
<tiny-select
ref="select"
v-model="value"
v-model="value1"
placeholder="请选择"
filterable
:filter-method="filter"
@ -17,6 +20,31 @@
>
</tiny-option>
</tiny-select>
<br />
<br />
<div>场景2多选 + 折叠tag + 可搜索</div>
<br />
<tiny-select
ref="select"
v-model="value2"
placeholder="请选择"
multiple
collapse-tags
filterable
:filter-method="filter"
clearable
title="标题"
>
<tiny-option
v-for="item in options"
v-show="!item.filter"
:key="item.value"
:label="item.label"
:value="item.value"
>
</tiny-option>
</tiny-select>
</div>
</template>
<script>
@ -51,7 +79,8 @@ export default {
label: '北京烤鸭'
}
],
value: ''
value1: '',
value2: []
}
},
methods: {

View File

@ -0,0 +1,24 @@
<template>
<div id="demo-loading"></div>
</template>
<script>
import { Loading } from '@opentiny/vue'
export default {
mounted() {
Loading.service({
background: '#19191960',
text: '自定义遮罩层背景色',
target: document.getElementById('demo-loading'),
tiny_mode: 'mobile'
})
}
}
</script>
<style scoped>
#demo-loading {
height: 100%;
}
</style>

View File

@ -1,14 +1,17 @@
<template>
<div>
<button @click="closeLoading">close Loading</button>
<div id="tiny-loading1" style="width: 100%; height: 120px"></div>
<div class="demo-loading">
<tiny-button type="secondary" @click="closeLoading">关闭 Loading</tiny-button>
<div id="loading-box"></div>
</div>
</template>
<script lang="jsx">
import { Loading } from '@opentiny/vue'
<script>
import { Loading, Button } from '@opentiny/vue'
export default {
components: {
TinyButton: Button
},
data() {
return {
loadingInstance: null
@ -17,7 +20,7 @@ export default {
mounted() {
this.loadingInstance = Loading.service({
tiny_mode: 'mobile',
target: document.getElementById('tiny-loading1')
target: document.getElementById('loading-box')
})
},
methods: {
@ -27,3 +30,10 @@ export default {
}
}
</script>
<style scoped>
#loading-box {
height: 200px;
margin-top: 16px;
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<div id="demo-loading"></div>
</template>
<script>
import { Loading } from '@opentiny/vue'
export default {
mounted() {
Loading.service({
text: '自定义 loading 类名',
customClass: 'my-loading',
target: document.getElementById('demo-loading'),
tiny_mode: 'mobile'
})
}
}
</script>
<style scoped>
#demo-loading {
height: 100%;
}
:deep(.my-loading) {
color: #fff;
background: rgba(0, 0, 0, 0.5);
}
</style>

View File

@ -0,0 +1,61 @@
<template>
<div class="demo-loading">
<tiny-button type="secondary" @click="handleClick" v-loading.lock.fullscreen="showLoading">
指令方式加载全屏Loading
</tiny-button>
<tiny-button type="secondary" @click="handleClick2"> 静态方法加载全屏Loading </tiny-button>
</div>
</template>
<script>
import { Loading, Button } from '@opentiny/vue'
export default {
components: {
TinyButton: Button
},
directives: {
loading: Loading.directive
},
data() {
return {
showLoading: false
}
},
methods: {
handleClick() {
this.showLoading = true
setTimeout(() => {
this.showLoading = false
}, 3000)
},
handleClick2() {
const loading = this.$loading({
lock: true,
text: 'Loading',
tiny_mode: 'mobile'
})
setTimeout(() => {
loading.close()
}, 3000)
}
}
}
</script>
<style scoped>
.demo-loading {
padding: 30px;
}
.demo-loading .tiny-mobile-button {
width: 100%;
margin-bottom: 30px;
}
#demo-loading {
width: 100px;
height: 100px;
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="demo-loading">
<div id="loading-box0"></div>
<div id="loading-box1"></div>
<div id="loading-box2"></div>
<div id="loading-box3"></div>
</div>
</template>
<script>
import { Loading } from '@opentiny/vue'
export default {
mounted() {
// mini
Loading.service({
size: 'mini',
text: 'mini 尺寸',
target: '#loading-box0',
tiny_mode: 'mobile'
})
// small
Loading.service({
size: 'small',
text: 'small 尺寸',
target: '#loading-box1',
tiny_mode: 'mobile'
})
// medium
Loading.service({
size: 'medium',
text: 'medium 尺寸',
target: '#loading-box2',
tiny_mode: 'mobile'
})
// large
Loading.service({
size: 'large',
text: 'large 尺寸',
target: '#loading-box3',
tiny_mode: 'mobile'
})
}
}
</script>
<style scoped>
.demo-loading div {
height: 100px;
border-bottom: 4px solid #fff;
}
#loading-box3 {
height: 150px;
}
</style>

View File

@ -0,0 +1,26 @@
<template>
<div id="demo-loading"></div>
</template>
<script>
import { Loading } from '@opentiny/vue'
import { iconLoading } from '@opentiny/vue-icon'
export default {
mounted() {
Loading.service({
spinner: iconLoading(),
text: '自定义loading图标',
target: document.getElementById('demo-loading'),
background: '#fff',
tiny_mode: 'mobile'
})
}
}
</script>
<style scoped>
#demo-loading {
height: 120px;
}
</style>

View File

@ -0,0 +1,23 @@
<template>
<div id="demo-loading"></div>
</template>
<script>
import { Loading } from '@opentiny/vue'
export default {
mounted() {
Loading.service({
text: '加载中...',
tiny_mode: 'mobile',
target: document.getElementById('demo-loading')
})
}
}
</script>
<style scoped>
#demo-loading {
height: 120px;
}
</style>

View File

@ -6,25 +6,171 @@ export default {
demoId: 'base',
name: {
'zh-CN': '基础用法',
'en-US': 'button type'
'en-US': 'Basic usage'
},
desc: {
'zh-CN': '<p>基础用法</p>',
'en-US': '<p>button type</p>'
'zh-CN': '<p>通过 <code>service</code> 方法显示 Loading ,再通过 <code>close</code> 方法关闭。</p>',
'en-US':
'<p>Use the <code>service</code> method to display Loading, then close it using the <code>close</code> method.</p>'
},
codeFiles: ['base.vue']
},
{
demoId: 'type',
demoId: 'spinner',
name: {
'zh-CN': '类型',
'en-US': 'button type'
'zh-CN': '自定义加载图标',
'en-US': 'Custom Icon'
},
desc: {
'zh-CN': '<p>类型</p>',
'en-US': '<p>button type</p>'
'zh-CN': '<p>通过 <code>spinner</code> 属性自定义加载图标。<p>',
'en-US': '<p>Customize loading icon using the <code>spinner</code> attribute.</p>'
},
codeFiles: ['type.vue']
codeFiles: ['spinner.vue']
},
{
demoId: 'tip-text',
name: {
'zh-CN': '自定义加载提示',
'en-US': 'Custom Tip'
},
desc: {
'zh-CN': '<p>通过 <code>text</code> 自定义加载文字的提示文本。<p>',
'en-US': '<p>Use <code>text</code> to customize loading tip. </p>'
},
codeFiles: ['tip-text.vue']
},
{
demoId: 'background',
name: {
'zh-CN': '自定义遮罩背景色',
'en-US': 'CUstom Mask Background'
},
desc: {
'zh-CN': '<p>通过 <code>background</code> 自定义遮罩层背景颜色。<p>',
'en-US': '<p>Use <code>background</code> property to customize background color of mask.</p>'
},
codeFiles: ['background.vue']
},
{
demoId: 'custom-class',
name: {
'zh-CN': '自定义样式',
'en-US': 'events'
},
desc: {
'zh-CN': '<p>通过 <code>custom-class</code> 指定类名进行样式修改。<p>',
'en-US': '<p>Specify the class name using <code>custom-class</code> to modify the style.</p>'
},
codeFiles: ['custom-class.vue']
},
{
demoId: 'size',
name: {
'zh-CN': '尺寸',
'en-US': 'Size'
},
desc: {
'zh-CN':
'<p>通过在 <code>Loading.service</code> 中设置 <code>size</code> 属性加载不同的大小尺寸,包括 <code>large</code> | <code>medium</code> | <code>small</code> | <code>mini</code> 四种不同大小。不设置时为默认尺寸。<p>',
'en-US':
'<p>By setting the <code>size</code> attribute in the Loading.service, different size dimensions can be loaded, including <code>large</code>, <code>medium</code>, <code>small</code> and <code>mini</code>. If not set, the default size will be used.</p>'
},
codeFiles: ['size.vue']
},
{
demoId: 'global-registry.',
name: {
'zh-CN': '全局加载',
'en-US': 'Global Registry'
},
desc: {
'zh-CN': `<p>通过 <code>v-loading.lock.fullscreen</code> 指令方式或者服务方式进行全局加载,如需使用指令方式全局加载需要如下操作:
<p> Vue 2 版本环境中添加 <code>Vue.use(Loading)</code></p><p>Vue 3 <code>app.use(Loading)</code>.<p>`,
'en-US': `<p>Global loading can be achieved through the <code>v-loading.lock.fullscreen</code> directive or service. To use the directive for global loading, follow these steps:</p><p>For Vue 2 environment, add <code>Vue.use(Loading)</code>;</p><p>For Vue 3 environment, add <code>app.use(Loading)</code>.</p>`
},
codeFiles: ['global-registry.vue']
}
],
apis: [
{
name: 'Loading', // 组件名称展示使用
type: 'loading', // API 类型
properties: [
{
name: 'type',
type: 'string',
defaultValue: 'primary',
desc: {
'zh-CN': '<p>通过type设置不同的加载样式</p>',
'en-US': 'display different button'
},
demoId: 'type'
},
{
name: 'background',
type: 'string',
defaultValue: `'#0000004b'`,
desc: {
'zh-CN': '遮罩层背景色',
'en-US': 'mask background color'
},
demoId: 'background'
},
{
name: 'custom-class',
type: 'string',
defaultValue: '--',
desc: {
'zh-CN': '自定义类名',
'en-US': 'Custom class name'
},
demoId: 'custom-class'
},
{
name: 'spinner',
type: 'Component',
defaultValue: '--',
desc: {
'zh-CN': '自定义加载图标',
'en-US': 'display different button'
},
demoId: 'spinner'
},
{
name: 'target',
type: 'DOM',
defaultValue: 'document.body',
desc: {
'zh-CN':
'需要覆盖的 DOM 节点。可传入一个 DOM 对象或字符串;若传入字符串,则会将其作为参数传入 document.querySelector 以获取到对应 DOM 节点',
'en-US':
'The DOM node to be targeted for coverage. It can be a DOM object or a string; if a string is passed, it will be used as a parameter for document.querySelector to obtain the corresponding DOM node'
},
demoId: 'base'
}
],
method: [
{
name: 'close',
type: '() => void',
defaultValue: '',
desc: {
'zh-CN': '关闭 Loading',
'en-US': 'close loading'
},
demoId: 'base'
},
{
name: 'service',
type: '() => Component',
defaultValue: '',
desc: {
'zh-CN': '创建一个 Loading 组件实例并展示',
'en-US': 'Create a Loading component instance and display it'
},
demoId: 'base'
}
]
}
]
}

View File

@ -14,7 +14,7 @@
<script setup>
import { ref } from 'vue'
import { Alert as TinyAlert, Switch as TinySwitch, Modal } from '@opentiny/vue'
import { Alert as TinyAlert, Switch as TinySwitch, Notify } from '@opentiny/vue'
import { iconCloseCircle } from '@opentiny/vue-icon'
const show = ref(true)
@ -22,6 +22,11 @@ const show = ref(true)
const TinyIconCloseCircle = iconCloseCircle()
const close = () => {
Modal.message('关闭了')
Notify({
type: 'success',
message: '触发关闭事件',
position: 'top-right',
duration: 1000
})
}
</script>

View File

@ -49,7 +49,7 @@ test('关闭按钮事件', async ({ page }) => {
// 点击关闭后警告消失自定义事件modalBox提示出现
await close.click()
await expect(alertWarning).not.toBeVisible()
await expect(page.locator('.tiny-modal__box').getByText('关闭了')).toBeVisible()
await expect(page.locator('.tiny-notify__content').getByText('触发关闭事件')).toBeVisible()
})
test('不可关闭警告', async ({ page }) => {

View File

@ -13,7 +13,7 @@
</template>
<script>
import { Alert, Modal, Switch } from '@opentiny/vue'
import { Alert, Notify, Switch } from '@opentiny/vue'
import { iconCloseCircle } from '@opentiny/vue-icon'
export default {
@ -29,7 +29,12 @@ export default {
},
methods: {
close() {
Modal.message('关闭了')
Notify({
type: 'success',
message: '触发关闭事件',
position: 'top-right',
duration: 10000
})
}
}
}

View File

@ -9,7 +9,7 @@ test('文件选择前确认', async ({ page }) => {
await upload.click()
await page.locator('.tiny-modal').filter({ hasText: 'beforeAddFile 钩子函数' }).isVisible()
const confirmBtn = page.getByRole('button', { name: '确' })
const confirmBtn = page.getByRole('button', { name: '确' })
const [fileChooser] = await Promise.all([page.waitForEvent('filechooser'), confirmBtn.click()])
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires

View File

@ -9,12 +9,12 @@ test('手动上传', async ({ page }) => {
const lists = page.locator('.tiny-upload-list__item')
const [fileChooser] = await Promise.all([page.waitForEvent('filechooser'), upload.click()])
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const path = require('node:path')
const currentPath = path.resolve(__dirname, '测试.jpg')
await fileChooser.setFiles(currentPath)
await expect(lists).toHaveText(/测试.jpg/)
await server.click()
await lists.waitFor({ state: 'hidden', timeout: 3000 })
await expect(lists).toHaveCount(0)
})

View File

@ -20,6 +20,7 @@ test('照片墙', async ({ page }) => {
const dialogClose = page.getByRole('button', { name: 'Close' })
const { width, height } = await first.boundingBox()
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const path = require('node:path')
const currentPath = path.resolve(__dirname, '测试.jpg')
@ -34,5 +35,5 @@ test('照片墙', async ({ page }) => {
await first.hover()
await delButton.click()
await page.waitForTimeout(200)
await li.isHidden()
await first.isHidden()
})

View File

@ -16,7 +16,7 @@ test('图片列表缩略图', async ({ page }) => {
const path = require('node:path')
const currentPath = path.resolve(__dirname, '测试.jpg')
await expect(width).toBeGreaterThanOrEqual(700)
await expect(width).toBeGreaterThanOrEqual(height)
await expect(height).toBeGreaterThanOrEqual(56)
await expect(lists).toHaveCount(2)
await fileChooser.setFiles(currentPath)

View File

@ -13,7 +13,7 @@ test('文件列表', async ({ page }) => {
const path = require('node:path')
const currentPath = path.resolve(__dirname, '测试.jpg')
await expect(width).toBeGreaterThanOrEqual(700)
await expect(width).toBeGreaterThanOrEqual(height)
await expect(height).toBeGreaterThanOrEqual(25, 0)
await expect(items).toHaveCount(2)
await expect(items).toHaveText([/test1/, /test2/])

View File

@ -9,11 +9,11 @@ test('个性化按钮点击事件', async ({ page }) => {
await expect(
page.getByText('点击了确认按钮{"sortType":"page","pageSize":10,"columns":[{"property":"name","order":nu')
).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.locator('.tiny-grid-custom__setting-btn').click()
await page.getByRole('button', { name: '重置' }).click()
await expect(page.getByText('点击了重置按钮')).toBeVisible()
await page.getByRole('button', { name: '确认' }).click()
await page.getByRole('button', { name: '确定' }).nth(1).click()
await page.getByRole('button', { name: '取消' }).click()
await expect(page.getByText('点击了取消按钮undefined')).toBeVisible
})

View File

@ -0,0 +1,50 @@
<template>
<tiny-grid :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="created_date" title="创建时间"></tiny-grid-column>
<tiny-grid-column
field="employees"
title="人数"
:editor="{ component: 'input' }"
:equals="employeesEquals"
></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介"></tiny-grid-column>
</tiny-grid>
</template>
<script setup>
import { ref } from 'vue'
import { Grid as TinyGrid, GridColumn as TinyGridColumn } from '@opentiny/vue'
const tableData = ref([
{
id: '1',
created_date: '2014-04-30 00:56:00',
employees: 800,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '2',
created_date: '2016-07-08 12:36:22',
employees: 300,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '3',
created_date: '2014-02-14 14:14:14',
employees: 1300,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '4',
created_date: '2013-01-13 13:13:13',
employees: 360,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
}
])
const employeesEquals = ({ value, originalValue }) => {
// true
return Number(value) === Number(originalValue)
}
</script>

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('grid-edit#edit-grid-equals')
await page.getByText('800').click()
await page.getByRole('row', { name: '1 2014-04-30 00:56:00' }).getByRole('textbox').fill('800')
await page.getByRole('cell', { name: '人数' }).locator('div').first().click()
await expect(page.locator('.tiny-grid .col__dirty')).toHaveCount(0)
})

View File

@ -0,0 +1,60 @@
<template>
<tiny-grid :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="created_date" title="创建时间"></tiny-grid-column>
<tiny-grid-column
field="employees"
title="人数"
:editor="{ component: 'input' }"
:equals="employeesEquals"
></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介"></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { Grid, GridColumn } from '@opentiny/vue'
export default {
components: {
TinyGrid: Grid,
TinyGridColumn: GridColumn
},
methods: {
employeesEquals({ value, originalValue }) {
// true
return Number(value) === Number(originalValue)
}
},
data() {
return {
tableData: [
{
id: '1',
created_date: '2014-04-30 00:56:00',
employees: 800,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '2',
created_date: '2016-07-08 12:36:22',
employees: 300,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '3',
created_date: '2014-02-14 14:14:14',
employees: 1300,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '4',
created_date: '2013-01-13 13:13:13',
employees: 360,
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
}
]
}
}
}
</script>

View File

@ -7,7 +7,7 @@ test('检查数据是否改变', async ({ page }) => {
await page.getByRole('row', { name: '1 保存' }).locator('input[type="text"]').fill('sdf')
await page.getByRole('row', { name: '1 保存' }).getByRole('button', { name: '保存' }).click()
await expect(page.getByText('保存成功!')).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page
.getByRole('row', {
name: '2 WWWW科技YX公司 华南区 深圳福田区 公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。 保存'
@ -15,5 +15,5 @@ test('检查数据是否改变', async ({ page }) => {
.getByRole('button', { name: '保存' })
.click()
await expect(page.getByText('当前数据未改变!')).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
})

View File

@ -14,23 +14,23 @@ test('获取表格行方法', async ({ page }) => {
await expect(
page.getByText('当前行数据是:{"id":"1","name":"GFD科技YX公司","area":"华东区","address":"福州","introduction":"')
).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '当前行号' }).click()
await expect(page.getByText('当前选中行号是0')).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: 'Radio单选选中行' }).click()
await expect(
page.getByText('单选选中行数据是:{"id":"1","name":"GFD科技YX公司","area":"华东区","address":"福州","introduction"')
).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: 'rowId获取当前行' }).click()
await expect(
page.getByText('根据 rowId 获取的当前行:{"id":"1","name":"GFD科技YX公司","area":"华东区","address":"福州","introd')
).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: 'tr元素获取行信息' }).click()
await expect(

View File

@ -1,5 +1,5 @@
<template>
<tiny-grid border :data="tableData">
<tiny-grid :data="tableData">
<tiny-grid-column type="index" width="60" fixed="left"></tiny-grid-column>
<tiny-grid-column type="selection" width="60" fixed="left"></tiny-grid-column>
<tiny-grid-column field="name" title="公司名称" width="500"></tiny-grid-column>

View File

@ -0,0 +1,132 @@
<template>
<tiny-grid
:data="tableData"
column-min-width="100"
auto-resize
:column-anchor="columnAnchor"
:optimization="{ scrollX: { gt: 20 } }"
>
<tiny-grid-column field="name0" title="名称0" sortable fixed="left"></tiny-grid-column>
<tiny-grid-column field="name1" title="名称1" sortable fixed="left"></tiny-grid-column>
<tiny-grid-column field="name2" title="名称2" sortable fixed="left"></tiny-grid-column>
<tiny-grid-column field="name3" title="名称3" sortable></tiny-grid-column>
<tiny-grid-column field="name4" title="名称4" sortable></tiny-grid-column>
<tiny-grid-column field="name5" title="名称5" sortable></tiny-grid-column>
<tiny-grid-column field="name6" title="名称6" sortable></tiny-grid-column>
<tiny-grid-column field="name7" title="名称7" sortable></tiny-grid-column>
<tiny-grid-column field="name8" title="名称8" sortable></tiny-grid-column>
<tiny-grid-column field="name9" title="名称9" sortable></tiny-grid-column>
<tiny-grid-column field="name10" title="名称10" sortable></tiny-grid-column>
<tiny-grid-column field="name" title="名称" sortable></tiny-grid-column>
<tiny-grid-column field="name11" title="名称11" sortable></tiny-grid-column>
<tiny-grid-column field="name12" title="名称12" sortable></tiny-grid-column>
<tiny-grid-column field="name13" title="名称13" sortable></tiny-grid-column>
<tiny-grid-column field="name14" title="名称14" sortable></tiny-grid-column>
<tiny-grid-column field="name15" title="名称15" sortable></tiny-grid-column>
<tiny-grid-column field="name16" title="名称16" sortable></tiny-grid-column>
<tiny-grid-column field="name17" title="名称17" sortable></tiny-grid-column>
<tiny-grid-column field="name18" title="名称18" sortable></tiny-grid-column>
<tiny-grid-column field="name19" title="名称19" sortable></tiny-grid-column>
<tiny-grid-column field="name20" title="名称20" sortable></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数" sortable></tiny-grid-column>
<tiny-grid-column field="name21" title="名称21" sortable></tiny-grid-column>
<tiny-grid-column field="name22" title="名称22" sortable></tiny-grid-column>
<tiny-grid-column field="name23" title="名称23" sortable></tiny-grid-column>
<tiny-grid-column field="name24" title="名称24" sortable></tiny-grid-column>
<tiny-grid-column field="name25" title="名称25" sortable></tiny-grid-column>
<tiny-grid-column field="name26" title="名称26" sortable></tiny-grid-column>
<tiny-grid-column field="name27" title="名称27" sortable></tiny-grid-column>
<tiny-grid-column field="name28" title="名称28" sortable></tiny-grid-column>
<tiny-grid-column field="name29" title="名称29" sortable></tiny-grid-column>
<tiny-grid-column field="name30" title="名称30" sortable></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="name31" title="名称31" sortable></tiny-grid-column>
<tiny-grid-column field="name32" title="名称32" sortable></tiny-grid-column>
<tiny-grid-column field="name33" title="名称33" sortable></tiny-grid-column>
<tiny-grid-column field="name34" title="名称34" sortable></tiny-grid-column>
<tiny-grid-column field="name35" title="名称35" sortable></tiny-grid-column>
<tiny-grid-column field="name36" title="名称36" sortable></tiny-grid-column>
<tiny-grid-column field="name37" title="名称37" sortable></tiny-grid-column>
<tiny-grid-column field="name38" title="名称38" sortable></tiny-grid-column>
<tiny-grid-column field="name39" title="名称39" sortable></tiny-grid-column>
<tiny-grid-column field="name40" title="名称40" sortable></tiny-grid-column>
<tiny-grid-column field="name41" title="名称41" sortable></tiny-grid-column>
<tiny-grid-column field="name42" title="名称42" sortable></tiny-grid-column>
<tiny-grid-column field="name43" title="名称43" sortable></tiny-grid-column>
<tiny-grid-column field="name44" title="名称44" sortable></tiny-grid-column>
<tiny-grid-column field="name45" title="名称45" sortable></tiny-grid-column>
<tiny-grid-column field="name46" title="名称46" sortable></tiny-grid-column>
<tiny-grid-column field="name47" title="名称47" sortable></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
<tiny-grid-column field="name48" title="名称48" sortable fixed="right"></tiny-grid-column>
<tiny-grid-column field="name49" title="名称49" sortable fixed="right"></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { Grid, GridColumn } from '@opentiny/vue'
import { IconMarkOn } from '@opentiny/vue-icon'
export default {
components: {
TinyGrid: Grid,
TinyGridColumn: GridColumn
},
data() {
return {
columnAnchor: [
'name',
'address',
['introduction', '简单介绍'],
[
'employees',
[
'雇员数量-自定义渲染',
({ h, anchor: { active, field, label }, action }) =>
h(
'div',
{
class: { 'tiny-grid__column-anchor-item': true, 'tiny-grid__column-anchor-item--active': active },
on: { click: (e) => action(field, e) }
},
[active ? h(IconMarkOn(), { class: 'tiny-grid__column-anchor-item-icon' }) : null, h('span', label)]
)
]
]
],
tableData: [
{
id: '1',
name: 'GFD科技有限公司',
address: '福州',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。',
employees: 800,
name0: '1-name-0',
name1: '1-name-1',
name2: '1-name-2'
},
{
id: '2',
name: 'WWW科技有限公司',
address: '深圳福田区',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。',
employees: 300,
name0: '2-name-0',
name1: '2-name-1',
name2: '2-name-2'
},
{
id: '3',
name: 'RFV有限责任公司',
address: '中山市',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。',
employees: 1300,
name0: '3-name-0',
name1: '3-name-1',
name2: '3-name-2'
}
]
}
}
}
</script>

View File

@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'
test('滚动到指定位置', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-large-data#large-data-scroll-to')
await page.waitForTimeout(1000)
await page.waitForTimeout(3000)
await page.getByRole('button', { name: '滚动到500列' }).click()
await page.waitForTimeout(200)
await expect(page.getByText('col508')).toBeVisible()

View File

@ -4,5 +4,5 @@ test('开启加载中状态', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-loading#loading-grid-loading-tip')
await page.getByRole('button', { name: '重试' }).click()
await expect(page.locator('.tiny-grid-loading__wrap')).toBeVisible()
await expect(page.locator('.tiny-grid-loading')).toBeVisible()
})

View File

@ -122,6 +122,6 @@ function getData({ page }) {
}
function pageChange() {
Modal.message('取消分页')
Modal.message({ message: '取消分页', status: 'info' })
}
</script>

View File

@ -129,7 +129,7 @@ export default {
})
},
pageChange() {
Modal.message('取消分页')
Modal.message({ message: '取消分页', status: 'info' })
}
}
}

View File

@ -0,0 +1,40 @@
<template>
<tiny-grid :data="tableData">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="date1" title="日期1" format-text="date"></tiny-grid-column>
<tiny-grid-column field="date2" title="日期2" format-text="date"></tiny-grid-column>
<tiny-grid-column
field="date3"
title="日期3"
format-text="date"
:format-config="{ valueFormat: 'dd/MM/yyyy' }"
></tiny-grid-column>
<tiny-grid-column
field="date4"
title="日期4"
format-text="date"
:format-config="{ valueFormat: 'dd/MM/yyyy' }"
></tiny-grid-column>
<tiny-grid-column
field="date5"
title="日期5"
format-text="date"
:format-config="{ valueFormat: 'MM/dd/yyyy' }"
></tiny-grid-column>
</tiny-grid>
</template>
<script setup>
import { ref } from 'vue'
import { Grid as TinyGrid, GridColumn as TinyGridColumn } from '@opentiny/vue'
const tableData = ref([
{
id: '1',
date1: 1719849600000,
date2: new Date(),
date3: '07/02/2024', // Date.parse 7 2
date4: '15/02/2024', // Date.parse
date5: '02/15/2024' // Date.parse 2 15
}
])
</script>

View File

@ -0,0 +1,9 @@
import { test, expect } from '@playwright/test'
test('日期渲染器', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-renderer#renderer-inner-renderer-date')
await expect(page.getByText('-07-02')).toBeVisible()
await expect(page.getByText('-02-15').first()).toBeVisible()
await expect(page.getByText('-02-15').nth(1)).toBeVisible()
})

View File

@ -0,0 +1,50 @@
<template>
<tiny-grid :data="tableData">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="date1" title="日期1" format-text="date"></tiny-grid-column>
<tiny-grid-column field="date2" title="日期2" format-text="date"></tiny-grid-column>
<tiny-grid-column
field="date3"
title="日期3"
format-text="date"
:format-config="{ valueFormat: 'dd/MM/yyyy' }"
></tiny-grid-column>
<tiny-grid-column
field="date4"
title="日期4"
format-text="date"
:format-config="{ valueFormat: 'dd/MM/yyyy' }"
></tiny-grid-column>
<tiny-grid-column
field="date5"
title="日期5"
format-text="date"
:format-config="{ valueFormat: 'MM/dd/yyyy' }"
></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { Grid, GridColumn } from '@opentiny/vue'
export default {
components: {
TinyGrid: Grid,
TinyGridColumn: GridColumn
},
data() {
return {
tableData: [
{
id: '1',
date1: 1719849600000,
date2: new Date(),
date3: '07/02/2024', // Date.parse 7 2
date4: '15/02/2024', // Date.parse
date5: '02/15/2024' // Date.parse 2 15
}
]
}
}
}
</script>

View File

@ -1,33 +1,40 @@
<template>
<p>medium</p>
<tiny-grid :data="tableData" size="medium">
<tiny-tabs v-model="activeName" tab-style="card">
<tiny-tab-item title="medium" name="medium">
<tiny-grid :data="tableData" size="medium" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
<p>small</p>
<tiny-grid :data="tableData" size="small">
</tiny-tab-item>
<tiny-tab-item title="small" name="small">
<tiny-grid :data="tableData" size="small" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
<p>mini</p>
<tiny-grid :data="tableData" size="mini">
</tiny-tab-item>
<tiny-tab-item title="mini" name="mini">
<tiny-grid :data="tableData" size="mini" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
</tiny-tab-item>
</tiny-tabs>
</template>
<script setup lang="jsx">
<script setup>
import { ref } from 'vue'
import { Grid as TinyGrid, GridColumn as TinyGridColumn } from '@opentiny/vue'
import { Grid as TinyGrid, GridColumn as TinyGridColumn, Tabs as TinyTabs, TabItem as TinyTabItem } from '@opentiny/vue'
const activeName = ref('medium')
const tableData = ref([
{

View File

@ -5,11 +5,15 @@ test('尺寸', async ({ page }) => {
await page.goto('grid-size#size-grid-size')
const mediumTd = page.locator('.size__medium .tiny-grid-body__column').first()
const { height: heightMedium } = await mediumTd.boundingBox()
await page.getByText('small').click()
await page.waitForTimeout(500)
const smallTd = page.locator('.size__small .tiny-grid-body__column').first()
const { height: heightSamll } = await smallTd.boundingBox()
const miniTd = page.locator('.size__small .tiny-grid-body__column').first()
await page.getByText('mini').click()
await page.waitForTimeout(500)
const miniTd = page.locator('.size__mini .tiny-grid-body__column').first()
const { height: heightMini } = await miniTd.boundingBox()
await expect(heightMedium).toBeGreaterThanOrEqual(heightSamll)
await expect(heightSamll).toBeGreaterThanOrEqual(heightMini)
await expect(heightMedium).toBeGreaterThan(heightSamll)
await expect(heightSamll).toBeGreaterThan(heightMini)
})

View File

@ -1,35 +1,42 @@
<template>
<p>medium</p>
<tiny-grid :data="tableData" size="medium">
<tiny-tabs v-model="activeName" tab-style="card">
<tiny-tab-item title="medium" name="medium">
<tiny-grid :data="tableData" size="medium" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
<p>small</p>
<tiny-grid :data="tableData" size="small">
</tiny-tab-item>
<tiny-tab-item title="small" name="small">
<tiny-grid :data="tableData" size="small" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
<p>mini</p>
<tiny-grid :data="tableData" size="mini">
</tiny-tab-item>
<tiny-tab-item title="mini" name="mini">
<tiny-grid :data="tableData" size="mini" :auto-resize="true">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
<tiny-grid-column field="employees" title="员工数"></tiny-grid-column>
<tiny-grid-column field="createdDate" title="创建日期"></tiny-grid-column>
<tiny-grid-column field="city" title="城市"></tiny-grid-column>
</tiny-grid>
</tiny-tab-item>
</tiny-tabs>
</template>
<script lang="jsx">
import { Grid, GridColumn } from '@opentiny/vue'
import { Grid, GridColumn, Tabs, TabItem } from '@opentiny/vue'
export default {
components: {
TinyTabs: Tabs,
TinyTabItem: TabItem,
TinyGrid: Grid,
TinyGridColumn: GridColumn
},
@ -101,7 +108,8 @@ export default {
}
]
return {
tableData
tableData,
activeName: 'medium'
}
}
}

View File

@ -8,7 +8,6 @@
:data="tableData"
ref="grid"
highlight-current-row
border
:edit-config="{ trigger: 'click', mode: 'cell', showStatus: true }"
>
<tiny-grid-column type="index" width="60"></tiny-grid-column>

View File

@ -0,0 +1,65 @@
<template>
<tiny-teleport to="body" :disabled="!isFullscreen">
<tiny-grid :data="tableData" :style="fullscreenStyle" @toolbar-button-click="toolbarButtonClick">
<template #toolbar>
<tiny-grid-toolbar :buttons="toolbarButtons"></tiny-grid-toolbar>
</template>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
</tiny-grid>
</tiny-teleport>
</template>
<script setup>
import { Teleport as TinyTeleport } from '@opentiny/vue-common'
import { ref, nextTick } from 'vue'
import { Grid as TinyGrid, GridColumn as TinyGridColumn, GridToolbar as TinyGridToolbar } from '@opentiny/vue'
const isFullscreen = ref(false)
const fullscreenStyle = ref('')
const toolbarButtons = [
{ code: 'enterFullscreen', name: '进入全屏模式' },
{ code: 'exitFullscreen', name: '退出全屏模式' }
]
const tableData = ref([
{
id: '1',
name: 'GFD科技有限公司',
area: '华东区',
address: '福州',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '2',
name: 'WWWW科技有限公司',
area: '华南区',
address: '深圳福田区',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '3',
name: 'RFV有限责任公司',
area: '华南区',
address: '中山市',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
}
])
const toolbarButtonClick = ({ code, $grid }) => {
isFullscreen.value = false
fullscreenStyle.value = ''
if (code === 'enterFullscreen') {
isFullscreen.value = true
fullscreenStyle.value = 'position:fixed;width:100vw;height:100vh;z-index:9999;top:0;left:0;'
}
nextTick(() => {
$grid.recalculate()
})
}
</script>

View File

@ -0,0 +1,10 @@
import { test, expect } from '@playwright/test'
test('推荐基于 Teleport 的全屏方案', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-toolbar#toolbar-grid-full-screen-teleport')
await page.getByRole('button', { name: '进入全屏模式' }).click()
await expect(page.locator('body>.tiny-grid')).toBeVisible()
await page.getByRole('button', { name: '退出全屏模式' }).click()
await expect(page.locator('body>.tiny-grid')).toHaveCount(0)
})

View File

@ -0,0 +1,73 @@
<template>
<tiny-teleport to="body" :disabled="!isFullscreen">
<tiny-grid :data="tableData" :style="fullscreenStyle" @toolbar-button-click="toolbarButtonClick">
<template #toolbar>
<tiny-grid-toolbar :buttons="toolbarButtons"></tiny-grid-toolbar>
</template>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
</tiny-grid>
</tiny-teleport>
</template>
<script>
import { Teleport } from '@opentiny/vue-common'
import { Grid, GridColumn, GridToolbar } from '@opentiny/vue'
export default {
components: {
TinyTeleport: Teleport,
TinyGrid: Grid,
TinyGridColumn: GridColumn,
TinyGridToolbar: GridToolbar
},
data() {
return {
isFullscreen: false,
fullscreenStyle: '',
toolbarButtons: [
{ code: 'enterFullscreen', name: '进入全屏模式' },
{ code: 'exitFullscreen', name: '退出全屏模式' }
],
tableData: [
{
id: '1',
name: 'GFD科技有限公司',
area: '华东区',
address: '福州',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '2',
name: 'WWWW科技有限公司',
area: '华南区',
address: '深圳福田区',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
},
{
id: '3',
name: 'RFV有限责任公司',
area: '华南区',
address: '中山市',
introduction: '公司技术和研发实力雄厚是国家863项目的参与者并被政府认定为“高新技术企业”。'
}
]
}
},
methods: {
toolbarButtonClick({ code, $grid }) {
this.isFullscreen = false
this.fullscreenStyle = ''
if (code === 'enterFullscreen') {
this.isFullscreen = true
this.fullscreenStyle = 'position:fixed;width:100vw;height:100vh;z-index:9999;top:0;left:0;'
}
this.$nextTick(() => $grid.recalculate())
}
}
}
</script>

View File

@ -8,7 +8,7 @@ test.describe('树表增删改功能', () => {
await page.getByRole('row', { name: '1 新数据' }).getByRole('textbox').click()
await page.getByRole('row', { name: '1 新数据' }).getByRole('textbox').fill('zzcd')
await page.locator('.tiny-grid-toolbar').click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await expect(page.getByRole('cell', { name: 'zzcd' })).toBeVisible()
await page.getByRole('row', { name: '1 新数据' }).locator('path').nth(1).click()
await page.getByRole('button', { name: '移除选中' }).click()

View File

@ -13,7 +13,7 @@ test('提交前校验', async ({ page }) => {
await page.getByRole('button', { name: '提交数据' }).click()
await expect(page.getByText('校验不通过', { exact: true })).toBeVisible()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '确' }).click()
await page.getByRole('button', { name: '保存 Promise' }).click()
await expect(page.getByText('校验不通过,触发了 catch', { exact: true })).toBeVisible()
})

View File

@ -79,6 +79,17 @@ export default {
},
'codeFiles': ['edit/status-of-editing.vue']
},
{
'demoId': 'edit-grid-equals',
'name': { 'zh-CN': '自定义比较方法', 'en-US': 'Enable editing' },
'desc': {
'zh-CN':
'<p>配置列属性 <code>equals</code> 可实现列值自定义比较。此方法接收字段原始值和当前值等作为参数,期望用户返回布尔结果。返回 <code>false</code> 表示已改变,<code>true</code> 表示未改变,其它值表示使用内部预置比较。表格也支持 <code>equals</code> 属性,用于定义所有字段的比较方法,使用参数 <code>field</code> 区分具体的字段,此方式的影响范围是整个表格,需要谨慎使用。</p>',
'en-US':
'<p>Table attribute settings<code>edit-config</code>Enable the editing mode, Set <code>showStatus</code> in the attribute object to enable or disable the cell update status (inverted triangle update flag in the upper left corner of the cell). The default value is <code>true</code>. </p>\n'
},
'codeFiles': ['edit/grid-equals.vue']
},
{
'demoId': 'edit-trigger-mode-for-editing',
'name': { 'zh-CN': '触发编辑方式', 'en-US': 'Click to trigger editing' },

View File

@ -30,55 +30,7 @@ export default {
'name': { 'zh-CN': '树表虚拟滚动', 'en-US': 'Virtual scrolling of the tree table' },
'desc': {
'zh-CN': `
<p> <code>optimization</code> </p>
<table class="api-table">
<thead>
<tr>
<th>名称</th>
<th>类型</th>
<th>描述</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>optimization.scrollX.gt</td> <td>number</td>
<td>指定大于多少列时自动启动 X 虚拟滚动</td>
<td>100</td></tr> <tr><td>optimization.scrollX.rSize</td>
<td>number</td>
<td>每次渲染列数</td>
<td></td>
</tr>
<tr>
<td>optimization.scrollX.vSize</td>
<td>number</td>
<td>指定可视区域列数</td>
<td></td>
</tr>
<tr>
<td>optimization.scrollX.adaptive</td> <td>boolean</td>
<td>自动适配最优的渲染方式设置为 false 列数组只会在滚动完成后截取一次便于大数据场景提升性能但是会短暂白屏渲染完成后即恢复</td>
<td>true</td>
</tr>
<tr>
<td>optimization.scrollY.gt</td>
<td>number</td>
<td>指定大于多少行时自动启动 Y 虚拟滚动</td>
<td>500</td>
</tr>
<tr>
<td>optimization.scrollY.rSize</td>
<td>number</td> <td></td>
<td></td>
</tr>
<tr>
<td>optimization.scrollY.adaptive</td>
<td>boolean</td>
<td>自动适配最优的渲染方式设置为 false 行数组只会在滚动完成后截取一次便于大数据场景提升性能但是会短暂白屏渲染完成后即恢复</td>
<td>true</td>
</tr>
</tbody>
</table>
<p>通过 <code>optimization</code> <code>IOptimizationConfig</code> </p>
`,
'en-US': ''
},

View File

@ -44,6 +44,17 @@ export default {
},
'codeFiles': ['renderer/custom-renderer.vue']
},
{
'demoId': 'renderer-inner-renderer-date',
'name': { 'zh-CN': '日期渲染器', 'en-US': 'Custom Renderer' },
'desc': {
'zh-CN':
'<p>在日期字段为字符串值时,需要给日期渲染器提供 <code>valueFormat</code> 配置才能正常解析日期字符串。</p>\n',
'en-US':
'<p>The custom renderer can customize the rendering of cells or a <code>vue component</code>. Configure <code>renderer</code> in the <code>grid-column</code> column to support method and object configuration. For details, see the following example. </p>\n'
},
'codeFiles': ['renderer/inner-renderer-date.vue']
},
{
'demoId': 'render-async-colunm-render',
'name': { 'zh-CN': '列异步数据渲染', 'en-US': 'Column Asynchronous Data Rendering' },

View File

@ -114,6 +114,15 @@ export default {
},
'codeFiles': ['toolbar/grid-full-screen-height.vue']
},
{
'demoId': 'toolbar-grid-full-screen-teleport',
'name': { 'zh-CN': '推荐基于 Teleport 的全屏方案', 'en-US': 'Change the table height in full screen mode' },
'desc': {
'zh-CN': '<p>通过 <code>teleport</code> 实现表格全屏。</p>',
'en-US': 'For details, see the following example.'
},
'codeFiles': ['toolbar/grid-full-screen-teleport.vue']
},
{
'demoId': 'toolbar-custom-toolbar',
'name': { 'zh-CN': '工具栏自定义插槽', 'en-US': 'Toolbar Custom Slot' },

View File

@ -22,14 +22,14 @@ function baseClick() {
{
on: {
click: (e) => {
params.footerSlotParams.confirm(e)
params.confirm(e)
}
},
props: { type: 'primary' }
},
'点我确定'
),
h(TinyButton, { on: { click: (e) => params.footerSlotParams.cancel(e) } }, '点我取消')
h(TinyButton, { on: { click: (e) => params.cancel(e) } }, '点我取消')
]
}
})

View File

@ -1,10 +1,13 @@
<template>
<tiny-numeric v-model="value" @change="onChange"></tiny-numeric>
<div style="display: flex">
<tiny-numeric :change-compat="false" v-model="value" @change="onChange"></tiny-numeric>
<tiny-button @click="clickChange">修改</tiny-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Numeric as TinyNumeric, Modal } from '@opentiny/vue'
import { Numeric as TinyNumeric, Modal, Button as TinyButton } from '@opentiny/vue'
const value = ref(1)
@ -14,4 +17,8 @@ const onChange = (newVal: number, oldVal: number) => {
status: 'info'
})
}
const clickChange = () => {
value.value = 22
}
</script>

View File

@ -1,13 +1,17 @@
<template>
<tiny-numeric v-model="value" @change="onChange"></tiny-numeric>
<div style="display: flex">
<tiny-numeric :change-compat="false" v-model="value" @change="onChange"></tiny-numeric>
<tiny-button @click="clickChange">修改</tiny-button>
</div>
</template>
<script lang="ts">
import { Numeric, Modal } from '@opentiny/vue'
import { Numeric, Modal, Button } from '@opentiny/vue'
export default {
components: {
TinyNumeric: Numeric
TinyNumeric: Numeric,
TinyButton: Button
},
data() {
return {
@ -20,6 +24,9 @@ export default {
message: '新值:' + newVal + ',旧值:' + oldVal,
status: 'info'
})
},
clickChange() {
this.value = 22
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<p>1数值精度</p>
<tiny-numeric v-model="value" :precision="precision" :format="format"></tiny-numeric>
<tiny-numeric v-model="value" :precision="precision"></tiny-numeric>
<p>2数值格式</p>
<tiny-numeric style="width: 300px" v-model="value1" :format="format"></tiny-numeric>
</template>

View File

@ -4,19 +4,18 @@ test('数值精度', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('numeric#precision')
const numericInput = page.getByRole('spinbutton')
await numericInput.fill('2.2222')
await numericInput.blur()
const numericInput = page.getByRole('spinbutton').first()
let actualValue = await numericInput.inputValue()
expect(actualValue).toEqual('2.22')
await numericInput.fill('8.88888')
await page.locator('#precision').getByRole('button').nth(1).click()
await page.locator('#precision').getByRole('button').first().click()
await numericInput.blur()
actualValue = await numericInput.inputValue()
expect(actualValue).toEqual('8.89')
expect(actualValue).toEqual('1.00')
await numericInput.fill('6.66')
await numericInput.blur()
actualValue = await numericInput.inputValue()
expect(actualValue).toEqual('6.66')
const numericFormatInput = page.getByRole('spinbutton').nth(1)
let actualFormatValue = await numericFormatInput.inputValue()
await page.locator('#precision').getByRole('button').nth(3).click()
await page.locator('#precision').getByRole('button').nth(2).click()
await numericFormatInput.blur()
expect(actualFormatValue).toEqual('$12,34,56,879.4455@')
})

View File

@ -9,7 +9,7 @@ test('分页变更前置处理', async ({ page }) => {
const prev = pager.locator('.tiny-pager__btn-prev')
const next = pager.locator('.tiny-pager__btn-next')
const tipModal = page.locator('.tiny-modal__box').filter({ hasText: '消息提示' })
const confirmBtn = tipModal.getByRole('button', { name: '确' })
const confirmBtn = tipModal.getByRole('button', { name: '确' })
await pager.locator('li').getByText('6').click()
await expect(tipModal).toBeVisible()

View File

@ -13,7 +13,7 @@ test('禁用和尺寸', async ({ page }) => {
await demo.locator('.tiny-switch').click()
await expect(sizeChange).toHaveCSS('color', 'rgb(138, 142, 153)')
await expect(sizeChange).toHaveCSS('border-top-color', 'rgb(223, 225, 230)')
await expect(sizeChange).toHaveCSS('border-top-color', 'rgba(0, 0, 0, 0)')
await expect(prev).toBeDisabled()
await expect(next).toBeDisabled()
await expect(pageItem.first()).toHaveCSS('cursor', 'not-allowed')

View File

@ -11,7 +11,7 @@
import { ref } from 'vue'
import { Steps as TinySteps, Modal } from '@opentiny/vue'
const advancedActive = ref(2)
const advancedActive = ref(1)
const data = ref([
{ name: 'Basic Info', count: 3, status: 'doing' },
{ name: 'BOQ Info', count: 0, status: 'done' },

View File

@ -11,11 +11,11 @@ test('高级向导', async ({ page }) => {
await expect(stepsWrapper).toBeVisible()
await expect(nodes.first()).toHaveClass('doing')
await expect(nodes.nth(1)).toHaveClass('done')
await expect(nodes.nth(2)).toHaveClass(/current/)
await expect(nodes.nth(1)).toHaveClass(/done/)
await expect(nodes.nth(1)).toHaveClass(/current/)
await nodes.nth(3).click()
await expect(nodes.nth(3)).toHaveClass(/current/)
await expect(nodes.nth(2)).not.toHaveClass(/current/)
await expect(nodes.nth(1)).not.toHaveClass(/current/)
// advanced 高级向导模式
const advancedSteps = page.locator('.pc-demo .tiny-steps-senior')

View File

@ -16,7 +16,7 @@ export default {
},
data() {
return {
advancedActive: 2,
advancedActive: 1,
data: [
{ name: 'Basic Info', count: 3, status: 'done' },
{ name: 'BOQ Info', count: 0, status: 'doing' },

View File

@ -3,7 +3,7 @@
<tiny-button type="primary" @click="changeShape">
点击切换 shape {{ shape === 'dot' ? 'circle' : 'dot' }}
</tiny-button>
<tiny-time-line vertical :data="data" :shape="shape"></tiny-time-line>
<tiny-time-line vertical :data="data" :shape="shape" :active="active" @click="onClick"></tiny-time-line>
</div>
</template>
@ -17,12 +17,15 @@ const data = reactive([
{ name: '已签收', time: '2019-11-13 20:45:50' },
{ name: '已评价', time: '2019-11-14 20:45:50' }
])
const active = ref(1)
const shape = ref('dot')
const changeShape = () => {
shape.value = shape.value === 'dot' ? 'circle' : 'dot'
}
const onClick = (index: number) => {
active.value = index
}
</script>
<style>

View File

@ -7,6 +7,8 @@ test('圆点外观', async ({ page }) => {
const timeline = page.locator('#shape .tiny-steps-timeline')
await expect(timeline.locator('.dot').first()).toBeVisible()
await expect(timeline.locator('.tiny-timeline-item__content .time').first()).toBeVisible()
await timeline.getByText('已签收').click()
await expect(timeline.locator('.tiny-timeline-item').nth(2)).toHaveClass(/process-current/)
await page.getByRole('button', { name: '点击切换 shape 为 circle' }).click()
await expect(timeline.locator('.dot').first()).not.toBeVisible()

View File

@ -3,7 +3,7 @@
<tiny-button type="primary" @click="changeShape">
点击切换 shape {{ shape === 'dot' ? 'circle' : 'dot' }}
</tiny-button>
<tiny-time-line vertical :data="data" :shape="shape"></tiny-time-line>
<tiny-time-line vertical :data="data" :shape="shape" :active="active" @click="onClick"></tiny-time-line>
</div>
</template>
@ -23,12 +23,16 @@ export default {
{ name: '已签收', time: '2019-11-13 20:45:50' },
{ name: '已评价', time: '2019-11-14 20:45:50' }
],
active: 1,
shape: 'dot'
}
},
methods: {
changeShape() {
this.shape = this.shape === 'dot' ? 'circle' : 'dot'
},
onClick(index: number) {
this.active = index
}
}
}

View File

@ -1,4 +1,5 @@
<template>
<div>
<div class="box">
Effects 示例
<tiny-tooltip content="dark 提示文字" placement="top">
@ -26,6 +27,7 @@
<tiny-button>success</tiny-button>
</tiny-tooltip>
</div>
</div>
</template>
<script setup lang="jsx">

View File

@ -1,4 +1,5 @@
<template>
<div>
<div class="box">
Effects 示例
<tiny-tooltip content="dark 提示文字" placement="top">
@ -26,6 +27,7 @@
<tiny-button>success</tiny-button>
</tiny-tooltip>
</div>
</div>
</template>
<script lang="jsx">

View File

@ -8,6 +8,7 @@ import { reactive } from 'vue'
const data = reactive({
imgUrl: `${import.meta.env.VITE_APP_BUILD_BASE_URL}static/images/ld.png`,
userName: '张三',
roleNumber: 'z00100001',
values: [
{ text: '部门', value: '某部门' },

View File

@ -16,7 +16,7 @@ export default {
},
desc: {
'zh-CN':
'<p>通过 <code>content</code> 属性设置水印的文字。</br>通过 <code>font</code> 属性设置水印的样式</p>',
'<p>通过 <code>content</code> 属性设置水印的文字。</br>通过 <code>font</code> 属性设置水印的样式</p>',
'en-US':
'Set the text of the watermark through the <code>content</code> attribute.</br> Set the style of the watermark through the <code>font</code> attribute. '
},

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/vue-docs",
"version": "2.2.13",
"version": "2.2.20",
"license": "MIT",
"scripts": {
"start": "vite",

View File

@ -36,6 +36,19 @@ const getRuntime = (version) => {
return `${cdnHost}/@opentiny/vue@${useVersion}/runtime/`
}
const changeImportSuffix = (imports, verison) => {
const newImports = {}
Object.keys(imports).forEach((key) => {
const url = imports[key]
if (url.startsWith(getRuntime(verison))) {
newImports[key] = url.replace('.mjs', '.js')
} else {
newImports[key] = url
}
})
return newImports
}
const createImportMap = (version) => {
const imports = {
'@opentiny/vue': `${getRuntime(version)}tiny-vue.mjs`,
@ -54,7 +67,7 @@ const createImportMap = (version) => {
imports['@opentiny/vue-icon'] = `${getRuntime(version)}tiny-vue-icon-saas.mjs`
}
return {
imports
imports: changeImportSuffix(imports, version)
}
}
@ -62,6 +75,9 @@ const getTinyTheme = (version) => {
if (isMobileFirst) {
return `${getRuntime(version)}tailwind.css`
}
if (isSaas) {
return `${getRuntime(version)}index.css`
}
let theme = tinyTheme
if (!['smb', 'default', 'aurora', 'saas'].includes(theme)) {
theme = 'default'

View File

@ -452,6 +452,7 @@ table.api-table {
text-decoration: none;
color: #5e7ce0;
cursor: pointer;
word-wrap: break-word;
}
tbody tr:hover {

View File

@ -30,7 +30,7 @@
"@playwright/test": "^1.40.1",
"@vitest/ui": "^0.31.0",
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
"@vue/composition-api": "1.2.2",
"@vue/composition-api": "1.7.2",
"@vue/runtime-dom": "^3.2.31",
"@vue/test-utils": "^1.3.3",
"jsdom": "^21.0.0",

View File

@ -33,7 +33,21 @@ export const getVuePlugins = (vueVersion: string) => {
}
}
}),
vue2SvgPlugin({ svgoConfig: { plugins: ['preset-default', 'prefixIds'] } })
vue2SvgPlugin({
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
},
'prefixIds'
]
}
})
]
},
'2.7': () => {
@ -52,7 +66,21 @@ export const getVuePlugins = (vueVersion: string) => {
}
}),
vue27JsxPlugin({ injectH: false }),
vue2SvgPlugin({ svgoConfig: { plugins: ['preset-default', 'prefixIds'] } })
vue2SvgPlugin({
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
},
'prefixIds'
]
}
})
]
},
'3': () => {
@ -68,7 +96,22 @@ export const getVuePlugins = (vueVersion: string) => {
}
}),
vue3JsxPlugin(),
vue3SvgPlugin({ defaultImport: 'component', svgoConfig: { plugins: ['preset-default', 'prefixIds'] } })
vue3SvgPlugin({
defaultImport: 'component',
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
},
'prefixIds'
]
}
})
]
}
}
@ -146,7 +189,7 @@ export const getBaseConfig = ({ vueVersion, dtsInclude, dts, buildTarget, isRunt
})
if (filePath.includes('vue-common') && vueVersion === '2') {
dependencies['@vue/composition-api'] = '~1.2.2'
dependencies['@vue/composition-api'] = '1.7.2'
}
const matchList = ['vue-icon', 'vue-icon-saas', 'vue', 'design/smb', 'design/aurora', 'design/saas']

View File

@ -1,6 +1,6 @@
{
"name": "opentiny-vue",
"version": "3.7.12",
"version": "3.14.0",
"private": true,
"packageManager": "pnpm@8.3.1",
"description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.",
@ -136,7 +136,7 @@
"dev:solid": "pnpm -C examples/solid-docs run dev"
},
"dependencies": {
"@vue/composition-api": "1.2.2",
"@vue/composition-api": "1.7.2",
"color": "^4.2.3",
"cropperjs": "1.5.12",
"crypto-js": "4.2.0",

View File

@ -7,6 +7,7 @@ import Form from './src/form'
import Grid from './src/grid'
import Switch from './src/switch'
import Select from './src/select'
import Loading from './src/loading'
import Popover from './src/popover'
import TimeLine from './src/time-line'
import TimelineItem from './src/timeline-item'
@ -14,6 +15,7 @@ import Input from './src/input'
import DateRange from './src/date-range'
import Pager from './src/pager'
import Wizard from './src/wizard'
import DialogBox from './src/dialog-box'
import { version } from './package.json'
export default {
@ -30,11 +32,13 @@ export default {
Switch,
Select,
Popover,
Loading,
TimeLine,
TimelineItem,
Input,
DateRange,
Pager,
Wizard
Wizard,
DialogBox
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/vue-design-aurora",
"version": "3.13.0",
"version": "3.14.0",
"main": "index.ts",
"sideEffects": false,
"type": "module",

View File

@ -0,0 +1,6 @@
export default {
state: {
// 覆盖 dialog-box top 默认值
top: '0'
}
}

View File

@ -0,0 +1,7 @@
import loadingImg from '@opentiny/vue-theme-saas/images/loading.png'
export default {
props: {
loadingImg
}
}

View File

@ -15,6 +15,7 @@ import Loading from './src/loading'
import Input from './src/input'
import DateRange from './src/date-range'
import Pager from './src/pager'
import DialogBox from './src/dialog-box'
import { version } from './package.json'
export default {
@ -37,6 +38,7 @@ export default {
TimelineItem,
Input,
DateRange,
Pager
Pager,
DialogBox
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/vue-design-saas",
"version": "3.13.0",
"version": "3.14.0",
"main": "index.ts",
"sideEffects": false,
"type": "module",

View File

@ -0,0 +1,5 @@
export default {
state: {
top: '0'
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/vue-design-smb",
"version": "3.13.0",
"version": "3.14.0",
"sideEffects": false,
"type": "module",
"main": "index.ts",

View File

@ -99,10 +99,10 @@ export const vc = VueClassName
export const getElementCssClass = (classes = {}, key) => {
if (typeof key === 'object') {
const keys = Object.keys(key)
const keys = Array.isArray(key) ? key : Object.keys(key).filter((k) => key[k])
let cls = ''
keys.forEach((k) => {
if (key[k] && classes[k]) cls += `${classes[k]} `
if (classes[k]) cls += `${classes[k]} `
})
return cls
} else {

View File

@ -99,10 +99,10 @@ export const vc = VueClassName
export const getElementCssClass = (classes = {}, key) => {
if (typeof key === 'object') {
const keys = Object.keys(key)
const keys = Array.isArray(key) ? key : Object.keys(key).filter((k) => key[k])
let cls = ''
keys.forEach((k) => {
if (key[k] && classes[k]) cls += `${classes[k]} `
if (classes[k]) cls += `${classes[k]} `
})
return cls
} else {

View File

@ -1,7 +1,7 @@
{
"name": "@opentiny/vue-renderless",
"private": true,
"version": "3.13.0",
"version": "3.14.0",
"description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.",
"homepage": "https://opentiny.design/tiny-vue",
"keywords": [

View File

@ -115,7 +115,7 @@ export const watchCheckedValue =
}
nextTick(() => {
dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormBlur, [
dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormChange, [
state.multiple ? state.presentText : state.inputValue
])
})

View File

@ -81,6 +81,9 @@ const getTimezone = (date) => {
*/
export const isLeapYear = (year) => year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0)
const getMilliseconds = (milliseconds) =>
milliseconds > maxDateValues.MILLISECOND ? Number(String(milliseconds).substring(0, 3)) : milliseconds
const getDateFromData = ({ year, month, date, hours, minutes, seconds, milliseconds }) => {
let daysInMonth = daysInMonths[month]
@ -89,7 +92,7 @@ const getDateFromData = ({ year, month, date, hours, minutes, seconds, milliseco
}
if (date <= daysInMonth) {
return new Date(year, month, date, hours, minutes, seconds, milliseconds)
return new Date(year, month, date, hours, minutes, seconds, getMilliseconds(milliseconds))
}
}
@ -176,7 +179,8 @@ const iso8601DateParser = (m) => {
actHours = sign === '+' ? hours - offsetHours - offset / 60 : Number(hours) + Number(offsetHours) - offset / 60
actMinutes = sign === '+' ? minutes - offsetMinutes : Number(minutes) + Number(offsetMinutes)
}
return new Date(year, month, date, actHours, actMinutes, seconds, milliseconds)
return new Date(year, month, date, actHours, actMinutes, seconds, getMilliseconds(milliseconds))
}
}
@ -441,24 +445,26 @@ export const format = function (date, dateFormat = 'yyyy/MM/dd hh:mm:ss') {
/**
*
*
* let date = new Date(2017, 0, 1)
* var date = new Date(2017, 0, 1)
* getDateWithNewTimezone(date, 0, -2)
*
* @param {Date} date Date
* @param {Number} otz -12~13
* @param {Number} ntz -12~13
* @param {Boolean} TimezoneOffset
* @returns {Date}
*/
export const getDateWithNewTimezone = (date, otz, ntz) => {
if (!isDate(date) || !isNumeric(otz) || !isNumeric(ntz)) {
export const getDateWithNewTimezone = (date, otz, ntz, timezoneOffset = 0) => {
if (!isDate(date) || !isNumeric(otz) || !isNumeric(ntz) || !isNumeric(timezoneOffset)) {
return
}
const otzOffset = -otz * 60
const ntzOffset = -ntz * 60
const dstOffeset = timezoneOffset * 60
const utc = date.getTime() + otzOffset * 60000
return new Date(utc - ntzOffset * 60000)
return new Date(utc - (ntzOffset - dstOffeset) * 60000)
}
/**

View File

@ -23,15 +23,18 @@ if (!isServer) {
on(document, 'mouseup', (event) => {
nodeList.forEach((node) => node[nameSpace].documentHandler(event, startClick))
startClick = void 0
})
}
const createDocumentHandler = (el, binding, vnode) =>
function (mouseup = {}, mousedown = {}) {
let popperElm = vnode.context.popperElm || vnode.context.state.popperElm
let popperElm = vnode.context.popperElm || (vnode.context.state && vnode.context.state.popperElm)
if (
!mouseup ||
!mouseup.target ||
!mousedown ||
!mousedown.target ||
el.contains(mouseup.target) ||
el.contains(mousedown.target) ||
@ -89,6 +92,10 @@ export default {
}
}
if (nodeList.length === 0 && startClick) {
startClick = null
}
delete el[nameSpace]
}
}

View File

@ -10,7 +10,7 @@
*
*/
import { on, off } from './dom'
import { on, off, isDisplayNone } from './dom'
import PopupManager from './popup-manager'
import globalConfig from '../global'
import { typeOf } from '../type'
@ -25,11 +25,12 @@ const DEFAULTS = {
boundariesPadding: 5,
flipBehavior: 'flip', // 全局没有修改过它所以它一直是flip
forceAbsolute: false,
gpuAcceleration: true, // 这个用不到了默认使用tranform3d
gpuAcceleration: true,
offset: 0,
placement: 'bottom',
preventOverflowOrder: positions,
modifiers // 此处是string数组 构造函数调用之后转为函数数组
modifiers, // 此处是string数组 构造函数调用之后转为函数数组
updateHiddenPopperOnScroll: false // 滚动过程中是否更新隐藏的弹出层位置
}
/** 用 styles 对象赋值el.style */
@ -292,6 +293,7 @@ interface PopperState {
scrollTarget: HTMLElement | null
scrollTargets: HTMLElement[] | null
updateBoundFn: () => void
scrollUpdate: () => void
}
interface ReferenceOffsets {
@ -753,6 +755,14 @@ class Popper {
_setupEventListeners() {
this.state.updateBoundFn = this.update.bind(this)
this.state.scrollUpdate = () => {
if (this._options.updateHiddenPopperOnScroll) {
this.state.updateBoundFn()
} else {
if (isDisplayNone(this._popper)) return
this.state.updateBoundFn()
}
}
on(window, 'resize', this.state.updateBoundFn)
@ -770,10 +780,10 @@ class Popper {
this.state.scrollTargets = targets || []
targets.forEach((target) => {
on(target, 'scroll', this.state.updateBoundFn)
on(target, 'scroll', this.state.scrollUpdate)
})
} else {
on(target, 'scroll', this.state.updateBoundFn)
on(target, 'scroll', this.state.scrollUpdate)
}
}
}
@ -782,7 +792,7 @@ class Popper {
off(window, 'resize', this.state.updateBoundFn)
if (this._options.boundariesElement !== 'window' && this.state.scrollTarget) {
off(this.state.scrollTarget, 'scroll', this.state.updateBoundFn)
off(this.state.scrollTarget, 'scroll', this.state.scrollUpdate)
this.state.scrollTarget = null
// 移除祖先监听
@ -790,13 +800,14 @@ class Popper {
let targets = this.state.scrollTargets || []
targets.forEach((target) => {
off(target, 'scroll', this.state.updateBoundFn)
off(target, 'scroll', this.state.scrollUpdate)
})
this.state.scrollTargets = null
}
}
this.state.updateBoundFn = null as any
this.state.scrollUpdate = null as any
}
/** 实时计算一下Boundary的位置 */

View File

@ -1,45 +0,0 @@
/**
* 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.
*
*/
!(function () {
let lastTime = 0
const vendors = ['ms', 'moz', 'webkit', 'o']
for (let x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (callback) {
const now = new Date().getTime()
const timeToCall = Math.max(0, 16 - (now - lastTime))
const timeCallback = now + timeToCall
const id = window.setTimeout(() => {
callback(timeCallback)
}, timeToCall)
lastTime = now + timeToCall
return id
}
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id)
}
}
})()

View File

@ -293,7 +293,7 @@ export default class Node {
if (!(child instanceof Node)) {
if (!batch) {
const children = this.getChildren(true)
const children = this.getChildren(true) || []
if (!~children.indexOf(child.data)) {
insertNode({ arr: children, index, item: child.data })

View File

@ -12,7 +12,7 @@
import PopupManager from './popup-manager'
import PopperJS from './popper'
import { on } from './dom'
import { on, off, isDisplayNone } from './dom'
import type { ISharedRenderlessFunctionParams } from 'types/shared.type'
import type Popper from './popper'
@ -92,7 +92,7 @@ export default (options: IPopperInputParams) => {
const { followReferenceHide = true } = props?.popperOptions || {}
const { _popper: popper, _reference: reference } = popperInstance
if (followReferenceHide && getComputedStyle(reference).position !== 'fixed' && reference.offsetParent === null) {
if (followReferenceHide && isDisplayNone(reference)) {
popper.style.display = 'none'
}
}
@ -108,7 +108,7 @@ export default (options: IPopperInputParams) => {
return
}
const options = props.popperOptions || {}
const options = props.popperOptions || { gpuAcceleration: false }
state.popperElm = state.popperElm || props.popper || vm.$refs.popper || popperVmRef.popper
const popper = state.popperElm
let reference = getReference({ state, props, vm, slots })
@ -185,6 +185,7 @@ export default (options: IPopperInputParams) => {
const destroyPopper = (remove: 'remove' | boolean) => {
if (remove) {
if (state.popperElm) {
off(state.popperElm, 'click', stop)
state.popperElm.remove()
}
}
@ -207,13 +208,17 @@ export default (options: IPopperInputParams) => {
onBeforeUnmount(() => {
nextTick(() => {
doDestroy(true)
if (props.appendToBody || props.popperAppendToBody) {
destroyPopper('remove')
}
})
})
onDeactivated(() => {
doDestroy(true)
if (props.appendToBody || props.popperAppendToBody) {
destroyPopper('remove')
}
})
return { updatePopper, destroyPopper, doDestroy, ...toRefs(state) }

View File

@ -202,7 +202,7 @@ export const DATEPICKER = {
queryClass: '.tiny-picker-panel__content',
disableClass: '.time-select-item:not(.disabled)',
defaultClass: '.default',
Qurtyli: 'li',
Qurtyli: '[data-tag="li"]',
MappingKeyCode: { 40: 1, 38: -1 },
DatePicker: 'DatePicker',
TimePicker: 'TimePicker'

View File

@ -12,7 +12,7 @@
import { isPlainObject, isNumber, isNumeric, isNull } from './type'
import { getObj, toJsonStr } from './object'
import { toFixed } from './decimal'
import { toFixed, Decimal } from './decimal'
/**
*
@ -711,7 +711,7 @@ export const toBoolValue = (value) => {
* @returns {String}
*/
export const toRate = (value, total = 1, fraction = 2) =>
isNumber(value) && isNumber(total) ? `${toDecimal((value * 100) / total, fraction)}%` : value
isNumber(value) && isNumber(total) ? toDecimal(Decimal(value).mul(100).div(total).toNumber(), fraction) + '%' : value
/**
*

View File

@ -23,13 +23,13 @@ export const computedAnimationName =
export const computedAddUnit = (value: string): string => (isNaN(Number(value)) ? value : value + 'px')
export const computedStyle =
({ props, state }: Pick<IDialogBoxRenderlessParams, 'props' | 'state'>) =>
({ props, state, designConfig }: Pick<IDialogBoxRenderlessParams, 'props' | 'state' | 'designConfig'>) =>
(): IDialogBoxStyle => {
const style = {} as IDialogBoxStyle
let style = {} as IDialogBoxStyle
let { width, top, rightSlide, maxHeight } = props
if (top === undefined) {
top = rightSlide ? '0' : '15vh'
top = rightSlide ? '0' : designConfig?.state?.top ? '' : '15vh'
}
width = computedAddUnit(width)
@ -49,6 +49,10 @@ export const computedStyle =
}
}
if (state.dragStyle) {
style = { ...style, ...state.dragStyle }
}
return style
}
@ -155,15 +159,35 @@ export const unMounted =
}
}
export const useMouseEventDown =
({ state }: Pick<IDialogBoxRenderlessParams, 'state'>) =>
(event: MouseEvent): void => {
state.mouseDownWrapperFlag = false
if (/tiny-dialog-box__wrapper/.test(event.target.className) && event.type === 'mousedown') {
state.mouseDownWrapperFlag = true
}
}
export const useMouseEventUp =
({ state }: Pick<IDialogBoxRenderlessParams, 'state'>) =>
(event: MouseEvent): void => {
state.mouseUpWrapperFlag = false
if (/tiny-dialog-box__wrapper/.test(event.target.className) && event.type === 'mouseup') {
state.mouseUpWrapperFlag = true
}
}
export const handleWrapperClick =
({ api, props }: Pick<IDialogBoxRenderlessParams, 'api' | 'props'>) =>
({ api, props, state }: Pick<IDialogBoxRenderlessParams, 'api' | 'props' | 'state'>) =>
(): void => {
if (!props.closeOnClickModal) {
return
}
// mouseDownFlag、mouseUpFlag判断是否点击wrapper状态
if (state.mouseDownWrapperFlag && state.mouseUpWrapperFlag) {
api.handleClose('mask')
}
}
export const handleClose =
({
@ -315,8 +339,8 @@ export const handleDrag =
top = event.clientY < 0 ? -disY : top > maxY ? maxY : top
}
modalBoxElem.style.left = `${left}px`
modalBoxElem.style.top = `${top}px`
state.dragStyle = { left: `${left}px`, top: `${top}px` }
state.left = `${left}px`
state.top = `${top}px`

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