ActivityModal.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <template>
  2. <el-dialog :close-on-click-modal="false" title="提示" :visible.sync="activityVisible" width="50%">
  3. <el-steps align-center :active="currentStep" finish-status="success" style="margin-bottom: 10px">
  4. <el-step title="基本信息"></el-step>
  5. <el-step title="其他信息"></el-step>
  6. </el-steps>
  7. <el-carousel :height="heightInfo.swiperHeight + 'px'" ref="elCarouselRef" :autoplay="false" indicator-position="none" arrow="never">
  8. <el-carousel-item name="step-one">
  9. <el-form ref="activityModalFormRef" :model="activityForm" :rules="activityRules.oneRules" label-width="auto">
  10. <el-form-item label="请选择俱乐部" prop="club.id" v-if="!activityForm.activities.id">
  11. <el-select style="width: 100%" v-model="activityForm.club.id" placeholder="请选择">
  12. <el-option v-for="item in clubList" :key="item.id" :label="item.name" :value="item.id"></el-option>
  13. </el-select>
  14. </el-form-item>
  15. <el-form-item label="活动类型" prop="activities.typeEnum">
  16. <el-select style="width: 100%" v-model="activityForm.activities.typeEnum" placeholder="请选择活动类型" @change="handleChangeTypeState">
  17. <el-option v-for="item in ACTIVITY_TYPE_INFO" :key="item.typeId" :label="item.name" :value="item.enumKey"></el-option>
  18. </el-select>
  19. </el-form-item>
  20. <el-form-item label="参与人数" v-if="!NOT_LUAN_QI_BA_ZAO.includes(activityForm.activities.typeEnum)" prop="activities.peopleNumber">
  21. <el-input :min="1" placeholder="请输入参与人数" type="number" v-model.number="activityForm.activities.peopleNumber"></el-input>
  22. </el-form-item>
  23. <el-form-item label="参与用户范围" prop="activities.registrationScope" v-if="!NOT_LUAN_QI_BA_ZAO.includes(activityForm.activities.typeEnum)">
  24. <el-select style="width: 100%" v-model="activityForm.activities.registrationScope" placeholder="请选择参与用户范围">
  25. <el-option label="所有用户" :value="1"></el-option>
  26. <el-option label="仅限俱乐部会员" :value="2"></el-option>
  27. </el-select>
  28. </el-form-item>
  29. <el-form-item label="活动标题" prop="activities.title">
  30. <el-input placeholder="请输入活动标题" v-model="activityForm.activities.title"></el-input>
  31. </el-form-item>
  32. <el-form-item :label="startTimeLabel" prop="activities.startTime">
  33. <el-date-picker
  34. value-format="yyyy-MM-dd HH:mm:ss"
  35. style="width: 100%"
  36. v-model="activityForm.activities.startTime"
  37. type="datetime"
  38. :placeholder="`请选择${startTimeLabel}`"
  39. align="right"
  40. ></el-date-picker>
  41. </el-form-item>
  42. <el-form-item v-if="!NOT_LUAN_QI_BA_ZAO.includes(activityForm.activities.typeEnum)" label="结束时间" prop="activities.endTime">
  43. <el-date-picker
  44. @change="handleSelectEndTime"
  45. :disabled="!activityForm.activities.startTime"
  46. value-format="yyyy-MM-dd HH:mm:ss"
  47. style="width: 100%"
  48. v-model="activityForm.activities.endTime"
  49. type="datetime"
  50. placeholder="请选择结束时间"
  51. align="right"
  52. ></el-date-picker>
  53. </el-form-item>
  54. <el-form-item
  55. v-if="!NOT_LUAN_QI_BA_ZAO.includes(activityForm.activities.typeEnum)"
  56. label="地址"
  57. prop="activities.activitiesAddress"
  58. :rules="[{ required: true, message: '请选择地址', trigger: 'change' }]"
  59. >
  60. <el-cascader
  61. v-show="isEditAdddress || !activityForm.activities.id"
  62. v-model="activityForm.activities.activitiesAddress"
  63. style="width: 100%"
  64. :props="areaData"
  65. ref="cascaderRef"
  66. @change="handleCascaderChange"
  67. ></el-cascader>
  68. <div :style="{ display: isEditAdddress ? 'none' : 'flex' }" v-if="activityForm.activities.id">
  69. <el-input readonly :value="activityForm.activities.activitiesAddress"></el-input>
  70. <el-button style="margin-left: 20px" type="primary" size="mini" @click="isEditAdddress = true">修改区域</el-button>
  71. </div>
  72. </el-form-item>
  73. <el-form-item
  74. v-if="!NOT_LUAN_QI_BA_ZAO.includes(activityForm.activities.typeEnum)"
  75. label="详细地址"
  76. prop="activities.activitiesDetailAddress"
  77. :rules="[{ required: true, message: '请输入详细地址', trigger: 'blur' }]"
  78. >
  79. <el-input type="textarea" placeholder="请输入详细地址" v-model="activityForm.activities.activitiesDetailAddress"></el-input>
  80. </el-form-item>
  81. <el-form-item label="发布人昵称" prop="activities.publishMemberName">
  82. <el-input placeholder="请输入发布人昵称" v-model="activityForm.activities.publishMemberName"></el-input>
  83. </el-form-item>
  84. <el-form-item label="发布人头像" prop="activities.publishMemberAvatar">
  85. <ImageUpload v-model="activityForm.activities.publishMemberAvatar" :limit="1"></ImageUpload>
  86. </el-form-item>
  87. <el-form-item
  88. :rules="[{ required: true, message: '请选择商机类型', trigger: 'change' }]"
  89. label="商机类型"
  90. prop="activities.opportunityType"
  91. v-if="activityForm.activities.typeEnum === BUSINESS_ACTIVITY_TYPE"
  92. >
  93. <el-select style="width: 100%" v-model="activityForm.activities.opportunityType" placeholder="请选择商机类型">
  94. <el-option label="寻求合作" :value="1"></el-option>
  95. <el-option label="寻求采购" :value="2"></el-option>
  96. </el-select>
  97. </el-form-item>
  98. </el-form>
  99. </el-carousel-item>
  100. <el-carousel-item name="step-two">
  101. <el-form ref="activityNextModalFormRef" label-width="auto" :model="activityForm" :rules="activityRules.twoRules">
  102. <el-form-item v-if="NOTICE_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="封面" prop="activities.cover">
  103. <ImageUpload v-model="activityForm.activities.cover" :limit="1"></ImageUpload>
  104. </el-form-item>
  105. <el-form-item v-if="NOTICE_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="宣传视频" prop="activities.promotionalVideo">
  106. <el-upload
  107. style="width: 200px; height: 100px; border-radius: 5px; background-color: #fbfdff; border: 1px dashed #c0ccda; display: flex; align-items: center; justify-content: center"
  108. :action="uploadUrl"
  109. :show-file-list="false"
  110. :on-success="handleUplaodVideoSuccess"
  111. :before-upload="hanldeBeforeUploadVideo"
  112. class="upload-video"
  113. >
  114. <video style="width: 200px; height: 100px" controls v-if="activityForm.activities.promotionalVideo" :src="activityForm.activities.promotionalVideo"></video>
  115. </el-upload>
  116. </el-form-item>
  117. <el-form-item label="内容" prop="activities.content">
  118. <div class="editor-wrapper" v-loading.lock="editorCustomOptions.showLoading">
  119. <quillEditor ref="quillEditorRef" v-model="activityForm.activities.content"></quillEditor>
  120. </div>
  121. </el-form-item>
  122. <el-form-item v-if="BUSINESS_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="联系方式" prop="activities.opportunityContactNumber">
  123. <el-input placeholder="请填写商机联系方式" v-model="activityForm.activities.opportunityContactNumber"></el-input>
  124. </el-form-item>
  125. <el-form-item v-if="BUSINESS_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="邮箱" prop="activities.opportunityContactEmail">
  126. <el-input placeholder="请填写商机联系邮箱" v-model="activityForm.activities.opportunityContactEmail"></el-input>
  127. </el-form-item>
  128. <el-form-item v-if="BUSINESS_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="公司广网链接" prop="activities.participationMethod">
  129. <el-input placeholder="请填写公司官网链接" v-model="activityForm.activities.participationMethod"></el-input>
  130. </el-form-item>
  131. <el-form-item v-if="NOTICE_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="参与方式" prop="activities.opportunityAuthorityUrl">
  132. <el-input :rows="3" type="textarea" placeholder="请填写参与方式" v-model="activityForm.activities.opportunityAuthorityUrl"></el-input>
  133. </el-form-item>
  134. <el-form-item v-if="NOTICE_ACTIVITY_TYPE !== activityForm.activities.typeEnum" label="注意事项" prop="activities.precautions">
  135. <el-input :rows="3" type="textarea" placeholder="请填写参与方式" v-model="activityForm.activities.precautions"></el-input>
  136. </el-form-item>
  137. </el-form>
  138. </el-carousel-item>
  139. </el-carousel>
  140. <template #footer>
  141. <span class="dialog-footer">
  142. <el-button @click="close">取消</el-button>
  143. <el-button type="info" v-if="currentStep === 0" @click="handleNextStep">下一步</el-button>
  144. <el-button type="info" v-if="currentStep === 1" @click="handlePrev">上一步</el-button>
  145. <el-button @click="handleOpActivity(ACTIVITY_STATUS_INFO[0].enumKey)" v-if="currentStep === 1" :loading="isLoading === ACTIVITY_STATUS_INFO[0].enumKey">存为草稿</el-button>
  146. <el-button @click="handleOpActivity(ACTIVITY_STATUS_INFO[1].enumKey)" v-if="currentStep === 1" :loading="isLoading === ACTIVITY_STATUS_INFO[1].enumKey">
  147. {{ activityForm.activities.id ? '确认编辑' : '创建并发布' }}
  148. </el-button>
  149. </span>
  150. </template>
  151. </el-dialog>
  152. </template>
  153. <script>
  154. import { quillEditor } from 'vue-quill-editor'
  155. import ImageUpload from '@/components/ImageUpload'
  156. import { uploadUrl } from '@/utils/request'
  157. import { getClubListApi, createActivityApi, patchPeopleBankActivitiesByIdApi } from '@/api/rm-bank'
  158. import { getProvinceList, getChildAreaList } from '@/api/address'
  159. import {
  160. ACTIVITY_STATUS_INFO,
  161. listenEditorContentHeightChange,
  162. getCurrentActivityRules,
  163. getDefaultActivityForm,
  164. ACTIVITY_TYPE_INFO,
  165. NOTICE_ACTIVITY_TYPE,
  166. BUSINESS_ACTIVITY_TYPE,
  167. interceptQuillImageUpload,
  168. NOT_LUAN_QI_BA_ZAO,
  169. DEFAULT_ACTIVITY_ADDRESS,
  170. getTypeEnumByValue,
  171. getStateEnumByValue
  172. } from './utils'
  173. // 引入 editor 样式
  174. import 'quill/dist/quill.core.css'
  175. import 'quill/dist/quill.snow.css'
  176. import 'quill/dist/quill.bubble.css'
  177. export default {
  178. components: {
  179. quillEditor,
  180. ImageUpload
  181. },
  182. data() {
  183. return {
  184. NOTICE_ACTIVITY_TYPE,
  185. ACTIVITY_TYPE_INFO,
  186. BUSINESS_ACTIVITY_TYPE,
  187. ACTIVITY_STATUS_INFO,
  188. activityVisible: false,
  189. activityForm: getDefaultActivityForm(),
  190. clubList: [],
  191. editorCustomOptions: {
  192. showLoading: false
  193. },
  194. isLoading: false,
  195. currentStep: 0,
  196. heightInfo: {
  197. swiperHeight: 0
  198. },
  199. NOT_LUAN_QI_BA_ZAO,
  200. isEditAdddress: false,
  201. selectAddressLabel: '',
  202. areaData: {
  203. lazy: true,
  204. label: 'name',
  205. value: 'id',
  206. lazyLoad(node, resolve) {
  207. const { level, value } = node
  208. if (level === 0) {
  209. getProvinceList().then((res) => {
  210. resolve(res.data)
  211. })
  212. } else if (level != 0) {
  213. getChildAreaList(value).then((res) => {
  214. resolve(
  215. res.data.map((item) => {
  216. item.leaf = level === 3
  217. return item
  218. })
  219. )
  220. })
  221. }
  222. }
  223. },
  224. uploadUrl,
  225. uploadVideoLoading: false,
  226. stopObserver: null
  227. }
  228. },
  229. mounted() {
  230. // 获取俱乐部列表
  231. this.getClubList()
  232. },
  233. methods: {
  234. show(row) {
  235. this.activityVisible = true
  236. setTimeout(this.calcSwiperHeight)
  237. // 拦截editor的图片上传 转码 -> 服务器上传
  238. // 监听输入框的高度变化,然后动态修改轮播图的高度
  239. this.$nextTick(() => {
  240. interceptQuillImageUpload(this, this.editorCustomOptions)
  241. this.stopObserver = listenEditorContentHeightChange(this, this.calcSwiperHeight)
  242. this.resetDialog()
  243. if (row) {
  244. const activitiesAddress = row.activitiesAddress !== DEFAULT_ACTIVITY_ADDRESS ? row.activitiesAddress : ''
  245. let activitiesAddressTemp = ''
  246. let activitiesDetailAddressTemp = ''
  247. if (activitiesAddress) {
  248. activitiesAddressTemp = (activitiesAddress.split(' ')[0] || '').replace('undefined', '')
  249. activitiesDetailAddressTemp = (activitiesAddress.split(' ')[1] || '').replace('undefined', '')
  250. this.selectAddressLabel = (activitiesAddress.split(' ')[0] || '').replace('undefined', '')
  251. }
  252. Object.assign(this.activityForm.activities, row, {
  253. activitiesAddress: activitiesAddressTemp,
  254. activitiesDetailAddress: activitiesDetailAddressTemp,
  255. id: row.id,
  256. typeEnum: getTypeEnumByValue(Number(row.type)),
  257. stateEnum: getStateEnumByValue(Number(row.state))
  258. })
  259. this.handleChangeTypeState()
  260. }
  261. })
  262. },
  263. // 获取俱乐部列表
  264. async getClubList() {
  265. const res = await getClubListApi({ page: 1, pageSize: 100 })
  266. this.clubList = res.data.list
  267. },
  268. // 点击下一步
  269. async handleNextStep() {
  270. await this.$refs.activityModalFormRef.validate()
  271. this.$refs.elCarouselRef.next()
  272. this.currentStep = 1
  273. this.calcSwiperHeight()
  274. },
  275. // 点击上一步
  276. handlePrev() {
  277. this.$refs.elCarouselRef.prev()
  278. this.currentStep = 0
  279. this.calcSwiperHeight()
  280. },
  281. // 点击完成
  282. async handleOpActivity(status) {
  283. try {
  284. this.isLoading = status
  285. await this.$refs.activityNextModalFormRef.validate()
  286. const data = this.transformData(status)
  287. data.activities.id && delete data.club
  288. const api = data.activities.id ? patchPeopleBankActivitiesByIdApi : createActivityApi
  289. await api(data)
  290. this.$message.success(data.activities.id ? '编辑成功' : '操作成功')
  291. this.close()
  292. this.$emit('refresh')
  293. } catch (error) {
  294. } finally {
  295. this.isLoading = ''
  296. }
  297. },
  298. close() {
  299. this.resetDialog()
  300. this.$nextTick(() => {
  301. this.activityVisible = false
  302. })
  303. },
  304. resetDialog() {
  305. this.activityForm = getDefaultActivityForm()
  306. this.currentStep = 0
  307. this.$refs.elCarouselRef && this.$refs.elCarouselRef.setActiveItem('step-one')
  308. this.$refs.activityModalFormRef.clearValidate()
  309. this.$refs.activityNextModalFormRef.clearValidate()
  310. this.$refs.activityModalFormRef.resetFields()
  311. this.$refs.activityNextModalFormRef.resetFields()
  312. this.selectAddressLabel = ''
  313. typeof this.stopObserver === 'function' && this.stopObserver()
  314. },
  315. // 计算swiper的高度
  316. calcSwiperHeight() {
  317. this.$nextTick(() => {
  318. const refName = this.currentStep === 0 ? 'activityModalFormRef' : 'activityNextModalFormRef'
  319. const containerRef = this.$refs[refName]
  320. if (!containerRef) return
  321. const { height } = containerRef.$el.getBoundingClientRect()
  322. this.heightInfo.swiperHeight = height
  323. })
  324. },
  325. // 活动类型发生了变化
  326. handleChangeTypeState() {
  327. this.activityForm = getDefaultActivityForm(true, { ...this.activityForm })
  328. // 拦截editor的图片上传 转码 -> 服务器上传
  329. // 监听输入框的高度变化,然后动态修改轮播图的高度
  330. this.calcSwiperHeight()
  331. },
  332. handleCascaderChange() {
  333. const checkedNode = this.$refs.cascaderRef.getCheckedNodes()
  334. const { pathLabels } = checkedNode[0]
  335. this.selectAddressLabel = pathLabels.join('')
  336. },
  337. // 选择了结束时间
  338. handleSelectEndTime() {
  339. const startTime = new Date(this.activityForm.activities.startTime)
  340. const endTime = new Date(this.activityForm.activities.endTime)
  341. if (endTime - startTime <= 0) {
  342. this.$message.warning('结束时间不能早于开始时间')
  343. this.activityForm.activities.endTime = ''
  344. }
  345. },
  346. // 转化数据
  347. transformData(activityStatus) {
  348. if (!activityStatus) {
  349. throw new Error('请填写活动发布状态')
  350. }
  351. const data = JSON.parse(JSON.stringify(this.activityForm))
  352. data.activities.stateEnum = activityStatus
  353. if (NOT_LUAN_QI_BA_ZAO.includes(data.activities.typeEnum)) {
  354. data.activities.activitiesAddress = DEFAULT_ACTIVITY_ADDRESS
  355. } else {
  356. data.activities.activitiesAddress = this.selectAddressLabel
  357. if (data.activities.activitiesDetailAddress) {
  358. data.activities.activitiesAddress = data.activities.activitiesAddress + ' ' + data.activities.activitiesDetailAddress
  359. }
  360. }
  361. delete data.activities.activitiesDetailAddress
  362. return data
  363. },
  364. // 上传视频之前
  365. hanldeBeforeUploadVideo(file) {
  366. const isVideo = file.type.startsWith('video/')
  367. const isLimitSize = file.size / 1024 / 1024 < 50 // 转换为MB
  368. if (!isVideo) {
  369. this.$message.error('上传文件必须是视频文件!')
  370. }
  371. if (!isLimitSize) {
  372. this.$message.error('上传的视频大小不能超过 50MB!')
  373. }
  374. return isVideo && isLimitSize
  375. },
  376. // 上传视频完成之后
  377. handleUplaodVideoSuccess(e) {
  378. if (!e.code) {
  379. this.activityForm.activities.promotionalVideo = e.data.url
  380. }
  381. }
  382. },
  383. computed: {
  384. startTimeLabel() {
  385. const DEFAULT_LABEL = '开始时间'
  386. const publishTimeType = [NOTICE_ACTIVITY_TYPE, BUSINESS_ACTIVITY_TYPE]
  387. const { typeEnum } = this.activityForm.activities
  388. return typeEnum ? (publishTimeType.includes(typeEnum) ? '发布时间' : DEFAULT_LABEL) : DEFAULT_LABEL
  389. },
  390. activityRules() {
  391. return getCurrentActivityRules()
  392. }
  393. }
  394. }
  395. </script>
  396. <style lang="scss" scoped></style>