Skip to content

Commit 2264eaa

Browse files
committed
feat: 新增集成tdesign组件的apps
1 parent 38f91da commit 2264eaa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2957
-0
lines changed

apps/web-tdesign/.env

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# 应用标题
2+
VITE_APP_TITLE=Vben Admin Antd
3+
4+
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
5+
VITE_APP_NAMESPACE=vben-web-antd
6+
7+
# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
8+
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key

apps/web-tdesign/.env.analyze

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# public path
2+
VITE_BASE=/
3+
4+
# Basic interface address SPA
5+
VITE_GLOB_API_URL=/api
6+
7+
VITE_VISUALIZER=true

apps/web-tdesign/.env.development

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# 端口号
2+
VITE_PORT=5666
3+
4+
VITE_BASE=/
5+
6+
# 接口地址
7+
VITE_GLOB_API_URL=/api
8+
9+
# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
10+
VITE_NITRO_MOCK=false
11+
12+
# 是否打开 devtools,true 为打开,false 为关闭
13+
VITE_DEVTOOLS=false
14+
15+
# 是否注入全局loading
16+
VITE_INJECT_APP_LOADING=true

apps/web-tdesign/.env.production

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
VITE_BASE=/
2+
3+
# 接口地址
4+
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
5+
6+
# 是否开启压缩,可以设置为 none, brotli, gzip
7+
VITE_COMPRESS=none
8+
9+
# 是否开启 PWA
10+
VITE_PWA=false
11+
12+
# vue-router 的模式
13+
VITE_ROUTER_HISTORY=hash
14+
15+
# 是否注入全局loading
16+
VITE_INJECT_APP_LOADING=true
17+
18+
# 打包后是否生成dist.zip
19+
VITE_ARCHIVER=true

apps/web-tdesign/index.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!doctype html>
2+
<html lang="zh">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
6+
<meta name="renderer" content="webkit" />
7+
<meta name="description" content="A Modern Back-end Management System" />
8+
<meta name="keywords" content="Vben Admin Vue3 Vite" />
9+
<meta name="author" content="Vben" />
10+
<meta
11+
name="viewport"
12+
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
13+
/>
14+
<!-- 由 vite 注入 VITE_APP_TITLE 变量,在 .env 文件内配置 -->
15+
<title><%= VITE_APP_TITLE %></title>
16+
<link rel="icon" href="/favicon.ico" />
17+
<script>
18+
// 生产环境下注入百度统计
19+
if (window._VBEN_ADMIN_PRO_APP_CONF_) {
20+
var _hmt = _hmt || [];
21+
(function () {
22+
var hm = document.createElement('script');
23+
hm.src =
24+
'https://hm.baidu.com/hm.js?b38e689f40558f20a9a686d7f6f33edf';
25+
var s = document.getElementsByTagName('script')[0];
26+
s.parentNode.insertBefore(hm, s);
27+
})();
28+
}
29+
</script>
30+
</head>
31+
<body>
32+
<div id="app"></div>
33+
<script type="module" src="/src/main.ts"></script>
34+
</body>
35+
</html>

apps/web-tdesign/package.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "@vben/web-tdesign",
3+
"version": "5.5.9",
4+
"homepage": "https://vben.pro",
5+
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
6+
"repository": {
7+
"type": "git",
8+
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
9+
"directory": "apps/web-naive"
10+
},
11+
"license": "MIT",
12+
"author": {
13+
"name": "vben",
14+
"email": "[email protected]",
15+
"url": "https://github.com/anncwb"
16+
},
17+
"type": "module",
18+
"scripts": {
19+
"build": "pnpm vite build --mode production",
20+
"build:analyze": "pnpm vite build --mode analyze",
21+
"dev": "pnpm vite --mode development",
22+
"preview": "vite preview",
23+
"typecheck": "vue-tsc --noEmit --skipLibCheck"
24+
},
25+
"imports": {
26+
"#/*": "./src/*"
27+
},
28+
"dependencies": {
29+
"@vben/access": "workspace:*",
30+
"@vben/common-ui": "workspace:*",
31+
"@vben/constants": "workspace:*",
32+
"@vben/hooks": "workspace:*",
33+
"@vben/icons": "workspace:*",
34+
"@vben/layouts": "workspace:*",
35+
"@vben/locales": "workspace:*",
36+
"@vben/plugins": "workspace:*",
37+
"@vben/preferences": "workspace:*",
38+
"@vben/request": "workspace:*",
39+
"@vben/stores": "workspace:*",
40+
"@vben/styles": "workspace:*",
41+
"@vben/types": "workspace:*",
42+
"@vben/utils": "workspace:*",
43+
"@vueuse/core": "catalog:",
44+
"dayjs": "catalog:",
45+
"pinia": "catalog:",
46+
"tdesign-vue-next": "^1.17.1",
47+
"vue": "catalog:",
48+
"vue-router": "catalog:"
49+
},
50+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from '@vben/tailwind-config/postcss';
5.3 KB
Binary file not shown.
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import type { Component } from 'vue';
2+
3+
import type { BaseFormComponentType } from '@vben/common-ui';
4+
import type { Recordable } from '@vben/types';
5+
6+
import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
7+
8+
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
9+
import { $t } from '@vben/locales';
10+
11+
import { notification } from 'ant-design-vue';
12+
/**
13+
* 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
14+
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
15+
*/
16+
17+
const AutoComplete = defineAsyncComponent(
18+
() => import('tdesign-vue-next/es/auto-complete'),
19+
);
20+
const Button = defineAsyncComponent(() => import('tdesign-vue-next/es/button'));
21+
const Checkbox = defineAsyncComponent(
22+
() => import('tdesign-vue-next/es/checkbox'),
23+
);
24+
const CheckboxGroup = defineAsyncComponent(() =>
25+
import('tdesign-vue-next/es/checkbox').then((res) => res.CheckboxGroup),
26+
);
27+
const DatePicker = defineAsyncComponent(
28+
() => import('tdesign-vue-next/es/date-picker'),
29+
);
30+
const Divider = defineAsyncComponent(
31+
() => import('tdesign-vue-next/es/divider'),
32+
);
33+
const Input = defineAsyncComponent(() => import('tdesign-vue-next/es/input'));
34+
const InputNumber = defineAsyncComponent(
35+
() => import('tdesign-vue-next/es/input-number'),
36+
);
37+
// const InputPassword = defineAsyncComponent(() =>
38+
// import('tdesign-vue-next/es/input').then((res) => res.InputPassword),
39+
// );
40+
// const Mentions = defineAsyncComponent(
41+
// () => import('tdesign-vue-next/es/mentions'),
42+
// );
43+
const Radio = defineAsyncComponent(() => import('tdesign-vue-next/es/radio'));
44+
const RadioGroup = defineAsyncComponent(() =>
45+
import('tdesign-vue-next/es/radio').then((res) => res.RadioGroup),
46+
);
47+
const RangePicker = defineAsyncComponent(() =>
48+
import('tdesign-vue-next/es/date-picker').then((res) => res.DateRangePicker),
49+
);
50+
const Rate = defineAsyncComponent(() => import('tdesign-vue-next/es/rate'));
51+
const Select = defineAsyncComponent(() => import('tdesign-vue-next/es/select'));
52+
const Space = defineAsyncComponent(() => import('tdesign-vue-next/es/space'));
53+
const Switch = defineAsyncComponent(() => import('tdesign-vue-next/es/switch'));
54+
const Textarea = defineAsyncComponent(
55+
() => import('tdesign-vue-next/es/textarea'),
56+
);
57+
const TimePicker = defineAsyncComponent(
58+
() => import('tdesign-vue-next/es/time-picker'),
59+
);
60+
const TreeSelect = defineAsyncComponent(
61+
() => import('tdesign-vue-next/es/tree-select'),
62+
);
63+
const Upload = defineAsyncComponent(() => import('tdesign-vue-next/es/upload'));
64+
65+
const withDefaultPlaceholder = <T extends Component>(
66+
component: T,
67+
type: 'input' | 'select',
68+
componentProps: Recordable<any> = {},
69+
) => {
70+
return defineComponent({
71+
name: component.name,
72+
inheritAttrs: false,
73+
setup: (props: any, { attrs, expose, slots }) => {
74+
const placeholder =
75+
props?.placeholder ||
76+
attrs?.placeholder ||
77+
$t(`ui.placeholder.${type}`);
78+
// 透传组件暴露的方法
79+
const innerRef = ref();
80+
expose(
81+
new Proxy(
82+
{},
83+
{
84+
get: (_target, key) => innerRef.value?.[key],
85+
has: (_target, key) => key in (innerRef.value || {}),
86+
},
87+
),
88+
);
89+
return () =>
90+
h(
91+
component,
92+
{ ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
93+
slots,
94+
);
95+
},
96+
});
97+
};
98+
99+
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
100+
export type ComponentType =
101+
| 'ApiSelect'
102+
| 'ApiTreeSelect'
103+
| 'AutoComplete'
104+
| 'Checkbox'
105+
| 'CheckboxGroup'
106+
| 'DatePicker'
107+
| 'DefaultButton'
108+
| 'Divider'
109+
| 'IconPicker'
110+
| 'Input'
111+
| 'InputNumber'
112+
// | 'InputPassword'
113+
// | 'Mentions'
114+
| 'PrimaryButton'
115+
| 'Radio'
116+
| 'RadioGroup'
117+
| 'RangePicker'
118+
| 'Rate'
119+
| 'Select'
120+
| 'Space'
121+
| 'Switch'
122+
| 'Textarea'
123+
| 'TimePicker'
124+
| 'TreeSelect'
125+
| 'Upload'
126+
| BaseFormComponentType;
127+
128+
async function initComponentAdapter() {
129+
const components: Partial<Record<ComponentType, Component>> = {
130+
// 如果你的组件体积比较大,可以使用异步加载
131+
// Button: () =>
132+
// import('xxx').then((res) => res.Button),
133+
ApiSelect: withDefaultPlaceholder(
134+
{
135+
...ApiComponent,
136+
name: 'ApiSelect',
137+
},
138+
'select',
139+
{
140+
component: Select,
141+
loadingSlot: 'suffixIcon',
142+
visibleEvent: 'onDropdownVisibleChange',
143+
modelPropName: 'value',
144+
},
145+
),
146+
ApiTreeSelect: withDefaultPlaceholder(
147+
{
148+
...ApiComponent,
149+
name: 'ApiTreeSelect',
150+
},
151+
'select',
152+
{
153+
component: TreeSelect,
154+
fieldNames: { label: 'label', value: 'value', children: 'children' },
155+
loadingSlot: 'suffixIcon',
156+
modelPropName: 'value',
157+
optionsPropName: 'treeData',
158+
visibleEvent: 'onVisibleChange',
159+
},
160+
),
161+
AutoComplete,
162+
Checkbox,
163+
CheckboxGroup,
164+
DatePicker,
165+
// 自定义默认按钮
166+
DefaultButton: (props, { attrs, slots }) => {
167+
return h(Button, { ...props, attrs, theme: 'default' }, slots);
168+
},
169+
Divider,
170+
IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
171+
iconSlot: 'addonAfter',
172+
inputComponent: Input,
173+
modelValueProp: 'value',
174+
}),
175+
Input: withDefaultPlaceholder(Input, 'input'),
176+
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
177+
// InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
178+
// Mentions: withDefaultPlaceholder(Mentions, 'input'),
179+
// 自定义主要按钮
180+
PrimaryButton: (props, { attrs, slots }) => {
181+
return h(Button, { ...props, attrs, theme: 'primary' }, slots);
182+
},
183+
Radio,
184+
RadioGroup,
185+
RangePicker,
186+
Rate,
187+
Select: withDefaultPlaceholder(Select, 'select'),
188+
Space,
189+
Switch,
190+
Textarea: withDefaultPlaceholder(Textarea, 'input'),
191+
TimePicker,
192+
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
193+
Upload,
194+
};
195+
196+
// 将组件注册到全局共享状态中
197+
globalShareState.setComponents(components);
198+
199+
// 定义全局共享状态中的消息提示
200+
globalShareState.defineMessage({
201+
// 复制成功消息提示
202+
copyPreferencesSuccess: (title, content) => {
203+
notification.success({
204+
description: content,
205+
message: title,
206+
placement: 'bottomRight',
207+
});
208+
},
209+
});
210+
}
211+
212+
export { initComponentAdapter };

0 commit comments

Comments
 (0)