feat: modify the resource file loading mode and add postcss plugin configuration. (#3615)
This commit is contained in:
parent
6b27c3076a
commit
eaeb9325da
|
@ -1,6 +1,10 @@
|
|||
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 demoStr = import.meta.glob('../../sites/demos/mobile-first/app/**/*.vue', {
|
||||
eager: false,
|
||||
query: '?raw',
|
||||
import: 'default'
|
||||
})
|
||||
export const demoVue = import.meta.glob('../../sites/demos/mobile-first/app/**/*.vue', { eager: false })
|
||||
|
||||
// demos配置
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
// 同web-doc的菜单资源
|
||||
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 demoStr = import.meta.glob('../../sites/demos/pc/app/**/*.vue', {
|
||||
eager: false,
|
||||
query: '?raw',
|
||||
import: 'default'
|
||||
})
|
||||
export const demoVue = import.meta.glob('../../sites/demos/pc/app/**/*.vue', { eager: false })
|
||||
|
||||
// demos配置
|
||||
|
|
|
@ -460,13 +460,13 @@ export default {
|
|||
},
|
||||
{
|
||||
name: 'input',
|
||||
type: 'Function(value)',
|
||||
type: '(event: InputEvent) => void',
|
||||
defaultValue: '',
|
||||
desc: {
|
||||
'zh-CN': '输入值时触发事件',
|
||||
'en-US': ''
|
||||
'en-US': 'Trigger event when input value is entered '
|
||||
},
|
||||
mode: ['mobile-first'],
|
||||
mode: ['pc', 'mobile-first'],
|
||||
mfDemo: ''
|
||||
}
|
||||
],
|
||||
|
|
|
@ -86,6 +86,17 @@ export default {
|
|||
mode: ['pc'],
|
||||
pcDemo: 'three-areas'
|
||||
},
|
||||
{
|
||||
name: 'trigger-simple',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
desc: {
|
||||
'zh-CN': '是否启用简易模式',
|
||||
'en-US': 'Whether to enable simplified mode.'
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: 'trigger-simple'
|
||||
},
|
||||
{
|
||||
name: 'border',
|
||||
type: 'boolean',
|
||||
|
|
|
@ -199,6 +199,17 @@ export default {
|
|||
mode: ['mobile-first'],
|
||||
mfDemo: 'sub-field'
|
||||
},
|
||||
{
|
||||
name: 'text-position',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
desc: {
|
||||
'zh-CN': `节点文案位置。默认名称和时间分别展示在图标上下方;可选值:'right',只有名称展示名称在右方`,
|
||||
'en-US': `Node copy position. The default name and time are displayed above and below the icon, respectively; optional value: 'right', where only the name is displayed on the right side. `
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: 'text-position'
|
||||
},
|
||||
{
|
||||
name: 'time-field',
|
||||
type: 'string',
|
||||
|
@ -249,6 +260,15 @@ export default {
|
|||
],
|
||||
methods: [],
|
||||
slots: [
|
||||
{
|
||||
name: 'default',
|
||||
desc: {
|
||||
'zh-CN': '组件默认插槽。组件显示为插槽内容',
|
||||
'en-US': 'Component default slot. The component displays as the slot content. '
|
||||
},
|
||||
mode: ['pc'],
|
||||
pcDemo: 'slot-default'
|
||||
},
|
||||
{
|
||||
name: 'bottom',
|
||||
desc: {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('dialogSelect 表格单选', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('dialog-select#nest-grid-single')
|
||||
|
||||
await page.locator('#nest-grid-single').getByRole('button', { name: '打开窗口' }).click()
|
||||
|
||||
await page.locator('.tiny-grid-body__row').first().waitFor()
|
||||
let rows
|
||||
rows = await page.locator('.tiny-grid-body__row').all()
|
||||
for (const row of rows) {
|
||||
const checked = await row.locator('input[type="radio"]').isChecked()
|
||||
expect(checked).toBe(false)
|
||||
}
|
||||
|
||||
await page.getByRole('row', { name: 'GFD 科技有限公司 福建 福州' }).locator('path').nth(1).click()
|
||||
|
||||
rows = await page.locator('.tiny-grid-body__row').all()
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const checked = await rows[i].locator('input[type="radio"]').first().isChecked()
|
||||
if (i === 0) {
|
||||
expect(checked).toBe(true)
|
||||
} else {
|
||||
expect(checked).toBe(false)
|
||||
}
|
||||
}
|
||||
|
||||
await page.getByRole('row', { name: 'WWW 科技有限公司 广东 深圳' }).locator('path').nth(1).click()
|
||||
|
||||
rows = await page.locator('.tiny-grid-body__row').all()
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const checked = await rows[i].locator('input[type="radio"]').first().isChecked()
|
||||
if (i === 1) {
|
||||
expect(checked).toBe(true)
|
||||
} else {
|
||||
expect(checked).toBe(false)
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,53 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('dialogSelect 树多选', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('dialog-select#nest-tree-multi')
|
||||
await page.locator('#nest-tree-multi').getByRole('button', { name: '打开窗口', exact: true }).click()
|
||||
|
||||
const nodeContent = await page.locator('.tiny-tree-node__content-left label').nth(1)
|
||||
|
||||
const iconNode = await nodeContent.getAttribute('class')
|
||||
|
||||
expect(iconNode?.includes('tiny-radio')).toBe(false)
|
||||
expect(iconNode?.includes('tiny-checkbox')).toBe(true)
|
||||
|
||||
let isChecked
|
||||
|
||||
isChecked = await page
|
||||
.getByRole('treeitem', { name: '三级 9' })
|
||||
.locator('.tiny-checkbox input[type="checkbox"]')
|
||||
.isChecked()
|
||||
expect(isChecked).toBeFalsy()
|
||||
|
||||
let current
|
||||
current = await page.getByRole('treeitem', { name: '三级 9' }).locator('path').nth(1)
|
||||
await current.click()
|
||||
|
||||
isChecked = await page
|
||||
.getByRole('treeitem', { name: '三级 9' })
|
||||
.locator('.tiny-checkbox input[type="checkbox"]')
|
||||
.isChecked()
|
||||
expect(isChecked).toBeTruthy()
|
||||
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^一级 1二级 4三级 8三级 9暂无数据$/ })
|
||||
.getByRole('img')
|
||||
.nth(3)
|
||||
.click()
|
||||
|
||||
isChecked = await page
|
||||
.getByRole('treeitem', { name: '三级 9' })
|
||||
.locator('.tiny-checkbox input[type="checkbox"]')
|
||||
.isChecked()
|
||||
expect(isChecked).toBeFalsy()
|
||||
|
||||
await page.getByRole('treeitem', { name: '三级 9' }).locator('path').nth(1).click()
|
||||
|
||||
isChecked = await page
|
||||
.getByRole('treeitem', { name: '三级 9' })
|
||||
.locator('.tiny-checkbox input[type="checkbox"]')
|
||||
.isChecked()
|
||||
expect(isChecked).toBeTruthy()
|
||||
})
|
|
@ -0,0 +1,25 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('dialogSelect 树单选', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('dialog-select#nest-tree-single')
|
||||
await page.locator('#nest-tree-single').getByRole('button', { name: '打开窗口' }).click()
|
||||
|
||||
const nodeContent = await page.locator('.tiny-tree-node__content-left label').nth(1)
|
||||
|
||||
const iconNode = await nodeContent.getAttribute('class')
|
||||
|
||||
expect(iconNode?.includes('tiny-radio')).toBe(true)
|
||||
expect(iconNode?.includes('tiny-checkbox')).toBe(false)
|
||||
|
||||
let current
|
||||
current = await page.getByText('201一级')
|
||||
await current.click()
|
||||
expect(await current.locator('input[type="radio"]').isChecked()).toBe(true)
|
||||
|
||||
current = await page.getByRole('treeitem', { name: '二级 6' }).locator('label')
|
||||
await current.click()
|
||||
expect(await current.locator('input[type="radio"]').isChecked()).toBe(true)
|
||||
current = await page.getByText('201一级')
|
||||
expect(await current.locator('input[type="radio"]').isChecked()).toBe(false)
|
||||
})
|
|
@ -0,0 +1,20 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('dialogSelect 设置多选状态', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('dialog-select#set-selection')
|
||||
await page.locator('#set-selection').getByRole('button', { name: '打开窗口' }).click()
|
||||
await page.getByRole('button', { name: 'Close' }).click()
|
||||
await page.getByRole('button', { name: '切换第二行选中状态' }).click()
|
||||
await page.locator('#set-selection').getByRole('button', { name: '打开窗口' }).click()
|
||||
|
||||
const trs = await page.locator('.tiny-grid table tbody tr').all()
|
||||
for (let i = 0; i < trs.length; i++) {
|
||||
const classes = await trs[i].getAttribute('class')
|
||||
if (i === 1) {
|
||||
expect(classes?.includes('row__selected')).toBeTruthy()
|
||||
} else {
|
||||
expect(classes?.includes('row__selected')).toBeFalsy()
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<div>
|
||||
<tiny-numeric v-model="value" @input="onInput"></tiny-numeric>
|
||||
<div>
|
||||
input事件触发了:<span class="count">{{ inputCount }}</span> 次
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { TinyModal, TinyNumeric } from '@opentiny/vue'
|
||||
|
||||
const value = ref(1)
|
||||
const inputCount = ref(0)
|
||||
|
||||
const onInput = (event: InputEvent) => {
|
||||
const currentValue = (event.target as HTMLInputElement).value
|
||||
TinyModal.message({
|
||||
message: `新值: ${currentValue}`,
|
||||
status: 'info'
|
||||
})
|
||||
inputCount.value++
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,15 @@
|
|||
import { expect, test } from '@playwright/test'
|
||||
|
||||
test('输入事件', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
await page.goto('numeric#input-event')
|
||||
let count
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await page.locator('.tiny-numeric__input-inner').fill(String(Math.random()))
|
||||
|
||||
count = await page.locator('.count').textContent()
|
||||
|
||||
expect(Number(count)).toBe(i + 1)
|
||||
}
|
||||
})
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<div>
|
||||
<tiny-numeric v-model="value" @input="onInput"></tiny-numeric>
|
||||
<div>
|
||||
input事件触发了:<span class="count">{{ inputCount }}</span> 次
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { TinyModal, TinyNumeric } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyNumeric
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: 1,
|
||||
inputCount: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onInput(event: InputEvent) {
|
||||
const currentValue = (event.target as HTMLInputElement).value
|
||||
TinyModal.message({
|
||||
message: `新值: ${currentValue}`,
|
||||
status: 'info'
|
||||
})
|
||||
this.inputCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -165,6 +165,18 @@ export default {
|
|||
},
|
||||
codeFiles: ['change-event.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'input-event',
|
||||
name: {
|
||||
'zh-CN': '输入事件',
|
||||
'en-US': 'Input Event'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '<p>输入时触发<code>input</code>事件。<p>',
|
||||
'en-US': '<p>Trigger the <code>input</code> event upon input. </p>'
|
||||
},
|
||||
codeFiles: ['input-event.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'focus-event',
|
||||
name: {
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
<template>
|
||||
<div class="content">
|
||||
<tiny-pager align="left" :total="100"></tiny-pager>
|
||||
<tiny-pager align="center" :total="100"></tiny-pager>
|
||||
<tiny-pager align="right" :total="100"></tiny-pager>
|
||||
<TinyRadioGroup v-model="state.align" type="button" :options="state.options"></TinyRadioGroup>
|
||||
<TinyPager :align="state.align" :total="100"></TinyPager>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { TinyPager } from '@opentiny/vue'
|
||||
</script>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.tiny-radio-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
import { TinyPager, TinyRadioGroup } from '@opentiny/vue'
|
||||
|
||||
const state = reactive({
|
||||
align: 'left',
|
||||
options: ['left', 'center', 'right'].map((item) => ({ label: item, text: item }))
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,12 @@ test('对齐方式', async ({ page }) => {
|
|||
const demo = page.locator('#align')
|
||||
const pager = demo.locator('.tiny-pager')
|
||||
|
||||
await expect(pager.first()).toHaveCSS('text-align', 'left')
|
||||
await expect(pager.nth(1)).toHaveCSS('text-align', 'center')
|
||||
await expect(pager.nth(2)).toHaveCSS('text-align', 'right')
|
||||
await expect(pager).toHaveCSS('text-align', 'left')
|
||||
|
||||
await page.click('text=center')
|
||||
await expect(pager).toHaveCSS('text-align', 'center')
|
||||
await page.click('text=right')
|
||||
await expect(pager).toHaveCSS('text-align', 'right')
|
||||
await page.click('text=left')
|
||||
await expect(pager).toHaveCSS('text-align', 'left')
|
||||
})
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
<template>
|
||||
<div class="content">
|
||||
<tiny-pager align="left" :total="100"></tiny-pager>
|
||||
<tiny-pager align="center" :total="100"></tiny-pager>
|
||||
<tiny-pager align="right" :total="100"></tiny-pager>
|
||||
<TinyRadioGroup v-model="align" type="button" :options="options"></TinyRadioGroup>
|
||||
<tiny-pager :align="align" :total="100"></tiny-pager>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TinyPager } from '@opentiny/vue'
|
||||
import { TinyPager, TinyRadioGroup } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyPager
|
||||
TinyPager,
|
||||
TinyRadioGroup
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
align: 'left',
|
||||
options: ['left', 'center', 'right'].map((item) => ({ label: item, text: item }))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
<template>
|
||||
<div class="qr-code-attr">
|
||||
iconSize:
|
||||
<tiny-numeric v-model="params.iconSize" :min="1" :max="params.size * 0.3" />
|
||||
size: <tiny-numeric v-model="params.size" :min="1" :max="1e4" />
|
||||
</div>
|
||||
<tiny-qr-code v-bind="params"></tiny-qr-code>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { TinyQrCode } from '@opentiny/vue'
|
||||
import { reactive } from 'vue'
|
||||
import { TinyNumeric, TinyQrCode } from '@opentiny/vue'
|
||||
|
||||
const params = {
|
||||
const params = reactive({
|
||||
value: '测试二维码数据',
|
||||
icon: import.meta.env.VITE_APP_BUILD_BASE_URL
|
||||
? `${import.meta.env.VITE_APP_BUILD_BASE_URL}static/images/mountain.png`
|
||||
: 'https://res.hc-cdn.com/tinyui-design-common/1.0.5.20230707170109/assets/tinyvue.svg',
|
||||
iconSize: 60,
|
||||
size: 250
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.qr-code-attr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,4 +8,23 @@ test('自定义 icon', async ({ page }) => {
|
|||
const canvasImg = page.locator('.tiny-qr-code .mask-icon img')
|
||||
await expect(canvas).toBeVisible()
|
||||
await expect(canvasImg).toBeVisible()
|
||||
|
||||
await page.getByLabel('示例', { exact: true }).getByRole('button').nth(1).click()
|
||||
const iconSize = await canvasImg.evaluate((el) => {
|
||||
return window.getComputedStyle(el)
|
||||
})
|
||||
const inputIconSizeWidth = await page.getByRole('spinbutton').first().inputValue()
|
||||
|
||||
expect(iconSize.width === `${inputIconSizeWidth}px`).toBe(true)
|
||||
expect(iconSize.height === `${inputIconSizeWidth}px`).toBe(true)
|
||||
await page.getByLabel('示例', { exact: true }).getByRole('button').nth(3).click()
|
||||
const [qrWidth, qrHeight] = await page.locator('.tiny-qr-code').evaluate((el) => {
|
||||
const style = window.getComputedStyle(el)
|
||||
return [style.width, style.height]
|
||||
})
|
||||
|
||||
const inputSizeWidth = await page.getByRole('spinbutton').nth(1).inputValue()
|
||||
|
||||
expect(qrWidth === `${inputSizeWidth}px`).toBe(true)
|
||||
expect(qrHeight === `${inputSizeWidth}px`).toBe(true)
|
||||
})
|
||||
|
|
|
@ -1,25 +1,46 @@
|
|||
<template>
|
||||
<div class="qr-code-attr">
|
||||
iconSize:
|
||||
<tiny-numeric v-model="iconSize" :min="1" :max="size * 0.3" />
|
||||
size: <tiny-numeric v-model="size" :min="1" :max="1e4" />
|
||||
</div>
|
||||
|
||||
<tiny-qr-code v-bind="params"></tiny-qr-code>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TinyQrCode } from '@opentiny/vue'
|
||||
import { TinyNumeric, TinyQrCode } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyNumeric,
|
||||
TinyQrCode
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
params: {
|
||||
size: 250,
|
||||
iconSize: 60
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
params() {
|
||||
return {
|
||||
value: '测试二维码数据',
|
||||
icon: import.meta.env.VITE_APP_BUILD_BASE_URL
|
||||
? `${import.meta.env.VITE_APP_BUILD_BASE_URL}static/images/mountain.png`
|
||||
: 'https://res.hc-cdn.com/tinyui-design-common/1.0.5.20230707170109/assets/tinyvue.svg',
|
||||
iconSize: 60,
|
||||
size: 250
|
||||
iconSize: this.iconSize,
|
||||
size: this.size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.qr-code-attr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
<template>
|
||||
<div class="qr-code-container">
|
||||
<div>
|
||||
<tiny-button @click="changeColor">改变颜色</tiny-button>
|
||||
</div>
|
||||
<br />
|
||||
<tiny-color-picker v-model="params.color" />
|
||||
<tiny-qr-code v-bind="params"></tiny-qr-code>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { TinyQrCode, TinyButton } from '@opentiny/vue'
|
||||
import { TinyQrCode, TinyColorPicker } from '@opentiny/vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const params = ref({
|
||||
|
@ -18,8 +15,4 @@ const params = ref({
|
|||
size: 250,
|
||||
style: { background: '#f5f5f5', padding: '24px' }
|
||||
})
|
||||
|
||||
const changeColor = () => {
|
||||
params.value.color = '#666'
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -6,4 +6,35 @@ test('自定义样式', async ({ page }) => {
|
|||
|
||||
const canvas = page.locator('.tiny-qr-code canvas')
|
||||
await expect(canvas).toBeVisible()
|
||||
|
||||
const backgroundColor0 = await canvas.evaluate(
|
||||
(el: any, { x, y }) => {
|
||||
const ctx = el.getContext('2d')
|
||||
const pixel = ctx.getImageData(x, y, 1, 1).data
|
||||
const toHex = (num: number) => num.toString(16).padStart(2, '0')
|
||||
return `#${toHex(pixel[0])}${toHex(pixel[1])}${toHex(pixel[2])}`
|
||||
},
|
||||
{ x: 1, y: 1 }
|
||||
)
|
||||
|
||||
expect(backgroundColor0 === '#1677ff').toBeTruthy()
|
||||
await page.locator('.tiny-color-picker__inner').click()
|
||||
await page.locator('.black').click()
|
||||
await page.locator('.tiny-color-select-panel__inner__hue-select').click()
|
||||
await page.getByRole('button', { name: '选择' }).click()
|
||||
const backgroundColor1 = await page.locator('.tiny-color-picker__inner').evaluate((el) => {
|
||||
return window.getComputedStyle(el).backgroundColor
|
||||
})
|
||||
|
||||
const backgroundColor2 = await canvas.evaluate(
|
||||
(el: any, { x, y }) => {
|
||||
const ctx = el.getContext('2d')
|
||||
const pixel = ctx.getImageData(x, y, 1, 1).data
|
||||
|
||||
return `rgb(${pixel[0]}, ${pixel[1]}, ${pixel[2]})`
|
||||
},
|
||||
{ x: 1, y: 1 }
|
||||
)
|
||||
|
||||
expect(backgroundColor1 === backgroundColor2).toBeTruthy()
|
||||
})
|
||||
|
|
|
@ -1,26 +1,29 @@
|
|||
<template>
|
||||
<div class="qr-code-container">
|
||||
<div>
|
||||
<tiny-button @click="changeColor">改变颜色</tiny-button>
|
||||
</div>
|
||||
<br />
|
||||
<div>改变颜色<tiny-color-picker v-model="color" /></div>
|
||||
|
||||
<tiny-qr-code v-bind="params"></tiny-qr-code>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TinyQrCode, TinyButton } from '@opentiny/vue'
|
||||
import { TinyQrCode, TinyColorPicker } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyQrCode,
|
||||
TinyButton
|
||||
TinyColorPicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
params: {
|
||||
color: '#1677ff'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
params() {
|
||||
return {
|
||||
value: '测试二维码数据',
|
||||
color: '#1677ff',
|
||||
color: this.color,
|
||||
size: 250,
|
||||
style: { background: '#f5f5f5', padding: '24px' }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<div class="demo-timeline-default-slot">
|
||||
<tiny-time-line>
|
||||
<template #default>
|
||||
<ol>
|
||||
<li v-for="(item, index) in items" :key="index" :class="item.status">
|
||||
<div>
|
||||
<div class="index-icon">{{ index + 1 }}</div>
|
||||
<hr />
|
||||
</div>
|
||||
<div>{{ item.desc }}</div>
|
||||
<div v-if="item.user">{{ item.user }}</div>
|
||||
<div v-if="item.datetime">{{ item.datetime }}</div>
|
||||
</li>
|
||||
</ol>
|
||||
</template>
|
||||
</tiny-time-line>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { TinyTimeLine } from '@opentiny/vue'
|
||||
|
||||
const items = [
|
||||
{ desc: '提交申请', user: '张三', datetime: new Date(Date.now() - 60 * 60 * 1e3).toLocaleString(), status: 'done' },
|
||||
{ desc: '直接主管', user: '李四', datetime: new Date().toLocaleString(), status: 'done' },
|
||||
{ desc: '部门主管', user: '王五' },
|
||||
{ desc: '完成' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.demo-timeline-default-slot {
|
||||
--color: #d3d5d6;
|
||||
.done {
|
||||
--color: #5073e5;
|
||||
}
|
||||
--size: 20px;
|
||||
}
|
||||
|
||||
ol {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 10px 0;
|
||||
color: var(--color);
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
color: var(--active-color);
|
||||
&:not(:first-of-type) {
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
background-color: var(--color);
|
||||
}
|
||||
|
||||
.index-icon {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
border-radius: var(--size);
|
||||
background-color: var(--color);
|
||||
color: #fff;
|
||||
padding: 1px;
|
||||
font-size: calc(var(--size) - 2px);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('默认插槽', async ({ page }) => {
|
||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||
|
||||
await page.goto('time-line#slot-default')
|
||||
|
||||
const timeline = await page.locator('.tiny-timeline')
|
||||
await page.waitForSelector('.tiny-timeline')
|
||||
|
||||
expect(await timeline.locator('.tiny-timeline-item').count()).toBe(0)
|
||||
expect(await timeline.locator('ol').count()).toBeGreaterThan(0)
|
||||
})
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div class="demo-timeline">
|
||||
<tiny-time-line vertical shape="dot">
|
||||
<template #default>
|
||||
<ol>
|
||||
<li v-for="(item, index) in items" :key="index" :class="item.status">
|
||||
<div>
|
||||
<div class="index-icon">{{ index + 1 }}</div>
|
||||
<hr />
|
||||
</div>
|
||||
<div>{{ item.desc }}</div>
|
||||
<div v-if="item.user">{{ item.user }}</div>
|
||||
<div v-if="item.datetime">{{ item.datetime }}</div>
|
||||
</li>
|
||||
</ol>
|
||||
</template>
|
||||
</tiny-time-line>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TinyTimeLine } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyTimeLine
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
desc: '提交申请',
|
||||
user: '张三',
|
||||
datetime: new Date(Date.now() - 60 * 60 * 1e3).toLocaleString(),
|
||||
status: 'done'
|
||||
},
|
||||
{ desc: '直接主管', user: '李四', datetime: new Date().toLocaleString(), status: 'done' },
|
||||
{ desc: '部门主管', user: '王五' },
|
||||
{ desc: '完成' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.demo-timeline {
|
||||
--color: #d3d5d6;
|
||||
.done {
|
||||
--color: #5073e5;
|
||||
}
|
||||
--size: 20px;
|
||||
}
|
||||
|
||||
ol {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 10px 0;
|
||||
color: var(--color);
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
color: var(--active-color);
|
||||
&:not(:first-of-type) {
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
background-color: var(--color);
|
||||
}
|
||||
|
||||
.index-icon {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
border-radius: var(--size);
|
||||
background-color: var(--color);
|
||||
color: #fff;
|
||||
padding: 1px;
|
||||
font-size: calc(var(--size) - 2px);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
|
@ -186,6 +186,18 @@ export default {
|
|||
'en-US': '<p>Add description information for a single node through the <code>description</code> slot.</p>'
|
||||
},
|
||||
codeFiles: ['slot-description.vue']
|
||||
},
|
||||
{
|
||||
demoId: 'slot-default',
|
||||
name: {
|
||||
'zh-CN': '默认插槽',
|
||||
'en-US': 'Default Slot'
|
||||
},
|
||||
desc: {
|
||||
'zh-CN': '组件默认插槽',
|
||||
'en-US': 'Component Default Slot'
|
||||
},
|
||||
codeFiles: ['slot-default.vue']
|
||||
}
|
||||
],
|
||||
features: [
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
'tailwindcss/nesting': 'postcss-nesting',
|
||||
tailwindcss: {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import { type PropType } from '@opentiny/vue-common'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { Tag as TinyTag, Alert as TinyAlert, Tooltip as TinyTooltip } from '@opentiny/vue'
|
||||
import { getWord } from '@/tools'
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"test:e2e": "playwright test",
|
||||
"test:unit": "vitest",
|
||||
"install:browser": "playwright install",
|
||||
"codegen": "playwright codegen localhost:3101"
|
||||
"codegen": "playwright codegen localhost:3101",
|
||||
"open:report": "playwright show-report"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opentiny-internal/playwright-config": "workspace:^1.0.1-beta.0",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
'tailwindcss/nesting': 'postcss-nesting',
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
global.ResizeObserver = class ResizeObserver {
|
||||
constructor(callback) {
|
||||
this.callback = callback
|
||||
}
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
|
@ -94,6 +94,8 @@
|
|||
"install:browser": "pnpm -C examples/vue3 install:browser",
|
||||
"// ---------- e2e测试代码生成器 ----------": "",
|
||||
"codegen": "pnpm -C examples/vue3 codegen",
|
||||
"// ---------- e2e测试展示报告 ----------": "",
|
||||
"open:report": "pnpm -C examples/vue3 open:report",
|
||||
"format": "prettier --write --cache packages/**/{*.vue,*.js,*.ts,*.jsx,*.tsx,*.less} examples/**/{*.vue,*.js,*.ts,*.jsx,*.tsx} internals/**/{*.js,*.ts}",
|
||||
"lint": "eslint \"packages/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
||||
"lint:doc": "eslint \"examples/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
||||
|
@ -141,6 +143,7 @@
|
|||
"lint-staged": "^15.2.0",
|
||||
"minimist": "^1.2.8",
|
||||
"node-xlsx": "^0.21.0",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
"prettier": "^3.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"shelljs": "^0.8.5",
|
||||
|
|
|
@ -33,8 +33,11 @@
|
|||
border-radius: var(--tv-Button-border-radius-round);
|
||||
}
|
||||
|
||||
&.is-circle {
|
||||
&.is-circle.is-circle {
|
||||
border-radius: var(--tv-Button-border-radius-circle);
|
||||
aspect-ratio: 1;
|
||||
min-width: initial;
|
||||
padding: initial;
|
||||
}
|
||||
|
||||
/** 2、尺寸场景 */
|
||||
|
|
|
@ -9,9 +9,8 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import { $props, $prefix, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc'
|
||||
import type { PropType } from 'vue'
|
||||
import type { IButtonGroupNode } from '@opentiny/vue-renderless/types/button-group.type'
|
||||
|
||||
export const buttonGroupProps = {
|
||||
|
|
|
@ -9,14 +9,18 @@ describe('PC Mode', () => {
|
|||
/**
|
||||
* attrs
|
||||
*/
|
||||
test('visible', () => {
|
||||
const visible = true
|
||||
const wrapper = mount(() => (
|
||||
<DialogBox v-model:visible={visible}>
|
||||
<span>dialog-box内容</span>
|
||||
</DialogBox>
|
||||
))
|
||||
expect(wrapper.find('.tiny-dialog-box').exists()).toBe(true)
|
||||
test('visible', async () => {
|
||||
const wrapper = mount(DialogBox, {
|
||||
props: {
|
||||
visible: false
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').isVisible()).toBeFalsy()
|
||||
|
||||
await wrapper.setProps({ visible: true })
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').isVisible()).toBeTruthy()
|
||||
})
|
||||
|
||||
test.todo('center 弹出框的头部与底部内容是否自动居中')
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import { $prefix, $props, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc|mobile-first'
|
||||
|
||||
export const $constants = {
|
||||
|
@ -131,6 +130,9 @@ export const dialogBoxProps = {
|
|||
customStyle: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
onClose: {
|
||||
type: Function as PropType<() => void>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import { mountPcMode } from '@opentiny-internal/vue-test-utils'
|
||||
import DialogSelect from '@opentiny/vue-dialog-select'
|
||||
import Grid from '@opentiny/vue-grid'
|
||||
import Tree from '@opentiny/vue-tree'
|
||||
import { describe, expect, test } from 'vitest'
|
||||
|
||||
const treeData = [
|
||||
{ id: 1, pid: null, label: '一级 1' },
|
||||
{ id: 2, pid: null, label: '一级 2' },
|
||||
{ id: 3, pid: null, label: '一级 3', isLeaf: true, children: [] },
|
||||
{ id: 4, pid: 1, label: '二级 4' },
|
||||
{ id: 5, pid: 1, label: '二级 5', isLeaf: true, children: [] },
|
||||
{ id: 6, pid: 2, label: '二级 6', isLeaf: true, children: [] },
|
||||
{ id: 7, pid: 2, label: '二级 7', isLeaf: true, children: [] },
|
||||
{ id: 8, pid: 4, label: '三级 8', isLeaf: true, children: [] },
|
||||
{ id: 9, pid: 4, label: '三级 9', isLeaf: true, children: [] }
|
||||
]
|
||||
|
||||
describe('PC Mode', () => {
|
||||
const mount = mountPcMode
|
||||
|
||||
test('visible', async () => {
|
||||
const wrapper = mount(DialogSelect, {
|
||||
props: {
|
||||
visible: false
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').isVisible()).toBeFalsy()
|
||||
|
||||
await wrapper.setProps({ visible: true })
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').isVisible()).toBeTruthy()
|
||||
|
||||
await wrapper.setProps({ visible: false })
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').isVisible()).toBeFalsy()
|
||||
})
|
||||
|
||||
test('grid data', async () => {
|
||||
const wrapper = mount(DialogSelect, {
|
||||
props: {
|
||||
visible: true,
|
||||
gridOp: {
|
||||
columns: [{ field: 'id', title: 'ID' }]
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(wrapper.findComponent(Grid).vm.data).toBeUndefined()
|
||||
const gridData = Array.from({ length: ~~(Math.random() * 10) }).map((_, i) => ({ id: i }))
|
||||
await wrapper.setProps({
|
||||
gridOp: {
|
||||
columns: [{ field: 'id', title: 'ID' }],
|
||||
data: gridData
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.findComponent(Grid).vm.data).toStrictEqual(gridData)
|
||||
})
|
||||
|
||||
test('grid multi or not', async () => {
|
||||
const wrapper = mount(DialogSelect, {
|
||||
props: {
|
||||
visible: true,
|
||||
multi: false,
|
||||
gridOp: {
|
||||
columns: [{ field: 'id', title: 'ID' }],
|
||||
data: [{ id: 1 }]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.findComponent(Grid).vm.columns.some((item) => item.type === 'radio')).toBeTruthy()
|
||||
|
||||
await wrapper.setProps({ multi: true })
|
||||
|
||||
expect(wrapper.findComponent(Grid).vm.columns.some((item) => item.type === 'selection')).toBeTruthy()
|
||||
})
|
||||
|
||||
test('tree multi or not', async () => {
|
||||
const wrapper = mount(DialogSelect, {
|
||||
props: {
|
||||
visible: true,
|
||||
multi: false,
|
||||
popseletor: 'tree',
|
||||
treeOp: {
|
||||
nodeKey: 'id',
|
||||
load: () => treeData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.findComponent(Tree).vm.showRadio).toBeTruthy()
|
||||
|
||||
await wrapper.setProps({ multi: true })
|
||||
expect(wrapper.findComponent(Tree).vm.showCheckbox).toBeTruthy()
|
||||
})
|
||||
|
||||
test('slot', async () => {
|
||||
const geneSlot = (name: string) => {
|
||||
const el = <div>this is a {name}</div>
|
||||
const wrapper = mount(() => el)
|
||||
return { el, wrapper }
|
||||
}
|
||||
|
||||
const { el: footer, wrapper: footerWrapper } = geneSlot('footer')
|
||||
const { el: title, wrapper: titleWrapper } = geneSlot('title')
|
||||
const { el: footerButtons, wrapper: footerButtonsWrapper } = geneSlot('footer-buttons')
|
||||
|
||||
const wrapper = mount(DialogSelect, {
|
||||
props: {
|
||||
visible: true
|
||||
},
|
||||
slots: { title, 'footer-buttons': footerButtons }
|
||||
})
|
||||
|
||||
expect(wrapper.find('.tiny-dialog-box').text()).contains(titleWrapper.text())
|
||||
expect(wrapper.find('.tiny-dialog-box').text()).contains(footerButtonsWrapper.text())
|
||||
|
||||
await wrapper.setProps({ slots: { footer, footerButtons } })
|
||||
expect(wrapper.find('.tiny-dialog-box').text()).contains(footerWrapper.text())
|
||||
})
|
||||
})
|
|
@ -1,5 +1,4 @@
|
|||
import type { PropType } from 'vue'
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import { $props, $prefix, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc|mobile-first'
|
||||
|
||||
export const pagerProps = {
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
import type { PropType } from 'vue'
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import { $props, $prefix, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc|mobile-first'
|
||||
|
||||
export const radioGroupProps = {
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
import type { PropType } from 'vue'
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import { $props, $prefix, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc|mobile-first'
|
||||
|
||||
export const $constants = {
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||
import type { PropType } from 'vue'
|
||||
import { $props, $prefix, $setup, defineComponent, type PropType } from '@opentiny/vue-common'
|
||||
import template from 'virtual-template?pc|mobile-first'
|
||||
|
||||
export const $constants = {
|
||||
|
|
Loading…
Reference in New Issue