QuickMonitor.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <template>
  2. <van-popover
  3. v-model="showPopover"
  4. v-if="model.canFollow"
  5. trigger="click"
  6. :placement="placement"
  7. :offset="calcOffset"
  8. >
  9. <FollowPopoverContentCard
  10. :used="model.expands?.used"
  11. :surplus="model.expands?.surplus"
  12. @toTimeline="toProjectDetail"
  13. @toMyFollow="toMonitorList"
  14. @applyMore="applyMoreMonitor"
  15. @cancelFollow="cancelMonitor"
  16. />
  17. <template #reference>
  18. <TabActionItem
  19. showText
  20. :direction="direction"
  21. class="action-monitor"
  22. @click.native.stop="changePopoverState"
  23. >
  24. <AppIcon
  25. slot="icon"
  26. :name="model.follow ? 'yijiankong' : 'jiankong'"
  27. size="20"
  28. ></AppIcon>
  29. <template #text>{{ model.follow ? '已监控' : '监控' }}</template>
  30. </TabActionItem>
  31. </template>
  32. </van-popover>
  33. </template>
  34. <script>
  35. import TabActionItem from '@/views/article/ui/TabActionItem.vue'
  36. import FollowPopoverContentCard from '@/views/article/ui/FollowPopoverContentCard.vue'
  37. import { useQuickMonitorModel } from '@/composables/quick-monitor/index'
  38. import { Popover } from 'vant'
  39. import { AppIcon } from '@/ui'
  40. import { reactive, toRefs, toRef } from 'vue'
  41. import { mapGetters } from 'vuex'
  42. import { LINKS } from '@/data'
  43. import { openAppOrWxPage } from '@/utils/'
  44. import { cloneDeep } from 'lodash'
  45. const confMap = {
  46. project: {
  47. key: '项目',
  48. pushActiveKey: 2,
  49. applyMoreSource: 'article_project_more',
  50. limitSource: 'article_project_limit',
  51. toPushSettingDialogMessage:
  52. '您可前往“工作台-商机-项目进度监控”查看项目最新招标/采购进度。',
  53. successDialogMessage: '监控成功,您可前往“工作台-商机-项目进度监控”查看',
  54. monitorListLink: LINKS.项目进度监控列表,
  55. monitorListLinkBusiness: LINKS.商机管理项目进度监控列表
  56. },
  57. ent: {
  58. key: '企业',
  59. pushActiveKey: 4,
  60. applyMoreSource: 'article_ent_more',
  61. limitSource: 'article_ent_limit',
  62. toPushSettingDialogMessage:
  63. '您可前往“工作台-商机-企业情报监控”查看项目最新招标/采购进度。',
  64. successDialogMessage: '监控成功,您可前往“工作台-商机-企业情报监控”查看',
  65. monitorListLink: LINKS.企业情报监控列表
  66. },
  67. client: {
  68. key: '业主',
  69. pushActiveKey: 3,
  70. applyMoreSource: 'article_client_more',
  71. limitSource: 'article_client_limit',
  72. freeSource: 'buyer_monitor_freeuser',
  73. toPushSettingDialogMessage:
  74. '您可前往“工作台-商机-业主监控”查看项目最新招标/采购进度。',
  75. successDialogMessage: '监控成功,您可前往“工作台-商机-业主监控”查看',
  76. monitorListLink: LINKS.业主监控列表
  77. }
  78. }
  79. export default {
  80. name: 'QuickMonitor',
  81. components: {
  82. [Popover.name]: Popover,
  83. FollowPopoverContentCard,
  84. TabActionItem,
  85. AppIcon
  86. },
  87. props: {
  88. id: {
  89. type: String,
  90. default: '',
  91. required: true
  92. },
  93. type: {
  94. type: String,
  95. default: 'project',
  96. validator(t) {
  97. return ['project', 'ent', 'client'].includes(t)
  98. }
  99. },
  100. // 是否使用popover
  101. popover: {
  102. type: Boolean,
  103. default: false
  104. },
  105. cache: {
  106. type: Boolean,
  107. default: false
  108. },
  109. // 默认doFetch请求
  110. auto: {
  111. type: Boolean,
  112. default: false
  113. },
  114. direction: {
  115. type: String,
  116. default: 'column',
  117. validator(d) {
  118. return ['row', 'column'].includes(d)
  119. }
  120. },
  121. placement: {
  122. type: String,
  123. default: 'top'
  124. },
  125. beforeLeavePage: Function, // 跳转前处理
  126. beforeAction: Function
  127. },
  128. setup(props, { emit }) {
  129. const useMonitor = useQuickMonitorModel({
  130. type: props.type,
  131. cache: props.cache,
  132. params: {
  133. id: props.id
  134. }
  135. })
  136. const { model, fid } = toRefs(reactive(useMonitor))
  137. const { follow } = toRef(model)
  138. const { doFetch, doChange } = useMonitor
  139. if (props.auto) {
  140. doFetch().finally(() => {
  141. emit('initd', {
  142. model: cloneDeep(model.value),
  143. id: props.id,
  144. fid: fid?.value
  145. })
  146. })
  147. }
  148. return {
  149. useMonitor,
  150. model,
  151. follow,
  152. fid,
  153. doFetch,
  154. doChange
  155. }
  156. },
  157. data() {
  158. return {
  159. showPopover: false
  160. // model: {
  161. // canFollow: false,
  162. // follow: false,
  163. // expands: {
  164. // surplus: 0,
  165. // used: 0
  166. // }
  167. // }
  168. }
  169. },
  170. computed: {
  171. ...mapGetters('user', [
  172. 'isLogin',
  173. 'isFree',
  174. 'isMember',
  175. 'isBusiness',
  176. 'isNewBusiness',
  177. 'isEntService'
  178. ]),
  179. isMemberOrBusiness() {
  180. return this.isMember || this.isBusiness
  181. },
  182. conf() {
  183. const conf = confMap[this.type] || confMap.project
  184. // 项目监控跳转 - 商机管理跳转单独计算
  185. if (this.type === 'project' && conf.monitorListLinkBusiness) {
  186. if (this.isEntService || this.isNewBusiness || this.isBusiness) {
  187. conf.monitorListLink = conf.monitorListLinkBusiness
  188. }
  189. }
  190. return conf
  191. },
  192. calcOffset() {
  193. if (this.placement === 'top-start') {
  194. return [5, 10]
  195. } else if (this.placement === 'top-end') {
  196. return [-5, 10]
  197. } else {
  198. return undefined
  199. }
  200. }
  201. },
  202. methods: {
  203. async toProjectDetail() {
  204. const params = {
  205. fid: this.fid,
  206. sid: this.id
  207. }
  208. sessionStorage.setItem('bigvip-fid', JSON.stringify(params))
  209. if (this.beforeLeavePage) {
  210. await this.beforeLeavePage()
  211. }
  212. openAppOrWxPage(LINKS.项目详情页)
  213. },
  214. async toMonitorList() {
  215. if (this.beforeLeavePage) {
  216. await this.beforeLeavePage()
  217. }
  218. openAppOrWxPage(this.conf.monitorListLink)
  219. this.showPopoverList(false)
  220. },
  221. async applyMoreMonitor() {
  222. if (this.isMemberOrBusiness) {
  223. // 弹窗提示监控更多项目/企业/业主
  224. this.concatKfDialog({
  225. title: `申请监控更多${this.conf.key}`,
  226. message: `您可联系客服,申请升级产品套餐,监控更多${this.conf.key}`
  227. })
  228. } else {
  229. if (this.beforeLeavePage) {
  230. await this.beforeLeavePage()
  231. }
  232. // 留资页面
  233. this.$leaveInfo.toLeaveInfoPage({
  234. source: `${this.$env.platform}_${this.conf.applyMoreSource}`
  235. })
  236. }
  237. this.showPopoverList(false)
  238. },
  239. async concatKfDialog({ message, title }) {
  240. try {
  241. await this.$dialog.alert({
  242. title: title || '',
  243. messageAlign: 'center',
  244. className: 'j-confirm-dialog',
  245. confirmButtonText: '联系客服',
  246. showCancelButton: true,
  247. cancelButtonText: '我再想想',
  248. message: message
  249. })
  250. // 联系客服
  251. if (this.beforeLeavePage) {
  252. await this.beforeLeavePage()
  253. }
  254. openAppOrWxPage(LINKS.客服)
  255. } catch (error) {
  256. console.log('我再想想', error)
  257. }
  258. },
  259. async cancelMonitorDialog() {
  260. return await this.$dialog.alert({
  261. title: '确定不再监控?',
  262. messageAlign: 'center',
  263. className: 'j-confirm-dialog',
  264. confirmButtonText: '我再想想',
  265. showCancelButton: true,
  266. cancelButtonText: '确定取消',
  267. message: `取消监控,将错过${this.conf.key}最新动态推送`
  268. })
  269. },
  270. async cancelMonitor() {
  271. try {
  272. await this.cancelMonitorDialog()
  273. } catch (error) {
  274. this.doCancelMonitor()
  275. }
  276. },
  277. showPopoverList(f = false) {
  278. this.showPopover = f
  279. },
  280. async changePopoverState() {
  281. if (this.model.follow) {
  282. // 已监控
  283. if (this.popover) {
  284. this.showPopoverList(!this.showPopover)
  285. } else {
  286. // 点击弹窗,二次确认是否取消监控
  287. this.cancelMonitor()
  288. }
  289. } else {
  290. this.doMonitor()
  291. }
  292. },
  293. async successMonitorAndToPushSettingDialog() {
  294. try {
  295. await this.$dialog.alert({
  296. title: '监控成功',
  297. messageAlign: 'center',
  298. className: 'j-confirm-dialog',
  299. confirmButtonText: '去开启',
  300. showCancelButton: true,
  301. cancelButtonText: '暂不开启',
  302. message: `${this.conf.toPushSettingDialogMessage}为保证您能及时获取新增监控信息推送,请前往开启推送提醒。`
  303. })
  304. // 去开启提醒
  305. this.$router.push({
  306. path: '/push/pushsetting',
  307. query: {
  308. active: this.conf.pushActiveKey
  309. }
  310. })
  311. } catch (error) {
  312. console.log('暂不开启推送提醒', error)
  313. }
  314. },
  315. // 监控
  316. /**
  317. * 监控操作业务流程
  318. * 0. 前置权益判断
  319. * 0.1 判断是否非大会员、非商机管理用户
  320. * > 否,留资弹窗
  321. * 1. 监控成功
  322. * 1.1 判断是否开启推送提醒
  323. * > 是,提醒开启推送提醒 success-monitor
  324. * > 否,toast 提醒监控成功 success-toast
  325. * 2. 监控失败
  326. * 2.1 超出可监控项目个数
  327. * 2.1.1 判断是否非大会员、非商机管理用户
  328. * > 是,弹窗提醒 max-monitor
  329. * > 否,留资弹窗
  330. * 2.2 其他错误,toast 提醒
  331. * @return {Promise<void>}
  332. */
  333. async doMonitor() {
  334. if (this.beforeAction) {
  335. const r = await this.beforeAction()
  336. if (!r) {
  337. return
  338. }
  339. }
  340. // 采购单位监控,仅大会员和商机管理可用
  341. if (this.conf.freeSource) {
  342. if (this.isFree) {
  343. if (this.beforeLeavePage) {
  344. await this.beforeLeavePage()
  345. }
  346. return openAppOrWxPage(LINKS.留资, {
  347. query: {
  348. source: `${this.$env.platform}_${this.conf.freeSource}`
  349. }
  350. })
  351. }
  352. }
  353. const loading = this.$toast.loading()
  354. // do something...
  355. try {
  356. const { success, msg, data } = await this.doChange()
  357. loading.clear()
  358. if (success) {
  359. // 判断是否开启推送提醒
  360. if (data?.msg_open) {
  361. // 推送提醒已经开启
  362. this.$toast(this.conf.successDialogMessage)
  363. } else {
  364. // 提醒未开启
  365. this.successMonitorAndToPushSettingDialog()
  366. }
  367. } else {
  368. // 判断是否超出可监控项目个数
  369. if (data?.limit_count) {
  370. if (!this.isFree) {
  371. const count = data?.limit_count || 0
  372. this.concatKfDialog({
  373. title: `监控${this.conf.key}个数已达上限`,
  374. message: `您最多可监控${count}个${this.conf.key},可联系客服,申请监控更多${this.conf.key}`
  375. })
  376. } else {
  377. // 留资
  378. if (this.beforeLeavePage) {
  379. await this.beforeLeavePage()
  380. }
  381. return openAppOrWxPage(LINKS.留资, {
  382. query: {
  383. source: `${this.$env.platform}_${this.conf.limitSource}`
  384. }
  385. })
  386. }
  387. }
  388. // if (msg) {
  389. // this.$toast(msg)
  390. // }
  391. }
  392. this.syncValue(true)
  393. } catch (error) {
  394. // loading.clear()
  395. console.log(error)
  396. } finally {
  397. this.emitChange()
  398. }
  399. },
  400. emitChange() {
  401. this.$emit('change', {
  402. model: cloneDeep(this.model),
  403. id: this.id,
  404. fid: this.fid
  405. })
  406. },
  407. // 取消监控
  408. async doCancelMonitor() {
  409. const loading = this.$toast.loading()
  410. const { success, msg } = await this.doChange()
  411. if (success) {
  412. this.emitChange()
  413. console.log('取消监控成功')
  414. } else {
  415. console.log('取消监控失败', msg)
  416. }
  417. loading.clear()
  418. this.syncValue(false)
  419. },
  420. syncValue(v) {
  421. this.$emit('input', v)
  422. }
  423. }
  424. }
  425. </script>
  426. <style lang="scss" scoped>
  427. ::v-deep {
  428. .tab-action-item {
  429. height: 100%;
  430. }
  431. .icon-jiankong {
  432. color: #9b9ca3;
  433. }
  434. .icon-yijiankong {
  435. color: #ff9f40;
  436. }
  437. }
  438. </style>