basic.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <script lang="ts" setup>
  2. import type { CollapsibleParamSchema } from '@vben-core/shadcn-ui';
  3. import { ref } from 'vue';
  4. import { Page, useVbenModal, z } from '@vben/common-ui';
  5. import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
  6. import {
  7. NButton,
  8. NCard,
  9. NRadioButton,
  10. NRadioGroup,
  11. useMessage,
  12. } from 'naive-ui';
  13. import { useVbenForm } from '#/adapter/form';
  14. import { getAllMenusApi } from '#/api';
  15. import modalDemo from './modal.vue';
  16. const message = useMessage();
  17. const layouts = [
  18. { label: 'Vertical', value: 'vertical' },
  19. { label: 'Horizontal', value: 'horizontal' },
  20. { label: 'Inline', value: 'inline' },
  21. ];
  22. const layout = ref(layouts[0].value);
  23. function getNumberValidator(key: string, limit?: [number, number]) {
  24. let validator = z.number({
  25. required_error: `${key} 值不能为空`,
  26. invalid_type_error: `${key} 值只能为数字`,
  27. });
  28. if (limit) {
  29. validator = validator
  30. .min(limit[0], { message: `${key} 值不在区间范围内` })
  31. .max(limit[1], { message: `${key} 值不在区间范围内` });
  32. }
  33. return validator.default(null);
  34. }
  35. const paramsSchema = [
  36. {
  37. key: 'micro_batch_size',
  38. description: `批次大小,代表模型训练过程中,模型更新模型参数的数据步长,可理解为模型每看多少数据即更新一次模型参数,
  39. 一般建议的批次大小为16/32,表示模型每看16或32条数据即更新一次参数`,
  40. // defaultValue: 8,
  41. option: {
  42. min: 8,
  43. max: 1024,
  44. step: 8,
  45. },
  46. },
  47. {
  48. key: 'learning_rate',
  49. description:
  50. '学习率,代表每次更新数据的增量参数权重,学习率数值越大参数变化越大,对模型影响越大',
  51. // defaultValue: 1e-5,
  52. option: {
  53. step: 1e-4,
  54. type: 'exponential',
  55. },
  56. },
  57. {
  58. key: 'eval_steps',
  59. description:
  60. '验证步数,训练阶段针模型的验证间隔步长,用于阶段性评估模型训练准确率、训练损失',
  61. // defaultValue: 50,
  62. option: {
  63. min: 1,
  64. max: 2_147_483_647,
  65. },
  66. },
  67. {
  68. key: 'num_train_epochs',
  69. description:
  70. '循环次数,代表模型训练过程中模型学习数据集的次数,可理解为看几遍数据,一般建议的范围是1-3遍即可,可依据需求进行调整',
  71. // defaultValue: 3,
  72. option: {
  73. min: 1,
  74. max: 200,
  75. },
  76. },
  77. {
  78. key: 'max_length',
  79. description: `序列长度,单个训练数据样本的最大长度,超出配置长度将丢弃`,
  80. // defaultValue: 32_768,
  81. option: {
  82. min: 500,
  83. max: 131_072,
  84. },
  85. },
  86. {
  87. key: 'warmup_ratio',
  88. description: '学习率预热比例,学习率预热阶段占总训练步数的比例',
  89. // defaultValue: 0.05,
  90. option: {
  91. min: 0,
  92. max: 1,
  93. precision: 2,
  94. step: 0.01,
  95. },
  96. },
  97. {
  98. key: 'save_steps',
  99. description: 'Checkpoint保存间隔',
  100. // defaultValue: 50,
  101. option: {
  102. min: 1,
  103. max: 2_147_483_647,
  104. },
  105. },
  106. ] as CollapsibleParamSchema[];
  107. const paramsValidator = z.object({
  108. micro_batch_size: getNumberValidator('micro_batch_size', [8, 1024]),
  109. learning_rate: getNumberValidator('learning_rate'),
  110. eval_steps: getNumberValidator('eval_steps', [1, 2_147_483_647]),
  111. num_train_epochs: getNumberValidator('num_train_epochs', [1, 200]),
  112. max_length: getNumberValidator('max_length', [1, 131_072]),
  113. warmup_ratio: getNumberValidator('warmup_ratio', [0, 1]),
  114. save_steps: getNumberValidator('save_steps', [1, 2_147_483_647]),
  115. });
  116. const [Form, formApi] = useVbenForm({
  117. commonConfig: {
  118. // 所有表单项
  119. componentProps: {
  120. class: 'w-full',
  121. },
  122. },
  123. layout: 'vertical',
  124. // 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
  125. wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
  126. handleSubmit: (values) => {
  127. message.success(`表单数据:${JSON.stringify(values)}`);
  128. },
  129. schema: [
  130. {
  131. // 组件需要在 #/adapter.ts内注册,并加上类型
  132. component: 'ApiSelect',
  133. // 对应组件的参数
  134. componentProps: {
  135. // 菜单接口转options格式
  136. afterFetch: (data: { name: string; path: string }[]) => {
  137. return data.map((item: any) => ({
  138. label: item.name,
  139. value: item.path,
  140. }));
  141. },
  142. // 菜单接口
  143. api: getAllMenusApi,
  144. },
  145. // 字段名
  146. fieldName: 'api',
  147. // 界面显示的label
  148. label: 'ApiSelect',
  149. rules: 'required',
  150. },
  151. {
  152. component: 'ApiTreeSelect',
  153. // 对应组件的参数
  154. componentProps: {
  155. // 菜单接口
  156. api: getAllMenusApi,
  157. childrenField: 'children',
  158. // 菜单接口转options格式
  159. labelField: 'name',
  160. valueField: 'path',
  161. },
  162. // 字段名
  163. fieldName: 'apiTree',
  164. // 界面显示的label
  165. label: 'ApiTreeSelect',
  166. rules: 'required',
  167. },
  168. {
  169. component: 'Input',
  170. fieldName: 'string',
  171. label: 'String',
  172. rules: 'required',
  173. },
  174. {
  175. component: 'InputNumber',
  176. fieldName: 'number',
  177. label: 'Number',
  178. rules: 'required',
  179. },
  180. {
  181. component: 'RadioGroup',
  182. fieldName: 'radio',
  183. label: 'Radio',
  184. componentProps: {
  185. options: [
  186. { value: 'A', label: 'A' },
  187. { value: 'B', label: 'B' },
  188. { value: 'C', label: 'C' },
  189. { value: 'D', label: 'D' },
  190. { value: 'E', label: 'E' },
  191. ],
  192. },
  193. rules: 'selectRequired',
  194. },
  195. {
  196. component: 'RadioGroup',
  197. fieldName: 'radioButton',
  198. label: 'RadioButton',
  199. componentProps: {
  200. isButton: true,
  201. class: 'flex flex-wrap', // 如果选项过多,可以添加class来自动折叠
  202. options: [
  203. { value: 'A', label: '选项A' },
  204. { value: 'B', label: '选项B' },
  205. { value: 'C', label: '选项C' },
  206. { value: 'D', label: '选项D' },
  207. { value: 'E', label: '选项E' },
  208. ],
  209. },
  210. rules: 'selectRequired',
  211. },
  212. {
  213. component: 'CheckboxGroup',
  214. fieldName: 'checkbox',
  215. label: 'Checkbox',
  216. componentProps: {
  217. options: [
  218. { value: 'A', label: '选项A' },
  219. { value: 'B', label: '选项B' },
  220. { value: 'C', label: '选项C' },
  221. ],
  222. },
  223. rules: 'selectRequired',
  224. },
  225. {
  226. component: 'DatePicker',
  227. fieldName: 'date',
  228. label: 'Date',
  229. rules: 'required',
  230. },
  231. {
  232. component: 'Input',
  233. fieldName: 'textArea',
  234. label: 'TextArea',
  235. componentProps: {
  236. type: 'textarea',
  237. },
  238. rules: 'required',
  239. },
  240. {
  241. component: 'Input',
  242. fieldName: 'collapsibleTextArea',
  243. label: 'vertical时可折叠',
  244. componentProps: {
  245. type: 'textarea',
  246. },
  247. collapsible: true,
  248. },
  249. {
  250. component: VbenCollapsibleParams,
  251. componentProps: {
  252. params: paramsSchema,
  253. },
  254. modelPropName: 'value',
  255. fieldName: 'params',
  256. label: '参数配置',
  257. formItemClass: 'col-span-2',
  258. rules: paramsValidator,
  259. },
  260. ],
  261. });
  262. function setFormValues() {
  263. formApi.setValues({
  264. string: 'string',
  265. number: 123,
  266. radio: 'B',
  267. radioButton: 'C',
  268. checkbox: ['A', 'C'],
  269. date: Date.now(),
  270. params: {
  271. micro_batch_size: 8,
  272. learning_rate: 1e-5,
  273. eval_steps: 50,
  274. num_train_epochs: 3,
  275. max_length: 32_768,
  276. warmup_ratio: 0.05,
  277. save_steps: 50,
  278. },
  279. });
  280. }
  281. const [Modal, modalApi] = useVbenModal({
  282. connectedComponent: modalDemo,
  283. });
  284. function onLayoutChange(layout: string) {
  285. formApi.setState({
  286. layout,
  287. });
  288. }
  289. </script>
  290. <template>
  291. <Page
  292. description="表单适配器重新包装了CheckboxGroup和RadioGroup,可以通过options属性传递选项数据(选项数据将作为子组件的属性)"
  293. title="表单演示"
  294. >
  295. <NCard title="基础表单" header-extra-class="gap-4">
  296. <template #header-extra>
  297. <NRadioGroup v-model:value="layout" @update:value="onLayoutChange">
  298. <NRadioButton
  299. v-for="layoutItem in layouts"
  300. :key="layoutItem.value"
  301. :value="layoutItem.value"
  302. :label="layoutItem.label"
  303. />
  304. </NRadioGroup>
  305. <NButton type="primary" @click="setFormValues">设置表单值</NButton>
  306. <NButton type="primary" @click="modalApi.open()" class="ml-2">
  307. 打开弹窗
  308. </NButton>
  309. </template>
  310. <Form />
  311. </NCard>
  312. <Modal />
  313. </Page>
  314. </template>