page-rule.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <template>
  2. <view>
  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" placeholder="请输入搜索内容" />
  7. <button class="uni-button" type="default" size="mini" @click="search">搜索</button>
  8. </view>
  9. </view>
  10. <view class="uni-container">
  11. <view class="uni-stat--x flex p-1015">
  12. <view class="uni-stat--app-select" style="width: 500px;">
  13. <uni-data-select ref="appListRef" collection="opendb-app-list" field="appid as value, name as text" orderby="text asc" label="应用选择" v-model="appid" @change="search" />
  14. </view>
  15. </view>
  16. <unicloud-db ref="udb" :collection="collectionList" field="title,path,page_rules,appid" :where="where" page-data="replace" :orderby="orderby" :getcount="true"
  17. :page-size="options.pageSize" :page-current="options.pageCurrent" v-slot:default="{data,pagination,loading,error,options}" :options="options" loadtime="manual"
  18. @load="onqueryload">
  19. <uni-table ref="table" :loading="loading" :emptyText="errorMessage || error.message || '没有更多数据'" border stripe>
  20. <uni-tr>
  21. <uni-th align="center">序号</uni-th>
  22. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'title')">页面标题</uni-th>
  23. <uni-th align="left" filter-type="search" @filter-change="filterChange($event, 'path')">页面URL</uni-th>
  24. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'appid')">appid</uni-th>
  25. <uni-th align="center">操作</uni-th>
  26. </uni-tr>
  27. <uni-tr v-for="(item,index) in data" :key="index">
  28. <uni-td align="center">{{ (pagination.current - 1) * pagination.size + (index+1) }}</uni-td>
  29. <uni-td align="center">{{ item.title }}</uni-td>
  30. <uni-td align="left">{{ item.path }}</uni-td>
  31. <uni-td align="center">{{ item.appid }}</uni-td>
  32. <uni-td align="center">
  33. <view class="uni-group">
  34. <button @click="editRule(item)" class="uni-button" size="mini" type="primary">编辑规则</button>
  35. </view>
  36. </uni-td>
  37. </uni-tr>
  38. </uni-table>
  39. <view class="uni-pagination-box">
  40. <uni-pagination show-icon :page-size="pagination.size" v-model="pagination.current" :total="pagination.count" @change="onPageChanged" />
  41. </view>
  42. </unicloud-db>
  43. </view>
  44. <uni-popup ref="editRulePopup" type="center">
  45. <view class="edit-rule-popup">
  46. <view class="uni-title">页面规则</view>
  47. <view class="edit-rule-popup-tips">
  48. <uni-notice-bar :font-size="12" :text="editRulePopup.tips" />
  49. </view>
  50. <view class="edit-rule-popup-list">
  51. <view class="edit-rule-popup-item" v-for="(item1,index1) in pageInfo.page_rules" :key="index1">
  52. <view class="name">
  53. 规则 {{ pageInfo.page_rules.length - index1 }}
  54. </view>
  55. <view class="tags">
  56. <view class="tags-item tags-item-text" v-for="(item2, index2) in item1" :key="index2">
  57. <text class="text">{{ item2 }}</text> <uni-icons class="pointer" type="closeempty" size="12" color="#42b983" @click="deleteParamItem(index1,index2)"></uni-icons>
  58. </view>
  59. <view class="tags-item tags-item-add">
  60. <input class="tags-item-add-input" type="text" v-model.trim="editRulePopup.addParamInfo.value"
  61. :focus="editRulePopup.addParamInfo.index1 === index1 && editRulePopup.isAddParam" @blur="confirmAddParamItem(index1)"
  62. v-if="editRulePopup.addParamInfo.index1 === index1 && editRulePopup.isAddParam" placeholder="输入参数名" />
  63. <button size="mini" class="tags-item-add-btn" @click="addParamItem(index1)" v-else> + 添加参数 </button>
  64. </view>
  65. </view>
  66. <view class="btn">
  67. <uni-icons type="plus" size="28" class="pointer" color="#606266" @click="addRulesItem" v-if="index1 === 0 && pageInfo.page_rules.length < 5"></uni-icons>
  68. <uni-icons type="minus" size="28" class="pointer" color="#606266" @click="deleteRulesItem(index1)" v-else></uni-icons>
  69. </view>
  70. </view>
  71. </view>
  72. <view class="edit-rule-popup-btn">
  73. <button class="uni-button btn" type="primary" size="default" :loading="editRulePopup.loading" :disabled="editRulePopup.loading" @click="saveRule">保存</button>
  74. <button class="uni-button btn" type="default" size="default" @click="closeEditRulePopup">取消</button>
  75. </view>
  76. </view>
  77. </uni-popup>
  78. </view>
  79. </template>
  80. <script>
  81. import { enumConverter, filterToWhere } from '../../../js_sdk/validator/uni-stat-pages.js';
  82. const db = uniCloud.database()
  83. // 表查询配置
  84. const dbOrderBy = '' // 排序字段
  85. const dbSearchFields = ['title', 'path'] // 模糊搜索字段,支持模糊搜索的字段列表。联表查询格式: 主表字段名.副表字段名,例如用户表关联角色表 role.role_name
  86. // 分页配置
  87. const pageSize = 20
  88. const pageCurrent = 1
  89. const orderByMapping = {
  90. "ascending": "asc",
  91. "descending": "desc"
  92. }
  93. export default {
  94. data() {
  95. return {
  96. collectionList: "uni-stat-pages",
  97. appid: '',
  98. query: '',
  99. where: '',
  100. orderby: dbOrderBy,
  101. orderByFieldName: "",
  102. selectedIndexs: [],
  103. errorMessage: '',
  104. options: {
  105. pageSize,
  106. pageCurrent,
  107. filterData: {},
  108. ...enumConverter
  109. },
  110. imageStyles: {
  111. width: 64,
  112. height: 64
  113. },
  114. exportExcel: {
  115. "filename": "uni-stat-pages.xls",
  116. "type": "xls",
  117. "fields": {
  118. "title": "title",
  119. "path": "path"
  120. }
  121. },
  122. exportExcelData: [],
  123. pageInfo: {
  124. _id: "",
  125. page_rules: []
  126. },
  127. editRulePopup: {
  128. loading: false,
  129. tips: `页面规则说明:
  130. 1. 用于生成内容统计 url 的规则。通过设置页面有效参数,通过带参数的 url 对内容进行标识。例如有一个详情页面的请求有三个参数 page/detail/detail?id=1&type=1&t=1565943419,其中 t 为时间戳或随机数,则 id 和 type 为有效参数,需要在页面规则 page/detail/detail 中添加 id,type 这两个参数。
  131. 2. 每条规则可以添加多个参数,进行匹配时,每条规则单独生效。
  132. 3. 每个页面可以添加多个规则(最多 5 个规则),进行匹配时,后添加的规则优先级较高
  133. 4. 目前的匹配规则只能处理通过 url 显式传递参数,且参数形式为上述示例中的键值对格式。`,
  134. isAddParam: false,
  135. addParamInfo: {
  136. index1: "",
  137. value: ""
  138. },
  139. },
  140. }
  141. },
  142. onLoad() {
  143. this._filter = {}
  144. },
  145. onReady() {
  146. if (this.$refs.appListRef) {
  147. this.appid = this.$refs.appListRef.getCache();
  148. this.search()
  149. }
  150. },
  151. methods: {
  152. onqueryload(data) {
  153. this.exportExcelData = data
  154. },
  155. getWhere() {
  156. const query = this.query.trim()
  157. let queryStr = ''
  158. if (query) {
  159. const queryRe = new RegExp(query, 'i')
  160. queryStr = dbSearchFields.map(name => queryRe + '.test(' + name + ')').join(' || ')
  161. }
  162. if (this.appid) {
  163. if (query) {
  164. queryStr = `appid=='${this.appid}' && (${queryStr})`;
  165. } else {
  166. queryStr = `appid=='${this.appid}'`;
  167. }
  168. }
  169. return queryStr
  170. },
  171. search() {
  172. this.errorMessage = "";
  173. const newWhere = this.getWhere()
  174. this.where = newWhere
  175. this.$nextTick(() => {
  176. this.loadData()
  177. })
  178. },
  179. loadData(clear = true) {
  180. this.$refs.udb.loadData({
  181. clear
  182. })
  183. },
  184. onPageChanged(e) {
  185. this.selectedIndexs.length = 0
  186. this.$refs.table.clearSelection()
  187. this.$refs.udb.loadData({
  188. current: e.current
  189. })
  190. },
  191. navigateTo(url, clear) {
  192. // clear 表示刷新列表时是否清除页码,true 表示刷新并回到列表第 1 页,默认为 true
  193. uni.navigateTo({
  194. url,
  195. events: {
  196. refreshData: () => {
  197. this.loadData(clear)
  198. }
  199. }
  200. })
  201. },
  202. sortChange(e, name) {
  203. this.orderByFieldName = name;
  204. if (e.order) {
  205. this.orderby = name + ' ' + orderByMapping[e.order]
  206. } else {
  207. this.orderby = ''
  208. }
  209. this.$refs.table.clearSelection()
  210. this.$nextTick(() => {
  211. this.$refs.udb.loadData()
  212. })
  213. },
  214. filterChange(e, name) {
  215. this._filter[name] = {
  216. type: e.filterType,
  217. value: e.filter
  218. }
  219. let newWhere = filterToWhere(this._filter, db.command)
  220. if (Object.keys(newWhere).length) {
  221. this.where = newWhere
  222. } else {
  223. this.where = ''
  224. }
  225. this.$nextTick(() => {
  226. this.$refs.udb.loadData()
  227. })
  228. },
  229. // 编辑规则
  230. editRule(item) {
  231. // 显示弹窗
  232. this.$refs.editRulePopup.open();
  233. // 如果没有规则,则初始化一个空规则
  234. if (!item.page_rules || !item.page_rules.length) {
  235. item.page_rules = [
  236. []
  237. ];
  238. }
  239. this.pageInfo = {
  240. _id: item._id,
  241. page_rules: JSON.parse(JSON.stringify(item.page_rules)), // 深拷贝,解除引用关系
  242. };
  243. },
  244. // 添加一个规则
  245. addRulesItem() {
  246. this.pageInfo.page_rules.unshift([]);
  247. },
  248. // 删除一个规则
  249. deleteRulesItem(index1) {
  250. this.pageInfo.page_rules.splice(index1, 1);
  251. },
  252. // 添加一个参数
  253. addParamItem(index1) {
  254. this.editRulePopup.isAddParam = true;
  255. this.editRulePopup.addParamInfo.value = "";
  256. this.editRulePopup.addParamInfo.index1 = index1;
  257. },
  258. // 确认添加参数
  259. confirmAddParamItem(index1) {
  260. if (this.editRulePopup.addParamInfo.value) {
  261. this.pageInfo.page_rules[index1].push(this.editRulePopup.addParamInfo.value);
  262. }
  263. this.editRulePopup.isAddParam = false;
  264. },
  265. // 删除某一个参数
  266. deleteParamItem(index1, index2) {
  267. this.pageInfo.page_rules[index1].splice(index2, 1);
  268. },
  269. // 关闭编辑规则弹窗
  270. closeEditRulePopup() {
  271. this.$refs.editRulePopup.close();
  272. },
  273. // 保存规则
  274. saveRule() {
  275. this.editRulePopup.loading = true;
  276. // 过滤下空的规则
  277. let page_rules = JSON.parse(JSON.stringify(this.pageInfo.page_rules));
  278. page_rules = page_rules.filter(item => item.length > 0);
  279. // 保存到数据库
  280. this.$refs.udb.update(this.pageInfo._id, {
  281. page_rules
  282. }, {
  283. showToast: false,
  284. needLoading: false,
  285. success: () => {
  286. // 修改本地列表中的数据
  287. this.$refs.udb.dataList.forEach(item => {
  288. if (item._id === this.pageInfo._id) {
  289. item.page_rules = page_rules;
  290. }
  291. });
  292. // 关闭弹窗
  293. this.closeEditRulePopup();
  294. },
  295. complete: () => {
  296. this.editRulePopup.loading = false;
  297. }
  298. });
  299. }
  300. }
  301. }
  302. </script>
  303. <style lang="scss" scoped>
  304. .edit-rule-popup {
  305. padding: 20px;
  306. background-color: #fff;
  307. width: 900px;
  308. max-width: 90vw;
  309. .edit-rule-popup-tips {
  310. margin-top: 20px;
  311. }
  312. .edit-rule-popup-list {
  313. margin-top: 20px;
  314. .edit-rule-popup-item {
  315. display: flex;
  316. align-items: center;
  317. justify-content: space-between;
  318. margin-top: 20px;
  319. color: #606266;
  320. font-size: 14px;
  321. word-break: break-all;
  322. &:first-child {
  323. margin-top: 0;
  324. }
  325. .name {
  326. width: 80px;
  327. }
  328. .tags {
  329. flex: 1;
  330. border: 1px solid #eee;
  331. padding: 5px 10px;
  332. display: flex;
  333. flex-wrap: wrap;
  334. .tags-item {
  335. display: inline-block;
  336. height: 32px;
  337. line-height: 30px;
  338. font-size: 12px;
  339. box-sizing: border-box;
  340. white-space: nowrap;
  341. margin: 5px;
  342. .text {
  343. margin-right: 8px;
  344. }
  345. &.tags-item-text {
  346. background-color: #ecf8f3;
  347. border-color: #d9f1e6;
  348. padding: 0 10px;
  349. color: #42b983;
  350. border-width: 1px;
  351. border-style: solid;
  352. border-radius: 4px;
  353. }
  354. &.tags-item-add {
  355. .tags-item-add-input {
  356. width: 100px;
  357. height: 32px;
  358. line-height: 32px;
  359. padding: 0 10px;
  360. box-sizing: border-box;
  361. border-radius: 4px;
  362. border: 1px solid #dcdfe6;
  363. font-size: 12px;
  364. color: #606266;
  365. outline: none;
  366. &:focus {
  367. border-color: #409eff;
  368. }
  369. }
  370. .tags-item-add-btn {
  371. height: 32px;
  372. line-height: 32px;
  373. }
  374. }
  375. }
  376. }
  377. .btn {
  378. width: 80px;
  379. text-align: center;
  380. }
  381. }
  382. }
  383. .edit-rule-popup-btn {
  384. /* 按钮显示在右边 */
  385. display: flex;
  386. justify-content: flex-end;
  387. margin-top: 20px;
  388. .btn {
  389. margin-left: 10px;
  390. }
  391. }
  392. .pointer {
  393. cursor: pointer;
  394. }
  395. }
  396. </style>