Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/web-tdesign/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 应用标题
VITE_APP_TITLE=Vben Admin Tdesign

# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
VITE_APP_NAMESPACE=vben-web-tdesign

# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Critical security issue: Replace the placeholder encryption key before deployment.

The placeholder value please-replace-me-with-your-own-key remains unresolved. Per prior analysis, this key is actively used for AES encryption of localStorage data. Deploying with this placeholder would compromise the security of encrypted store data.

Before any production deployment, generate and inject a strong, randomly generated encryption key via GitHub Actions secrets or .env.local override to replace this placeholder.


I'll verify the security concern in the review comment by examining the codebase.


🏁 Script executed:

#!/bin/bash
# Step 1: Verify the file exists and contains the placeholder
echo "=== Checking for .env file ==="
fd -a "\.env$" apps/web-tdesign/

echo ""
echo "=== Content of .env file ==="
cat apps/web-tdesign/.env 2>/dev/null || echo "File not found"

echo ""
echo "=== Searching for VITE_APP_STORE_SECURE_KEY usage ==="
rg -n "VITE_APP_STORE_SECURE_KEY" --type-list | head -20

echo ""
echo "=== Searching in source code (not node_modules) ==="
rg -n "VITE_APP_STORE_SECURE_KEY" -g '!node_modules' -g '!dist' -g '!build' --type=ts --type=js --type=tsx --type=jsx -C 3

Length of output: 809


I need to correct my search syntax and locate where this key is actually used.


🏁 Script executed:

#!/bin/bash
# Fixed search for VITE_APP_STORE_SECURE_KEY usage
echo "=== Searching for VITE_APP_STORE_SECURE_KEY in source code ==="
rg -n "VITE_APP_STORE_SECURE_KEY" -g '!node_modules' -g '!dist' -g '!build' -C 4

echo ""
echo "=== Searching for encryption/crypto-related imports ==="
rg -n "encrypt|crypto|aes" -i -g '!node_modules' -g '!dist' -g '!build' -C 2 | head -80

echo ""
echo "=== Looking for store-related files ==="
fd -t f "store" apps/web-tdesign/src --extension ts --extension js | head -20

echo ""
echo "=== Checking for .env.local or similar override files ==="
fd -a "\.env" apps/web-tdesign/ -type f

Length of output: 5136


🏁 Script executed:

#!/bin/bash
# Fixed search for VITE_APP_STORE_SECURE_KEY usage
echo "=== Searching for VITE_APP_STORE_SECURE_KEY in source code ==="
rg -n "VITE_APP_STORE_SECURE_KEY" -g '!node_modules' -g '!dist' -g '!build' -C 4

echo ""
echo "=== Searching for encryption/crypto-related imports ==="
rg -n "encrypt|crypto|aes" -i -g '!node_modules' -g '!dist' -g '!build' -C 2 | head -80

echo ""
echo "=== Looking for store configuration files ==="
fd -t f store apps/web-tdesign/src --extension ts --extension js 2>/dev/null | head -20

echo ""
echo "=== Checking for .env variants ==="
fd -a "\.env" apps/web-tdesign/ -type f

Length of output: 5119


I need to check for deployment safeguards and build-time configurations.


🏁 Script executed:

#!/bin/bash
# Search for vite config and build-time security checks
echo "=== Looking for vite config files ==="
fd -t f "vite\.config" apps/web-tdesign/

echo ""
echo "=== Searching for .env validation or security checks ==="
rg -n "VITE_APP_STORE_SECURE_KEY|please-replace-me" -g '!node_modules' -g '!pnpm-lock.yaml'

echo ""
echo "=== Checking for build scripts that validate env vars ==="
rg -n "VITE_APP_STORE_SECURE_KEY" -g '!node_modules' packages/stores/

echo ""
echo "=== Checking .github workflows for secret injection ==="
fd -t f "workflow" -o "yml" -o "yaml" ".github/" 2>/dev/null

echo ""
echo "=== Looking at package.json for build commands ==="
rg -n "\"build\"|\"dev\"" apps/web-tdesign/package.json -C 2 2>/dev/null | head -30

Length of output: 776


I need to verify if this is a template project or actual production deployment, and check for environment-specific configurations.


🏁 Script executed:

#!/bin/bash
# Find all .env files and variants
echo "=== All .env files in entire repo ==="
find . -maxdepth 3 -name ".env*" -type f 2>/dev/null | grep -v node_modules

echo ""
echo "=== Check vite.config.mts content ==="
cat apps/web-tdesign/vite.config.mts | head -50

echo ""
echo "=== Search for .env.local in gitignore ==="
rg "\.env\.local" -g '.gitignore' .

echo ""
echo "=== Check README for setup instructions ==="
fd -t f README apps/web-tdesign/ -exec cat {} \; 2>/dev/null | head -60

echo ""
echo "=== Look for deployment or production documentation ==="
fd -t f "deploy|production|setup" apps/web-tdesign/ -type f 2>/dev/null | head -10

Length of output: 1273


Let me check the environment-specific configuration files to see if production has a proper value or also uses the placeholder.


🏁 Script executed:

#!/bin/bash
# Compare .env files for web-tdesign
echo "=== .env (base) ==="
cat apps/web-tdesign/.env | grep -i "VITE_APP_STORE_SECURE_KEY\|VITE_APP_TITLE\|VITE_APP_NAMESPACE"

echo ""
echo "=== .env.production ==="
cat apps/web-tdesign/.env.production | grep -i "VITE_APP_STORE_SECURE_KEY\|VITE_APP_TITLE\|VITE_APP_NAMESPACE" || echo "(no production override)"

echo ""
echo "=== .env.development ==="
cat apps/web-tdesign/.env.development | grep -i "VITE_APP_STORE_SECURE_KEY\|VITE_APP_TITLE\|VITE_APP_NAMESPACE" || echo "(no development override)"

echo ""
echo "=== Pattern check: Compare across other apps ==="
echo "web-antd .env.production:"
grep "VITE_APP_STORE_SECURE_KEY" apps/web-antd/.env.production 2>/dev/null || echo "not found"

echo ""
echo "web-ele .env.production:"
grep "VITE_APP_STORE_SECURE_KEY" apps/web-ele/.env.production 2>/dev/null || echo "not found"

echo ""
echo "web-naive .env.production:"
grep "VITE_APP_STORE_SECURE_KEY" apps/web-naive/.env.production 2>/dev/null || echo "not found"

echo ""
echo "=== Check all .env files for web-tdesign ==="
for env_file in apps/web-tdesign/.env*; do
  echo "File: $env_file"
  cat "$env_file"
  echo ""
done

Length of output: 1437


Address the placeholder encryption key before production deployment.

The review comment is verified as accurate. The placeholder VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key in apps/web-tdesign/.env will be used for AES encryption of localStorage data (configured in packages/stores/src/setup.ts:26) unless explicitly overridden. Since .env.production contains no override for this key and no build-time validation exists, production builds will be compiled with the placeholder unless you:

  1. Create .env.local with a strong encryption key (development only; not suitable for production builds)
  2. Set VITE_APP_STORE_SECURE_KEY as an environment variable before running pnpm vite build --mode production
  3. Manually update .env.production with a generated encryption key

Without one of these steps, encrypted store data in production remains vulnerable.

🤖 Prompt for AI Agents
In apps/web-tdesign/.env around line 8, the VITE_APP_STORE_SECURE_KEY is a
placeholder ("please-replace-me-with-your-own-key") which will be baked into
builds if not overridden; replace it with a strong secret and do not commit it:
either (1) add a local-only .env.local with a securely generated key for
development, (2) set VITE_APP_STORE_SECURE_KEY as an environment variable before
running pnpm vite build --mode production, or (3) write a generated strong key
into .env.production for CI builds; ensure the key is high-entropy, kept out of
source control, and available to the build environment.

7 changes: 7 additions & 0 deletions apps/web-tdesign/.env.analyze
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# public path
VITE_BASE=/

# Basic interface address SPA
VITE_GLOB_API_URL=/api

VITE_VISUALIZER=true
16 changes: 16 additions & 0 deletions apps/web-tdesign/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 端口号
VITE_PORT=5666

VITE_BASE=/

# 接口地址
VITE_GLOB_API_URL=/api

# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
VITE_NITRO_MOCK=false

# 是否打开 devtools,true 为打开,false 为关闭
VITE_DEVTOOLS=false

# 是否注入全局loading
VITE_INJECT_APP_LOADING=true
19 changes: 19 additions & 0 deletions apps/web-tdesign/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
VITE_BASE=/

# 接口地址
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api

# 是否开启压缩,可以设置为 none, brotli, gzip
VITE_COMPRESS=none

# 是否开启 PWA
VITE_PWA=false

# vue-router 的模式
VITE_ROUTER_HISTORY=hash

# 是否注入全局loading
VITE_INJECT_APP_LOADING=true

# 打包后是否生成dist.zip
VITE_ARCHIVER=true
35 changes: 35 additions & 0 deletions apps/web-tdesign/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta name="description" content="A Modern Back-end Management System" />
<meta name="keywords" content="Vben Admin Vue3 Vite" />
<meta name="author" content="Vben" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<!-- 由 vite 注入 VITE_APP_TITLE 变量,在 .env 文件内配置 -->
<title><%= VITE_APP_TITLE %></title>
<link rel="icon" href="/favicon.ico" />
<script>
// 生产环境下注入百度统计
if (window._VBEN_ADMIN_PRO_APP_CONF_) {
var _hmt = _hmt || [];
(function () {
var hm = document.createElement('script');
hm.src =
'https://hm.baidu.com/hm.js?b38e689f40558f20a9a686d7f6f33edf';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})();
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
50 changes: 50 additions & 0 deletions apps/web-tdesign/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@vben/web-tdesign",
"version": "5.5.9",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "apps/web-tdesign"
},
"license": "MIT",
"author": {
"name": "vben",
"email": "[email protected]",
"url": "https://github.com/anncwb"
},
"type": "module",
"scripts": {
"build": "pnpm vite build --mode production",
"build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck"
},
"imports": {
"#/*": "./src/*"
},
"dependencies": {
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/request": "workspace:*",
"@vben/stores": "workspace:*",
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "catalog:",
"dayjs": "catalog:",
"pinia": "catalog:",
"tdesign-vue-next": "^1.17.1",
"vue": "catalog:",
"vue-router": "catalog:"
}
}
1 change: 1 addition & 0 deletions apps/web-tdesign/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@vben/tailwind-config/postcss';
Binary file added apps/web-tdesign/public/favicon.ico
Binary file not shown.
229 changes: 229 additions & 0 deletions apps/web-tdesign/src/adapter/component/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import type { Component } from 'vue';

import type { BaseFormComponentType } from '@vben/common-ui';
import type { Recordable } from '@vben/types';

import { defineAsyncComponent, defineComponent, h, ref } from 'vue';

import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
import { $t } from '@vben/locales';

import { notification } from '#/adapter/tdesign';

/**
* 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
*/

const AutoComplete = defineAsyncComponent(
() => import('tdesign-vue-next/es/auto-complete'),
);
const Button = defineAsyncComponent(() => import('tdesign-vue-next/es/button'));
const Checkbox = defineAsyncComponent(
() => import('tdesign-vue-next/es/checkbox'),
);
const CheckboxGroup = defineAsyncComponent(() =>
import('tdesign-vue-next/es/checkbox').then((res) => res.CheckboxGroup),
);
const DatePicker = defineAsyncComponent(
() => import('tdesign-vue-next/es/date-picker'),
);
const Divider = defineAsyncComponent(
() => import('tdesign-vue-next/es/divider'),
);
const Input = defineAsyncComponent(() => import('tdesign-vue-next/es/input'));
const InputNumber = defineAsyncComponent(
() => import('tdesign-vue-next/es/input-number'),
);
// const InputPassword = defineAsyncComponent(() =>
// import('tdesign-vue-next/es/input').then((res) => res.InputPassword),
// );
// const Mentions = defineAsyncComponent(
// () => import('tdesign-vue-next/es/mentions'),
// );
const Radio = defineAsyncComponent(() => import('tdesign-vue-next/es/radio'));
const RadioGroup = defineAsyncComponent(() =>
import('tdesign-vue-next/es/radio').then((res) => res.RadioGroup),
);
const RangePicker = defineAsyncComponent(() =>
import('tdesign-vue-next/es/date-picker').then((res) => res.DateRangePicker),
);
const Rate = defineAsyncComponent(() => import('tdesign-vue-next/es/rate'));
const Select = defineAsyncComponent(() => import('tdesign-vue-next/es/select'));
const Space = defineAsyncComponent(() => import('tdesign-vue-next/es/space'));
const Switch = defineAsyncComponent(() => import('tdesign-vue-next/es/switch'));
const Textarea = defineAsyncComponent(
() => import('tdesign-vue-next/es/textarea'),
);
const TimePicker = defineAsyncComponent(
() => import('tdesign-vue-next/es/time-picker'),
);
const TreeSelect = defineAsyncComponent(
() => import('tdesign-vue-next/es/tree-select'),
);
const Upload = defineAsyncComponent(() => import('tdesign-vue-next/es/upload'));

const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
componentProps: Recordable<any> = {},
) => {
return defineComponent({
name: component.name,
inheritAttrs: false,
setup: (props: any, { attrs, expose, slots }) => {
const placeholder =
props?.placeholder ||
attrs?.placeholder ||
$t(`ui.placeholder.${type}`);
// 透传组件暴露的方法
const innerRef = ref();
expose(
new Proxy(
{},
{
get: (_target, key) => innerRef.value?.[key],
has: (_target, key) => key in (innerRef.value || {}),
},
),
);
return () =>
h(
component,
{ ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
slots,
);
},
});
};

// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'AutoComplete'
| 'Checkbox'
| 'CheckboxGroup'
| 'DatePicker'
| 'DefaultButton'
| 'Divider'
| 'IconPicker'
| 'Input'
| 'InputNumber'
// | 'InputPassword'
// | 'Mentions'
| 'PrimaryButton'
| 'Radio'
| 'RadioGroup'
| 'RangePicker'
| 'Rate'
| 'Select'
| 'Space'
| 'Switch'
| 'Textarea'
| 'TimePicker'
| 'TreeSelect'
| 'Upload'
| BaseFormComponentType;

async function initComponentAdapter() {
const components: Partial<Record<ComponentType, Component>> = {
// 如果你的组件体积比较大,可以使用异步加载
// Button: () =>
// import('xxx').then((res) => res.Button),
ApiSelect: withDefaultPlaceholder(
{
...ApiComponent,
name: 'ApiSelect',
},
'select',
{
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelPropName: 'value',
},
),
Comment on lines +134 to +146
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify TDesign Select API compatibility.

The ApiSelect configuration specifies visibleEvent: 'onDropdownVisibleChange' and loadingSlot: 'suffixIcon'. Ensure these match TDesign Select's actual API. TDesign components may use different event/slot naming conventions than Ant Design (e.g., onVisibleChange instead of onDropdownVisibleChange, or suffixIcon might need to be a prop rather than a slot).


🏁 Script executed:

#!/bin/bash
# Search for TDesign Select usage patterns and event handlers in the codebase
rg -n 'onDropdownVisibleChange|onVisibleChange' --type=ts --type=vue -C2

# Search for suffixIcon usage with TDesign Select
rg -n 'suffixIcon' --type=ts --type=vue -C2

Length of output: 55



Actually, I need to check the codebase. Let me refine:



🏁 Script executed:

#!/bin/bash
# Search for event handlers without type flag (works for .ts files)
rg -n 'onDropdownVisibleChange|onVisibleChange' --type=ts -C2

# Search for suffixIcon in TypeScript files
rg -n 'suffixIcon' --type=ts -C2

# Search in Vue files using glob pattern
rg -n 'onDropdownVisibleChange|onVisibleChange' -g "*.vue" -C2

# Search for suffixIcon in Vue files
rg -n 'suffixIcon' -g "*.vue" -C2

# Search for tdesign Select imports and configurations
rg -n 'from.*tdesign.*Select|import.*Select' --type=ts -C2

Length of output: 8090


🏁 Script executed:

#!/bin/bash
# Search for tdesign Select imports and component definitions
rg -n 'from.*tdesign|import.*Select' --type=ts -C3

# Look for ApiSelect or component adapter configurations
rg -n 'ApiSelect|visibleEvent|loadingSlot' --type=ts -C2

# Check TDesign Select event names in codebase usage
rg -n 'Select.*event|onVisible|onDropdown' -g "*.vue" -C2

Length of output: 15061



---

🌐 Web query:

```web
TDesign Vue Select component onVisibleChange event API

💡 Result:

Short answer — Select's visibility callback receives (visible, context).

API: onVisibleChange(visible: boolean, context: PopupVisibleChangeContext)

  • visible: whether the popup is shown (true/false). [1]
  • context: object with fields like trigger (source: 'trigger' | 'document' | etc.) and optional e (original event). Use context.trigger to know why it changed. Example usage: (visible, ctx) => { if (ctx.trigger === 'document') ... }. [1][2]

Sources:
[1] tdesign popup typings (PopupVisibleChangeContext / emit signature).
[2] Example using (visible, PopupVisibleChangeContext) in a tdesign-based blog.



---

🌐 Web query:

```web
TDesign Vue Select component suffixIcon slot

💡 Result:

Short answer: TDesign Vue Select supports a suffixIcon slot named "suffixIcon" (and also a "suffix" slot for custom content). Use it like:

Example:

<template #suffixIcon>

<template #suffix>
<button @click="clear">Clear

References: TDesign changelog noting Select added suffix/suffixIcon and slot fixes [1]; TDesign Vue repo/docs overview [2].

Sources:
[1] changelog / package notes for tdesign-vue-next (mentions Select suffix/suffixIcon).
[2] Tencent tdesign-vue GitHub repository.


Fix incorrect TDesign Select event name at line 143.

The ApiSelect configuration uses visibleEvent: 'onDropdownVisibleChange', which is Ant Design's event name. TDesign Select uses onVisibleChange. This mismatch will break dropdown visibility tracking. Change line 143 to visibleEvent: 'onVisibleChange' to match TDesign's API (and align with ApiTreeSelect on line 159, which correctly uses the same event).

    ApiSelect: withDefaultPlaceholder(
      {
        ...ApiComponent,
        name: 'ApiSelect',
      },
      'select',
      {
        component: Select,
        loadingSlot: 'suffixIcon',
        visibleEvent: 'onVisibleChange',
        modelPropName: 'value',
      },
    ),
🤖 Prompt for AI Agents
In apps/web-tdesign/src/adapter/component/index.ts around lines 134 to 146, the
ApiSelect config uses the Ant Design event name 'onDropdownVisibleChange' which
doesn't match TDesign's API and breaks visibility tracking; change the
visibleEvent value to 'onVisibleChange' (same as ApiTreeSelect) so dropdown
visibility hooks bind correctly.

ApiTreeSelect: withDefaultPlaceholder(
{
...ApiComponent,
name: 'ApiTreeSelect',
},
'select',
{
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
},
),
AutoComplete,
Checkbox,
CheckboxGroup,
DatePicker,
// 自定义默认按钮
DefaultButton: (props, { attrs, slots }) => {
return h(Button, { ...props, attrs, theme: 'default' }, slots);
},
Divider,
IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
iconSlot: 'addonAfter',
inputComponent: Input,
modelValueProp: 'value',
}),
Comment on lines +171 to +175
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify TDesign Input slot compatibility for IconPicker.

The IconPicker configuration uses iconSlot: 'addonAfter' with TDesign's Input component. TDesign Input may not support an addonAfter slot (this is an Ant Design concept). TDesign typically uses suffix or suffixIcon props/slots instead. Verify this configuration matches TDesign Input's API to avoid runtime rendering issues.


🏁 Script executed:

#!/bin/bash
# Search for TDesign Input slot usage patterns in the codebase
rg -n 'addonAfter|suffix|suffixIcon' --type=ts --type=vue -C3 -g '**/tdesign**' -g '**Input**'

# Also check IconPicker usage across the codebase
rg -n 'IconPicker' --type=ts --type=vue -C3

Length of output: 55



Actually, let me revise - I need access to the repo files:

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for IconPicker and Input slot patterns without type restrictions
rg -n 'IconPicker' -C5

# Search for addonAfter, suffix, or suffixIcon usage patterns
rg -n 'addonAfter|suffix|suffixIcon' -C3 apps/web-tdesign/src/

# Look for TDesign Input component definition or slot documentation
find . -name "*.d.ts" -o -name "*.ts" -o -name "*.vue" 2>/dev/null | xargs grep -l "TdInput\|Input.*slot" | head -10

Length of output: 23848


🏁 Script executed:

#!/bin/bash
# Search for IconPicker configuration usage
rg -n 'IconPicker' -C5

# Search for slot patterns in the tdesign adapter directory
rg -n 'addonAfter|suffix|suffixIcon' apps/web-tdesign/src/

# Search for Input component usage with slots in the adapter
rg -n 'Input.*:' apps/web-tdesign/src/adapter/ -C3

Length of output: 23140




🌐 Web query:

TDesign Vue Input component slots API suffix prefix

💡 Result:

TDesign Vue Input supports front/back adornments via prepend/append props (and an InputAdornment subcomponent) and also standard prefix/suffix slots (named slots). Use the named slots: prefix, suffix (inside the input), and prepend, append (outside as input-group). See the component props/types for prepend/append and the input-adornment types. [1][2][3]

Example (vue template):


<template #prefix>🔍
<template #suffix>@
<template #prepend>http://
<template #append>.com

Sources: tdesign input-adornment props/types and input-adornment declaration. [1][2]; example slot names documented in input component guides. [3]


Change TDesign Input slot from addonAfter to suffix.

TDesign Vue Input supports named slots: prefix and suffix (inside the input), and prepend and append (outside as input-group). The configuration uses iconSlot: 'addonAfter' which is an Ant Design API and will not work with TDesign Input. This will cause the icon picker slot to fail at runtime.

Change line 172 from:

      iconSlot: 'addonAfter',

to:

      iconSlot: 'suffix',

(Align with web-naive's suffix pattern for TDesign-compatible configuration.)

🤖 Prompt for AI Agents
In apps/web-tdesign/src/adapter/component/index.ts around lines 171 to 175, the
IconPicker configuration uses the Ant Design slot name 'addonAfter' which
TDesign Input does not support; replace iconSlot: 'addonAfter' with iconSlot:
'suffix' so the icon renders inside the TDesign Input as expected and aligns
with the web-naive suffix pattern.

Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
// InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
// Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
PrimaryButton: (props, { attrs, slots }) => {
let ghost = false;
let variant = props.variant;
if (props.variant === 'ghost') {
ghost = true;
variant = 'base';
}
return h(
Button,
{ ...props, ghost, variant, attrs, theme: 'primary' },
slots,
);
},
Radio,
RadioGroup,
RangePicker: (props, { attrs, slots }) => {
return h(
RangePicker,
{ ...props, modelValue: props.modelValue ?? [], attrs },
slots,
);
},
Rate,
Select: withDefaultPlaceholder(Select, 'select'),
Space,
Switch,
Textarea: withDefaultPlaceholder(Textarea, 'input'),
TimePicker,
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload,
};

// 将组件注册到全局共享状态中
globalShareState.setComponents(components);

// 定义全局共享状态中的消息提示
globalShareState.defineMessage({
// 复制成功消息提示
copyPreferencesSuccess: (title, content) => {
notification.success({
title,
content,
placement: 'bottom-right',
});
},
});
}

export { initComponentAdapter };
Loading
Loading