diff --git a/examples/sites/demos/apis/action-menu.js b/examples/sites/demos/apis/action-menu.js index c2c7b90d9b..212fc68104 100644 --- a/examples/sites/demos/apis/action-menu.js +++ b/examples/sites/demos/apis/action-menu.js @@ -165,11 +165,11 @@ export default { type: 'interface', code: ` interface IItemData { - label: string // 菜单项文本 - disabled: boolean // 是否禁用 - divided: boolean // 是否显示分割线 - children: IItemData[] // 菜单项子集 - icon: Component // 菜单项图标 + label?: string // 菜单项文本 + disabled?: boolean // 是否禁用 + divided?: boolean // 是否显示分割线 + children?: IItemData[] // 菜单项子集 + icon?: Component // 菜单项图标 } ` }, diff --git a/examples/sites/demos/pc/app/action-menu/basic-usage-composition-api.vue b/examples/sites/demos/pc/app/action-menu/basic-usage-composition-api.vue index 6a9d810496..eaa27836d6 100644 --- a/examples/sites/demos/pc/app/action-menu/basic-usage-composition-api.vue +++ b/examples/sites/demos/pc/app/action-menu/basic-usage-composition-api.vue @@ -17,7 +17,8 @@ const options = ref([ label: '关机' }, { - label: '重启' + label: '重启', + divided: true }, { label: '网络设置', diff --git a/examples/sites/demos/pc/app/action-menu/basic-usage.vue b/examples/sites/demos/pc/app/action-menu/basic-usage.vue index 77361dc0e1..be3952fa1c 100644 --- a/examples/sites/demos/pc/app/action-menu/basic-usage.vue +++ b/examples/sites/demos/pc/app/action-menu/basic-usage.vue @@ -22,7 +22,8 @@ export default { label: '关机' }, { - label: '重启' + label: '重启', + divided: true }, { label: '网络设置', diff --git a/examples/sites/demos/pc/webdoc/import-components.md b/examples/sites/demos/pc/webdoc/import-components.md index bf1670ae55..3b152dfb00 100644 --- a/examples/sites/demos/pc/webdoc/import-components.md +++ b/examples/sites/demos/pc/webdoc/import-components.md @@ -22,7 +22,7 @@ Vite import autoImportPlugin from '@opentiny/unplugin-tiny-vue' export default { - plugins: [autoImportPlugin()] + plugins: [autoImportPlugin('vite')] } ``` @@ -33,13 +33,53 @@ Webpack const autoImportPlugin = require('@opentiny/unplugin-tiny-vue') -module.exports = { - plugins: [autoImportPlugin()] -} +module.exports = defineConfig({ + configureWebpack: { + plugins: [autoImportPlugin('webpack')] + } +}) ``` 这样你就能直接在项目中使用 TinyVue 的组件,这些组件都是自动按需导入的,无需手动导入,且不用担心项目体积变得太大。 +你也可以只使用 TinyVueResolver,这样就可以和其他组件库一起使用。 + +Vite + +```ts +// vite.config.ts + +import Components from 'unplugin-vue-components/vite' +import autoImportPlugin from '@opentiny/unplugin-tiny-vue' + +export default { + plugins: [ + Components({ + resolvers: [TinyVueResolver] + }) + ] +} +``` + +Webpack + +```js +// webpack.config.js + +const Components = require('unplugin-vue-components/webpack').default +const TinyVueResolver = require('@opentiny/unplugin-tiny-vue').TinyVueResolver + +module.exports = defineConfig({ + configureWebpack: { + plugins: [ + Components({ + resolvers: [TinyVueResolver] + }) + ] + } +}) +``` + 想了解更多自动按需导入的信息,请参考:[unplugin-vue-components](https://github.com/antfu/unplugin-vue-components) 和 [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import)。 ### 多组件引入 diff --git a/examples/sites/playground/App.vue b/examples/sites/playground/App.vue index 991435243f..61eb335e38 100644 --- a/examples/sites/playground/App.vue +++ b/examples/sites/playground/App.vue @@ -17,7 +17,7 @@ import logoUrl from './assets/opentiny-logo.svg?url' import GitHub from './icons/Github.vue' import Share from './icons/Share.vue' -const VERSION = 'tiny-vue-version-3.14' +const VERSION = 'tiny-vue-version-3.16' const LAYOUT = 'playground-layout' const LAYOUT_REVERSE = 'playground-layout-reverse' @@ -28,7 +28,7 @@ const isMobileFirst = tinyMode === 'mobile-first' const isSaas = tinyTheme === 'saas' const isPreview = searchObj.get('openMode') === 'preview' // 是否多端弹窗预览 -const versions = ['3.15', '3.14', '3.13', '3.12', '3.11', '3.10', '3.9', '3.8'] +const versions = ['3.16', '3.15', '3.14', '3.13', '3.12', '3.11', '3.10', '3.9', '3.8'] const latestVersion = isPreview ? versions[0] : localStorage.getItem(VERSION) || versions[0] const cdnHost = localStorage.getItem('setting-cdn') diff --git a/internals/unplugin-tiny-vue/package.json b/internals/unplugin-tiny-vue/package.json index b62878729a..12ec3b7e32 100644 --- a/internals/unplugin-tiny-vue/package.json +++ b/internals/unplugin-tiny-vue/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/unplugin-tiny-vue", - "version": "0.0.1", + "version": "0.0.2", "description": "A vite auto import plugin for TinyVue", "main": "dist/index.cjs", "module": "dist/index.js", @@ -35,7 +35,7 @@ "vite": ">=4" }, "dependencies": { - "magic-string": "^0.27.0" + "unplugin-vue-components": "^0.26.0" }, "devDependencies": { "rimraf": "^5.0.5", diff --git a/internals/unplugin-tiny-vue/src/index.ts b/internals/unplugin-tiny-vue/src/index.ts index 7405f5d206..e7a4a6244b 100644 --- a/internals/unplugin-tiny-vue/src/index.ts +++ b/internals/unplugin-tiny-vue/src/index.ts @@ -1,62 +1,37 @@ -import MagicString from 'magic-string' -import type { Plugin } from 'vite' - -function pascalCase(str: string) { - const camelCaseStr = str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')) - return camelCaseStr.charAt(0).toUpperCase() + camelCaseStr.slice(1) +import AutoVite from 'unplugin-vue-components/vite' +import AutoWebpack from 'unplugin-vue-components/webpack' +import AutoRollup from 'unplugin-vue-components/rollup' +import AutoEsbuild from 'unplugin-vue-components/esbuild' +import AutoRspack from 'unplugin-vue-components/rspack' + +const supportMap = { + 'vite': AutoVite, + 'webpack': AutoWebpack, + 'rollup': AutoRollup, + 'esbuild': AutoEsbuild, + 'rspack': AutoRspack } -const resolveVue = (code: string, s: MagicString) => { - const results: any = [] - - for (const match of code.matchAll(/_resolveComponent[0-9]*\("(.+?)"\)/g)) { - const matchedName = match[1] - if (match.index != null && matchedName && !matchedName.startsWith('_')) { - const start = match.index - const end = start + match[0].length - results.push({ - rawName: matchedName, - replace: (resolved: string) => s.overwrite(start, end, resolved) - }) +export const TinyVueResolver = (componentName) => { + if (componentName.startsWith('Tiny') && !componentName.startsWith('TinyIcon')) { + return { + name: componentName.slice(4), + from: '@opentiny/vue' } } - - return results -} - -const findComponent = (rawName: string, name: string, s: MagicString) => { - if (!name.match(/^Tiny[a-zA-Z]/)) { - return - } - s.prepend(`import ${name} from '@opentiny/vue-${rawName.slice(5)}';\n`) -} - -const transformCode = (code) => { - const s = new MagicString(code) - const results = resolveVue(code, s) - - for (const { rawName, replace } of results) { - const name = pascalCase(rawName) - findComponent(rawName, name, s) - replace(name) - } - - const result = s.toString() - return result } -export default function vitePluginAutoImport(): Plugin { - return { - name: '@opentiny/auto-import', - - transform(code, id) { - // 不处理node_modules内的依赖 - if (/\.(?:vue)$/.test(id) && !/(node_modules)/.test(id)) { - return { - code: transformCode(code), - map: null - } - } - } - } +/** TinyVue 自动导入组件的插件,支持Vite,Webpack,Rollup 等常见的构建工具。 + * 目前不支持Tiny Icon的自动导入 + * @example + * import autoImportPlugin from '@opentiny/unplugin-tiny-vue' + * plugins: [autoImportPlugin('vite')] + */ +export default (name) => { + // 兼容webpack/vite的差异 + const autoPlugin = supportMap[name].default || supportMap[name] + + return autoPlugin({ + resolvers: [TinyVueResolver] + }) } diff --git a/packages/renderless/package.json b/packages/renderless/package.json index 23952e48b9..440f717135 100644 --- a/packages/renderless/package.json +++ b/packages/renderless/package.json @@ -1,7 +1,7 @@ { "name": "@opentiny/vue-renderless", "private": true, - "version": "3.16.0", + "version": "3.16.1", "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": [ @@ -43,4 +43,4 @@ "esno": "^0.16.3", "tsup": "7.2.0" } -} \ No newline at end of file +} diff --git a/packages/renderless/src/tabs/index.ts b/packages/renderless/src/tabs/index.ts index 5898e61867..6859a65772 100644 --- a/packages/renderless/src/tabs/index.ts +++ b/packages/renderless/src/tabs/index.ts @@ -11,6 +11,58 @@ */ import type { ITabsRenderlessParams, ITabsPane, ITabsCustomEvent, ITabsPaneVm } from '@/types' +// 此处与aui区别开,将tabNav的方法抽离出来,从源头解决pane的排序问题 +const getOrderedPanes = (parent, panes) => { + const slotDefault = parent.$slots.default + let orders + + if (typeof slotDefault === 'function') { + orders = [] + + const tabVnodes = slotDefault() + const handler = ({ type, componentOptions, props }) => { + let componentName = type && type.componentName + + if (!componentName) componentName = componentOptions && componentOptions.Ctor.extendOptions.componentName + + if (componentName === 'TabItem') { + const paneName = (props && props.name) || (componentOptions && componentOptions.propsData.name) + + orders.push(paneName) + } + } + + tabVnodes.forEach(({ type, componentOptions, props, children }) => { + if ( + type && + (type.toString() === 'Symbol(Fragment)' || // vue@3.3之前的开发模式 + type.toString() === 'Symbol(v-fgt)' || // vue@3.3.1 的变更 + type.toString() === 'Symbol()') // 构建后 + ) { + Array.isArray(children) && + children.forEach(({ type, componentOptions, props }) => handler({ type, componentOptions, props })) + } else { + handler({ type, componentOptions, props }) + } + }) + } + + // 此处不同步aui,vue3情况下插槽使用v-if生成的slotDefault有差异 + if (orders.length > 0) { + let tmpPanes = [] + + orders.forEach((paneName) => { + let pane = panes.find((pane) => pane.name === paneName) + + if (pane) tmpPanes.push(pane) + }) + + panes = tmpPanes + } + + return panes +} + export const calcPaneInstances = ({ constants, @@ -44,24 +96,7 @@ export const calcPaneInstances = index > -1 ? (currentPanes[index] = vm) : currentPanes.push(vm) } }) - - const currentPaneStates = currentPanes.map((pane) => pane.state) - const paneStates = state.panes.map((pane) => pane.state) - - let newPanes = [] as ITabsPaneVm[] - for (let i = 0; i < paneStates.length; i++) { - const paneState = paneStates[i] - const index = currentPaneStates.indexOf(paneState) - - if (index > -1) { - newPanes.push(state.panes[i]) - currentPanes.splice(index, 1) - - currentPaneStates.splice(index, 1) - } - } - - newPanes = newPanes.concat(currentPanes) + const newPanes = getOrderedPanes(parent, currentPanes) as ITabsPaneVm[] const panesChanged = !( newPanes.length === state.panes.length && diff --git a/packages/renderless/types/action-menu.type.ts b/packages/renderless/types/action-menu.type.ts index 279dc3e350..b8b2337c09 100644 --- a/packages/renderless/types/action-menu.type.ts +++ b/packages/renderless/types/action-menu.type.ts @@ -23,14 +23,23 @@ import type { computedSuffixIcon } from '../src/action-menu' +export interface IActonMenuOptionsItem { + label?: string + disabled?: boolean + divided?: boolean + children?: IActonMenuOptionsItem[] + icon?: any + [key: string]: any +} + export interface IActionMenuState { - visibleOptions: object - moreOptions: object + visibleOptions: IActonMenuOptionsItem[] + moreOptions: IActonMenuOptionsItem[] isCardMode: boolean spacing: string | number maxShowNum: number moreText: string - suffixIcon: string | Object + suffixIcon: string | object } export type IActionMenuProps = ExtractPropTypes diff --git a/packages/theme-saas/package.json b/packages/theme-saas/package.json index b1c9fc5730..b6fe0c51a5 100644 --- a/packages/theme-saas/package.json +++ b/packages/theme-saas/package.json @@ -101,4 +101,4 @@ ] } } -} \ No newline at end of file +} diff --git a/packages/theme-saas/src/index.less b/packages/theme-saas/src/index.less index 83c5dd8020..d96cd84b86 100644 --- a/packages/theme-saas/src/index.less +++ b/packages/theme-saas/src/index.less @@ -99,6 +99,7 @@ @import './logout/index.less'; @import './menubar/index.less'; @import './milestone/index.less'; +@import './mind-map/index.less'; @import './modal/index.less'; @import './month-range/index.less'; @import './month-table/index.less'; diff --git a/packages/theme-saas/src/mind-map/index.less b/packages/theme-saas/src/mind-map/index.less new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/theme/package.json b/packages/theme/package.json index 4e3cf21512..4bddbc1891 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/vue-theme", - "version": "3.16.1", + "version": "3.16.2", "description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.", "main": "index.css", "homepage": "https://opentiny.design/tiny-vue", diff --git a/packages/theme/src/grid/table.less b/packages/theme/src/grid/table.less index f890e6c68b..e22bdb36c7 100644 --- a/packages/theme/src/grid/table.less +++ b/packages/theme/src/grid/table.less @@ -1015,6 +1015,9 @@ // 部分场景下浏览器缩放比例导致表头和表体错位问题 th.col__gutter { width: 0; + position: sticky; + right: 0; + background-color: var(--ti-grid-header-background-color); } } diff --git a/packages/vue/src/action-menu/package.json b/packages/vue/src/action-menu/package.json index 2078ecef20..f8af08294c 100644 --- a/packages/vue/src/action-menu/package.json +++ b/packages/vue/src/action-menu/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/vue-action-menu", - "version": "3.16.0", + "version": "3.16.1", "description": "", "main": "lib/index.js", "module": "index.ts", @@ -24,4 +24,4 @@ "@opentiny/vue-icon": "workspace:~" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/vue/src/action-menu/src/pc.vue b/packages/vue/src/action-menu/src/pc.vue index a587f22828..7a383be142 100644 --- a/packages/vue/src/action-menu/src/pc.vue +++ b/packages/vue/src/action-menu/src/pc.vue @@ -42,6 +42,7 @@ { - const slotDefault = state.rootTabs.$slots.default - let orders - - if (typeof slotDefault === 'function') { - orders = [] - - const tabVnodes = slotDefault() - const handler = ({ type, componentOptions, props }) => { - let componentName = type && type.componentName - - if (!componentName) componentName = componentOptions && componentOptions.Ctor.extendOptions.componentName - - if (componentName === 'TabItem') { - const paneName = (props && props.name) || (componentOptions && componentOptions.propsData.name) - - orders.push(paneName) - } - } - - tabVnodes.forEach(({ type, componentOptions, props, children }) => { - if ( - type && - (type.toString() === 'Symbol(Fragment)' || // vue@3.3之前的开发模式 - type.toString() === 'Symbol(v-fgt)' || // vue@3.3.1 的变更 - type.toString() === 'Symbol()') // 构建后 - ) { - Array.isArray(children) && - children.forEach(({ type, componentOptions, props }) => handler({ type, componentOptions, props })) - } else { - handler({ type, componentOptions, props }) - } - }) - } - - if (orders) { - let tmpPanes = [] - - orders.forEach((paneName) => { - let pane = panes.find((pane) => pane.name === paneName) - - if (pane) tmpPanes.push(pane) - }) - - panes = tmpPanes - } - - return panes -} - export default defineComponent({ name: $prefix + 'TabNav', components: { @@ -198,7 +148,7 @@ export default defineComponent({ ) } - const tabs = getOrderedPanes(state, panes).map((pane, index) => { + const tabs = panes.map((pane, index) => { let tabName = pane.name || pane.state.index || index const withClose = pane.state.isClosable || editable