upsert.mdc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. ---
  2. description: cl-upsert 组件示例
  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>base</el-tag>
  11. <span>起步</span>
  12. </div>
  13. <div class="c">
  14. <el-button @click="open">预览</el-button>
  15. <demo-code :files="['upsert/base.vue']" />
  16. <!-- 自定义表格组件 -->
  17. <cl-dialog v-model="visible" title="起步" width="80%">
  18. <cl-crud ref="Crud">
  19. <cl-row>
  20. <!-- 打开新增表单的按钮 -->
  21. <cl-add-btn />
  22. </cl-row>
  23. <cl-row>
  24. <cl-table ref="Table" />
  25. </cl-row>
  26. <cl-row>
  27. <cl-flex1 />
  28. <cl-pagination />
  29. </cl-row>
  30. <!--【很重要】新增、编辑的表单组件 -->
  31. <cl-upsert ref="Upsert" />
  32. </cl-crud>
  33. </cl-dialog>
  34. </div>
  35. <div class="f">
  36. <span class="date">2024-01-01</span>
  37. </div>
  38. </div>
  39. </template>
  40. <script setup lang="ts">
  41. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  42. import { ref } from 'vue';
  43. import { useDict } from '/$/dict';
  44. const { dict } = useDict();
  45. // cl-crud 配置
  46. const Crud = useCrud(
  47. {
  48. service: 'test'
  49. },
  50. app => {
  51. app.refresh();
  52. }
  53. );
  54. // cl-table 配置
  55. const Table = useTable({
  56. autoHeight: false,
  57. contextMenu: ['refresh'],
  58. columns: [
  59. {
  60. label: '姓名',
  61. prop: 'name',
  62. minWidth: 140
  63. },
  64. {
  65. label: '手机号',
  66. prop: 'phone',
  67. minWidth: 140
  68. },
  69. {
  70. label: '工作',
  71. prop: 'occupation',
  72. dict: dict.get('occupation'),
  73. minWidth: 140
  74. },
  75. {
  76. label: '创建时间',
  77. prop: 'createTime',
  78. minWidth: 170,
  79. sortable: 'desc'
  80. },
  81. {
  82. type: 'op',
  83. // edit 打开编辑表单
  84. buttons: ['edit', 'delete']
  85. }
  86. ]
  87. });
  88. // cl-upsert 配置
  89. //【很重要】该组件基于 cl-form 故很多示例都可复用
  90. const Upsert = useUpsert({
  91. // 配置如 cl-form 一样
  92. items: [
  93. {
  94. label: '姓名',
  95. prop: 'name',
  96. component: {
  97. name: 'el-input'
  98. }
  99. },
  100. {
  101. label: '手机号',
  102. prop: 'phone',
  103. component: {
  104. name: 'el-input'
  105. }
  106. },
  107. {
  108. label: '工作',
  109. prop: 'occupation',
  110. component: {
  111. name: 'cl-select',
  112. props: {
  113. tree: true,
  114. checkStrictly: true,
  115. options: dict.get('occupation')
  116. }
  117. }
  118. }
  119. ]
  120. });
  121. const visible = ref(false);
  122. function open() {
  123. visible.value = true;
  124. }
  125. </script>
  126. ```
  127. ## 打开、关闭、提交等事件 示例
  128. ```vue
  129. <template>
  130. <div class="scope">
  131. <div class="h">
  132. <el-tag size="small" effect="dark" disable-transitions>event</el-tag>
  133. <span>打开、关闭、提交等事件</span>
  134. </div>
  135. <div class="c">
  136. <el-button @click="open">预览</el-button>
  137. <demo-code :files="['upsert/event.vue']" />
  138. <!-- 自定义表格组件 -->
  139. <cl-dialog v-model="visible" title="事件" width="80%">
  140. <cl-crud ref="Crud">
  141. <cl-row>
  142. <!-- 打开新增表单的按钮 -->
  143. <cl-add-btn />
  144. </cl-row>
  145. <cl-row>
  146. <cl-table ref="Table" />
  147. </cl-row>
  148. <cl-row>
  149. <cl-flex1 />
  150. <cl-pagination />
  151. </cl-row>
  152. <!--【很重要】新增、编辑的表单组件 -->
  153. <cl-upsert ref="Upsert" />
  154. </cl-crud>
  155. </cl-dialog>
  156. </div>
  157. <div class="f">
  158. <span class="date">2024-01-01</span>
  159. </div>
  160. </div>
  161. </template>
  162. <script setup lang="ts">
  163. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  164. import { ref } from 'vue';
  165. import { useDict } from '/$/dict';
  166. import { useCool } from '/@/cool';
  167. const { service } = useCool();
  168. const { dict } = useDict();
  169. // cl-crud 配置
  170. const Crud = useCrud(
  171. {
  172. service: 'test'
  173. },
  174. app => {
  175. app.refresh();
  176. }
  177. );
  178. // cl-table 配置
  179. const Table = useTable({
  180. autoHeight: false,
  181. contextMenu: ['refresh'],
  182. columns: [
  183. {
  184. label: '姓名',
  185. prop: 'name',
  186. minWidth: 140
  187. },
  188. {
  189. label: '手机号',
  190. prop: 'phone',
  191. minWidth: 140
  192. },
  193. {
  194. label: '工作',
  195. prop: 'occupation',
  196. dict: dict.get('occupation'),
  197. minWidth: 140
  198. },
  199. {
  200. label: '创建时间',
  201. prop: 'createTime',
  202. minWidth: 170,
  203. sortable: 'desc'
  204. },
  205. {
  206. type: 'op',
  207. // edit 打开编辑表单
  208. buttons: ['edit', 'delete']
  209. }
  210. ]
  211. });
  212. // cl-upsert 配置
  213. const Upsert = useUpsert({
  214. items: [
  215. {
  216. label: '姓名',
  217. prop: 'name',
  218. component: {
  219. name: 'el-input'
  220. }
  221. },
  222. {
  223. label: '手机号',
  224. prop: 'phone',
  225. component: {
  226. name: 'el-input'
  227. }
  228. },
  229. {
  230. label: '工作',
  231. prop: 'occupation',
  232. component: {
  233. name: 'cl-select',
  234. props: {
  235. tree: true,
  236. checkStrictly: true,
  237. options: dict.get('occupation')
  238. }
  239. }
  240. }
  241. ],
  242. // 以下事件按顺序触发
  243. // 弹窗打开的事件,这个时候还未有表单数据
  244. onOpen() {
  245. console.log('onOpen');
  246. },
  247. // 获取详情,编辑的时候会触发
  248. async onInfo(data, { next, done }) {
  249. // 不配置 onInfo 的时候默认执行 next(data),调用 service 的 info 接口获取详情
  250. // next(data);
  251. // 自定义,需要对请求数据进行处理或者返回处理后的数据
  252. const res = await next({
  253. id: data.id
  254. });
  255. done({
  256. ...res,
  257. name: `[${res.name}]`
  258. });
  259. },
  260. // 弹窗打开后,已经得到了表单数据
  261. onOpened(data) {
  262. // 判定是否编辑模式
  263. if (Upsert.value?.mode == 'update') {
  264. // 对数据处理
  265. data.phone += '000';
  266. }
  267. },
  268. // 提交事件的钩子
  269. // data 表单提交数据
  270. // next 继续往下执行
  271. // done 关闭加载
  272. // close 关闭弹窗
  273. async onSubmit(data, { next, done, close }) {
  274. // 不配置 onSubmit 的时候默认执行 next(data),提交后会去请求 service 的 update/add 接口
  275. // next(data);
  276. // 自定义如下
  277. // 场景1:提交时对参数额外的处理
  278. // next({
  279. // ...data,
  280. // status: 1,
  281. // createTime: dayjs().format("YYYY-MM-DD")
  282. // });
  283. // 场景2:提交前、后的操作
  284. // 之前,模拟获取 userId
  285. const userId = await service.base.sys.user.info({ id: 1 });
  286. // 返回值
  287. const res = await next({
  288. userId,
  289. data
  290. });
  291. // 之后
  292. // console.log(res);
  293. },
  294. // 关闭时触发
  295. onClose(action, done) {
  296. // action 关闭的类型
  297. console.log('action,', action);
  298. // 使用 done 关闭窗口
  299. done();
  300. },
  301. // 关闭后触发
  302. onClosed() {
  303. console.log('onClosed');
  304. }
  305. });
  306. const visible = ref(false);
  307. function open() {
  308. visible.value = true;
  309. }
  310. </script>
  311. ```
  312. ## Hook的使用 示例
  313. ```vue
  314. <template>
  315. <div class="scope">
  316. <div class="h">
  317. <el-tag size="small" effect="dark" disable-transitions>hook</el-tag>
  318. <span>Hook的使用</span>
  319. </div>
  320. <div class="c">
  321. <el-button @click="open">预览</el-button>
  322. <demo-code :files="['upsert/hook/index.vue', 'upsert/hook/reg-pca2.ts']" />
  323. <!-- 自定义表格组件 -->
  324. <cl-dialog v-model="visible" title="Hook的使用" width="80%">
  325. <cl-crud ref="Crud">
  326. <cl-row>
  327. <!-- 打开新增表单的按钮 -->
  328. <cl-add-btn />
  329. </cl-row>
  330. <cl-row>
  331. <cl-table ref="Table" />
  332. </cl-row>
  333. <cl-row>
  334. <cl-flex1 />
  335. <cl-pagination />
  336. </cl-row>
  337. <!--【很重要】新增、编辑的表单组件 -->
  338. <cl-upsert ref="Upsert" />
  339. </cl-crud>
  340. </cl-dialog>
  341. </div>
  342. <div class="f">
  343. <span class="date">2024-01-01</span>
  344. </div>
  345. </div>
  346. </template>
  347. <script setup lang="ts">
  348. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  349. import { ref } from 'vue';
  350. import { useDict } from '/$/dict';
  351. const { dict } = useDict();
  352. // cl-crud 配置
  353. const Crud = useCrud(
  354. {
  355. service: 'test'
  356. },
  357. app => {
  358. app.refresh();
  359. }
  360. );
  361. // cl-table 配置
  362. const Table = useTable({
  363. autoHeight: false,
  364. contextMenu: ['refresh'],
  365. columns: [
  366. {
  367. label: '姓名',
  368. prop: 'name',
  369. minWidth: 140
  370. },
  371. {
  372. label: '手机号',
  373. prop: 'phone',
  374. minWidth: 140
  375. },
  376. {
  377. label: '省市区',
  378. prop: 'pca',
  379. formatter(row) {
  380. return row.province ? row.province + '-' + row.city + '-' + row.district : '-';
  381. },
  382. minWidth: 140
  383. },
  384. {
  385. label: '工作',
  386. prop: 'occupation',
  387. dict: dict.get('occupation'),
  388. minWidth: 140
  389. },
  390. {
  391. label: '创建时间',
  392. prop: 'createTime',
  393. minWidth: 170,
  394. sortable: 'desc'
  395. },
  396. {
  397. type: 'op',
  398. buttons: ['edit', 'delete']
  399. }
  400. ]
  401. });
  402. // cl-upsert 配置
  403. const Upsert = useUpsert({
  404. items: [
  405. {
  406. label: '姓名',
  407. prop: 'name',
  408. component: {
  409. name: 'el-input'
  410. }
  411. },
  412. {
  413. label: '手机号',
  414. prop: 'phone',
  415. component: {
  416. name: 'el-input'
  417. }
  418. },
  419. {
  420. label: '省市区',
  421. prop: 'pca2',
  422. //【很重要】hook 参数配置
  423. hook: {
  424. bind(value, { form }) {
  425. // 将3个参数合并成一个数组,带入级联选择器
  426. return [form.province, form.city, form.district];
  427. },
  428. submit(value, { form, prop }) {
  429. // 提交的时候将数组拆分成3个字段提交
  430. const [province, city, district] = value || [];
  431. form.province = province;
  432. form.city = city;
  433. form.district = district;
  434. // 删除 prop 绑定值
  435. form[prop] = undefined;
  436. }
  437. },
  438. // 注册到全局后可直接使用,注册代码看 ./reg-pca2.ts
  439. // hook: "pca2",
  440. component: {
  441. name: 'cl-distpicker'
  442. }
  443. },
  444. {
  445. label: '标签',
  446. prop: 'labels',
  447. //【很重要】使用内置方法,避免一些辣鸡后端要你这么传给他
  448. hook: {
  449. // labels 的数据为 1,2,3
  450. // 绑定的时候将 labels 按 , 分割成数组
  451. bind: ['split', 'number'],
  452. // 提交的时候将 labels 拼接成字符串
  453. submit: ['join']
  454. },
  455. component: {
  456. name: 'el-select',
  457. props: {
  458. multiple: true
  459. },
  460. options: [
  461. {
  462. label: '帅气',
  463. value: 1
  464. },
  465. {
  466. label: '多金',
  467. value: 2
  468. },
  469. {
  470. label: '有才华',
  471. value: 3
  472. }
  473. ]
  474. }
  475. },
  476. {
  477. label: '工作',
  478. prop: 'occupation',
  479. component: {
  480. name: 'cl-select',
  481. props: {
  482. tree: true,
  483. checkStrictly: true,
  484. options: dict.get('occupation')
  485. }
  486. }
  487. }
  488. ]
  489. });
  490. const visible = ref(false);
  491. function open() {
  492. visible.value = true;
  493. }
  494. </script>
  495. ```
  496. ## 新增、编辑、详情模式 示例
  497. ```vue
  498. <template>
  499. <div class="scope">
  500. <div class="h">
  501. <el-tag size="small" effect="dark" disable-transitions>mode</el-tag>
  502. <span>新增、编辑、详情模式</span>
  503. </div>
  504. <div class="c">
  505. <el-button @click="open">预览</el-button>
  506. <demo-code :files="['upsert/mode.vue']" />
  507. <!-- 自定义表格组件 -->
  508. <cl-dialog v-model="visible" title="不同模式" width="80%">
  509. <cl-crud ref="Crud">
  510. <cl-row>
  511. <!-- 打开新增表单的按钮 -->
  512. <cl-add-btn />
  513. </cl-row>
  514. <cl-row>
  515. <cl-table ref="Table" />
  516. </cl-row>
  517. <cl-row>
  518. <cl-flex1 />
  519. <cl-pagination />
  520. </cl-row>
  521. <!--【很重要】新增、编辑的表单组件 -->
  522. <cl-upsert ref="Upsert" />
  523. </cl-crud>
  524. </cl-dialog>
  525. </div>
  526. <div class="f">
  527. <span class="date">2024-01-01</span>
  528. </div>
  529. </div>
  530. </template>
  531. <script setup lang="ts">
  532. import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
  533. import { ref } from 'vue';
  534. import { useDict } from '/$/dict';
  535. import { ElMessage } from 'element-plus';
  536. const { dict } = useDict();
  537. // cl-crud 配置
  538. const Crud = useCrud(
  539. {
  540. service: 'test'
  541. },
  542. app => {
  543. app.refresh();
  544. }
  545. );
  546. // cl-table 配置
  547. const Table = useTable({
  548. autoHeight: false,
  549. contextMenu: ['refresh'],
  550. columns: [
  551. {
  552. label: '姓名',
  553. prop: 'name',
  554. minWidth: 140
  555. },
  556. {
  557. label: '手机号',
  558. prop: 'phone',
  559. minWidth: 140
  560. },
  561. {
  562. label: '工作',
  563. prop: 'occupation',
  564. dict: dict.get('occupation'),
  565. minWidth: 140
  566. },
  567. {
  568. label: '创建时间',
  569. prop: 'createTime',
  570. minWidth: 170,
  571. sortable: 'desc'
  572. },
  573. {
  574. type: 'op',
  575. width: 240,
  576. buttons: ['info', 'edit', 'delete']
  577. }
  578. ]
  579. });
  580. // cl-upsert 配置
  581. const Upsert = useUpsert({
  582. items: [
  583. {
  584. label: '姓名',
  585. prop: 'name',
  586. component: {
  587. name: 'el-input'
  588. }
  589. },
  590. //【很重要】只有返回方法的时候才能使用 Upsert
  591. () => {
  592. return {
  593. label: '手机号',
  594. prop: 'phone',
  595. // 新增的时候隐藏
  596. // hidden: Upsert.value?.mode == "add",
  597. component: {
  598. name: 'el-input',
  599. props: {
  600. // 编辑的时候禁用
  601. disabled: Upsert.value?.mode == 'update'
  602. }
  603. }
  604. };
  605. },
  606. {
  607. label: '工作',
  608. prop: 'occupation',
  609. component: {
  610. name: 'cl-select',
  611. props: {
  612. tree: true,
  613. checkStrictly: true,
  614. options: dict.get('occupation')
  615. }
  616. }
  617. }
  618. ],
  619. onOpen() {
  620. ElMessage.info(`当前模式:` + Upsert.value?.mode);
  621. }
  622. });
  623. const visible = ref(false);
  624. function open() {
  625. visible.value = true;
  626. }
  627. </script>
  628. ```