follow.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <!--
  2. * @Author: liuzhenlin 461480418@qq.ocm
  3. * @Date: 2023-01-12 11:57:48
  4. * @LastEditors: liuzhenlin
  5. * @LastEditTime: 2023-05-22 17:37:35
  6. * @Description: file content
  7. * @FilePath: \oms\pages\publicPages\follow.vue
  8. -->
  9. <template>
  10. <view class="home">
  11. <view class="nav">
  12. <view :style="{ paddingTop }">
  13. <view class="title" :style="[{ height }, { lineHeight: height }]">
  14. <view class="back" @click="goBack()">
  15. <u-icon name="arrow-left" color="#ffffff" size="22"></u-icon>
  16. </view>
  17. <text>填写跟进</text>
  18. </view>
  19. </view>
  20. </view>
  21. <view class="main">
  22. <u-form :model="addForm" :rules="rules" ref="addForm" label-width="0">
  23. <u-form-item prop="content" customStyle="padding:40rpx 0 34rpx">
  24. <view class="form-label flex_l">
  25. <view class="label-tag"></view>
  26. 本次跟进内容
  27. </view>
  28. <u-textarea
  29. v-model="addForm.content"
  30. placeholder="请输入本次跟进内容"
  31. maxlength="500"
  32. height="150rpx"
  33. count
  34. customStyle="border-radius:16rpx"></u-textarea>
  35. </u-form-item>
  36. <u-form-item prop="effect" customStyle="padding:40rpx 0 34rpx">
  37. <view class="form-label flex_l">
  38. <view class="label-tag"></view>
  39. 达成效果
  40. </view>
  41. <u-textarea
  42. v-model="addForm.effect"
  43. placeholder="请输入达成效果"
  44. maxlength="500"
  45. height="150rpx"
  46. count
  47. customStyle="border-radius:16rpx"></u-textarea>
  48. </u-form-item>
  49. <u-form-item prop="issue" customStyle="padding:40rpx 0 34rpx">
  50. <view class="form-label flex_l">
  51. <view class="label-tag"></view>
  52. 问题或困难
  53. </view>
  54. <u-textarea
  55. v-model="addForm.issue"
  56. placeholder="请输入问题或困难"
  57. maxlength="500"
  58. height="150rpx"
  59. count
  60. customStyle="border-radius:16rpx"></u-textarea>
  61. </u-form-item>
  62. <u-form-item prop="furtherPlan" customStyle="padding:40rpx 0 34rpx">
  63. <view class="form-label flex_l">
  64. <view class="label-tag"></view>
  65. 下一步跟进计划和目标
  66. </view>
  67. <u-textarea
  68. v-model="addForm.furtherPlan"
  69. placeholder="请输入下一步跟进计划和目标"
  70. maxlength="500"
  71. height="150rpx"
  72. count
  73. customStyle="border-radius:16rpx"></u-textarea>
  74. </u-form-item>
  75. <u-form-item prop="date" borderBottom customStyle="padding:40rpx 0 30rpx" @click="showDate = true">
  76. <view class="form-label flex_l">
  77. <view class="label-tag"></view>
  78. 跟进时间
  79. </view>
  80. <u-input
  81. :readonly="true"
  82. placeholder="请选择跟进时间"
  83. v-model="addForm.date"
  84. border="none"
  85. suffixIcon="arrow-down"
  86. suffixIconStyle="color:#CDCDCD"
  87. clearable
  88. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  89. </u-form-item>
  90. <u-form-item prop="mode" borderBottom customStyle="padding:40rpx 0 30rpx" @click="showMode = true">
  91. <view class="form-label flex_l">
  92. <view class="label-tag"></view>
  93. 跟进方式
  94. </view>
  95. <u-input
  96. :readonly="true"
  97. placeholder="请选择跟进方式"
  98. v-model="addForm.mode"
  99. border="none"
  100. suffixIcon="arrow-down"
  101. suffixIconStyle="color:#CDCDCD"
  102. clearable
  103. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  104. </u-form-item>
  105. <u-form-item prop="contactsName" borderBottom customStyle="padding:40rpx 0 30rpx" @click="open">
  106. <view class="form-label flex_l">
  107. <view class="label-tag"></view>
  108. 联系人
  109. </view>
  110. <u-input
  111. :readonly="true"
  112. placeholder="请选择联系人"
  113. v-model="addForm.contactsName"
  114. border="none"
  115. suffixIcon="arrow-down"
  116. suffixIconStyle="color:#CDCDCD"
  117. clearable
  118. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  119. </u-form-item>
  120. </u-form>
  121. <view class="upload-file-box">
  122. <view class="form-label flex_l">附件</view>
  123. <uni-file-picker file-mediatype="all" v-model="imageValue" mode="grid" @select="select" ref="upload" limit="1">
  124. <view class="upload-btn">
  125. <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
  126. </view>
  127. </uni-file-picker>
  128. </view>
  129. <view class="save" @click="handleAdd" :class="!flag ? 'disabledBtn' : ''">保存</view>
  130. <!-- 选择跟进方式 -->
  131. <u-picker
  132. :show="showMode"
  133. :columns="modeColumns"
  134. keyName="label"
  135. @cancel="showMode = false"
  136. @confirm="pickMode"></u-picker>
  137. <!-- 选择下次时间 -->
  138. <u-datetime-picker
  139. v-if="showDate"
  140. :maxDate="maxDate"
  141. :show="showDate"
  142. mode="datetime"
  143. v-model="addForm.date"
  144. @cancel="showDate = false"
  145. @confirm="pickDate"></u-datetime-picker>
  146. </view>
  147. <u-notify ref="uNotify"></u-notify>
  148. <u-toast ref="uToast"></u-toast>
  149. <!-- 客户联系人 -->
  150. <customer-contact ref="concat" @close="closeConcat"></customer-contact>
  151. <!-- 项目联系人 -->
  152. <project-contact ref="projectConcat" @close="closeConcat"></project-contact>
  153. <!-- 经销商代理商联系人 -->
  154. <distr-contact ref="distrConcat" @close="closeConcat" :distrType="targetType"></distr-contact>
  155. </view>
  156. </template>
  157. <script>
  158. // import { mapGetters } from 'vuex'
  159. import to from 'await-to-js'
  160. import followApi from '../../api/follow'
  161. import distrApi from '../../api/base/distr'
  162. import custApi from '../../api/customer'
  163. import prodApi from '../../api/project'
  164. import bidApi from '../../api/bid'
  165. import ProjectContact from '@/components/ProjectContact'
  166. import CustomerContact from '@/components/CustomerContact'
  167. import DistrContact from '@/components/DistrContact'
  168. export default {
  169. name: 'omsIndex',
  170. components: {
  171. CustomerContact,
  172. ProjectContact,
  173. DistrContact,
  174. },
  175. data() {
  176. return {
  177. flag: true,
  178. imageValue: [],
  179. height: '',
  180. paddingTop: '',
  181. showMode: false, //选择行业
  182. showDate: false,
  183. maxDate: new Date() * 1,
  184. modeColumns: [
  185. [
  186. {
  187. label: '电话',
  188. id: '10',
  189. },
  190. {
  191. label: '邮件',
  192. id: '20',
  193. },
  194. {
  195. label: '拜访',
  196. id: '30',
  197. },
  198. ],
  199. ],
  200. addForm: {
  201. files: [],
  202. date: this.parseTime(new Date(), '{y}-{m}-{d}'), //时间
  203. content: '', //客户姓名
  204. furtherPlan: '', //下一步跟进计划
  205. effect: '',
  206. issue: '',
  207. mode: '', //跟进方式
  208. modeId: 0,
  209. fileName: '', //附件
  210. contactsName: '', //联系人
  211. contactsId: 0, //联系人ID
  212. },
  213. targetType: 0, //跟进对象类型(10客户,20项目,30合同,40回款,50经销商,50代理商)
  214. rules: {
  215. furtherPlan: {
  216. type: 'string',
  217. required: true,
  218. message: '请填写下一步跟进计划和目标',
  219. trigger: ['blur'],
  220. },
  221. content: {
  222. type: 'string',
  223. required: true,
  224. message: '请填写跟进内容',
  225. trigger: ['blur'],
  226. },
  227. effect: {
  228. type: 'string',
  229. required: true,
  230. message: '请填写达成效果',
  231. trigger: ['blur'],
  232. },
  233. issue: {
  234. type: 'string',
  235. required: true,
  236. message: '请填写问题或困难',
  237. trigger: ['blur'],
  238. },
  239. date: {
  240. type: 'string',
  241. required: true,
  242. message: '请填写跟进时间',
  243. trigger: ['blur'],
  244. },
  245. mode: {
  246. type: 'string',
  247. required: true,
  248. message: '请选择跟进方式',
  249. trigger: ['blur', 'change'],
  250. },
  251. contactsName: {
  252. type: 'string',
  253. required: true,
  254. message: '请选择联系人',
  255. trigger: ['blur', 'change'],
  256. },
  257. },
  258. details: {},
  259. curId: 0,
  260. }
  261. },
  262. computed: {
  263. // ...mapGetters(['details']),
  264. },
  265. onLoad(option) {
  266. this.targetType = option.targetType
  267. this.curId = parseInt(option.id)
  268. },
  269. created() {
  270. const navData = uni.getMenuButtonBoundingClientRect()
  271. this.height = navData.height + 'px'
  272. this.paddingTop = navData.top + 'px'
  273. },
  274. mounted() {
  275. this.initData()
  276. },
  277. methods: {
  278. // 获取客户/项目信息
  279. async initData() {
  280. if (this.targetType == '10') {
  281. //客户
  282. const [err, res] = await to(
  283. custApi.getDetail({
  284. ids: [this.curId],
  285. })
  286. )
  287. if (err) return
  288. if (res && res.code == 200) {
  289. this.details = res.data.list[0]
  290. }
  291. } else if (this.targetType == '20') {
  292. //项目
  293. const [err, res] = await to(
  294. prodApi.getDetail({
  295. id: this.curId,
  296. })
  297. )
  298. if (err) return
  299. if (res && res.code == 200) {
  300. this.details = res.data
  301. }
  302. } else if (this.targetType == '50') {
  303. // 经销商代理商
  304. const [err, res] = await to(distrApi.getEntity({ id: this.curId }))
  305. if (err) return
  306. if (res && res.code == 200) {
  307. this.details = res.data.list
  308. }
  309. } else if (this.targetType == '60') {
  310. // 招标信息
  311. const [err, res] = await to(bidApi.get({ id: this.curId }))
  312. if (err) return
  313. if (res && res.code == 200) {
  314. this.details = res.data
  315. }
  316. }
  317. },
  318. // 打开联系人
  319. open() {
  320. if (this.targetType == '10') {
  321. this.$refs.concat.open(this.details.id)
  322. } else if (this.targetType == '20') {
  323. this.$refs.projectConcat.open(this.details.id, this.details.custId)
  324. } else if (this.targetType == '50') {
  325. this.$refs.distrConcat.open(this.details.id)
  326. } else if (this.targetType == '60') {
  327. this.$refs.concat.open(this.details.custId)
  328. }
  329. },
  330. // 行业选择
  331. pickMode(e) {
  332. this.addForm.modeId = e.value[0].id
  333. this.addForm.mode = e.value[0].label
  334. this.showMode = false
  335. },
  336. async pickDate(e) {
  337. this.showDate = false
  338. const timeFormat = uni.$u.timeFormat
  339. let timeValue = await timeFormat(e.value, 'yyyy-mm-dd hh:MM')
  340. this.addForm.date = timeValue
  341. },
  342. // 关闭选择用户
  343. closeConcat(user) {
  344. if (user) {
  345. if (this.targetType == '50') {
  346. this.addForm.contactsName = user.name
  347. this.addForm.contactsId = user.id
  348. } else {
  349. this.addForm.contactsName = user.label
  350. this.addForm.contactsId = user.id
  351. }
  352. }
  353. },
  354. handleAdd() {
  355. if (!this.flag) return
  356. this.$refs.addForm
  357. .validate()
  358. .then(async () => {
  359. let idParams = {}
  360. if (this.targetType == '10') {
  361. idParams = {
  362. custId: this.details.id,
  363. custName: this.details.custName,
  364. targetId: this.details.id,
  365. targetName: this.details.custName,
  366. }
  367. } else if (this.targetType == '20') {
  368. idParams = {
  369. custId: this.details.custId,
  370. custName: this.details.custName,
  371. targetId: this.details.id,
  372. targetName: this.details.nboName,
  373. }
  374. } else if (this.targetType == '50') {
  375. idParams = {
  376. targetId: this.details.id,
  377. targetName: this.details.distName,
  378. }
  379. } else if (this.targetType == '60') {
  380. idParams = {
  381. targetId: this.details.id,
  382. targetName: this.details.title,
  383. }
  384. }
  385. let params = Object.assign(
  386. {
  387. contactsId: this.addForm.contactsId,
  388. contactsName: this.addForm.contactsName,
  389. followContent: this.addForm.content,
  390. effect: this.addForm.effect,
  391. issue: this.addForm.issue,
  392. followDate: this.addForm.date,
  393. followType: this.addForm.modeId,
  394. targetType: this.targetType,
  395. furtherPlan: this.addForm.furtherPlan,
  396. files: this.addForm.files,
  397. },
  398. idParams
  399. )
  400. this.flag = false
  401. const [err, res] = await to(followApi.createFollow(params))
  402. this.flag = true
  403. if (err) return
  404. if (res && res.code == 200) {
  405. this.$refs.uToast.show({
  406. type: 'success',
  407. message: '创建成功',
  408. complete: () => {
  409. uni.navigateBack({
  410. //关闭当前页面,返回上一页面或多级页面。
  411. delta: 1,
  412. })
  413. },
  414. })
  415. }
  416. })
  417. .catch((err) => {
  418. this.$refs.uNotify.show({
  419. top: this.height + this.paddingTop + 10,
  420. type: 'warning',
  421. message: err[0].message,
  422. duration: 1000 * 3,
  423. })
  424. })
  425. },
  426. // // 选择上传触发函数
  427. select(e) {
  428. let url = process.uniEnv.VUE_APP_UPLOAD_WEED
  429. uni.request({
  430. url, //仅为示例,并非真实接口地址。
  431. success: (res) => {
  432. this.uploadFiles(e, res.data)
  433. },
  434. })
  435. },
  436. // 上传函数
  437. async uploadFiles(e, res) {
  438. let action = process.uniEnv.VUE_APP_PROTOCOL + res.publicUrl + '/' + res.fid
  439. uni.uploadFile({
  440. url: action,
  441. filePath: e.tempFilePaths[0],
  442. name: 'file',
  443. formData: {
  444. file: e.tempFiles[0].file,
  445. },
  446. success: (res) => {
  447. this.$refs.uToast.show({
  448. type: 'success',
  449. message: '附件上传成功',
  450. })
  451. this.addForm.files = [
  452. {
  453. fileName: e.tempFiles[0].file.name,
  454. fileUrl: action,
  455. },
  456. ]
  457. },
  458. fail: (err) => {},
  459. })
  460. },
  461. goBack() {
  462. uni.navigateBack({
  463. //关闭当前页面,返回上一页面或多级页面。
  464. delta: 1,
  465. })
  466. },
  467. },
  468. }
  469. </script>
  470. <style>
  471. page {
  472. background: #f2f3f5;
  473. }
  474. </style>
  475. <style lang="scss" scoped>
  476. .home {
  477. padding-top: 188rpx;
  478. .nav {
  479. position: absolute;
  480. left: 0;
  481. top: 0;
  482. width: 100%;
  483. height: 284rpx;
  484. background: #3e7ef8;
  485. .title {
  486. position: relative;
  487. text-align: center;
  488. font-size: 32rpx;
  489. font-weight: bold;
  490. color: #ffffff;
  491. .back {
  492. position: absolute;
  493. top: 0;
  494. bottom: 0;
  495. margin: auto;
  496. left: 70rpx;
  497. display: flex;
  498. }
  499. }
  500. }
  501. .main {
  502. position: absolute;
  503. width: 100%;
  504. height: calc(100vh - 188rpx);
  505. background: #ffffff;
  506. box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
  507. border-radius: 31rpx 31rpx 0 0;
  508. padding: 0 32rpx;
  509. overflow: auto;
  510. padding-bottom: 64rpx;
  511. .form-label {
  512. font-size: 32rpx;
  513. font-weight: bold;
  514. color: #323232;
  515. padding-bottom: 18rpx;
  516. .label-tag {
  517. width: 15rpx;
  518. height: 15rpx;
  519. background: #ff4d4f;
  520. border-radius: 50%;
  521. margin-right: 10rpx;
  522. }
  523. }
  524. .upload-file-box {
  525. padding: 20rpx 0;
  526. position: relative;
  527. .upload-btn {
  528. position: absolute;
  529. right: 0;
  530. top: 26rpx;
  531. }
  532. }
  533. }
  534. .save {
  535. width: 569rpx;
  536. height: 92rpx;
  537. background: #3e7ef8;
  538. border-radius: 31rpx;
  539. margin: 116rpx auto 0;
  540. font-size: 32rpx;
  541. color: #ffffff;
  542. text-align: center;
  543. line-height: 92rpx;
  544. }
  545. }
  546. </style>