From 2f262000daf048bd06a690e11e0e97457d9f495a Mon Sep 17 00:00:00 2001 From: discreted Date: Wed, 6 May 2026 10:40:58 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=80=89=E9=A1=B9=E6=9B=BF=E6=8D=A2=E9=85=8D=E7=BD=AE=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E6=98=BE=E7=A4=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/search-box/index.ts | 31 ++++++++++++++++--- packages/search-box/package.json | 7 +++-- packages/search-box/scripts/plugin-utils.ts | 15 ++++++--- packages/search-box/shims-vue.d.ts | 6 ++++ .../src/composables/use-dropdown.ts | 15 +++++---- packages/search-box/src/index.ts | 4 +++ packages/search-box/src/pc.vue | 4 +++ packages/search-box/src/shims-vue.d.ts | 6 ++++ packages/search-box/theme-saas/index.less | 2 +- packages/search-box/theme/index.less | 2 +- packages/search-box/tsconfig.json | 2 +- 11 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 packages/search-box/shims-vue.d.ts create mode 100644 packages/search-box/src/shims-vue.d.ts diff --git a/packages/search-box/index.ts b/packages/search-box/index.ts index 7c5c596..c56e6eb 100644 --- a/packages/search-box/index.ts +++ b/packages/search-box/index.ts @@ -10,7 +10,7 @@ * */ -import TinySearchBox from "./src/index.ts"; +import TinySearchBox from "./src/index"; import { t, setGlobalApp } from './src/utils/i18n' import zhCN from './src/utils/zh_CN' import enUS from './src/utils/en_US' @@ -21,18 +21,39 @@ import { version } from "./package.json"; /* istanbul ignore next */ TinySearchBox.install = function (Vue) { - Vue.component(TinySearchBox.name, TinySearchBox); + const runtime: any = Vue + + // Vue3 app instance: install(app) + if (runtime && runtime.config && runtime.config.globalProperties) { + setGlobalApp(runtime) + } + + // Vue2 constructor: install(Vue) + // Capture a runtime vm/root with i18n once available, so users don't need setGlobalApp manually. + if (runtime && runtime.mixin && !runtime.__TINY_SEARCH_BOX_I18N_MIXIN__) { + runtime.__TINY_SEARCH_BOX_I18N_MIXIN__ = true + runtime.mixin({ + beforeCreate() { + const vm = this as any + if (vm && (vm.$i18n || vm.$t || (vm.$root && (vm.$root.$i18n || vm.$root.$t)))) { + setGlobalApp(vm.$root || vm) + } + } + }) + } + + runtime.component(TinySearchBox.name, TinySearchBox); }; TinySearchBox.version = version; /* istanbul ignore next */ -if (window && typeof window !== "undefined" && window.Vue) { - TinySearchBox.install(window.Vue); +if (typeof window !== "undefined" && (window as typeof window & { Vue?: unknown }).Vue) { + TinySearchBox.install((window as typeof window & { Vue: any }).Vue); } // 导出 -export * from './src/index.type.ts' +export * from './src/index.type' export { zhCN, enUS, t, setGlobalApp, TinySearchBoxFirstLevelPanel, TinySearchBoxSecondLevelPanel } export default TinySearchBox; diff --git a/packages/search-box/package.json b/packages/search-box/package.json index fd1958c..4991d3e 100644 --- a/packages/search-box/package.json +++ b/packages/search-box/package.json @@ -1,6 +1,6 @@ { "name": "@opentiny/vue-search-box", - "version": "3.28.5", + "version": "3.29.0-alpha.0", "description": "", "homepage": "https://github.com/opentiny/tiny-search-box#readme", "bugs": { @@ -19,7 +19,7 @@ "./theme/**/*", "./theme-saas/**/*" ], - "main": "lib/index.js", + "main": "index.js", "module": "index.ts", "keywords": [ "search", @@ -35,6 +35,7 @@ }, "scripts": { "build:all": "pnpm build:vue2 && pnpm build:vue2-saas && pnpm build:vue3 && pnpm build:vue3-saas", + "typecheck": "tsc --noEmit -p tsconfig.json", "build:vue2": "vite build --config vite.config.vue2.ts && node scripts/post-build.js vue2", "build:vue2-saas": "vite build --config vite.config.vue2-saas.ts && node scripts/post-build.js vue2-saas", "build:vue3": "vite build --config vite.config.vue3.ts && node scripts/post-build.js vue3", @@ -65,4 +66,4 @@ "vue2": "npm:vue@2.6.14", "vue3": "npm:vue@3.5.13" } -} \ No newline at end of file +} diff --git a/packages/search-box/scripts/plugin-utils.ts b/packages/search-box/scripts/plugin-utils.ts index 3725478..ccaf50f 100644 --- a/packages/search-box/scripts/plugin-utils.ts +++ b/packages/search-box/scripts/plugin-utils.ts @@ -72,19 +72,26 @@ export function moveTypesFiles(typesDir: string): Plugin { async closeBundle() { const typesPath = resolve(typesDir) const srcDir = resolve(typesPath, 'src') - const indexPath = resolve(srcDir, 'index.type.d.ts') + const srcIndexPath = resolve(srcDir, 'index.type.d.ts') + const flatIndexPath = resolve(typesPath, 'index.type.d.ts') const targetPath = resolve(typesPath, 'index.d.ts') + const indexPath = existsSync(srcIndexPath) ? srcIndexPath : flatIndexPath if (existsSync(indexPath)) { // 读取文件内容 const content = readFileSync(indexPath, 'utf-8') // 写入到目标位置 writeFileSync(targetPath, content, 'utf-8') - // 删除 src 目录 - rmSync(srcDir, { recursive: true, force: true }) + // 删除多余的类型中间文件/目录 + if (existsSync(srcDir)) { + rmSync(srcDir, { recursive: true, force: true }) + } + if (indexPath === flatIndexPath && existsSync(flatIndexPath)) { + unlinkSync(flatIndexPath) + } console.log('已移动类型文件到 types 目录') } else { - console.warn('⚠️ 类型文件不存在:', indexPath) + console.warn('⚠️ 类型文件不存在:', srcIndexPath, 'or', flatIndexPath) } } } diff --git a/packages/search-box/shims-vue.d.ts b/packages/search-box/shims-vue.d.ts new file mode 100644 index 0000000..794852f --- /dev/null +++ b/packages/search-box/shims-vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const component: DefineComponent, Record, unknown> + export default component +} diff --git a/packages/search-box/src/composables/use-dropdown.ts b/packages/search-box/src/composables/use-dropdown.ts index 115e2cc..e8cdc67 100644 --- a/packages/search-box/src/composables/use-dropdown.ts +++ b/packages/search-box/src/composables/use-dropdown.ts @@ -235,14 +235,17 @@ export function useDropdown({ props, emit, state, t, format, nextTick, vm, cance } // 无label的情况 } else { - const { items, defaultField } = props + const { items, defaultField, defaultFieldReplace } = props // 先根据 regexp 匹配找到对应的配置项 const currentItem = items.find((item) => { const { regexp } = item return regexp && regexp.test(state.inputValue) }) || (defaultField ? items.find((item) => item.field === defaultField) : state.allTypeAttri) - const { replace, type, mergeTag, regexp } = currentItem + const isDefaultKeywordField = currentItem?.field === state.allTypeAttri.field + const normalizedCurrentItem = + isDefaultKeywordField && defaultFieldReplace ? { ...currentItem, replace: true } : currentItem + const { replace, type, mergeTag, regexp } = normalizedCurrentItem const tagList = (type !== 'checkbox' && replace) || (type === 'checkbox' && mergeTag) ? [inputValue] @@ -265,7 +268,7 @@ export function useDropdown({ props, emit, state, t, format, nextTick, vm, cance for (const tag of tagList) { // 每个 tag 单独用 regexp 校验 if (regexp.test(tag)) { - updateModelValue(currentItem, {}, label, tag) + updateModelValue(normalizedCurrentItem, {}, label, tag) } else { invalidTags.push(tag) } @@ -274,14 +277,14 @@ export function useDropdown({ props, emit, state, t, format, nextTick, vm, cance if (invalidTags.length > 0) { emit('validate-error', { invalidValues: invalidTags, - field: currentItem.field, - label: currentItem.label, + field: normalizedCurrentItem.field, + label: normalizedCurrentItem.label, regexp: regexp.toString() }) } } else { for (const tag of tagList) { - updateModelValue(currentItem, {}, label, tag) + updateModelValue(normalizedCurrentItem, {}, label, tag) } } } diff --git a/packages/search-box/src/index.ts b/packages/search-box/src/index.ts index 1b7c24a..2ad558e 100644 --- a/packages/search-box/src/index.ts +++ b/packages/search-box/src/index.ts @@ -59,6 +59,10 @@ export const searchBoxProps = { type: String, default: '' }, + defaultFieldReplace: { + type: Boolean, + default: false + }, /** 是否可编辑 */ editable: { type: Boolean, diff --git a/packages/search-box/src/pc.vue b/packages/search-box/src/pc.vue index 1b64540..985c3e9 100644 --- a/packages/search-box/src/pc.vue +++ b/packages/search-box/src/pc.vue @@ -489,6 +489,10 @@ export default defineComponent({ type: String, default: '' }, + defaultFieldReplace: { + type: Boolean, + default: false + }, editable: { type: Boolean, default: false diff --git a/packages/search-box/src/shims-vue.d.ts b/packages/search-box/src/shims-vue.d.ts new file mode 100644 index 0000000..794852f --- /dev/null +++ b/packages/search-box/src/shims-vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const component: DefineComponent, Record, unknown> + export default component +} diff --git a/packages/search-box/theme-saas/index.less b/packages/search-box/theme-saas/index.less index 4120267..a546402 100644 --- a/packages/search-box/theme-saas/index.less +++ b/packages/search-box/theme-saas/index.less @@ -107,7 +107,7 @@ } } -.@{popper-tiny-cls}.@{search-box__dropdown-menu-cls}, +.@{popper-tiny-cls}.@{popper-tiny-cls}.@{search-box__dropdown-menu-cls}, .@{search-box__popover-cls} { @apply p-0; } diff --git a/packages/search-box/theme/index.less b/packages/search-box/theme/index.less index fcc48dc..abb3c43 100644 --- a/packages/search-box/theme/index.less +++ b/packages/search-box/theme/index.less @@ -111,7 +111,7 @@ margin-top: 0; } -.@{popper-tiny-cls}.@{search-box__dropdown-menu-cls}, +.@{popper-tiny-cls}.@{popper-tiny-cls}.@{search-box__dropdown-menu-cls}, .@{search-box__popover-cls} { padding: 0; } diff --git a/packages/search-box/tsconfig.json b/packages/search-box/tsconfig.json index 41aae5c..62b77f4 100644 --- a/packages/search-box/tsconfig.json +++ b/packages/search-box/tsconfig.json @@ -23,7 +23,7 @@ }, "types": ["node"] }, - "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "index.ts"], + "include": ["*.d.ts", "src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.vue", "index.ts"], "exclude": ["node_modules", "dist", "dist2", "dist3", "**/*.spec.ts", "**/*.test.ts"] } From 6a73f44a0cc458d5e43dbc454975bc540a58dde3 Mon Sep 17 00:00:00 2001 From: discreted Date: Wed, 6 May 2026 10:53:04 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81e2e=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E6=96=87=E6=A1=A3=E6=98=BE=E7=A4=BA=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/docs/.vitepress/config.mts | 38 +++++++++++++++++- packages/docs/.vitepress/theme/index.ts | 33 ++++++++++++++-- packages/docs/README.md | 26 ++++++++++++- packages/docs/apis/props.md | 5 ++- packages/docs/examples/auto-match.md | 8 +++- packages/docs/examples/basic-usage.md | 9 ++++- packages/docs/examples/default-field.md | 8 +++- packages/docs/examples/empty-placeholder.md | 8 +++- packages/docs/examples/help.md | 8 +++- packages/docs/examples/item-placeholder.md | 8 +++- packages/docs/examples/panel-max-height.md | 8 +++- packages/docs/examples/potential-match.md | 8 +++- packages/docs/examples/split-input-value.md | 8 +++- packages/docs/examples/v-model.md | 8 +++- packages/docs/guide/usage.md | 9 +++++ packages/docs/package.json | 17 ++++++-- packages/docs/playwright.config.ts | 39 +++++++++++++++++++ packages/docs/search-box/auto-match.spec.ts | 4 +- packages/docs/search-box/basic-usage.spec.ts | 4 +- packages/docs/search-box/basic-usage.vue | 4 +- packages/docs/search-box/custom-panel.spec.ts | 4 +- .../docs/search-box/default-field.spec.ts | 4 +- packages/docs/search-box/editable.spec.ts | 4 +- .../docs/search-box/empty-placeholder.spec.ts | 2 +- packages/docs/search-box/events.spec.ts | 4 +- packages/docs/search-box/group-key.spec.ts | 4 +- packages/docs/search-box/help.spec.ts | 2 +- packages/docs/search-box/id-map-key.spec.ts | 4 +- packages/docs/search-box/max-length.spec.ts | 4 +- .../docs/search-box/max-time-length.spec.ts | 4 +- packages/docs/search-box/merge-tag.spec.ts | 4 +- .../docs/search-box/potential-match.spec.ts | 4 +- packages/docs/search-box/settings.spec.ts | 4 +- packages/docs/search-box/v-model.spec.ts | 4 +- packages/docs/tsconfig.json | 28 +++++++++++++ 35 files changed, 286 insertions(+), 54 deletions(-) create mode 100644 packages/docs/playwright.config.ts create mode 100644 packages/docs/tsconfig.json diff --git a/packages/docs/.vitepress/config.mts b/packages/docs/.vitepress/config.mts index 889a7b7..9ed2166 100644 --- a/packages/docs/.vitepress/config.mts +++ b/packages/docs/.vitepress/config.mts @@ -5,6 +5,8 @@ import { containerPreview, componentPreview } from '@vitepress-demo-preview/plug const env = loadEnv(process.env.VITE_BASE_URL!, fileURLToPath(new URL('../', import.meta.url))) const __dirname = fileURLToPath(new URL('.', import.meta.url)) +const useLocalSearchBox = process.env.USE_LOCAL_SEARCH_BOX === 'true' +const docsNodeModules = path.resolve(__dirname, '../node_modules') export default defineConfig({ title: 'TinySearchBox', @@ -12,11 +14,43 @@ export default defineConfig({ base: env.VITE_BASE_URL || '/tiny-search-box/', cleanUrls: true, vite: { + resolve: { + alias: useLocalSearchBox + ? { + '@opentiny/vue-search-box': path.resolve(__dirname, '../../search-box/index.ts'), + '@opentiny/vue-search-box-theme': path.resolve(__dirname, '../../search-box/theme/index.less'), + '@opentiny/vue': path.resolve(docsNodeModules, '@opentiny/vue'), + '@opentiny/vue-common': path.resolve(docsNodeModules, '@opentiny/vue-common'), + '@opentiny/vue-icon': path.resolve(docsNodeModules, '@opentiny/vue-icon'), + '@opentiny/vue-locale': path.resolve(docsNodeModules, '@opentiny/vue-locale'), + vue: path.resolve(docsNodeModules, 'vue/dist/vue.runtime.esm-bundler.js') + } + : {} + }, optimizeDeps: { - exclude: ['@opentiny/vue-search-box', '@opentiny/vue-locale'] + include: [ + 'streamsaver', + 'quill-delta', + '@opentiny/vue', + '@opentiny/vue-common', + '@opentiny/vue-icon', + '@opentiny/vue-locale' + ], + exclude: useLocalSearchBox + ? [ + // Keep local package source in dev for HMR. + '@opentiny/vue-search-box' + ] + : [] + }, + server: { + fs: { + allow: [path.resolve(__dirname, '..'), path.resolve(__dirname, '../../search-box')] + } }, ssr: { - noExternal: [/@opentiny\//, '@opentiny/vue-search-box'] + // Reduce SSR transform scope to speed up dev startup. + noExternal: ['@opentiny/vue-search-box'] } }, markdown: { diff --git a/packages/docs/.vitepress/theme/index.ts b/packages/docs/.vitepress/theme/index.ts index 26af4c2..accdfdb 100644 --- a/packages/docs/.vitepress/theme/index.ts +++ b/packages/docs/.vitepress/theme/index.ts @@ -1,13 +1,40 @@ import DefaultTheme from 'vitepress/theme' import { NaiveUIContainer } from '@vitepress-demo-preview/component' -import '@vitepress-demo-preview/component/dist/style.css' // 导入 Font Awesome 图标 +import '@vitepress-demo-preview/component/dist/style.css' import './index.less' + export default { ...DefaultTheme, async enhanceApp({ app }) { if (!import.meta.env.SSR) { - const Module = await import('@opentiny/vue-search-box') - app.use(Module.default) + const [{ createI18n }, LocaleModule, SearchBoxModule] = await Promise.all([ + import('vue-i18n'), + import('@opentiny/vue-locale'), + import('@opentiny/vue-search-box') + ]) + + const locale = LocaleModule.default || LocaleModule + const TinySearchBox = SearchBoxModule.default + const { zhCN, enUS, setGlobalApp } = SearchBoxModule + + const messages = { + 'zh-CN': locale.extend(true, {}, locale.zhCN || {}, zhCN || {}), + 'en-US': locale.extend(true, {}, locale.enUS || {}, enUS || {}) + } + + const i18n = locale.initI18n({ + app, + createI18n, + messages, + i18n: { + legacy: false, + locale: 'zh-CN', + fallbackLocale: 'zh-CN' + } + }) + + app.use(i18n) + app.use(TinySearchBox) } app.component('demo-preview', NaiveUIContainer) } diff --git a/packages/docs/README.md b/packages/docs/README.md index 475741e..de4e34c 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -1 +1,25 @@ -# 文档 +# Docs Playground + +## Playwright E2E + +### Install + +```bash +pnpm install +pnpm exec playwright install +``` + +### Run tests + +```bash +pnpm test:e2e +``` + +### Optional modes + +```bash +pnpm test:e2e:headed +pnpm test:e2e:ui +pnpm test:e2e:report +pnpm test:e2e:codegen +``` diff --git a/packages/docs/apis/props.md b/packages/docs/apis/props.md index f15826e..5a9de66 100644 --- a/packages/docs/apis/props.md +++ b/packages/docs/apis/props.md @@ -2,8 +2,9 @@ | 名称 | 类型 | 默认值 | 说明 | | ------------------- | ------------------------------------------------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| default-field | string | -- | 自定义按下 enter 键时,默认搜索的范围;此属性为空时。则默认在关键字范围下搜索 xxx,即生成的搜索标签为“关键字:xxx” | -| editable | boolean | false | 是否开启标签编辑功能,(注:map 类型不支持编辑) | +| default-field | string | -- | 自定义按下 enter 键时,默认搜索的范围;此属性为空时。则默认在关键字范围下搜索 xxx,即生成的搜索标签为“关键字:xxx” | +| default-field-replace | boolean | false | 自定义按下 enter 键时,默认搜索的范围;此属性为true时。则默认替换上一个搜索条件 +| editable | boolean | false | 是否开启标签编辑功能,(注:map 类型不支持编辑) | | empty-placeholder | string | 默认按照关键字搜索 | 没有筛选项时的占位文本 | | id-map-key | string | -- | 配置用来识别筛选项的 id 键取值来源,默认取 props.items 数据的 id,一般用于接口返回的 props.items 数据字段不匹配,但是又需要其中一个键值来识别筛选项的情况;注意:不建议使用 label/value/field 等字段,会被覆盖 | | items | [ISearchBoxItem[]](types.md#isearchboxitem) | [] | 数据项 | diff --git a/packages/docs/examples/auto-match.md b/packages/docs/examples/auto-match.md index 2e9e64e..92ef11d 100644 --- a/packages/docs/examples/auto-match.md +++ b/packages/docs/examples/auto-match.md @@ -1,5 +1,11 @@ -## 自动匹配 +## 自动匹配 内置自动匹配功能,通过 :show-no-data-tip=false 隐藏面板的无数据提示,通过 search 监听搜索事件,change 监听搜索值变化事件。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/basic-usage.md b/packages/docs/examples/basic-usage.md index 4fc7ecc..09771e1 100644 --- a/packages/docs/examples/basic-usage.md +++ b/packages/docs/examples/basic-usage.md @@ -1,5 +1,12 @@ -## 基础用法 +## 基础用法 通过 items 配置搜索数据项 +### Data Source + +<<< ../search-box/data-source.ts + + + + diff --git a/packages/docs/examples/default-field.md b/packages/docs/examples/default-field.md index d825064..46e448e 100644 --- a/packages/docs/examples/default-field.md +++ b/packages/docs/examples/default-field.md @@ -1,5 +1,11 @@ -## 自定义默认搜索项 +## 自定义默认搜索项 通过 default-field 属性可以设置自定义默认搜索项。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/empty-placeholder.md b/packages/docs/examples/empty-placeholder.md index 29f850e..a6ed4f9 100644 --- a/packages/docs/examples/empty-placeholder.md +++ b/packages/docs/examples/empty-placeholder.md @@ -1,5 +1,11 @@ -## 没有筛选项时的占位文本 +## 没有筛选项时的占位文本 通过 empty-placeholder 属性可以设置没有筛选项时的占位文本。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/help.md b/packages/docs/examples/help.md index 0e9d40e..1d75310 100644 --- a/packages/docs/examples/help.md +++ b/packages/docs/examples/help.md @@ -1,5 +1,11 @@ -## help 提示场景 +## help 提示场景 通过 show-help 属性可以控制是否显示帮助图标,默认为 true。当设置为 false 时,不会显示帮助图标,使用 help 事件回调可自定义弹窗内容。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/item-placeholder.md b/packages/docs/examples/item-placeholder.md index d1395d2..6b8a049 100644 --- a/packages/docs/examples/item-placeholder.md +++ b/packages/docs/examples/item-placeholder.md @@ -1,5 +1,11 @@ -## 数据项占位文本 +## 数据项占位文本 通过 item.placeholder 属性可以设置数据项的占位文本。当数据项为空时,会显示该占位文本;item.editAttrDisabeld 可以设置编辑状态下此属性类型不可切换。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/panel-max-height.md b/packages/docs/examples/panel-max-height.md index 9dd64f3..523aabc 100644 --- a/packages/docs/examples/panel-max-height.md +++ b/packages/docs/examples/panel-max-height.md @@ -1,5 +1,11 @@ -## 面板最大高度 +## 面板最大高度 通过 panel-max-height 属性可以设置下拉面板的最大高度。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/potential-match.md b/packages/docs/examples/potential-match.md index 5508e4c..3ce6309 100644 --- a/packages/docs/examples/potential-match.md +++ b/packages/docs/examples/potential-match.md @@ -1,5 +1,11 @@ -## 潜在匹配项 +## 潜在匹配项 通过 potential-options 配置潜在匹配项。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/split-input-value.md b/packages/docs/examples/split-input-value.md index 6ba892f..032fbbe 100644 --- a/packages/docs/examples/split-input-value.md +++ b/packages/docs/examples/split-input-value.md @@ -1,5 +1,11 @@ -## 切分输入值 +## 切分输入值 通过 split-input-value 属性可以自定义输入值的切分符,默认为逗号。当输入值包含切分符时,会按照切分符自动切分成多个关键字。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/examples/v-model.md b/packages/docs/examples/v-model.md index e636912..6791073 100644 --- a/packages/docs/examples/v-model.md +++ b/packages/docs/examples/v-model.md @@ -1,5 +1,11 @@ -## 默认包含筛选项 +## 默认包含筛选项 通过 model-value/v-model 配置默认选中标签项。 +### Data Source + +<<< ../search-box/data-source.ts + + + diff --git a/packages/docs/guide/usage.md b/packages/docs/guide/usage.md index 7e57125..a62f21a 100644 --- a/packages/docs/guide/usage.md +++ b/packages/docs/guide/usage.md @@ -297,3 +297,12 @@ const tags: ISearchBoxTag[] = [] 4. **主题选择**:根据项目需求选择普通主题包(`@opentiny/vue-search-box`)或 SaaS 主题包(`@opentiny/vue-search-box-saas`) 5. **包结构**:每个包包含 `index.js`(组件代码)和 `index.css`(样式文件),样式会自动导入 + +## i18n 自动继承说明 + +从当前版本开始,组件在 `use` 时会自动继承宿主应用的 i18n(Vue2 / Vue3 都支持),通常不再需要手动调用 `setGlobalApp`。 + +- 推荐: + - Vue3:`app.use(i18n)` 后 `app.use(TinySearchBox)` + - Vue2:`Vue.use(TinySearchBox)`,并在根实例中配置 `i18n` +- 兼容:如遇多应用/微前端等特殊场景,仍可手动调用 `setGlobalApp` 指定实例。 diff --git a/packages/docs/package.json b/packages/docs/package.json index 8194b85..f7f3e2e 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -5,8 +5,15 @@ "private": true, "scripts": { "docs:dev": "vitepress dev", + "docs:dev:local": "cross-env USE_LOCAL_SEARCH_BOX=true vitepress dev", "docs:build": "vitepress build", - "docs:preview": "vitepress preview" + "docs:preview": "vitepress preview", + "test:e2e": "playwright test", + "test:e2e:headed": "playwright test --headed", + "test:e2e:ui": "playwright test --ui", + "test:e2e:report": "playwright show-report", + "test:e2e:codegen": "playwright codegen http://127.0.0.1:4174/examples/events", + "test:e2e:codegen:headed": "playwright codegen --device=\"Desktop Chrome\" http://127.0.0.1:4174/examples/events" }, "dependencies": { "vue": "^3.5.13", @@ -34,12 +41,16 @@ "@opentiny/vue-locale": "^3.28.0", "@opentiny/vue-theme": "^3.28.0", "@vitepress-demo-preview/component": "^2.3.2", - "less": "^4.3.0" + "less": "^4.3.0", + "streamsaver": "^2.0.6", + "vue-i18n": "^9.14.0" }, "devDependencies": { + "@playwright/test": "^1.55.0", "@rollup/plugin-commonjs": "^28.0.3", "@types/node": "^22.15.12", "@vitepress-demo-preview/plugin": "^1.4.0", + "cross-env": "^7.0.3", "vitepress": "^1.6.3" } -} \ No newline at end of file +} diff --git a/packages/docs/playwright.config.ts b/packages/docs/playwright.config.ts new file mode 100644 index 0000000..952443d --- /dev/null +++ b/packages/docs/playwright.config.ts @@ -0,0 +1,39 @@ +import { defineConfig, devices } from '@playwright/test' +import { fileURLToPath } from 'node:url' +import path from 'node:path' + +const port = 4174 +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export default defineConfig({ + testDir: './search-box', + testMatch: '*.spec.ts', + timeout: 60_000, + fullyParallel: false, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : [['list'], ['html']], + use: { + baseURL: `http://127.0.0.1:${port}/`, + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'retain-on-failure' + }, + webServer: { + command: `pnpm docs:dev --host 127.0.0.1 --port ${port}`, + cwd: __dirname, + port, + timeout: 120_000, + reuseExistingServer: false, + env: { + ...process.env, + VITE_BASE_URL: '/' + } + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ] +}) diff --git a/packages/docs/search-box/auto-match.spec.ts b/packages/docs/search-box/auto-match.spec.ts index 240d908..a2361cb 100644 --- a/packages/docs/search-box/auto-match.spec.ts +++ b/packages/docs/search-box/auto-match.spec.ts @@ -1,8 +1,8 @@ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' test('自动匹配', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) - await page.goto('search-box#auto-match') + await page.goto('/examples/auto-match') const container = page.locator('#auto-match') const tags = page.locator('.tvp-search-box__tag') diff --git a/packages/docs/search-box/basic-usage.spec.ts b/packages/docs/search-box/basic-usage.spec.ts index 3f49a6c..fd404aa 100644 --- a/packages/docs/search-box/basic-usage.spec.ts +++ b/packages/docs/search-box/basic-usage.spec.ts @@ -1,8 +1,8 @@ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' test('基础用法', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) - await page.goto('search-box#basic-usage') + await page.goto('/examples/basic-usage') const attrEls = page.locator('.tvp-search-box__first-panel > li .tiny-dropdown-item__label > span').first() const tags = page.locator('.tvp-search-box__tag') diff --git a/packages/docs/search-box/basic-usage.vue b/packages/docs/search-box/basic-usage.vue index b795b73..243b90a 100644 --- a/packages/docs/search-box/basic-usage.vue +++ b/packages/docs/search-box/basic-usage.vue @@ -1,7 +1,5 @@