unicloud-db.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. <template>
  2. <uni-cloud-db-element ref="UniCloudDB">
  3. <slot :data="dataList" :loading="loading" :hasMore="hasMore" :pagination="pagination" :error="error" />
  4. </uni-cloud-db-element>
  5. </template>
  6. <script lang="uts">
  7. //#ifdef APP
  8. import { SlotsType } from 'vue'
  9. //#endif
  10. //#ifdef WEB
  11. let registerFlag = false
  12. //#endif
  13. const EVENT_LOAD = 'load'
  14. const EVENT_ERROR = 'error'
  15. const PAGE_MODE_ADD = 'add'
  16. const PAGE_MODE_REPLACE = 'replace'
  17. const LOAD_MODE_AUTO = 'auto'
  18. const LOAD_MODE_MANUAL = 'manual'
  19. // const LOAD_MODE_ONREADY = 'onready'
  20. type SuccessCallback<T> = (res : T | null) => void | null
  21. type FailCallback = (err : any | null) => void | null
  22. type CompleteCallback = () => void | null
  23. type GetSuccessCallback = SuccessCallback<UniCloudDBGetResult>
  24. type AddSuccessCallback = SuccessCallback<UniCloudDBAddResult>
  25. type RemoveSuccessCallback = SuccessCallback<UniCloudDBRemoveResult>
  26. type UpdateSuccessCallback = SuccessCallback<UniCloudDBUpdateResult>
  27. export type UniCloudDBComponentPaginationType = {
  28. current : number,
  29. size : number,
  30. count : number
  31. }
  32. export type UniCloudDBComponentLoadDataOptions = {
  33. clear ?: boolean | null,
  34. current ?: number | null,
  35. success ?: GetSuccessCallback,
  36. fail ?: FailCallback,
  37. complete ?: CompleteCallback,
  38. }
  39. export type UniCloudDBComponentAddOptions = {
  40. /**
  41. * @default true
  42. */
  43. showToast ?: boolean | null,
  44. toastTitle ?: string | null,
  45. /**
  46. * @default true
  47. */
  48. needLoading ?: boolean | null,
  49. loadingTitle ?: string | null,
  50. success ?: AddSuccessCallback,
  51. fail ?: FailCallback,
  52. complete ?: CompleteCallback,
  53. }
  54. export type UniCloudDBComponentRemoveOptions = {
  55. confirmTitle ?: string | null,
  56. confirmContent ?: string | null,
  57. /**
  58. * @default true
  59. */
  60. needConfirm ?: boolean | null,
  61. /**
  62. * @default true
  63. */
  64. needLoading ?: boolean | null,
  65. loadingTitle ?: string | null,
  66. success ?: RemoveSuccessCallback,
  67. fail ?: FailCallback,
  68. complete ?: CompleteCallback,
  69. }
  70. export type UniCloudDBComponentUpdateOptions = {
  71. /**
  72. * @default true
  73. */
  74. showToast ?: boolean | null,
  75. toastTitle ?: string | null,
  76. confirmTitle ?: string | null,
  77. confirmContent ?: string | null,
  78. /**
  79. * @default true
  80. */
  81. needConfirm ?: boolean | null,
  82. /**
  83. * @default true
  84. */
  85. needLoading ?: boolean | null,
  86. loadingTitle ?: string | null,
  87. success ?: UpdateSuccessCallback,
  88. fail ?: FailCallback,
  89. complete ?: CompleteCallback,
  90. }
  91. function cast_callback<T>(options : any | null) : T | null {
  92. return options as T | null
  93. }
  94. //#ifdef APP
  95. export class UniCloudDBElement extends UniViewElementImpl {
  96. constructor(data : INodeData, pageNode : PageNode) {
  97. super(data, pageNode)
  98. }
  99. //#endif
  100. //#ifdef WEB
  101. export class UniCloudDBElement extends UniElementImpl {
  102. constructor(data : INodeData, pageNode : PageNode) {
  103. super(data, pageNode);
  104. const TagName = 'UNICLOUD-DB';
  105. Object.defineProperty(this, 'tagName', {
  106. value: TagName,
  107. writable: false
  108. });
  109. Object.defineProperty(this, 'nodeName', {
  110. value: TagName,
  111. writable: false
  112. });
  113. }
  114. //#endif
  115. dataList : Array<UTSJSONObject> = []
  116. loadData(options : UTSJSONObject = {}) {
  117. this.onLoadData({
  118. clear: options.getBoolean('clear'),
  119. current: options.getNumber('current'),
  120. success: cast_callback<GetSuccessCallback>(options['success']),
  121. fail: cast_callback<FailCallback>(options['fail']),
  122. complete: cast_callback<CompleteCallback>(options['complete'])
  123. } as UniCloudDBComponentLoadDataOptions)
  124. }
  125. loadMore() {
  126. this.onLoadMore()
  127. }
  128. add(value : UTSJSONObject, options : UTSJSONObject) {
  129. this.onAdd(value, {
  130. showToast: options.getBoolean('showToast') ?? true,
  131. toastTitle: options.getString('toastTitle'),
  132. needLoading: options.getBoolean('needLoading') ?? true,
  133. loadingTitle: options.getString('loadingTitle'),
  134. success: cast_callback<AddSuccessCallback>(options['success']),
  135. fail: cast_callback<FailCallback>(options['fail']),
  136. complete: cast_callback<CompleteCallback>(options['complete'])
  137. } as UniCloudDBComponentAddOptions)
  138. }
  139. // @ts-ignore
  140. remove(id : any, options : UTSJSONObject) {
  141. //#ifdef WEB
  142. // @ts-ignore
  143. if (arguments.length == 0) {
  144. super.remove()
  145. return
  146. }
  147. //#endif
  148. this.onRemove(id, {
  149. confirmTitle: options.getString('confirmTitle'),
  150. confirmContent: options.getString('confirmContent'),
  151. needConfirm: options.getBoolean('needConfirm') ?? true,
  152. needLoading: options.getBoolean('needLoading') ?? true,
  153. loadingTitle: options.getString('loadingTitle'),
  154. success: cast_callback<RemoveSuccessCallback>(options['success']),
  155. fail: cast_callback<FailCallback>(options['fail']),
  156. complete: cast_callback<CompleteCallback>(options['complete'])
  157. } as UniCloudDBComponentRemoveOptions)
  158. }
  159. update(id : string, value : UTSJSONObject, options : UTSJSONObject) {
  160. this.onUpdate(id, value, {
  161. showToast: options.getBoolean('showToast') ?? true,
  162. toastTitle: options.getString('toastTitle'),
  163. confirmTitle: options.getString('confirmTitle'),
  164. confirmContent: options.getString('confirmContent'),
  165. needConfirm: options.getBoolean('needConfirm') ?? true,
  166. needLoading: options.getBoolean('needLoading') ?? true,
  167. loadingTitle: options.getString('loadingTitle'),
  168. success: cast_callback<UpdateSuccessCallback>(options['success']),
  169. fail: cast_callback<FailCallback>(options['fail']),
  170. complete: cast_callback<CompleteCallback>(options['complete'])
  171. } as UniCloudDBComponentUpdateOptions)
  172. }
  173. onLoadData! : (_ : UniCloudDBComponentLoadDataOptions) => void
  174. onLoadMore! : () => void
  175. onAdd! : (value : UTSJSONObject, options : UniCloudDBComponentAddOptions) => void
  176. onUpdate!: (id : string, value : UTSJSONObject, options : UniCloudDBComponentUpdateOptions) => void
  177. onRemove!: (id : any, options : UniCloudDBComponentRemoveOptions) => void
  178. }
  179. export default {
  180. name: 'UniCloudDB',
  181. rootElement: {
  182. name: 'uni-cloud-db-element',
  183. class: UniCloudDBElement
  184. },
  185. slots: Object as SlotsType<{
  186. default : {
  187. data : Array<UTSJSONObject>,
  188. loading : boolean,
  189. hasMore : boolean,
  190. pagination : UniCloudDBComponentPaginationType,
  191. error : UniCloudError | null
  192. }
  193. }>,
  194. props: {
  195. collection: {
  196. type: [String, Object],
  197. default: ''
  198. },
  199. field: {
  200. type: String,
  201. default: ''
  202. },
  203. orderby: {
  204. type: String,
  205. default: ''
  206. },
  207. where: {
  208. type: [String, Object],
  209. default: ''
  210. },
  211. pageData: {
  212. type: String,
  213. default: 'add'
  214. },
  215. pageCurrent: {
  216. type: Number,
  217. default: 1
  218. },
  219. pageSize: {
  220. type: Number,
  221. default: 20
  222. },
  223. getcount: {
  224. type: Boolean,
  225. default: false
  226. },
  227. gettree: {
  228. type: [String, Object],
  229. default: ''
  230. },
  231. gettreepath: {
  232. type: Boolean,
  233. default: false
  234. },
  235. startwith: {
  236. type: String,
  237. default: ''
  238. },
  239. limitlevel: {
  240. type: Number,
  241. default: 10
  242. },
  243. groupby: {
  244. type: String,
  245. default: ''
  246. },
  247. groupField: {
  248. type: String,
  249. default: ''
  250. },
  251. distinct: {
  252. type: Boolean,
  253. default: false
  254. },
  255. pageIndistinct: {
  256. type: Boolean,
  257. default: false
  258. },
  259. foreignKey: {
  260. type: String,
  261. default: ''
  262. },
  263. loadtime: {
  264. type: String,
  265. default: 'auto'
  266. },
  267. manual: {
  268. type: Boolean,
  269. default: false
  270. }
  271. },
  272. data() {
  273. return {
  274. dataList: [] as Array<UTSJSONObject>,
  275. loading: false,
  276. hasMore: false,
  277. isEnded: false,
  278. pagination: {
  279. current: 1,
  280. size: 20,
  281. count: 0,
  282. } as UniCloudDBComponentPaginationType,
  283. error: null as UniCloudError | null
  284. }
  285. },
  286. //#ifdef WEB
  287. beforeCreate() {
  288. if (!registerFlag) {
  289. registerFlag = true
  290. // @ts-ignore
  291. window.customElements.define(
  292. 'uni-cloud-db-element',
  293. UniCloudDBElement,
  294. )
  295. }
  296. },
  297. //#endif
  298. created() {
  299. this.pagination.current = this.pageCurrent
  300. this.pagination.size = this.pageSize
  301. this.$watch(
  302. () : any => [
  303. this.pageCurrent,
  304. this.pageSize,
  305. this.collection,
  306. this.field,
  307. this.getcount,
  308. this.orderby,
  309. this.where,
  310. this.groupby,
  311. this.groupField,
  312. this.distinct
  313. ],
  314. (newValue : Array<any>, oldValue : Array<any>) => {
  315. this.pagination.size = this.pageSize
  316. if (newValue[0] !== oldValue[0]) {
  317. this.pagination.current = this.pageCurrent
  318. }
  319. if (this.loadtime == LOAD_MODE_MANUAL) {
  320. return
  321. }
  322. let needReset = false
  323. for (let i = 2; i < newValue.length; i++) {
  324. if (newValue[i] !== oldValue[i]) {
  325. needReset = true
  326. break
  327. }
  328. }
  329. if (needReset) {
  330. this.clear()
  331. this.reset()
  332. }
  333. this.get(null)
  334. }
  335. )
  336. if (!this.manual && this.loadtime == LOAD_MODE_AUTO) {
  337. if (typeof this.collection == 'string') {
  338. const collectionString = this.collection as string
  339. if (collectionString.length == 0) {
  340. return
  341. }
  342. } else if (Array.isArray(this.collection)) {
  343. const collectionArray = this.collection as Array<any>
  344. if (collectionArray.length == 0) {
  345. return
  346. }
  347. }
  348. this.get(null)
  349. }
  350. },
  351. mounted() {
  352. const uniCloudDBElement = this.$refs['UniCloudDB'] as UniCloudDBElement
  353. uniCloudDBElement.dataList = this.dataList;
  354. //#ifdef APP
  355. uniCloudDBElement.onLoadData = this.loadData;
  356. uniCloudDBElement.onLoadMore = this.loadMore;
  357. uniCloudDBElement.onAdd = this.add;
  358. uniCloudDBElement.onUpdate = this.update;
  359. uniCloudDBElement.onRemove = this.remove;
  360. //#endif
  361. //#ifdef WEB
  362. uniCloudDBElement.onLoadData = this.loadData.bind(this);
  363. uniCloudDBElement.onLoadMore = this.loadMore.bind(this);
  364. uniCloudDBElement.onAdd = this.add.bind(this);
  365. uniCloudDBElement.onUpdate = this.update.bind(this);
  366. uniCloudDBElement.onRemove = this.remove.bind(this);
  367. //#endif
  368. },
  369. methods: {
  370. loadData(options : UniCloudDBComponentLoadDataOptions) {
  371. let clear = (options.clear != null && options.clear == true)
  372. if (clear == true) {
  373. if (this.pageData == PAGE_MODE_REPLACE) {
  374. this.clear()
  375. }
  376. this.reset()
  377. }
  378. this.get(options)
  379. },
  380. loadMore() {
  381. if (this.isEnded || this.loading) {
  382. return
  383. }
  384. if (this.pageData == PAGE_MODE_ADD) {
  385. this.pagination.current++
  386. }
  387. this.get(null)
  388. },
  389. refresh() {
  390. this.clear()
  391. this.get(null)
  392. },
  393. clear() {
  394. this.isEnded = false
  395. this.dataList.length = 0
  396. },
  397. reset() {
  398. this.pagination.current = 1
  399. },
  400. get(options? : UniCloudDBComponentLoadDataOptions) {
  401. let loadAfterClear = false
  402. if (options != null && options.clear != null && options.clear == true) {
  403. loadAfterClear = true
  404. }
  405. if (options != null && options.current != null) {
  406. this.pagination.current = options.current!
  407. }
  408. this.error = null
  409. this.loading = true
  410. this.getExec().then((res : UniCloudDBGetResult) => {
  411. const data = res.data
  412. const count = res.count
  413. this.isEnded = (count != null) ? (this.pagination.current * this.pagination.size >= count) : (data.length < this.pageSize)
  414. this.hasMore = !this.isEnded
  415. if (this.getcount && count != null) {
  416. this.pagination.count = count
  417. }
  418. this._dispatchEvent(EVENT_LOAD, data)
  419. if (loadAfterClear || this.pageData == PAGE_MODE_REPLACE) {
  420. this.dataList = data
  421. } else {
  422. this.dataList.push(...data)
  423. }
  424. options?.success?.(res)
  425. }).catch((err : any | null) => {
  426. this._requestFail(err, null)
  427. options?.fail?.(err)
  428. }).finally(() => {
  429. this.loading = false
  430. options?.complete?.()
  431. })
  432. },
  433. add(value : UTSJSONObject, options : UniCloudDBComponentAddOptions) {
  434. this._needLoading(options.needLoading, options.loadingTitle)
  435. const db = uniCloud.databaseForJQL()
  436. db.collection(this._getMainCollection()).add(value).then<void>((res : UniCloudDBAddResult) => {
  437. options.success?.(res)
  438. this._isShowToast(options.showToast ?? false, options.toastTitle ?? 'add success')
  439. }).catch((err) => {
  440. this._requestFail(err, options.fail)
  441. }).finally(() => {
  442. this._requestComplete(options.complete, options.needLoading)
  443. })
  444. },
  445. update(id : string, value : UTSJSONObject, options : UniCloudDBComponentUpdateOptions) {
  446. if (options.needConfirm == true) {
  447. uni.showModal({
  448. title: options.confirmTitle,
  449. content: options.confirmContent,
  450. showCancel: true,
  451. success: (res) => {
  452. if (res.confirm) {
  453. this._update(id, value, options)
  454. }
  455. }
  456. })
  457. } else {
  458. this._update(id, value, options)
  459. }
  460. },
  461. remove(id : any, options : UniCloudDBComponentRemoveOptions) {
  462. const ids = Array.isArray(id) ? (id as Array<any>) : [id]
  463. if (options.needConfirm == true) {
  464. uni.showModal({
  465. title: options.confirmTitle,
  466. content: options.confirmContent,
  467. showCancel: true,
  468. success: (res) => {
  469. if (res.confirm) {
  470. this._remove(ids, options)
  471. }
  472. }
  473. })
  474. } else {
  475. this._remove(ids, options)
  476. }
  477. },
  478. _update(id : string, value : UTSJSONObject, options : UniCloudDBComponentUpdateOptions) {
  479. this._needLoading(options.needLoading, options.loadingTitle)
  480. const db = uniCloud.databaseForJQL()
  481. db.collection(this._getMainCollection()).doc(id).update(value).then((res) => {
  482. options.success?.(res)
  483. this._isShowToast(options.showToast ?? false, options.toastTitle ?? 'update success')
  484. }).catch((err : any | null) => {
  485. this._requestFail(err, options.fail)
  486. }).finally(() => {
  487. this._requestComplete(options.complete, options.needLoading)
  488. })
  489. },
  490. _remove(ids : Array<any>, options : UniCloudDBComponentRemoveOptions) {
  491. this._needLoading(options.needLoading, options.loadingTitle)
  492. const db = uniCloud.databaseForJQL()
  493. const dbCommand = db.command
  494. db.collection(this._getMainCollection()).where({
  495. _id: dbCommand.in(ids)
  496. }).remove().then((res) => {
  497. options.success?.(res)
  498. if (this.pageData == PAGE_MODE_REPLACE) {
  499. this.refresh()
  500. } else {
  501. this._removeData(ids)
  502. }
  503. }).catch((err : any | null) => {
  504. this._requestFail(err, options.fail)
  505. }).finally(() => {
  506. this._requestComplete(options.complete, options.needLoading)
  507. })
  508. },
  509. _removeData(ids : Array<any>) {
  510. const il = ids.slice(0)
  511. const dl = this.dataList
  512. for (let i = dl.length - 1; i >= 0; i--) {
  513. const index = il.indexOf(dl[i]['_id'])
  514. if (index >= 0) {
  515. dl.splice(i, 1)
  516. il.splice(index, 1)
  517. }
  518. }
  519. },
  520. _isShowToast(showToast : boolean, title : string) {
  521. if (showToast == true) {
  522. uni.showToast({
  523. title: title
  524. })
  525. }
  526. },
  527. _needLoading(needLoading ?: boolean | null, title ?: string | null) {
  528. if (needLoading == true) {
  529. uni.showLoading({
  530. mask: true,
  531. title: title ?? ''
  532. })
  533. }
  534. },
  535. _requestFail(err ?: any | null, callback ?: FailCallback) {
  536. callback?.(err)
  537. this.error = err as UniCloudError
  538. this.$emit(EVENT_ERROR, err)
  539. },
  540. _requestComplete(callback ?: CompleteCallback, needLoading ?: boolean | null) {
  541. callback?.()
  542. if (needLoading == true) {
  543. uni.hideLoading()
  544. }
  545. },
  546. getExec() : Promise<UniCloudDBGetResult> {
  547. return this.getTemp()
  548. },
  549. getTemp() : Promise<UniCloudDBGetResult> {
  550. let db = uniCloud.databaseForJQL()
  551. let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection)
  552. let filter : UniCloudDBFilter | null = null
  553. if (this.foreignKey.length > 0) {
  554. filter = collection.foreignKey(this.foreignKey)
  555. }
  556. if (typeof this.where == 'string') {
  557. const whereString = this.where as string
  558. if (whereString.length > 0) {
  559. filter = (filter != null) ? filter.where(this.where) : collection.where(this.where)
  560. }
  561. } else if (typeof this.where == 'object') {
  562. filter = (filter != null) ? filter.where(this.where) : collection.where(this.where)
  563. }
  564. let query : UniCloudDBQuery | null = null
  565. if (this.field.length > 0) {
  566. query = (filter != null) ? filter.field(this.field) : collection.field(this.field)
  567. }
  568. if (this.groupby.length > 0) {
  569. if (query != null) {
  570. query = query.groupBy(this.groupby)
  571. } else if (filter != null) {
  572. query = filter.groupBy(this.groupby)
  573. }
  574. }
  575. if (this.groupField.length > 0) {
  576. if (query != null) {
  577. query = query.groupField(this.groupField)
  578. } else if (filter != null) {
  579. query = filter.groupField(this.groupField)
  580. }
  581. }
  582. if (this.distinct == true) {
  583. if (query != null) {
  584. query = query.distinct(this.field)
  585. } else if (filter != null) {
  586. query = filter.distinct(this.field)
  587. }
  588. }
  589. if (this.orderby.length > 0) {
  590. if (query != null) {
  591. query = query.orderBy(this.orderby)
  592. } else if (filter != null) {
  593. query = filter.orderBy(this.orderby)
  594. } else {
  595. query = collection.orderBy(this.orderby)
  596. }
  597. }
  598. const size = this.pagination.size
  599. const current = this.pagination.current
  600. const skipSize = size * (current - 1)
  601. if (query != null) {
  602. query = query.skip(skipSize).limit(size)
  603. } else if (filter != null) {
  604. query = filter.skip(skipSize).limit(size)
  605. } else {
  606. query = collection.skip(skipSize).limit(size)
  607. }
  608. const getOptions = {}
  609. const treeOptions = {
  610. limitLevel: this.limitlevel,
  611. startWith: this.startwith
  612. }
  613. if (this.getcount == true) {
  614. getOptions['getCount'] = this.getcount
  615. }
  616. if (typeof this.gettree == 'string') {
  617. const getTreeString = this.gettree as string
  618. if (getTreeString.length > 0) {
  619. getOptions['getTree'] = treeOptions
  620. }
  621. } else if (typeof this.gettree == 'object') {
  622. getOptions['getTree'] = treeOptions
  623. }
  624. if (this.gettreepath == true) {
  625. getOptions['getTreePath'] = treeOptions
  626. }
  627. return query.get(getOptions)
  628. },
  629. _getMainCollection() : string {
  630. if (typeof this.collection === 'string') {
  631. return (this.collection as string).split(',')[0]
  632. }
  633. if (Array.isArray(this.collection)) {
  634. const array = this.collection as Array<any>
  635. const index = array[0] as UTSJSONObject
  636. const collection = index.getString('$db[0].$param[0]')
  637. return collection ?? ''
  638. }
  639. return ''
  640. },
  641. _dispatchEvent(type : string, data : Array<UTSJSONObject>) {
  642. this.$emit(type, data, this.isEnded, {
  643. current: this.pagination.current,
  644. size: this.pagination.size,
  645. count: this.pagination.count
  646. })
  647. }
  648. }
  649. }
  650. </script>