list.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. <template>
  2. <view class="fix-top-window">
  3. <view class="uni-header">
  4. <uni-stat-breadcrumb class="uni-stat-breadcrumb-on-phone" />
  5. <view class="uni-group">
  6. <input class="uni-search" type="text" v-model="query" @confirm="search"
  7. :placeholder="$t('common.placeholder.query')" />
  8. <button class="uni-button hide-on-phone" type="default" size="mini"
  9. @click="search">{{$t('common.button.search')}}</button>
  10. <button class="uni-button" type="primary" size="mini"
  11. @click="navigateTo('./add')">{{$t('common.button.add')}}</button>
  12. <button class="uni-button" type="warn" size="mini" :disabled="!selectedIndexs.length"
  13. @click="delTable">{{$t('common.button.batchDelete')}}</button>
  14. <button class="uni-button" type="primary" size="mini" :disabled="!selectedIndexs.length"
  15. @click="openTagsPopup">标签管理</button>
  16. <!-- #ifdef H5 -->
  17. <download-excel class="hide-on-phone" :fields="exportExcel.fields" :data="exportExcelData"
  18. :type="exportExcel.type" :name="exportExcel.filename">
  19. <button class="uni-button" type="primary" size="mini">{{$t('common.button.exportExcel')}}</button>
  20. </download-excel>
  21. <!-- #endif -->
  22. </view>
  23. </view>
  24. <view class="uni-container">
  25. <unicloud-db ref="udb" collection="uni-id-users,uni-id-roles"
  26. field="username,nickname,mobile,status,email,role{role_name},dcloud_appid,tags,last_login_date" :where="where"
  27. page-data="replace" :orderby="orderby" :getcount="true" :page-size="options.pageSize"
  28. :page-current="options.pageCurrent" v-slot:default="{data,pagination,loading,error,options}"
  29. :options="options" loadtime="manual" @load="onqueryload">
  30. <uni-table ref="table" :loading="loading" :emptyText="error.message || $t('common.empty')" border stripe
  31. type="selection" @selection-change="selectionChange">
  32. <uni-tr>
  33. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'username')"
  34. sortable @sort-change="sortChange($event, 'username')">用户名</uni-th>
  35. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'nickname')"
  36. sortable @sort-change="sortChange($event, 'nickname')">用户昵称</uni-th>
  37. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'mobile')"
  38. sortable @sort-change="sortChange($event, 'mobile')">手机号码</uni-th>
  39. <uni-th align="center" filter-type="select" :filter-data="options.filterData.status_localdata"
  40. @filter-change="filterChange($event, 'status')">用户状态</uni-th>
  41. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'email')"
  42. sortable @sort-change="sortChange($event, 'email')">邮箱</uni-th>
  43. <uni-th align="center">角色</uni-th>
  44. <uni-th align="center" filter-type="select" :filter-data="tagsData"
  45. @filter-change="filterChange($event, 'tags')">用户标签</uni-th>
  46. <uni-th align="center">可登录应用</uni-th>
  47. <uni-th align="center" filter-type="timestamp"
  48. @filter-change="filterChange($event, 'last_login_date')" sortable
  49. @sort-change="sortChange($event, 'last_login_date')">最后登录时间</uni-th>
  50. <uni-th align="center">操作</uni-th>
  51. </uni-tr>
  52. <uni-tr v-for="(item,index) in data" :key="index">
  53. <uni-td align="center">{{item.username}}</uni-td>
  54. <uni-td align="center">{{item.nickname}}</uni-td>
  55. <uni-td align="center">{{item.mobile}}</uni-td>
  56. <uni-td align="center">{{options.status_valuetotext[item.status]}}</uni-td>
  57. <uni-td align="center">
  58. <uni-link :href="'mailto:'+item.email" :text="item.email"></uni-link>
  59. </uni-td>
  60. <uni-td align="center">{{item.role}}</uni-td>
  61. <uni-td align="center">
  62. <template v-if="item.tags" v-for="tag in item.tags">
  63. <uni-tag type="primary" inverted size="small" :text="tag" style="margin: 0 5px;">
  64. </uni-tag>
  65. </template>
  66. </uni-td>
  67. <uni-td align="center">
  68. <uni-link v-if="item.dcloud_appid === undefined" :href="noAppidWhatShouldIDoLink">
  69. 未绑定可登录应用<view class="uni-icons-help"></view>
  70. </uni-link>
  71. {{item.dcloud_appid}}
  72. </uni-td>
  73. <uni-td align="center">
  74. <uni-dateformat :threshold="[0, 0]" :date="item.last_login_date"></uni-dateformat>
  75. </uni-td>
  76. <uni-td align="center">
  77. <view class="uni-group">
  78. <button @click="navigateTo('./edit?id='+item._id, false)" class="uni-button" size="mini"
  79. type="primary">{{$t('common.button.edit')}}</button>
  80. <button @click="confirmDelete(item._id)" class="uni-button" size="mini"
  81. type="warn">{{$t('common.button.delete')}}</button>
  82. </view>
  83. </uni-td>
  84. </uni-tr>
  85. </uni-table>
  86. <view class="uni-pagination-box">
  87. <uni-pagination show-iconn show-page-size :page-size="pagination.size" v-model="pagination.current"
  88. :total="pagination.count" @change="onPageChanged" @pageSizeChange="changeSize" />
  89. </view>
  90. </unicloud-db>
  91. </view>
  92. <!-- #ifndef H5 -->
  93. <fix-window />
  94. <!-- #endif -->
  95. <uni-popup ref="tagsPopup" type="center">
  96. <view class="tags-manager--x">
  97. <view class="tags-manager--header mb">管理标签</view>
  98. <uni-data-checkbox ref="checkbox" v-model="managerTags" class="mb ml" :multiple="true"
  99. collection="uni-id-tag" field="tagid as value, name as text"></uni-data-checkbox>
  100. <view class="uni-group">
  101. <button @click="managerMultiTag" class="uni-button" type="primary"
  102. style="margin-right: 75px;">保存</button>
  103. </view>
  104. </view>
  105. </uni-popup>
  106. </view>
  107. </template>
  108. <script>
  109. import {
  110. enumConverter,
  111. filterToWhere
  112. } from '../../../js_sdk/validator/uni-id-users.js';
  113. const db = uniCloud.database()
  114. // 表查询配置
  115. const dbOrderBy = 'last_login_date desc' // 排序字段
  116. const dbSearchFields = ['username', 'role.role_name', 'mobile', 'email'] // 支持模糊搜索的字段列表
  117. // 分页配置
  118. const pageSize = 20
  119. const pageCurrent = 1
  120. const orderByMapping = {
  121. "ascending": "asc",
  122. "descending": "desc"
  123. }
  124. export default {
  125. data() {
  126. return {
  127. query: '',
  128. where: '',
  129. orderby: dbOrderBy,
  130. orderByFieldName: "",
  131. selectedIndexs: [],
  132. pageSizeIndex: 0,
  133. pageSizeOption: [20, 50, 100, 500],
  134. tags: {},
  135. managerTags: [],
  136. queryTagid: '',
  137. options: {
  138. pageSize,
  139. pageCurrent,
  140. filterData: {
  141. "status_localdata": [{
  142. "text": "正常",
  143. "value": 0,
  144. "checked": true
  145. },
  146. {
  147. "text": "禁用",
  148. "value": 1
  149. },
  150. {
  151. "text": "审核中",
  152. "value": 2
  153. },
  154. {
  155. "text": "审核拒绝",
  156. "value": 3
  157. }
  158. ]
  159. },
  160. ...enumConverter
  161. },
  162. imageStyles: {
  163. width: 64,
  164. height: 64
  165. },
  166. exportExcel: {
  167. "filename": "uni-id-users.xls",
  168. "type": "xls",
  169. "fields": {
  170. "用户名": "username",
  171. "手机号码": "mobile",
  172. "用户状态": "status",
  173. "邮箱": "email",
  174. "角色": "role",
  175. "last_login_date": "last_login_date"
  176. }
  177. },
  178. exportExcelData: [],
  179. noAppidWhatShouldIDoLink: 'https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=makeup-dcloud-appid'
  180. }
  181. },
  182. onLoad(e) {
  183. this._filter = {}
  184. const tagid = e.tagid
  185. if (tagid) {
  186. this.queryTagid = tagid
  187. const options = {
  188. filterType: "select",
  189. filter: [tagid]
  190. }
  191. this.filterChange(options, "tags")
  192. }
  193. },
  194. onReady() {
  195. this.loadTags()
  196. if (!this.queryTagid) {
  197. this.$refs.udb.loadData()
  198. }
  199. },
  200. computed: {
  201. tagsData() {
  202. const dynamic_data = []
  203. for (const key in this.tags) {
  204. const tag = {
  205. value: key,
  206. text: this.tags[key]
  207. }
  208. if (key === this.queryTagid) {
  209. tag.checked = true
  210. }
  211. dynamic_data.push(tag)
  212. }
  213. return dynamic_data
  214. }
  215. },
  216. methods: {
  217. onqueryload(data) {
  218. for (var i = 0; i < data.length; i++) {
  219. let item = data[i]
  220. const roleArr = item.role.map(item => item.role_name)
  221. item.role = roleArr.join('、')
  222. const tagsArr = item.tags && item.tags.map(item => this.tags[item])
  223. item.tags = tagsArr
  224. if (Array.isArray(item.dcloud_appid)) {
  225. item.dcloud_appid = item.dcloud_appid.join('、')
  226. }
  227. item.last_login_date = this.$formatDate(item.last_login_date)
  228. }
  229. this.exportExcelData = data
  230. },
  231. changeSize(pageSize) {
  232. this.options.pageSize = pageSize
  233. this.options.pageCurrent = 1
  234. this.$nextTick(() => {
  235. this.loadData()
  236. })
  237. },
  238. openTagsPopup() {
  239. this.$refs.tagsPopup.open()
  240. },
  241. closeTagsPopup() {
  242. this.$refs.tagsPopup.close()
  243. },
  244. getWhere() {
  245. const query = this.query.trim()
  246. if (!query) {
  247. return ''
  248. }
  249. const queryRe = new RegExp(query, 'i')
  250. return dbSearchFields.map(name => queryRe + '.test(' + name + ')').join(' || ')
  251. },
  252. search() {
  253. const newWhere = this.getWhere()
  254. this.where = newWhere
  255. // 下一帧拿到查询条件
  256. this.$nextTick(() => {
  257. this.loadData()
  258. })
  259. },
  260. loadData(clear = true) {
  261. this.$refs.udb.loadData({
  262. clear
  263. })
  264. },
  265. onPageChanged(e) {
  266. this.selectedIndexs.length = 0
  267. this.$refs.table.clearSelection()
  268. this.$refs.udb.loadData({
  269. current: e.current
  270. })
  271. },
  272. navigateTo(url, clear) {
  273. // clear 表示刷新列表时是否清除页码,true 表示刷新并回到列表第 1 页,默认为 true
  274. uni.navigateTo({
  275. url,
  276. events: {
  277. refreshData: () => {
  278. this.loadTags()
  279. this.loadData(clear)
  280. }
  281. }
  282. })
  283. },
  284. // 多选处理
  285. selectedItems() {
  286. var dataList = this.$refs.udb.dataList
  287. return this.selectedIndexs.map(i => dataList[i]._id)
  288. },
  289. // 批量删除
  290. delTable() {
  291. this.$refs.udb.remove(this.selectedItems(), {
  292. success: (res) => {
  293. this.$refs.table.clearSelection()
  294. }
  295. })
  296. },
  297. // 多选
  298. selectionChange(e) {
  299. this.selectedIndexs = e.detail.index
  300. },
  301. confirmDelete(id) {
  302. this.$refs.udb.remove(id, {
  303. success: (res) => {
  304. this.$refs.table.clearSelection()
  305. }
  306. })
  307. },
  308. sortChange(e, name) {
  309. this.orderByFieldName = name;
  310. if (e.order) {
  311. this.orderby = name + ' ' + orderByMapping[e.order]
  312. } else {
  313. this.orderby = ''
  314. }
  315. this.$refs.table.clearSelection()
  316. this.$nextTick(() => {
  317. this.$refs.udb.loadData()
  318. })
  319. },
  320. filterChange(e, name) {
  321. this._filter[name] = {
  322. type: e.filterType,
  323. value: e.filter
  324. }
  325. let newWhere = filterToWhere(this._filter, db.command)
  326. if (Object.keys(newWhere).length) {
  327. this.where = newWhere
  328. } else {
  329. this.where = ''
  330. }
  331. this.$nextTick(() => {
  332. this.$refs.udb.loadData()
  333. })
  334. },
  335. loadTags() {
  336. db.collection('uni-id-tag').limit(500).get().then(res => {
  337. res.result.data.map(item => {
  338. this.tags[item.tagid] = item.name
  339. })
  340. }).catch(err => {
  341. uni.showModal({
  342. title: '提示',
  343. content: err.message,
  344. showCancel: false
  345. })
  346. })
  347. },
  348. managerMultiTag() {
  349. const ids = this.selectedItems()
  350. db.collection('uni-id-users').where({
  351. _id: db.command.in(ids)
  352. }).update({
  353. tags: this.managerTags
  354. }).then(() => {
  355. uni.showToast({
  356. title: '修改标签成功',
  357. duration: 2000
  358. })
  359. this.$refs.table.clearSelection()
  360. this.managerTags = []
  361. this.loadData()
  362. this.closeTagsPopup()
  363. }).catch(err => {
  364. uni.showModal({
  365. content: err.message || '请求服务失败',
  366. showCancel: false
  367. })
  368. }).finally(err => {
  369. uni.hideLoading()
  370. })
  371. }
  372. }
  373. }
  374. </script>
  375. <style lang="scss">
  376. .tags-manager {
  377. &--x {
  378. width: 400px;
  379. padding: 40px 30px;
  380. border-radius: 5px;
  381. background-color: #fff;
  382. }
  383. &--header {
  384. font-size: 22px;
  385. color: #333;
  386. text-align: center;
  387. }
  388. }
  389. .mb {
  390. margin-bottom: 80px;
  391. }
  392. .ml {
  393. margin-left: 30px;
  394. }
  395. </style>