keyset-detail.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342
  1. /*
  2. * 时间格式化函数(将时间格式化为,2019年08月12日,2019-08-12,2019/08/12的形式)
  3. *
  4. *
  5. * pattern参数(想要什么格式的数据就传入什么格式的数据)
  6. * · 'yyyy-MM-dd' ---> 输出如2019-09-20
  7. * · 'yyyy-MM-dd hh:mm' ---> 输出如2019-09-20 08:20
  8. * · 'yyyy-MM-dd hh:mm:ss' ---> 输出如2019-09-20 08:20:23
  9. * · 'yyyy/MM/dd' ---> 输出如2019/09/20
  10. * · 'yyyy年MM月dd日' ---> 输出如2019年09月20日
  11. * · 'yyyy年MM月dd日 hh时mm分' ---> 输出如2019年09月20日 08时20分
  12. * · 'yyyy年MM月dd日 hh时mm分ss秒' ---> 输出如2019年09月20日 08时20分23秒
  13. * · 'yyyy年MM月dd日 hh时mm分ss秒 EE' ---> 输出如2019年09月20日 08时20分23秒 周二
  14. * · 'yyyy年MM月dd日 hh时mm分ss秒 EEE' ---> 输出如2019年09月20日 08时20分23秒 星期二
  15. *
  16. * 参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
  17. */
  18. Date.prototype.pattern = function (fmt) {
  19. var o = {
  20. 'y+': this.getFullYear(),
  21. 'M+': this.getMonth() + 1, //月份
  22. 'd+': this.getDate(), //日
  23. 'h+': this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
  24. 'H+': this.getHours(), //小时
  25. 'm+': this.getMinutes(), //分
  26. 's+': this.getSeconds(), //秒
  27. 'q+': Math.floor((this.getMonth() + 3) / 3), //季度
  28. 'S': this.getMilliseconds(), //毫秒
  29. 'E+': this.getDay(), // 周
  30. };
  31. var week = {
  32. '0': '日',
  33. '1': '一',
  34. '2': '二',
  35. '3': '三',
  36. '4': '四',
  37. '5': '五',
  38. '6': '六'
  39. };
  40. if (/(y+)/.test(fmt)) {
  41. fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
  42. }
  43. if (/(E+)/.test(fmt)) {
  44. fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[
  45. this.getDay() + '']);
  46. }
  47. for (var k in o) {
  48. if (new RegExp('(' + k + ')').test(fmt)) {
  49. fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k])
  50. .length)));
  51. }
  52. }
  53. return fmt;
  54. }
  55. var pTools = {
  56. // 解决ios返回不刷新页面的问题
  57. iosBackRefresh: function () {
  58. var isPageHide = false;
  59. window.addEventListener('pageshow', function () {
  60. if (isPageHide) {
  61. location.reload();
  62. }
  63. });
  64. window.addEventListener('pagehide', function () {
  65. isPageHide = true;
  66. });
  67. },
  68. // 传入你要获取的参数的名字
  69. getParam: function (name) {
  70. var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
  71. var r = window.location.search.substr(1).match(reg); //获取url中'?'符后的字符串并正则匹配
  72. var context = '';
  73. if (r != null) context = r[2];
  74. // 释放变量
  75. reg = null;
  76. r = null;
  77. return context == null || context == '' || context == 'undefined' ? '' : context;
  78. },
  79. // 数组去重
  80. unique: function (arr) {
  81. if (!Array.isArray(arr)) {
  82. console.log('type error!')
  83. return
  84. }
  85. var array = [];
  86. for (var i = 0; i < arr.length; i++) {
  87. if (array.indexOf(arr[i]) === -1) {
  88. array.push(arr[i])
  89. }
  90. }
  91. return array;
  92. },
  93. // 数组对象根据某一个值进行冒泡排序
  94. // arr 数组
  95. // value 字符串
  96. bSort: function (arr, value) {
  97. var len = arr.length;
  98. for (var i = 0; i < len - 1; i++) {
  99. for (var j = 0; j < len - 1 - i; j++) {
  100. // 相邻元素两两对比,元素交换,大的元素交换到后面
  101. if (arr[j][value] * 10000000 > arr[j + 1][value] * 10000000) {
  102. var temp = arr[j];
  103. arr[j] = arr[j + 1];
  104. arr[j + 1] = temp;
  105. }
  106. }
  107. }
  108. return arr;
  109. },
  110. // 通过userAgent获取用户手机操作系统类型
  111. androidOrIOS: function () {
  112. var u = navigator.userAgent.toLowerCase();
  113. var app = navigator.appVersion;
  114. var agent = null;
  115. if (/iphone|ipod|ipad|ios/.test(u)) {
  116. agent = 'ios'
  117. } else {
  118. agent = 'android'
  119. }
  120. return agent
  121. },
  122. // 获取随机字符串
  123. // 不传参数则获取长度不固定的字符串
  124. getRandomString: function (len) {
  125. var randomString = '';
  126. if (len) {
  127. /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
  128. var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  129. var maxPos = $chars.length;
  130. for (i = 0; i < len; i++) {
  131. randomString += $chars.charAt(Math.floor(Math.random() * maxPos));
  132. }
  133. } else {
  134. // Math.random() 生成随机数字, eg: 0.123456
  135. // .toString(36) 转化成36进制 : "0.4fzyo82mvyr"
  136. // .substring(2) 去掉前面两位 : "yo82mvyr"
  137. // .slice(-8) 截取最后八位 : "yo82mvyr"
  138. randomString = Math.random().toString(36).substring(2)
  139. }
  140. return randomString;
  141. },
  142. // 数组去空格
  143. arrayRemoveSpace: function (arr) {
  144. if (!arr || !$.isArray(arr)) return []
  145. var index = arr.indexOf('')
  146. while (index !== -1) {
  147. arr.splice(index, 1)
  148. index = arr.indexOf('')
  149. }
  150. return arr
  151. },
  152. // 全局loading框
  153. jLoading: function (options) {
  154. // 设置options参数的默认值
  155. options.content = options.content || 'loading...'
  156. options.bgc = options.bgc || 'transparent'
  157. options.icon = options.icon || 'j-loading-icon'
  158. options.duration = options.duration || 0
  159. // 将其拼接成width: 5rem;的形式
  160. options.width = options.width ? 'width:' + options.width + ';' : ''
  161. // 结构
  162. // <div class="j-loading">
  163. // <div class="j-mask"></div>
  164. // <div class="j-toast">
  165. // <i class="j-toast_icon j-loading-icon"></i>
  166. // <p class="j-toast_content">loading</p>
  167. // </div>
  168. // </div>
  169. var that = this
  170. var bgcClassMap = {
  171. transparent: '',
  172. black: 'black'
  173. }
  174. var icon = options.iconHide ? '' : 'icon';
  175. var html = '<div class="j-loading"><div class="j-mask ' + bgcClassMap[options.bgc] + '"></div><div class="j-toast ' + icon + '" style="' + options.width + '"><i class="j-toast_icon ' + options.icon + '"></i><p class="j-toast_content">' + options.content + '</p></div></div>'
  176. var _html = $(html)
  177. $('body').append(_html)
  178. _html.fadeIn(this.loadingTransition)
  179. var _loading = {
  180. _html: _html,
  181. hide: function (callback) {
  182. this._html.fadeOut(that.loadingTransition, function () {
  183. this.remove()
  184. callback && callback()
  185. })
  186. }
  187. }
  188. if (options.duration > 0) {
  189. setTimeout(function () {
  190. _loading.hide(options.callback)
  191. }, options.duration)
  192. }
  193. return _loading
  194. },
  195. // FROM: https://www.jianshu.com/p/90ed8b728975
  196. // 比较两个对象是否相等
  197. // 返回true为相等,返回false为不相等
  198. deepCompare: function (x, y) {
  199. var i, l, leftChain, rightChain;
  200. function compare2Objects(x, y) {
  201. var p;
  202. // remember that NaN === NaN returns false
  203. // and isNaN(undefined) returns true
  204. if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
  205. return true;
  206. }
  207. // Compare primitives and functions.
  208. // Check if both arguments link to the same object.
  209. // Especially useful on the step where we compare prototypes
  210. if (x === y) {
  211. return true;
  212. }
  213. // Works in case when functions are created in constructor.
  214. // Comparing dates is a common scenario. Another built-ins?
  215. // We can even handle functions passed across iframes
  216. if ((typeof x === 'function' && typeof y === 'function') ||
  217. (x instanceof Date && y instanceof Date) ||
  218. (x instanceof RegExp && y instanceof RegExp) ||
  219. (x instanceof String && y instanceof String) ||
  220. (x instanceof Number && y instanceof Number)) {
  221. return x.toString() === y.toString();
  222. }
  223. // At last checking prototypes as good as we can
  224. if (!(x instanceof Object && y instanceof Object)) {
  225. return false;
  226. }
  227. if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
  228. return false;
  229. }
  230. if (x.constructor !== y.constructor) {
  231. return false;
  232. }
  233. if (x.prototype !== y.prototype) {
  234. return false;
  235. }
  236. // Check for infinitive linking loops
  237. if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
  238. return false;
  239. }
  240. // Quick checking of one object being a subset of another.
  241. // todo: cache the structure of arguments[0] for performance
  242. for (p in y) {
  243. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  244. return false;
  245. } else if (typeof y[p] !== typeof x[p]) {
  246. return false;
  247. }
  248. }
  249. for (p in x) {
  250. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  251. return false;
  252. } else if (typeof y[p] !== typeof x[p]) {
  253. return false;
  254. }
  255. switch (typeof (x[p])) {
  256. case 'object':
  257. case 'function':
  258. leftChain.push(x);
  259. rightChain.push(y);
  260. if (!compare2Objects(x[p], y[p])) {
  261. return false;
  262. }
  263. leftChain.pop();
  264. rightChain.pop();
  265. break;
  266. default:
  267. if (x[p] !== y[p]) {
  268. return false;
  269. }
  270. break;
  271. }
  272. }
  273. return true;
  274. }
  275. if (arguments.length < 1) {
  276. return true; //Die silently? Don't know how to handle such case, please help...
  277. // throw "Need two or more arguments to compare";
  278. }
  279. for (i = 1, l = arguments.length; i < l; i++) {
  280. leftChain = []; //Todo: this can be cached
  281. rightChain = [];
  282. if (!compare2Objects(arguments[0], arguments[i])) {
  283. return false;
  284. }
  285. }
  286. return true;
  287. }
  288. }
  289. // 页面逻辑部分
  290. var keySetDetail = new Vue({
  291. el: '.j-container',
  292. data: {
  293. // 限制数组最大长度
  294. conf: {
  295. maxKeyLength: 10,
  296. recommendTagsCount: 6
  297. },
  298. // 是编辑(查看详情)还是添加
  299. modeType: 'add',
  300. // 查询keyList请求返回的数据
  301. keysetReq: {},
  302. // 关键词数组
  303. keyList: [],
  304. // 是否显示选择区域picker
  305. showAreaPicker: false,
  306. // 是否显示选择类型picker
  307. showInfoTypePicker: false,
  308. // 新增时的关键词推荐(关键词输入框下方的,只显示一行,超出不显示)
  309. recommendTags: [],
  310. // 关键词刷新状态保存(类似前端分页)
  311. recListState: {
  312. loading: false, // 是否刷新中?
  313. loaded: false, // 请求是否完成
  314. pageNum: 1,
  315. pageSize: 6,
  316. total: 0, // 一共多少条数据
  317. list: [],
  318. listAll: [] // 后端返回的全部推荐关键词
  319. },
  320. // 当前关键词索引(如果为add状态,索引会加一)
  321. currentIndex: 0,
  322. // 有此项时。说明不是从列表页进入的
  323. fromPage: '',
  324. // 当前关键词展示详情
  325. currentInfo: {
  326. area: [],
  327. infotype: [],
  328. key: '',
  329. notkey: []
  330. },
  331. // 当前关键词备份,用来判断是否改变了
  332. currentInfoBackUp: {
  333. area: [],
  334. infotype: [],
  335. key: '',
  336. notkey: []
  337. },
  338. // 返回提示标志,为true则返回时不弹出提示
  339. iDoNotNeedConfirmed: true,
  340. // 输入节流定时器id
  341. timer: 0,
  342. // 省份原始数据
  343. provinceListMap: {
  344. '#': ['全国'],
  345. A: ['安徽', '澳门'],
  346. B: ['北京'],
  347. C: ['重庆'],
  348. F: ['福建'],
  349. G: ['广东', '广西', '贵州', '甘肃'],
  350. H: ['河北', '湖北', '黑龙江', '海南', '河南', '湖南'],
  351. J: ['吉林', '江苏', '江西'],
  352. L: ['辽宁'],
  353. N: ['内蒙古', '宁夏'],
  354. Q: ['青海'],
  355. S: ['山西', '陕西', '上海', '山东', '四川'],
  356. T: ['天津', '台湾'],
  357. X: ['西藏', '新疆', '香港'],
  358. Y: ['云南'],
  359. Z: ['浙江']
  360. },
  361. // indexBar数据
  362. indexList: [],
  363. indexListMap: {},
  364. // 信息类型数据
  365. infoTypeList: [
  366. {
  367. title: '拟建项目',
  368. value: '拟建',
  369. desc: '采集建筑工程、信息化等类项目在招标前由发改委等部门审批的信息,并向用户提供“拟建项目预告”功能。'
  370. },
  371. {
  372. title: '招标预告',
  373. value: '预告',
  374. desc: '在正式招标之前发布的公告信息,主要有采购计划、项目预告、采购预告、招标文件预公示、招标方式公示等信息'
  375. },
  376. {
  377. title: '招标公告',
  378. value: '招标',
  379. desc: '包括公开招标、邀请招标、询价采购、竞争性谈判、单一来源、公开竞价、电子反拍、变更公告等公告信息'
  380. },
  381. {
  382. title: '招标结果',
  383. value: '结果',
  384. desc: '包括中标公示、成交公告、废标公告、流标公告等'
  385. },
  386. {
  387. title: '其他信息',
  388. value: '其它',
  389. desc: '包括合同公告、验收公告、违规处理等'
  390. },
  391. ],
  392. infoTypeMapArr: [],
  393. noSpaceDialog: {
  394. // 触发提示的次数
  395. count: 0
  396. }
  397. },
  398. watch: {
  399. // 禁止输入空格
  400. 'currentInfo.key': function (newVal, oldVal) {
  401. var spaceReg = /\s+/g
  402. var hasSpace = spaceReg.test(newVal)
  403. if (hasSpace) {
  404. this.noSpaceDialog.count++
  405. if (this.noSpaceDialog.count <= 1) {
  406. this.showSpaceDialog()
  407. }
  408. this.currentInfo.key = this.currentInfo.key.replace(spaceReg, '')
  409. }
  410. }
  411. },
  412. computed: {
  413. typeConf: function () {
  414. var conf = {
  415. add: {
  416. bottomButtonGroupClass: 'single',
  417. },
  418. edit: {
  419. bottomButtonGroupClass: '',
  420. }
  421. }
  422. if (this.modeType) {
  423. return conf[this.modeType]
  424. } else {
  425. return conf.add
  426. }
  427. },
  428. confirmButtonDisabled: function () {
  429. var s = this.currentInfo.key.replace(/\s+/g, '')
  430. if (s) {
  431. return false
  432. } else {
  433. return true
  434. }
  435. }
  436. },
  437. created: function () {
  438. sessionStorage.removeItem('historypushDataCache')
  439. var t = pTools.getParam('type')
  440. var i = pTools.getParam('index')
  441. var f = pTools.getParam('from')
  442. if (t) {
  443. this.modeType = t
  444. }
  445. if (f) {
  446. this.fromPage = f
  447. }
  448. // 如果为新增(add),传过来的index值将不会被使用
  449. if (parseInt(i) >= 0) {
  450. var maxIndex = this.conf.maxKeyLength - 1
  451. var index = parseInt(i)
  452. this.currentIndex = index <= maxIndex ? index : maxIndex
  453. }
  454. // 清除订阅页面缓存
  455. if (this.currentIndex == '0' && this.modeType == 'add') {
  456. this.clearHistoryPushDataCache()
  457. }
  458. var recover = this.recoverPageStateFromCache()
  459. if (recover) {
  460. this.checkUpdate()
  461. } else {
  462. this.getKeyList()
  463. // 初始化页面必要数据
  464. this.initIndexBar()
  465. this.initInfoType()
  466. }
  467. },
  468. mounted: function () {
  469. var _this = this
  470. pTools.iosBackRefresh()
  471. this.popStateEvent()
  472. this.bindEvents()
  473. // 解决ios从上个页面返回触发popstate的问题
  474. setTimeout(function () {
  475. _this.iDoNotNeedConfirmed = false
  476. }, 100)
  477. },
  478. methods: {
  479. showDialog: function (conf) {
  480. var defaultConf = {
  481. title: '提示',
  482. message: 'message',
  483. className: 'j-confirm-dialog',
  484. showConfirmButton: true,
  485. showCancelButton: true,
  486. confirmButtonColor: '#2abed1'
  487. }
  488. if (conf) {
  489. Object.assign(defaultConf, conf)
  490. }
  491. return this.$dialog.confirm(defaultConf)
  492. },
  493. showSpaceDialog: function () {
  494. this.showDialog({
  495. title: '',
  496. message: '免费订阅关键词不可输入空格键,如需添加多个关键词,请前往购买超级订阅',
  497. confirmButtonText: '立即购买'
  498. }).then(function () {
  499. this.toVipPage()
  500. }.bind(this))
  501. .catch(function () {})
  502. },
  503. bindEvents: function () {
  504. var _this = this
  505. // 点击链接离开时,保存数据后在跳转
  506. $('body').on('click', '.ext-link', function (e) {
  507. e.preventDefault()
  508. _this.cachePageState()
  509. $(window).off('popstate')
  510. location.href = e.target.href
  511. })
  512. },
  513. // 获取关键词数据
  514. getKeyList: function () {
  515. var _this = this
  516. $.ajax({
  517. url: '/wxkeyset/ajaxReq?t=' + new Date().getTime(),
  518. type: 'POST',
  519. data: { reqType: 'getKeyset' },
  520. success: function (r) {
  521. if (!r) {
  522. return pTools.jLoading({
  523. iconHide: true,
  524. content: '请求失败',
  525. duration: 1500
  526. })
  527. }
  528. _this.keysetReq = r
  529. // 如果不是数组,则直接返回
  530. if (!(r.a_key instanceof Array)) {
  531. return
  532. }
  533. _this.keyList = r.a_key
  534. // 对数据进行处理(去除空字符串)
  535. if (r.a_key && $.isArray(r.a_key)) {
  536. r.a_key.forEach(function (item) {
  537. for (var key in item) {
  538. item[key] = pTools.arrayRemoveSpace(item[key])
  539. }
  540. })
  541. }
  542. if (_this.modeType === 'add') {
  543. _this.getKeyRecommend()
  544. }
  545. // 如果在其他地方添加关键词到10个,则刷新此页面会变为编辑状态
  546. if (_this.keyList.length >= _this.conf.maxKeyLength && _this.modeType === 'add') {
  547. _this.modeType = 'edit'
  548. _this.currentIndex = _this.keyList.length - 1
  549. }
  550. // 如果是添加,或者取到的值为空,则不赋值
  551. if (_this.modeType === 'add' || !_this.keyList[_this.currentIndex]) return
  552. // 给currentInfo赋值
  553. for (var key in _this.keyList[_this.currentIndex]) {
  554. if (key === 'key' && _this.keyList[_this.currentIndex][key] instanceof Array) {
  555. _this.currentInfo[key] = _this.keyList[_this.currentIndex][key].join(' ')
  556. } else {
  557. _this.currentInfo[key] = _this.keyList[_this.currentIndex][key]
  558. }
  559. }
  560. // 备份关键词
  561. _this.currentInfoBackUp = JSON.parse(JSON.stringify(_this.currentInfo))
  562. // 整理显示数据到picker
  563. _this.recoverAreaDataToPicker()
  564. _this.recoverInfoTypeDataToPicker()
  565. }
  566. })
  567. },
  568. // 获取关键词推荐
  569. getKeyRecommend: function () {
  570. var keysArr = []
  571. var _this = this
  572. this.keyList.forEach(function (item) {
  573. if (item.key) {
  574. if (item.key instanceof Array) {
  575. keysArr.push(item.key.join('+'))
  576. } else {
  577. keysArr.push(item.key)
  578. }
  579. }
  580. })
  581. if (keysArr.length === 0) return
  582. var fetchData = {
  583. count: 20,
  584. value: pTools.unique(keysArr).join(' ')
  585. }
  586. $.ajax({
  587. url: '/member/getRecomKWs',
  588. type: 'POST',
  589. data: fetchData,
  590. success: function (r) {
  591. if (r && r instanceof Array && r.length !== 0) {
  592. _this.loadKeyRecommend(r)
  593. }
  594. }
  595. })
  596. },
  597. loadKeyRecommend: function (list) {
  598. var _this = this
  599. var afterFilterArr = this.filterKeyRecommend(list)
  600. this.recommendTags = afterFilterArr.slice(0, this.conf.recommendTagsCount)
  601. this.recListState.listAll = afterFilterArr
  602. this.recListState.count = afterFilterArr.length
  603. this.nextPageRec()
  604. // 超出隐藏判断
  605. this.$nextTick(function () {
  606. var recommendTagsDOM = _this.$refs.recommendTags
  607. var boxWidth = $(recommendTagsDOM).width()
  608. var tagsDOMArr = $(recommendTagsDOM).children('.tag')
  609. var showIndex = 1
  610. // 倒着遍历
  611. for (var i = tagsDOMArr.length - 1; i >= 0; i--) {
  612. var cDOM = $(tagsDOMArr[i])
  613. var posL = cDOM.position().left
  614. // 最后一个能完全显示的元素
  615. if ((posL + cDOM.innerWidth()) < boxWidth) {
  616. showIndex = i
  617. break
  618. }
  619. }
  620. this.recommendTags.splice(showIndex + 1)
  621. })
  622. },
  623. // 过滤后端返回的推荐数组
  624. filterKeyRecommend: function (list) {
  625. var arr = pTools.bSort(list, 'sim')
  626. // 排序后
  627. var afterSort = arr.reverse()
  628. var afterTile = []
  629. // 已订阅关键词数组
  630. var allKeyArr = []
  631. var allKeyArrLower = []
  632. // 已订阅关键词的整理 -----
  633. // 将所有关键词整理到一个数组中
  634. this.keyList.forEach(function (item) {
  635. if (item.key instanceof Array) {
  636. allKeyArr = allKeyArr.concat(item.key)
  637. } else {
  638. allKeyArr.push(item.key)
  639. }
  640. })
  641. // 数组中英文转小写
  642. allKeyArr.forEach(function (item) {
  643. allKeyArrLower.push(item.toLowerCase())
  644. })
  645. // 推荐数组整理
  646. // 平铺,将数组中的对象去掉,使用字符串平铺内容
  647. afterSort.forEach(function (item) {
  648. afterTile.push(item.word.toLowerCase())
  649. })
  650. // 去重1,当前数组中的内容去重,不精确大小写
  651. var afterTileLength = afterTile.length
  652. for (var i = afterTileLength - 1; i >= 0; i--) {
  653. var aIndex = afterTile.indexOf(afterTile[i].toLowerCase())
  654. if (aIndex !== i) {
  655. afterTile.splice(i, 1)
  656. }
  657. }
  658. // 去重2,找到已经订阅过的,进行删除
  659. afterTileLength = afterTile.length
  660. for (var j = afterTileLength - 1; j >= 0; j--) {
  661. var aIndex = allKeyArrLower.indexOf(afterTile[j])
  662. if (aIndex !== -1) {
  663. afterTile.splice(j, 1)
  664. }
  665. }
  666. return afterTile
  667. },
  668. getRecListTags: function () {
  669. var listAll = this.recListState.listAll
  670. this.recListState.total = listAll.length
  671. var startIndex = ((this.recListState.pageNum - 1) * this.recListState.pageSize)
  672. var endIndex = (this.recListState.pageNum * this.recListState.pageSize)
  673. return listAll.slice(startIndex, endIndex)
  674. },
  675. nextPageRec: function () {
  676. if (this.recListState.loading) return
  677. this.recListState.loading = true
  678. this.recListState.list = this.getRecListTags()
  679. setTimeout(function () {
  680. this.recListState.loading = false
  681. }.bind(this), 500)
  682. // 最后一页,则重置页码
  683. if (this.recListState.pageNum * this.recListState.pageSize >= this.recListState.total) {
  684. this.recListState.pageNum = 1
  685. } else {
  686. this.recListState.pageNum++
  687. }
  688. return this.recListState.list
  689. },
  690. clickTag: function (item) {
  691. this.currentInfo.key = item
  692. this.checkUpdate()
  693. },
  694. onKeyInput: function () {
  695. var _this = this
  696. clearTimeout(this.timer)
  697. this.timer = setTimeout(function () {
  698. _this.saveKeyWordsRegTip()
  699. }, 600)
  700. this.checkUpdate()
  701. },
  702. // 设置关键词详情信息
  703. setKeyDetail: function (type) {
  704. var key = this.currentInfo.key.replace(/\s+/g, '')
  705. if (!key) {
  706. return pTools.jLoading({
  707. iconHide: true,
  708. content: '请先填写关键词',
  709. duration: 1500
  710. })
  711. }
  712. switch (type) {
  713. case 'area': {
  714. this.showAreaPicker = true
  715. break
  716. }
  717. case 'infotype': {
  718. this.showInfoTypePicker = true
  719. break
  720. }
  721. case 'notkey': {
  722. // 解绑popstate,解决ios从排除词页返回后对话框闪一下又消失
  723. $(window).off('popstate')
  724. // 离开页面先缓存一下数据
  725. this.cachePageState()
  726. // 编辑状态下,跳转到其他页面,则需要手动修改url
  727. if (this.modeType === 'add') {
  728. location.href = '/wxkeyset/keyset/notkey?type=add&index=' + this.keyList.length
  729. } else {
  730. location.href = '/wxkeyset/keyset/notkey?type=edit&index=' + this.currentIndex
  731. }
  732. break
  733. }
  734. default: {
  735. console.log('没有定义此类型')
  736. break
  737. }
  738. }
  739. },
  740. // 保存关键词字符验证,是否符合要要求通过
  741. saveKeyWordsRegTip: function () {
  742. // 验证关键词是否符合规范
  743. // 是否有非法字符
  744. var key = this.currentInfo.key
  745. if (key.match(/[^0-9a-zA-Z\u4E00-\u9FFF\s]/g)) {
  746. this.showKeyWordRegErrorToast('unexpectedChar')
  747. return false
  748. }
  749. // 关键词过长
  750. var keys = key.replace(/\s+/g, ' ').split(' ')
  751. for (var k = 0; k < keys.length; k++) {
  752. if (k > 1) {
  753. this.showKeyWordRegErrorToast('tooMuch')
  754. } else if (keys[k].length > 19) {
  755. this.showKeyWordRegErrorToast('tooLong')
  756. }
  757. }
  758. return true
  759. },
  760. // 保存关键词数据请求
  761. saveKeyList: function (data, showToast) {
  762. var _this = this
  763. $.ajax({
  764. url: '/wxkeyset/ajaxReq',
  765. type: 'POST',
  766. dataType: 'json',
  767. traditional: true,
  768. data: data,
  769. success: function (r) {
  770. if (!r) {
  771. return pTools.jLoading({
  772. iconHide: true,
  773. content: '请求失败',
  774. duration: 1500
  775. })
  776. }
  777. if (r.flag) {
  778. if (showToast) {
  779. weui.toast('订阅成功', {
  780. duration: 1000,
  781. className: 'j-toast',
  782. callback: function () {
  783. _this.pushListHistory()
  784. _this.goBack()
  785. }
  786. })
  787. }
  788. } else {
  789. pTools.jLoading({
  790. iconHide: true,
  791. content: '订阅失败',
  792. duration: 1000
  793. })
  794. }
  795. }
  796. })
  797. },
  798. deleteThisKey: function (keyword, index) {
  799. var _this = this
  800. $.ajax({
  801. url: '/wxkeyset/ajaxReq',
  802. type: 'POST',
  803. data: {
  804. reqType: 'delKeysWord',
  805. index: index,
  806. keyword: keyword
  807. },
  808. success: function (r) {
  809. if (!r) {
  810. return pTools.jLoading({
  811. iconHide: true,
  812. content: '请求失败',
  813. duration: 1500
  814. })
  815. }
  816. if (r.flag) {
  817. weui.toast('删除成功', {
  818. duration: 1000,
  819. className: 'j-toast',
  820. callback: function () {
  821. _this.goBack()
  822. }
  823. })
  824. } else {
  825. return pTools.jLoading({
  826. iconHide: true,
  827. content: '删除失败',
  828. duration: 1500
  829. })
  830. }
  831. }
  832. })
  833. },
  834. // 格式化关键词的每一项
  835. sortOutKey: function (info, type) {
  836. var text = ''
  837. info = info[type]
  838. switch (type) {
  839. case 'area': {
  840. if (info) {
  841. if ((info instanceof Array) && info.length === 0) {
  842. text = '全国'
  843. } else {
  844. text = info.join('、')
  845. }
  846. } else {
  847. text = '全国'
  848. }
  849. break
  850. }
  851. case 'infotype': {
  852. if (info) {
  853. if ((info instanceof Array) && info.length === 0) {
  854. text = '全部'
  855. } else {
  856. text = info.join('、')
  857. }
  858. } else {
  859. text = '全部'
  860. }
  861. break
  862. }
  863. case 'notkey': {
  864. if (info) {
  865. if ((info instanceof Array) && info.length === 0) {
  866. text = '添加不希望接收的关键词'
  867. } else {
  868. text = info.join('、')
  869. }
  870. } else {
  871. text = '添加不希望接收的关键词'
  872. }
  873. break
  874. }
  875. default: {
  876. text = ' - '
  877. break
  878. }
  879. }
  880. return text
  881. },
  882. // 删除关键词
  883. delThisKeyTip: function () {
  884. var _this = this
  885. var item = this.currentInfo
  886. var index = this.currentIndex
  887. var keyword = item.key
  888. weui.dialog({
  889. title: '删除关键词',
  890. content: '删除后将无法恢复,确定删除?',
  891. className: 'j-dialog',
  892. isAndroid: false,
  893. buttons: [{
  894. label: '取消',
  895. type: 'default',
  896. onClick: function () { }
  897. }, {
  898. label: '确定',
  899. type: 'warning',
  900. onClick: function () {
  901. _this.deleteThisKey(keyword, index)
  902. }
  903. }]
  904. })
  905. },
  906. toVipPage: function () {
  907. location.href = '/front/vipsubscribe/introducePage?typeinfo=free'
  908. },
  909. // 显示关键词输入提示
  910. showKeyWordRegErrorToast: function (type) {
  911. var conf = {
  912. iconHide: true,
  913. duration: 4000,
  914. width: '4.4rem',
  915. content: ''
  916. }
  917. if (type === 'tooMuch') {
  918. conf.content = $('#easy-alert .too-much').html().replace(/<scr(.*)<\/scr>/, '')
  919. } else if (type === 'tooLong') {
  920. conf.content = $('#easy-alert .too-long').html().replace(/<scr(.*)<\/scr>/, '')
  921. } else if (type === 'unexpectedChar') {
  922. conf.content = $('#easy-alert .unexpected-char').html().replace(/<scr(.*)<\/scr>/, '')
  923. }
  924. pTools.jLoading(conf)
  925. },
  926. // indexedBar逻辑(城市选择逻辑)
  927. // indexBar数据初始化函数
  928. initIndexBar: function () {
  929. // 整理数据得到indexListMap(),同时获得indexList
  930. var map = {}
  931. for (var key in this.provinceListMap) {
  932. var areaArr = []
  933. this.indexList.push(key)
  934. this.provinceListMap[key].forEach(function (item) {
  935. areaArr.push({
  936. name: item,
  937. selected: item === '全国'
  938. })
  939. })
  940. map[key] = areaArr
  941. }
  942. this.indexListMap = map
  943. // 给map赋值
  944. // for (var k in map) {
  945. // this.$set(this.indexListMap, k, map[k])
  946. // }
  947. },
  948. // 城市按钮点击事件
  949. indexBarItemClick: function (item) {
  950. // 选全国
  951. if (item.name === '全国') {
  952. this.setAllAreaDisSelected(false)
  953. item.selected = true
  954. } else {
  955. this.indexListMap['#'][0].selected = false
  956. item.selected = !item.selected
  957. }
  958. // 此处判断是否全部选中
  959. var state = this.getAllAreaStateExceptCountryWide()
  960. if (state !== 0) {
  961. // 全国选中
  962. this.setAllAreaDisSelected(false)
  963. this.indexListMap['#'][0].selected = true
  964. }
  965. },
  966. // 获得所有选中的省份名字的数组
  967. getSelectedAreaArr: function () {
  968. var arr = []
  969. for (var key in this.indexListMap) {
  970. this.indexListMap[key].forEach(function (item) {
  971. if (item.name !== '全国' && item.selected) {
  972. arr.push(item.name)
  973. }
  974. })
  975. }
  976. return arr
  977. },
  978. // 所有按钮设置状态
  979. setAllAreaDisSelected: function (state) {
  980. for (var key in this.indexListMap) {
  981. this.indexListMap[key].forEach(function (item) {
  982. item.selected = state
  983. })
  984. }
  985. },
  986. // 除了全国其余所有按钮是否全选或者是否全不选
  987. // 1 全选 -1 全部不选 0 其他
  988. getAllAreaStateExceptCountryWide: function () {
  989. var arr = []
  990. for (var i = 1; i < this.indexList.length; i++) {
  991. this.indexListMap[this.indexList[i]].forEach(function (item) {
  992. // 判断select的和是否为0,为0则全不选
  993. // state += item.selected
  994. arr.push(item.selected)
  995. })
  996. }
  997. var selectedCount = 0
  998. arr.forEach(function (item) {
  999. if (item) selectedCount++
  1000. })
  1001. if (selectedCount === arr.length) {
  1002. // 除了全国以外其他所有都被选中
  1003. return 1
  1004. } else if (selectedCount === 0) {
  1005. return -1
  1006. } else {
  1007. return 0
  1008. }
  1009. },
  1010. resetAreaAll: function () {
  1011. this.setAllAreaDisSelected(false)
  1012. this.indexListMap['#'][0].selected = true
  1013. },
  1014. areaConfirm: function () {
  1015. this.currentInfo.area = this.getSelectedAreaArr()
  1016. this.showAreaPicker = false
  1017. this.checkUpdate()
  1018. },
  1019. recoverAreaDataToPicker: function (areaArr) {
  1020. var _this = this
  1021. if (!areaArr) areaArr = this.currentInfo.area
  1022. if (!(areaArr instanceof Array) || areaArr.length === 0) return
  1023. this.indexListMap['#'][0].selected = false
  1024. areaArr.forEach(function (item) {
  1025. for (var key in _this.indexListMap) {
  1026. _this.indexListMap[key].forEach(function (iitem) {
  1027. if (iitem.name === item) {
  1028. iitem.selected = true
  1029. }
  1030. })
  1031. }
  1032. })
  1033. },
  1034. initInfoType: function () {
  1035. var arr = [
  1036. {
  1037. title: '全部',
  1038. desc: '',
  1039. value: [],
  1040. selected: true
  1041. }
  1042. ]
  1043. this.infoTypeList.forEach(function (item) {
  1044. arr.push({
  1045. title: item.title,
  1046. desc: item.desc,
  1047. value: item.value,
  1048. selected: false
  1049. })
  1050. })
  1051. this.infoTypeMapArr = arr
  1052. },
  1053. infoTypePickerClick: function (item) {
  1054. // 选全部
  1055. if (item.title === '全部') {
  1056. this.setAllInfoTypeDisSelected(false)
  1057. item.selected = true
  1058. } else {
  1059. this.infoTypeMapArr[0].selected = false
  1060. item.selected = !item.selected
  1061. }
  1062. // 此处判断是否全部选中
  1063. // 如果全部被子项选中,则全国选中。如果全部子项不被选中,则全国选中
  1064. // var state = this.getInfoTypeState()
  1065. // if (state !== 0) {
  1066. // // 全国选中
  1067. // this.setAllInfoTypeDisSelected(false)
  1068. // this.infoTypeMapArr[0].selected = true
  1069. // }
  1070. },
  1071. // state: true表示选中全部
  1072. setAllInfoTypeDisSelected: function (state) {
  1073. this.infoTypeMapArr.forEach(function (item) {
  1074. item.selected = state
  1075. })
  1076. },
  1077. // 获取信息类型的选中状态
  1078. // 1 全选 -1 全部不选 0 其他
  1079. getInfoTypeState: function () {
  1080. var tInfoTypeMapArr = JSON.parse(JSON.stringify(this.infoTypeMapArr))
  1081. tInfoTypeMapArr.shift()
  1082. var selectedCountArr = []
  1083. var selectedCount = 0
  1084. tInfoTypeMapArr.forEach(function (item, index) {
  1085. selectedCountArr.push(item.selected)
  1086. })
  1087. selectedCountArr.forEach(function (item) {
  1088. if (item) selectedCount++
  1089. })
  1090. if (selectedCount === tInfoTypeMapArr.length) {
  1091. // 除了全部以外其他所有都被选中
  1092. return 1
  1093. } else if (selectedCount === 0) {
  1094. return -1
  1095. } else {
  1096. return 0
  1097. }
  1098. },
  1099. // 获取你选择的内容
  1100. getInfoTypeData: function () {
  1101. var arr = []
  1102. for (var i = 0; i < this.infoTypeMapArr.length; i++) {
  1103. if (this.infoTypeMapArr[i].selected) {
  1104. if (i === 0) {
  1105. break
  1106. } else {
  1107. arr.push(this.infoTypeMapArr[i].value)
  1108. }
  1109. }
  1110. }
  1111. return arr
  1112. },
  1113. resetInfoType: function () {
  1114. this.setAllInfoTypeDisSelected(false)
  1115. this.infoTypeMapArr[0].selected = true
  1116. },
  1117. recoverInfoTypeDataToPicker: function (infoType) {
  1118. var _this = this
  1119. if (!infoType) infoType = this.currentInfo.infotype
  1120. if (!(infoType instanceof Array) || infoType.length === 0) return
  1121. _this.infoTypeMapArr[0].selected = false
  1122. infoType.forEach(function (item) {
  1123. _this.infoTypeMapArr.forEach(function (iitem) {
  1124. if (item === iitem.value) {
  1125. iitem.selected = true
  1126. }
  1127. })
  1128. })
  1129. },
  1130. infoTypeConfirm: function () {
  1131. this.currentInfo.infotype = this.getInfoTypeData()
  1132. this.showInfoTypePicker = false
  1133. this.checkUpdate()
  1134. },
  1135. cachePageState: function () {
  1136. var $data = JSON.stringify(this.$data)
  1137. sessionStorage.setItem('free-keyset-detail', $data)
  1138. },
  1139. recoverPageStateFromCache: function () {
  1140. var t = sessionStorage.getItem('free-keyset-detail')
  1141. // 以下字段,不做恢复
  1142. var excludeArr = ['iDoNotNeedConfirmed']
  1143. if (!t) return false
  1144. var state = JSON.parse(t)
  1145. for (var key in state) {
  1146. if (excludeArr.indexOf(key) === -1) {
  1147. this.$data[key] = state[key]
  1148. } else {
  1149. // console.log(key)
  1150. }
  1151. }
  1152. return true
  1153. },
  1154. anchorClick: function (e) {
  1155. var index = e.target.dataset.index
  1156. $('.van-index-anchor[data-index=' + index + ']')[0].scrollIntoView()
  1157. $(e.target).addClass('highlight-text').siblings().removeClass('highlight-text')
  1158. },
  1159. onConfirm: function () {
  1160. var t = this.currentInfo
  1161. var data = {
  1162. reqType: 'saveKeyWordsNew',
  1163. index: this.currentIndex,
  1164. area: t.area,
  1165. infotype: t.infotype,
  1166. notkey: t.notkey,
  1167. keyWords: t.key
  1168. }
  1169. this.saveKeyList(data, true)
  1170. },
  1171. // 检查数据是否更新了
  1172. checkUpdate: function () {
  1173. var _this = this
  1174. // 比较两个对象是否相等,不相等说明数据更新了
  1175. var hasUpdate = !pTools.deepCompare(this.currentInfo, this.currentInfoBackUp)
  1176. if (hasUpdate) {
  1177. this.pushHistory()
  1178. } else {
  1179. if (history.state) {
  1180. this.iDoNotNeedConfirmed = true
  1181. history.back()
  1182. setTimeout(function () {
  1183. _this.iDoNotNeedConfirmed = false
  1184. }, 10)
  1185. }
  1186. }
  1187. return hasUpdate
  1188. },
  1189. // 添加一个锚点
  1190. pushHistory: function () {
  1191. var pushContent = {
  1192. id: '1',
  1193. title: '返回确认',
  1194. url: '#change'
  1195. }
  1196. if (!history.state) {
  1197. history.pushState(pushContent, null, pushContent.url);
  1198. }
  1199. },
  1200. pushListHistory: function() {
  1201. // 如果不是直接从订阅页面进入,则直接退出函数
  1202. if (!this.fromPage) return
  1203. var state = history.state
  1204. var pushContent = {
  1205. id: '2',
  1206. title: '订阅关键词',
  1207. url: '/wxkeyset/keyset/index'
  1208. }
  1209. // 先取消此时的#change
  1210. if (state && state.id === '1') {
  1211. $(window).off('popstate')
  1212. history.go(-1)
  1213. }
  1214. // 将当前历史记录替换为列表页
  1215. history.replaceState(pushContent, null, pushContent.url)
  1216. },
  1217. // 正常返回的判断(不会显示提示弹窗)
  1218. goBack: function () {
  1219. $(window).off('popstate')
  1220. var state = history.state
  1221. if (state && state.id === '1') {
  1222. history.go(-2)
  1223. } else if (state && state.id === '2') {
  1224. location.reload()
  1225. } else {
  1226. history.go(-1)
  1227. }
  1228. },
  1229. // 监听返回事件
  1230. popStateEvent: function () {
  1231. var _this = this
  1232. $(window).on('popstate', function (e) {
  1233. // 如果picker打开,则关闭picker
  1234. if (_this.showAreaPicker) {
  1235. _this.showAreaPicker = false
  1236. _this.pushHistory()
  1237. } else if (_this.showInfoTypePicker) {
  1238. _this.showInfoTypePicker = false
  1239. _this.pushHistory()
  1240. } else {
  1241. if (_this.iDoNotNeedConfirmed) return
  1242. // 提示是否保存
  1243. weui.dialog({
  1244. content: '返回将无法保存关键词',
  1245. className: 'j-dialog no-header',
  1246. isAndroid: false,
  1247. buttons: [{
  1248. label: '不保存',
  1249. type: 'default',
  1250. onClick: function () {
  1251. // 返回上一页
  1252. _this.goBack()
  1253. }
  1254. }, {
  1255. label: '继续编辑',
  1256. type: 'primary',
  1257. onClick: function () {
  1258. // 取消弹框
  1259. // 并push一条历史记录
  1260. _this.pushHistory()
  1261. }
  1262. }]
  1263. })
  1264. }
  1265. })
  1266. },
  1267. // 清除订阅页面缓存
  1268. clearHistoryPushDataCache: function () {
  1269. sessionStorage.removeItem('historypushDataCache')
  1270. sessionStorage.removeItem('historypushPageIndexCache')
  1271. sessionStorage.removeItem('historypushScrollTop')
  1272. sessionStorage.removeItem('historypushHasNextPage')
  1273. sessionStorage.removeItem('closeAdvert')
  1274. }
  1275. }
  1276. })