crud.mdc 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480
  1. ---
  2. description: cl-crud 组件示例
  3. globs: *.tsx, *.ts, *.vue
  4. ---
  5. ## 完整示例 示例
  6. ```vue
  7. <template>
  8. <div class="scope">
  9. <div class="h">
  10. <el-tag size="small" effect="dark" disable-transitions>all</el-tag>
  11. <span>完整示例</span>
  12. </div>
  13. <div class="c">
  14. <el-button @click="open">预览</el-button>
  15. <demo-code :files="['crud/all.vue']" />
  16. <cl-dialog v-model="visible" title="完整示例" width="80%">
  17. <cl-crud ref="Crud">
  18. <cl-row>
  19. <!-- 刷新按钮 -->
  20. <cl-refresh-btn />
  21. <!-- 新增按钮 -->
  22. <cl-add-btn />
  23. <!-- 批量删除按钮 -->
  24. <cl-multi-delete-btn />
  25. <!-- 筛选 -->
  26. <cl-filter label="状态筛选">
  27. <!-- 配置prop,选择后会自动过滤列表 -->
  28. <cl-select :options="options.status" prop="status" :width="120" />
  29. </cl-filter>
  30. <!-- 字典 -->
  31. <cl-filter label="工作(字典)">
  32. <cl-select
  33. tree
  34. :options="dict.get('occupation')"
  35. prop="occupation"
  36. :width="140"
  37. check-strictly
  38. />
  39. </cl-filter>
  40. <cl-flex1 />
  41. <!-- 导入 -->
  42. <cl-import-btn template="/用户导入模版.xlsx" />
  43. <!-- 导出 -->
  44. <cl-export-btn :columns="Table?.columns" />
  45. <!-- 自定义列 -->
  46. <cl-column-custom
  47. :ref="setRefs('columnCustom')"
  48. :columns="Table?.columns"
  49. />
  50. <!-- 关键字搜索 -->
  51. <cl-search-key placeholder="搜索姓名、手机号" :width="250" />
  52. <!-- 高级搜索按钮 -->
  53. <cl-adv-btn />
  54. </cl-row>
  55. <cl-row>
  56. <!-- 表格 -->
  57. <cl-table
  58. ref="Table"
  59. show-summary
  60. :summary-method="onSummaryMethod"
  61. :auto-height="false"
  62. >
  63. <!-- 展开信息 -->
  64. <template #column-detail="{ scope }">
  65. <div style="padding: 0 10px">
  66. <el-descriptions border :column="3">
  67. <el-descriptions-item label="ID">
  68. {{ scope.row.id }}
  69. </el-descriptions-item>
  70. <el-descriptions-item label="姓名">
  71. {{ scope.row.name }}
  72. </el-descriptions-item>
  73. <el-descriptions-item label="存款">
  74. {{ scope.row.wages }}
  75. </el-descriptions-item>
  76. <el-descriptions-item label="出生年月">
  77. {{ scope.row.createTime }}
  78. </el-descriptions-item>
  79. </el-descriptions>
  80. </div>
  81. </template>
  82. <!-- 自定义列 -->
  83. <template #column-wages="{ scope }">
  84. <span>{{ scope.row.wages }}🤑</span>
  85. </template>
  86. </cl-table>
  87. </cl-row>
  88. <cl-row>
  89. <cl-flex1 />
  90. <!-- 分页 -->
  91. <cl-pagination />
  92. </cl-row>
  93. <!-- 新增、编辑 -->
  94. <cl-upsert ref="Upsert" />
  95. <!-- 高级搜索 -->
  96. <cl-adv-search ref="AdvSearch" />
  97. </cl-crud>
  98. </cl-dialog>
  99. </div>
  100. <div class="f">
  101. <span class="date">2024-01-01</span>
  102. </div>
  103. </div>
  104. </template>
  105. <script lang="tsx" setup>
  106. defineOptions({
  107. name: 'demo-crud'
  108. });
  109. import { useCrud, useUpsert, useTable, useAdvSearch, useSearch } from '@cool-vue/crud';
  110. import { useDict } from '/$/dict';
  111. import { reactive, ref } from 'vue';
  112. import { ElMessage, ElMessageBox } from 'element-plus';
  113. import { useCool } from '/@/cool';
  114. // 基础
  115. const { service, refs, setRefs } = useCool();
  116. // 字典
  117. const { dict } = useDict();
  118. // 选项,统一命名options,存放所有的下拉等其他选项列表数据
  119. const options = reactive({
  120. status: [
  121. {
  122. label: '启用',
  123. value: 1
  124. },
  125. {
  126. label: '禁用',
  127. type: 'danger',
  128. value: 0
  129. }
  130. ]
  131. });
  132. // 合计数据
  133. const subData = reactive({
  134. wages: 0
  135. });
  136. // crud
  137. const Crud = useCrud(
  138. {
  139. // 绑定的服务,如:service.demo.goods、service.base.sys.user
  140. service: 'test',
  141. // 刷新事件
  142. async onRefresh(params, { next }) {
  143. const res = await next(params);
  144. Object.assign(subData, res.subData);
  145. }
  146. },
  147. app => {
  148. // Crud 加载完,默认刷新一次
  149. app.refresh({
  150. size: 10
  151. // status: 1 // 带额外参数的请求
  152. });
  153. }
  154. );
  155. // 刷新列表,统一调用这个方法去刷新
  156. function refresh(params?: any) {
  157. Crud.value?.refresh(params);
  158. }
  159. // 新增、编辑
  160. // 插入类型 <Eps.UserInfoEntity>,prop 和 data 会有提示
  161. const Upsert = useUpsert<Eps.UserInfoEntity>({
  162. items: [
  163. // 分组
  164. {
  165. type: 'tabs',
  166. props: {
  167. type: 'card',
  168. labels: [
  169. {
  170. label: '基础信息',
  171. value: 'base'
  172. },
  173. {
  174. label: '其他配置',
  175. value: 'other'
  176. }
  177. ]
  178. }
  179. },
  180. {
  181. label: '头像',
  182. prop: 'avatarUrl',
  183. group: 'base',
  184. component: {
  185. name: 'cl-upload'
  186. }
  187. },
  188. {
  189. label: '账号',
  190. group: 'base',
  191. prop: 'account',
  192. component: {
  193. name: 'el-input'
  194. }
  195. },
  196. // 动态配置,新增显示、编辑隐藏
  197. () => {
  198. return () => {
  199. return {
  200. label: '密码',
  201. group: 'base',
  202. prop: 'password',
  203. hidden: Upsert.value?.mode == 'update', // 通过 mode 参数判断
  204. component: {
  205. name: 'el-input',
  206. props: {
  207. type: 'password'
  208. }
  209. }
  210. };
  211. };
  212. },
  213. {
  214. group: 'base',
  215. prop: 'user',
  216. component: {
  217. name: 'cl-form-card',
  218. props: {
  219. label: '用户信息(多层级展示)'
  220. }
  221. },
  222. children: [
  223. {
  224. label: '姓名',
  225. prop: 'name',
  226. required: true,
  227. component: {
  228. name: 'el-input'
  229. }
  230. },
  231. {
  232. label: '存款',
  233. prop: 'wages',
  234. component: {
  235. name: 'el-input-number'
  236. }
  237. }
  238. ]
  239. },
  240. {
  241. group: 'base',
  242. prop: 'contact',
  243. component: {
  244. name: 'cl-form-card',
  245. props: {
  246. label: '联系信息',
  247. expand: false
  248. }
  249. },
  250. children: [
  251. {
  252. label: '手机号',
  253. prop: 'phone',
  254. component: {
  255. name: 'el-input'
  256. }
  257. },
  258. {
  259. label: '省市区',
  260. prop: 'pca',
  261. group: 'base',
  262. component: {
  263. name: 'cl-distpicker'
  264. }
  265. }
  266. ]
  267. },
  268. {
  269. group: 'other',
  270. label: '工作',
  271. prop: 'occupation',
  272. component: {
  273. name: 'el-tree-select',
  274. props: {
  275. data: dict.get('occupation'),
  276. checkStrictly: true
  277. }
  278. }
  279. },
  280. {
  281. group: 'other',
  282. label: '身份证照片',
  283. prop: 'idCardPic',
  284. component: {
  285. name: 'cl-upload',
  286. props: {
  287. isSpace: true,
  288. size: [200, 300]
  289. }
  290. }
  291. }
  292. ],
  293. // 详情钩子
  294. onInfo(data, { next, done }) {
  295. // 继续请求 info 接口,可以带其他自定义参数
  296. // next({
  297. // id: data.id,
  298. // status: 1
  299. // });
  300. // 使用其他接口
  301. // service.demo.goods.info({ id: data.id }).then((res) => {
  302. // done(res);
  303. // });
  304. // 直接取列表的数据返回
  305. done(data);
  306. },
  307. // 提交钩子
  308. onSubmit(data, { next, close, done }) {
  309. console.log('onSubmit', data);
  310. // 继续请求 update/add 接口
  311. next(data);
  312. // 自定义接口
  313. // service.demo.goods
  314. // .update(data)
  315. // .then(() => {
  316. // ElMessage.success("保存成功");
  317. // // 操作完,刷新列表
  318. // refresh();
  319. // // 关闭窗口
  320. // close();
  321. // })
  322. // .catch((err) => {
  323. // ElMessage.error(err.message);
  324. // // 关闭加载状态
  325. // done();
  326. // });
  327. },
  328. // 打开后,数据加载完,onInfo 之后
  329. onOpened(data) {
  330. if (Upsert.value?.mode != 'info') {
  331. ElMessage.info('编辑中');
  332. }
  333. },
  334. // 关闭钩子
  335. onClose(action, done) {
  336. if (Upsert.value?.mode == 'update') {
  337. if (action == 'close') {
  338. return ElMessageBox.confirm('还没填完,确定关闭不?', '提示', {
  339. type: 'warning'
  340. })
  341. .then(() => {
  342. done();
  343. ElMessage.info('好吧');
  344. })
  345. .catch(() => {
  346. ElMessage.success('请继续编辑');
  347. });
  348. }
  349. }
  350. done();
  351. }
  352. });
  353. // 表格
  354. const Table = useTable({
  355. columns: [
  356. {
  357. type: 'selection',
  358. width: 60
  359. },
  360. // 展开列
  361. {
  362. label: '展开',
  363. type: 'expand',
  364. prop: 'detail',
  365. width: 60
  366. },
  367. {
  368. label: '头像',
  369. prop: 'avatar',
  370. width: 100,
  371. component: {
  372. name: 'cl-image',
  373. props: {
  374. size: 40
  375. }
  376. }
  377. },
  378. {
  379. label: '姓名',
  380. prop: 'name',
  381. minWidth: 120
  382. },
  383. {
  384. label: '手机号',
  385. prop: 'phone',
  386. minWidth: 140,
  387. // 带搜索组件
  388. search: {
  389. component: {
  390. name: 'el-input',
  391. props: {
  392. placeholder: '搜索手机号'
  393. }
  394. }
  395. }
  396. },
  397. {
  398. label: '账号',
  399. prop: 'account',
  400. minWidth: 150
  401. },
  402. {
  403. label: '存款(元)',
  404. prop: 'wages',
  405. minWidth: 150,
  406. sortable: 'desc' // 默认倒序
  407. },
  408. {
  409. label: '工作',
  410. prop: 'occupation',
  411. dict: dict.get('occupation'),
  412. dictColor: true,
  413. minWidth: 150,
  414. dictAllLevels: true, // 显示所有等级
  415. // 带搜索组件
  416. search: {
  417. component: {
  418. name: 'cl-select',
  419. props: {
  420. options: dict.get('occupation')
  421. }
  422. }
  423. }
  424. },
  425. {
  426. label: '状态',
  427. orderNum: 2,
  428. prop: 'status',
  429. minWidth: 100,
  430. component: {
  431. name: 'cl-switch'
  432. }
  433. },
  434. {
  435. label: '出生年月',
  436. orderNum: 1,
  437. minWidth: 165,
  438. prop: 'createTime',
  439. sortable: 'custom',
  440. search: {
  441. component: {
  442. name: 'cl-date-picker',
  443. props: {
  444. type: 'date',
  445. valueFormat: 'YYYY-MM-DD',
  446. placeholder: '搜索日期'
  447. }
  448. }
  449. }
  450. },
  451. {
  452. type: 'op',
  453. width: 340,
  454. // 静态配置按钮
  455. // buttons: ["info", "edit", "delete"],
  456. // 动态配置按钮
  457. buttons({ scope }) {
  458. return [
  459. 'info',
  460. 'edit',
  461. 'delete',
  462. {
  463. label: '自定义',
  464. onClick() {
  465. ElMessage.info(`他是:${scope.row.name}`);
  466. }
  467. }
  468. ];
  469. }
  470. }
  471. ]
  472. });
  473. // 合计
  474. function onSummaryMethod() {
  475. // 添加自定义列组件后
  476. return ['合计', '', ...refs.columnCustom.summary(subData)];
  477. }
  478. // 高级搜索
  479. const AdvSearch = useAdvSearch({
  480. items: [
  481. {
  482. label: '姓名',
  483. prop: 'name',
  484. component: {
  485. name: 'el-input',
  486. props: {
  487. clearable: true
  488. }
  489. }
  490. },
  491. {
  492. label: '手机号',
  493. prop: 'phone',
  494. component: {
  495. name: 'el-input',
  496. props: {
  497. clearable: true
  498. }
  499. }
  500. },
  501. {
  502. label: '工作',
  503. prop: 'occupation',
  504. hook: {
  505. bind: 'string'
  506. },
  507. component: {
  508. name: 'el-select',
  509. options: dict.get('occupation')
  510. }
  511. }
  512. ]
  513. });
  514. // 搜索
  515. const Search = useSearch({
  516. items: [
  517. {
  518. label: '姓名',
  519. prop: 'name',
  520. component: {
  521. name: 'el-input',
  522. props: {
  523. clearable: true
  524. }
  525. }
  526. }
  527. ]
  528. });
  529. const visible = ref(false);
  530. function open() {
  531. visible.value = true;
  532. }
  533. </script>
  534. ```
  535. ## 起步 示例
  536. ```vue
  537. <template>
  538. <div class="scope">
  539. <div class="h">
  540. <el-tag size="small" effect="dark" disable-transitions>base</el-tag>
  541. <span>起步</span>
  542. </div>
  543. <div class="c">
  544. <el-button @click="open">预览</el-button>
  545. <demo-code :files="['crud/base.vue']" />
  546. <!-- 自定义表格组件 -->
  547. <cl-dialog v-model="visible" title="起步" width="80%">
  548. <cl-crud ref="Crud">
  549. <cl-row>
  550. <cl-refresh-btn />
  551. <cl-add-btn />
  552. <cl-multi-delete-btn />
  553. <cl-flex1 />
  554. <cl-search-key />
  555. </cl-row>
  556. <cl-row>
  557. <cl-table ref="Table" />
  558. </cl-row>
  559. <cl-row>
  560. <cl-flex1 />
  561. <cl-pagination />
  562. </cl-row>
  563. <!-- 新增、编辑 -->
  564. <cl-upsert ref="Upsert" />
  565. </cl-crud>
  566. </cl-dialog>
  567. </div>
  568. <div class="f">
  569. <span class="date">2024-01-01</span>
  570. </div>
  571. </div>
  572. </template>
  573. <script setup lang="ts">
  574. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  575. import { ref } from 'vue';
  576. import { useDict } from '/$/dict';
  577. import { useCool } from '/@/cool';
  578. const { service } = useCool();
  579. const { dict } = useDict();
  580. // cl-crud 配置
  581. const Crud = useCrud(
  582. {
  583. // test 为测试数据模式,详细说明移步到 service 例子
  584. service: 'test'
  585. },
  586. app => {
  587. //【很重要】首次请求,数据一并添加到请求参数中
  588. app.refresh({
  589. size: 10,
  590. status: 1
  591. });
  592. }
  593. );
  594. // cl-table 配置
  595. const Table = useTable({
  596. autoHeight: false,
  597. contextMenu: ['refresh'],
  598. columns: [
  599. {
  600. type: 'selection'
  601. },
  602. {
  603. label: '姓名',
  604. prop: 'name',
  605. minWidth: 140
  606. },
  607. {
  608. label: '手机号',
  609. prop: 'phone',
  610. minWidth: 140
  611. },
  612. {
  613. label: '工作',
  614. prop: 'occupation',
  615. dict: dict.get('occupation'),
  616. minWidth: 140
  617. },
  618. {
  619. label: '创建时间',
  620. prop: 'createTime',
  621. minWidth: 170,
  622. sortable: 'desc'
  623. },
  624. {
  625. type: 'op',
  626. buttons: ['edit', 'delete']
  627. }
  628. ]
  629. });
  630. // cl-upsert 配置
  631. const Upsert = useUpsert({
  632. items: [
  633. {
  634. label: '姓名',
  635. prop: 'name',
  636. component: {
  637. name: 'el-input'
  638. }
  639. },
  640. {
  641. label: '手机号',
  642. prop: 'phone',
  643. component: {
  644. name: 'el-input'
  645. }
  646. },
  647. {
  648. label: '工作',
  649. prop: 'occupation',
  650. component: {
  651. name: 'cl-select',
  652. props: {
  653. tree: true,
  654. checkStrictly: true,
  655. options: dict.get('occupation')
  656. }
  657. }
  658. }
  659. ]
  660. });
  661. const visible = ref(false);
  662. function open() {
  663. visible.value = true;
  664. }
  665. </script>
  666. ```
  667. ## 修改文案 / 接口 示例
  668. ```vue
  669. <template>
  670. <div class="scope">
  671. <div class="h">
  672. <el-tag size="small" effect="dark" disable-transitions>dict</el-tag>
  673. <span>修改文案 / 接口</span>
  674. </div>
  675. <div class="c">
  676. <el-button @click="open">预览</el-button>
  677. <demo-code :files="['crud/dict.vue']" />
  678. <!-- 自定义表格组件 -->
  679. <cl-dialog v-model="visible" title="修改文案 / 接口" width="80%">
  680. <cl-crud ref="Crud">
  681. <cl-row>
  682. <cl-refresh-btn />
  683. <cl-add-btn />
  684. <cl-multi-delete-btn />
  685. <cl-flex1 />
  686. <cl-search-key />
  687. </cl-row>
  688. <cl-row>
  689. <cl-table ref="Table" />
  690. </cl-row>
  691. <cl-row>
  692. <cl-flex1 />
  693. <cl-pagination />
  694. </cl-row>
  695. <!-- 新增、编辑 -->
  696. <cl-upsert ref="Upsert" />
  697. </cl-crud>
  698. </cl-dialog>
  699. </div>
  700. <div class="f">
  701. <span class="date">2024-01-01</span>
  702. </div>
  703. </div>
  704. </template>
  705. <script setup lang="ts">
  706. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  707. import { ref } from 'vue';
  708. import { useDict } from '/$/dict';
  709. const { dict } = useDict();
  710. // cl-crud 配置
  711. const Crud = useCrud(
  712. {
  713. //【很重要】配置 service,如:service.base.sys.user
  714. service: 'test',
  715. //【很重要】字典配置,文案和请求方法等
  716. dict: {
  717. // 修改请求
  718. // 比如说默认列表请求的是 page 接口,可以修改成 getUserList 等等,这取决于后端有没有这个接口。
  719. api: {
  720. list: 'list',
  721. add: 'add',
  722. update: 'update',
  723. delete: 'delete',
  724. info: 'info',
  725. page: 'page'
  726. },
  727. // 修改文案
  728. label: {
  729. op: '操作',
  730. add: '添加',
  731. delete: '移除',
  732. multiDelete: '批量移除',
  733. update: '修改',
  734. refresh: '刷新',
  735. info: '详情'
  736. }
  737. }
  738. },
  739. app => {
  740. app.refresh();
  741. }
  742. );
  743. // cl-table 配置
  744. const Table = useTable({
  745. autoHeight: false,
  746. contextMenu: ['refresh'],
  747. columns: [
  748. {
  749. type: 'selection'
  750. },
  751. {
  752. label: '姓名',
  753. prop: 'name',
  754. minWidth: 140
  755. },
  756. {
  757. label: '手机号',
  758. prop: 'phone',
  759. minWidth: 140
  760. },
  761. {
  762. label: '工作',
  763. prop: 'occupation',
  764. dict: dict.get('occupation'),
  765. minWidth: 140
  766. },
  767. {
  768. label: '创建时间',
  769. prop: 'createTime',
  770. minWidth: 170,
  771. sortable: 'desc'
  772. },
  773. {
  774. type: 'op',
  775. buttons: ['edit', 'delete']
  776. }
  777. ]
  778. });
  779. // cl-upsert 配置
  780. const Upsert = useUpsert({
  781. items: [
  782. {
  783. label: '姓名',
  784. prop: 'name',
  785. component: {
  786. name: 'el-input'
  787. }
  788. },
  789. {
  790. label: '手机号',
  791. prop: 'phone',
  792. component: {
  793. name: 'el-input'
  794. }
  795. },
  796. {
  797. label: '工作',
  798. prop: 'occupation',
  799. component: {
  800. name: 'cl-select',
  801. props: {
  802. tree: true,
  803. checkStrictly: true,
  804. options: dict.get('occupation')
  805. }
  806. }
  807. }
  808. ]
  809. });
  810. const visible = ref(false);
  811. function open() {
  812. visible.value = true;
  813. }
  814. </script>
  815. ```
  816. ## 事件监听 示例
  817. ```vue
  818. <template>
  819. <div class="scope">
  820. <div class="h">
  821. <el-tag size="small" effect="dark" disable-transitions>event</el-tag>
  822. <span>事件监听</span>
  823. </div>
  824. <div class="c">
  825. <el-button @click="open">预览</el-button>
  826. <demo-code :files="['crud/event.vue']" />
  827. <!-- 自定义表格组件 -->
  828. <cl-dialog v-model="visible" title="事件监听" width="80%">
  829. <cl-crud ref="Crud">
  830. <cl-row>
  831. <cl-refresh-btn />
  832. <cl-add-btn />
  833. <cl-multi-delete-btn />
  834. <cl-flex1 />
  835. <cl-search-key />
  836. </cl-row>
  837. <cl-row>
  838. <cl-table ref="Table">
  839. <!-- 自定义按钮 -->
  840. <template #slot-btn="{ scope }">
  841. <el-button @click="onEvent(scope.row)">自定义事件</el-button>
  842. </template>
  843. </cl-table>
  844. </cl-row>
  845. <cl-row>
  846. <cl-flex1 />
  847. <cl-pagination />
  848. </cl-row>
  849. <!-- 新增、编辑 -->
  850. <cl-upsert ref="Upsert" />
  851. </cl-crud>
  852. </cl-dialog>
  853. </div>
  854. <div class="f">
  855. <span class="date">2024-01-01</span>
  856. </div>
  857. </div>
  858. </template>
  859. <script setup lang="ts">
  860. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  861. import { ref } from 'vue';
  862. import { useDict } from '/$/dict';
  863. import { useCool } from '/@/cool';
  864. import { ElMessage } from 'element-plus';
  865. const { service } = useCool();
  866. const { dict } = useDict();
  867. // cl-crud 配置
  868. const Crud = useCrud(
  869. {
  870. // 配置 service
  871. service: 'test',
  872. //【很重要】监听刷新事件,每次调用 Crud.value.refresh() 会触发
  873. onRefresh(params, { next }) {
  874. // 默认使用 next(params),也可以自己对数据进行处理
  875. next({
  876. ...params,
  877. status: 1
  878. });
  879. },
  880. // 监听删除事件,点击删除按钮触发
  881. onDelete(selection, { next }) {
  882. // 传入 ids,批量删除多个数据
  883. next({
  884. ids: selection.map(e => e.id)
  885. });
  886. }
  887. },
  888. app => {
  889. app.refresh();
  890. }
  891. );
  892. // cl-table 配置
  893. const Table = useTable({
  894. autoHeight: false,
  895. contextMenu: ['refresh'],
  896. columns: [
  897. {
  898. type: 'selection'
  899. },
  900. {
  901. label: '姓名',
  902. prop: 'name',
  903. minWidth: 140
  904. },
  905. {
  906. label: '手机号',
  907. prop: 'phone',
  908. minWidth: 140
  909. },
  910. {
  911. label: '工作',
  912. prop: 'occupation',
  913. dict: dict.get('occupation'),
  914. minWidth: 140
  915. },
  916. {
  917. label: '创建时间',
  918. prop: 'createTime',
  919. minWidth: 170,
  920. sortable: 'desc'
  921. },
  922. {
  923. type: 'op',
  924. width: 300,
  925. buttons: ['edit', 'delete', 'slot-btn']
  926. }
  927. ]
  928. });
  929. // cl-upsert 配置
  930. const Upsert = useUpsert({
  931. items: [
  932. {
  933. label: '姓名',
  934. prop: 'name',
  935. component: {
  936. name: 'el-input'
  937. }
  938. },
  939. {
  940. label: '手机号',
  941. prop: 'phone',
  942. component: {
  943. name: 'el-input'
  944. }
  945. },
  946. {
  947. label: '工作',
  948. prop: 'occupation',
  949. component: {
  950. name: 'cl-select',
  951. props: {
  952. tree: true,
  953. checkStrictly: true,
  954. options: dict.get('occupation')
  955. }
  956. }
  957. }
  958. ]
  959. });
  960. // 调用 Crud 方法
  961. function onEvent(row: any) {
  962. ElMessage.info('自定义打开新增');
  963. // 打开新增表单
  964. Crud.value?.rowAdd();
  965. // 打开编辑表单
  966. // Crud.value?.rowEdit(row);
  967. // 打开删除提示框
  968. // Crud.value?.rowDelete(row);
  969. // 获取已请求的参数
  970. // Crud.value?.getParams();
  971. }
  972. const visible = ref(false);
  973. function open() {
  974. visible.value = true;
  975. }
  976. </script>
  977. ```
  978. ## 选择表格 示例
  979. ```vue
  980. <template>
  981. <div class="scope">
  982. <div class="h">
  983. <el-tag size="small" effect="dark" disable-transitions>select-table</el-tag>
  984. <span>选择表格</span>
  985. </div>
  986. <div class="c">
  987. <el-button @click="open">预览</el-button>
  988. <demo-code :files="['crud/select-table.vue']" />
  989. <!-- 自定义表格组件 -->
  990. <cl-form ref="Form" />
  991. </div>
  992. <div class="f">
  993. <span class="date">2025-02-07</span>
  994. </div>
  995. </div>
  996. </template>
  997. <script setup lang="ts">
  998. import { useForm } from '@cool-vue/crud';
  999. import { useCool } from '/@/cool';
  1000. import UserSelect from '/$/user/components/user-select.vue';
  1001. const { service } = useCool();
  1002. const Form = useForm();
  1003. const columns = [
  1004. {
  1005. label: '头像',
  1006. prop: 'headImg',
  1007. component: {
  1008. name: 'cl-avatar'
  1009. }
  1010. },
  1011. {
  1012. label: '昵称',
  1013. prop: 'nickName'
  1014. },
  1015. {
  1016. label: '创建时间',
  1017. prop: 'createTime'
  1018. }
  1019. ];
  1020. function open() {
  1021. Form.value?.open({
  1022. width: '800px',
  1023. title: '选择表格',
  1024. items: [
  1025. {
  1026. label: '多选 - default',
  1027. prop: 'a',
  1028. value: [],
  1029. component: {
  1030. vm: UserSelect,
  1031. props: {
  1032. multiple: true
  1033. }
  1034. },
  1035. span: 12
  1036. },
  1037. {
  1038. label: '单选 - default',
  1039. prop: 'b',
  1040. component: {
  1041. name: 'cl-select-table',
  1042. props: {
  1043. pickerType: 'default',
  1044. multiple: false,
  1045. columns,
  1046. service: service.base.sys.user
  1047. }
  1048. },
  1049. span: 12
  1050. },
  1051. {
  1052. label: '多选 - text',
  1053. prop: 'c',
  1054. value: [],
  1055. component: {
  1056. name: 'cl-select-table',
  1057. props: {
  1058. pickerType: 'text',
  1059. multiple: true,
  1060. columns,
  1061. service: service.base.sys.user
  1062. }
  1063. },
  1064. span: 12
  1065. },
  1066. {
  1067. label: '单选 - text',
  1068. prop: 'd',
  1069. component: {
  1070. name: 'cl-select-table',
  1071. props: {
  1072. pickerType: 'text',
  1073. multiple: false,
  1074. columns,
  1075. service: service.base.sys.user
  1076. }
  1077. },
  1078. span: 12
  1079. },
  1080. {
  1081. label: '多选 - table',
  1082. prop: 'e',
  1083. value: [],
  1084. component: {
  1085. name: 'cl-select-table',
  1086. props: {
  1087. pickerType: 'table',
  1088. multiple: true,
  1089. columns,
  1090. service: service.base.sys.user
  1091. }
  1092. }
  1093. }
  1094. ]
  1095. });
  1096. }
  1097. </script>
  1098. ```
  1099. ## Service 配置 示例
  1100. ```vue
  1101. <template>
  1102. <div class="scope">
  1103. <div class="h">
  1104. <el-tag size="small" effect="dark" disable-transitions>service</el-tag>
  1105. <span>Service 配置</span>
  1106. </div>
  1107. <div class="c">
  1108. <el-button @click="open">预览</el-button>
  1109. <demo-code :files="['crud/service.vue']" />
  1110. <!-- 自定义表格组件 -->
  1111. <cl-dialog v-model="visible" title="Service 配置" width="80%">
  1112. <cl-crud ref="Crud">
  1113. <cl-row>
  1114. <cl-refresh-btn />
  1115. <cl-add-btn />
  1116. <cl-multi-delete-btn />
  1117. <cl-flex1 />
  1118. <cl-search-key />
  1119. </cl-row>
  1120. <cl-row>
  1121. <cl-table ref="Table" />
  1122. </cl-row>
  1123. <cl-row>
  1124. <cl-flex1 />
  1125. <cl-pagination />
  1126. </cl-row>
  1127. <!-- 新增、编辑 -->
  1128. <cl-upsert ref="Upsert" />
  1129. </cl-crud>
  1130. </cl-dialog>
  1131. </div>
  1132. <div class="f">
  1133. <span class="date">2024-01-01</span>
  1134. </div>
  1135. </div>
  1136. </template>
  1137. <script setup lang="ts">
  1138. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  1139. import { ref } from 'vue';
  1140. import { useCool } from '/@/cool';
  1141. import { useDict } from '/$/dict';
  1142. //【很重要】service 是所有请求的集合,是一个对象(刷新页面和保存代码会自动读取后端的所有接口)
  1143. const { service, route } = useCool();
  1144. console.log('service', service);
  1145. const { dict } = useDict();
  1146. // cl-crud 配置
  1147. const Crud = useCrud(
  1148. {
  1149. //【很重要】配置 service,如:service.base.sys.user
  1150. // 不需要到具体的方法,如:service.base.sys.user.page,这是错误的!
  1151. // 实际用法
  1152. // service: service.base.sys.user,
  1153. // 测试示例
  1154. service: 'test'
  1155. // 自定义配置1,添加本地 service 文件。
  1156. // 【很重要】参考 /src/modules/demo/service/test.ts
  1157. // 【很重要】必须放在目录 modules/*/service/ 下,才会自动注入到 service 中
  1158. // service: service.test
  1159. // 自定义配置2,针对一些特殊场景
  1160. // service: {
  1161. // page(params: any) {
  1162. // // params 请求参数
  1163. // //【很重要】必须返回一个 Promise 格式
  1164. // return Promise.resolve({
  1165. // list: [],
  1166. // pagination: {
  1167. // total: 1,
  1168. // page: 1,
  1169. // size: 20
  1170. // }
  1171. // });
  1172. // }
  1173. // // add、delete、update、info、list 也是如此配置
  1174. // }
  1175. },
  1176. app => {
  1177. // 首次调用刷新接口。在弹窗的情况下可以注释,用 Crud.value?.refresh() 方式手动调用
  1178. app.refresh();
  1179. // 带参数
  1180. // app.refresh({
  1181. // userId: route.query.id
  1182. // });
  1183. }
  1184. );
  1185. // cl-table 配置
  1186. const Table = useTable({
  1187. autoHeight: false,
  1188. contextMenu: ['refresh'],
  1189. columns: [
  1190. {
  1191. type: 'selection'
  1192. },
  1193. {
  1194. label: '姓名',
  1195. prop: 'name',
  1196. minWidth: 140
  1197. },
  1198. {
  1199. label: '手机号',
  1200. prop: 'phone',
  1201. minWidth: 140
  1202. },
  1203. {
  1204. label: '工作',
  1205. prop: 'occupation',
  1206. dict: dict.get('occupation'),
  1207. minWidth: 140
  1208. },
  1209. {
  1210. label: '创建时间',
  1211. prop: 'createTime',
  1212. minWidth: 170,
  1213. sortable: 'desc'
  1214. },
  1215. {
  1216. type: 'op',
  1217. buttons: ['edit', 'delete']
  1218. }
  1219. ]
  1220. });
  1221. // cl-upsert 配置
  1222. const Upsert = useUpsert({
  1223. items: [
  1224. {
  1225. label: '姓名',
  1226. prop: 'name',
  1227. component: {
  1228. name: 'el-input'
  1229. }
  1230. },
  1231. {
  1232. label: '手机号',
  1233. prop: 'phone',
  1234. component: {
  1235. name: 'el-input'
  1236. }
  1237. },
  1238. {
  1239. label: '工作',
  1240. prop: 'occupation',
  1241. component: {
  1242. name: 'cl-select',
  1243. props: {
  1244. tree: true,
  1245. checkStrictly: true,
  1246. options: dict.get('occupation')
  1247. }
  1248. }
  1249. }
  1250. ]
  1251. });
  1252. const visible = ref(false);
  1253. function open() {
  1254. visible.value = true;
  1255. }
  1256. </script>
  1257. ```
  1258. ## 选择成员 示例
  1259. ```vue
  1260. <template>
  1261. <div class="scope">
  1262. <div class="h">
  1263. <el-tag size="small" effect="dark" disable-transitions>user-select</el-tag>
  1264. <span>选择成员</span>
  1265. </div>
  1266. <div class="c">
  1267. <el-button @click="open">预览</el-button>
  1268. <demo-code :files="['crud/user-select.vue']" />
  1269. <!-- 自定义表格组件 -->
  1270. <cl-form ref="Form" />
  1271. </div>
  1272. <div class="f">
  1273. <span class="date">2025-02-07</span>
  1274. </div>
  1275. </div>
  1276. </template>
  1277. <script setup lang="ts">
  1278. import { useForm } from '@cool-vue/crud';
  1279. const Form = useForm();
  1280. function open() {
  1281. Form.value?.open({
  1282. title: '选择成员',
  1283. items: [
  1284. {
  1285. label: '单选',
  1286. prop: 'userId',
  1287. component: {
  1288. name: 'cl-user-select',
  1289. props: {
  1290. multiple: false,
  1291. onChange(val) {
  1292. console.log(val);
  1293. }
  1294. }
  1295. },
  1296. required: true
  1297. },
  1298. {
  1299. label: '多选',
  1300. prop: 'userIds',
  1301. component: {
  1302. name: 'cl-user-select',
  1303. props: {
  1304. multiple: true,
  1305. onChange(val) {
  1306. console.log(val);
  1307. }
  1308. }
  1309. },
  1310. required: true
  1311. }
  1312. ]
  1313. });
  1314. }
  1315. </script>
  1316. ```