index.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import type {
  2. AutoCompleteProps,
  3. ButtonProps,
  4. CheckboxGroupProps,
  5. CheckboxProps,
  6. DatePickerProps,
  7. DateRangePickerProps,
  8. DividerProps,
  9. InputNumberProps,
  10. InputProps,
  11. RadioGroupProps,
  12. RadioProps,
  13. SelectProps,
  14. SpaceProps,
  15. SwitchProps,
  16. TextareaProps,
  17. TimePickerProps,
  18. TreeSelectProps,
  19. } from 'tdesign-vue-next';
  20. import type { TdRateProps } from 'tdesign-vue-next/es/rate/type';
  21. import type { UploadProps } from 'tdesign-vue-next/es/upload/types';
  22. import type { Component } from 'vue';
  23. import type {
  24. ApiComponentSharedProps,
  25. BaseFormComponentType,
  26. IconPickerProps,
  27. } from '@vben/common-ui';
  28. import type { Recordable } from '@vben/types';
  29. import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
  30. import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
  31. import { $t } from '@vben/locales';
  32. import { notification } from '#/adapter/tdesign';
  33. /**
  34. * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
  35. * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
  36. */
  37. const AutoComplete = defineAsyncComponent(
  38. () => import('tdesign-vue-next/es/auto-complete'),
  39. );
  40. const Button = defineAsyncComponent(() => import('tdesign-vue-next/es/button'));
  41. const Checkbox = defineAsyncComponent(
  42. () => import('tdesign-vue-next/es/checkbox'),
  43. );
  44. const CheckboxGroup = defineAsyncComponent(() =>
  45. import('tdesign-vue-next/es/checkbox').then((res) => res.CheckboxGroup),
  46. );
  47. const DatePicker = defineAsyncComponent(
  48. () => import('tdesign-vue-next/es/date-picker'),
  49. );
  50. const Divider = defineAsyncComponent(
  51. () => import('tdesign-vue-next/es/divider'),
  52. );
  53. const Input = defineAsyncComponent(() => import('tdesign-vue-next/es/input'));
  54. const InputNumber = defineAsyncComponent(
  55. () => import('tdesign-vue-next/es/input-number'),
  56. );
  57. // const InputPassword = defineAsyncComponent(() =>
  58. // import('tdesign-vue-next/es/input').then((res) => res.InputPassword),
  59. // );
  60. // const Mentions = defineAsyncComponent(
  61. // () => import('tdesign-vue-next/es/mentions'),
  62. // );
  63. const Radio = defineAsyncComponent(() => import('tdesign-vue-next/es/radio'));
  64. const RadioGroup = defineAsyncComponent(() =>
  65. import('tdesign-vue-next/es/radio').then((res) => res.RadioGroup),
  66. );
  67. const RangePicker = defineAsyncComponent(() =>
  68. import('tdesign-vue-next/es/date-picker').then((res) => res.DateRangePicker),
  69. );
  70. const Rate = defineAsyncComponent(() => import('tdesign-vue-next/es/rate'));
  71. const Select = defineAsyncComponent(() => import('tdesign-vue-next/es/select'));
  72. const Space = defineAsyncComponent(() => import('tdesign-vue-next/es/space'));
  73. const Switch = defineAsyncComponent(() => import('tdesign-vue-next/es/switch'));
  74. const Textarea = defineAsyncComponent(
  75. () => import('tdesign-vue-next/es/textarea'),
  76. );
  77. const TimePicker = defineAsyncComponent(
  78. () => import('tdesign-vue-next/es/time-picker'),
  79. );
  80. const TreeSelect = defineAsyncComponent(
  81. () => import('tdesign-vue-next/es/tree-select'),
  82. );
  83. const Upload = defineAsyncComponent(() => import('tdesign-vue-next/es/upload'));
  84. const withDefaultPlaceholder = (
  85. component: Component,
  86. type: 'input' | 'select',
  87. componentProps: Recordable<any> = {},
  88. ) => {
  89. return defineComponent({
  90. name: component.name,
  91. inheritAttrs: false,
  92. setup: (props: any, { attrs, expose, slots }) => {
  93. const placeholder =
  94. props?.placeholder ||
  95. attrs?.placeholder ||
  96. $t(`ui.placeholder.${type}`);
  97. // 透传组件暴露的方法
  98. const innerRef = ref();
  99. expose(
  100. new Proxy(
  101. {},
  102. {
  103. get: (_target, key) => innerRef.value?.[key],
  104. has: (_target, key) => key in (innerRef.value || {}),
  105. },
  106. ),
  107. );
  108. return () =>
  109. h(
  110. component,
  111. { ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
  112. slots,
  113. );
  114. },
  115. });
  116. };
  117. // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
  118. export type ComponentType =
  119. | 'ApiSelect'
  120. | 'ApiTreeSelect'
  121. | 'AutoComplete'
  122. | 'Checkbox'
  123. | 'CheckboxGroup'
  124. | 'DatePicker'
  125. | 'DefaultButton'
  126. | 'Divider'
  127. | 'IconPicker'
  128. | 'Input'
  129. | 'InputNumber'
  130. // | 'InputPassword'
  131. // | 'Mentions'
  132. | 'PrimaryButton'
  133. | 'Radio'
  134. | 'RadioGroup'
  135. | 'RangePicker'
  136. | 'Rate'
  137. | 'Select'
  138. | 'Space'
  139. | 'Switch'
  140. | 'Textarea'
  141. | 'TimePicker'
  142. | 'TreeSelect'
  143. | 'Upload'
  144. | BaseFormComponentType;
  145. /**
  146. * 与 {@link ComponentType} 中注册的组件名一一对应,便于 Schema 上 `component` + `componentProps` 联动提示
  147. */
  148. export interface ComponentPropsMap {
  149. ApiSelect: ApiComponentSharedProps & SelectProps;
  150. ApiTreeSelect: ApiComponentSharedProps & TreeSelectProps;
  151. AutoComplete: AutoCompleteProps;
  152. Checkbox: CheckboxProps;
  153. CheckboxGroup: CheckboxGroupProps;
  154. DatePicker: DatePickerProps;
  155. DefaultButton: ButtonProps;
  156. Divider: DividerProps;
  157. IconPicker: IconPickerProps;
  158. Input: InputProps;
  159. InputNumber: InputNumberProps;
  160. PrimaryButton: ButtonProps;
  161. Radio: RadioProps;
  162. RadioGroup: RadioGroupProps;
  163. RangePicker: DateRangePickerProps;
  164. Rate: TdRateProps;
  165. Select: SelectProps;
  166. Space: SpaceProps;
  167. Switch: SwitchProps;
  168. Textarea: TextareaProps;
  169. TimePicker: TimePickerProps;
  170. TreeSelect: TreeSelectProps;
  171. Upload: UploadProps;
  172. }
  173. async function initComponentAdapter() {
  174. const components: Partial<Record<ComponentType, Component>> = {
  175. // 如果你的组件体积比较大,可以使用异步加载
  176. // Button: () =>
  177. // import('xxx').then((res) => res.Button),
  178. ApiSelect: withDefaultPlaceholder(
  179. {
  180. ...ApiComponent,
  181. name: 'ApiSelect',
  182. },
  183. 'select',
  184. {
  185. component: Select,
  186. loadingSlot: 'suffixIcon',
  187. visibleEvent: 'onDropdownVisibleChange',
  188. modelPropName: 'value',
  189. },
  190. ),
  191. ApiTreeSelect: withDefaultPlaceholder(
  192. {
  193. ...ApiComponent,
  194. name: 'ApiTreeSelect',
  195. },
  196. 'select',
  197. {
  198. component: TreeSelect,
  199. fieldNames: { label: 'label', value: 'value', children: 'children' },
  200. loadingSlot: 'suffixIcon',
  201. modelPropName: 'value',
  202. optionsPropName: 'treeData',
  203. visibleEvent: 'onVisibleChange',
  204. },
  205. ),
  206. AutoComplete,
  207. Checkbox,
  208. CheckboxGroup,
  209. DatePicker,
  210. // 自定义默认按钮
  211. DefaultButton: (props, { attrs, slots }) => {
  212. let ghost = false;
  213. let variant = props.variant;
  214. if (props.variant === 'ghost') {
  215. ghost = true;
  216. variant = 'base';
  217. }
  218. return h(
  219. Button,
  220. { ...props, ghost, variant, attrs, theme: 'default' },
  221. slots,
  222. );
  223. },
  224. Divider,
  225. IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
  226. iconSlot: 'addonAfter',
  227. inputComponent: Input,
  228. modelValueProp: 'value',
  229. }),
  230. Input: withDefaultPlaceholder(Input, 'input'),
  231. InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
  232. style: { width: '100%' },
  233. }),
  234. // InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
  235. // Mentions: withDefaultPlaceholder(Mentions, 'input'),
  236. // 自定义主要按钮
  237. PrimaryButton: (props, { attrs, slots }) => {
  238. let ghost = false;
  239. let variant = props.variant;
  240. if (props.variant === 'ghost') {
  241. ghost = true;
  242. variant = 'base';
  243. }
  244. return h(
  245. Button,
  246. { ...props, ghost, variant, attrs, theme: 'primary' },
  247. slots,
  248. );
  249. },
  250. Radio,
  251. RadioGroup,
  252. RangePicker: (props, { attrs, slots }) => {
  253. return h(
  254. RangePicker,
  255. { ...props, modelValue: props.modelValue ?? [], attrs },
  256. slots,
  257. );
  258. },
  259. Rate,
  260. Select: withDefaultPlaceholder(Select, 'select'),
  261. Space,
  262. Switch,
  263. Textarea: withDefaultPlaceholder(Textarea, 'input'),
  264. TimePicker,
  265. TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
  266. Upload,
  267. };
  268. // 将组件注册到全局共享状态中
  269. globalShareState.setComponents(components);
  270. // 定义全局共享状态中的消息提示
  271. globalShareState.defineMessage({
  272. // 复制成功消息提示
  273. copyPreferencesSuccess: (title, content) => {
  274. notification.success({
  275. title,
  276. content,
  277. placement: 'bottom-right',
  278. });
  279. },
  280. });
  281. }
  282. export { initComponentAdapter };