add.vue 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. <template>
  2. <el-card shadow="never" class="aui-card--fill">
  3. <div class="mod-home">
  4. <div class="top adfac">
  5. <div class="back adfac" @click="handleBack">
  6. <img src="@/assets/img/back.png">
  7. <span>返回</span>
  8. </div>
  9. <div class="line"></div>
  10. <div class="text">{{ basicForm.id?'编辑':'新增' }}活动</div>
  11. </div>
  12. <div class="tab adfac">
  13. <div class="tab-pre" :class="{'active':tidx==1}" @click="changeTab(1)">基础资料</div>
  14. <div class="tab-pre" :class="{'active':tidx==2}" @click="changeTab(2)">活动参与</div>
  15. </div>
  16. <div class="form" v-if="tidx===1">
  17. <el-form ref="basicRef" :model="basicForm" :rules="basicRules" label-width="125px">
  18. <el-form-item label="活动类型" prop="typeId">
  19. <el-select v-model="basicForm.typeId" placeholder="请选择活动类型" class="select-box" style="width: 440px;">
  20. <el-option v-for="item in typeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="活动分类" prop="categoryId">
  24. <el-select v-model="basicForm.categoryId" placeholder="请选择活动分类" class="select-box" style="width: 440px;">
  25. <el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
  26. </el-select>
  27. </el-form-item>
  28. <el-form-item label="活动名称" prop="activityName">
  29. <el-input v-model="basicForm.activityName" placeholder="请输入活动名称"></el-input>
  30. </el-form-item>
  31. <el-form-item label="报名时间" prop="">
  32. <div class="adfac">
  33. <el-date-picker v-model="basicForm.signupStartTime" @change="e=>handleSignupDateChange(e,'signupStartTime')" type="datetime" format="yyyy-MM-dd HH:mm" placeholder="年/月/日 - -:- -" style="width: 205px;"></el-date-picker>
  34. <span style="margin: 0 10px;">至</span>
  35. <el-date-picker v-model="basicForm.signupEndTime" @change="e=>handleSignupDateChange(e,'signupEndTime')" type="datetime" format="yyyy-MM-dd HH:mm" placeholder="年/月/日 - -:- -" style="width: 205px;"></el-date-picker>
  36. </div>
  37. </el-form-item>
  38. <el-form-item label="活动时间" prop="">
  39. <div class="adfac">
  40. <el-date-picker v-model="basicForm.activityStartTime" @change="e=>handleActivityDateChange(e,'activityStartTime')" type="datetime" format="yyyy-MM-dd HH:mm" placeholder="年/月/日 - -:- -" style="width: 205px;"></el-date-picker>
  41. <span style="margin: 0 10px;">至</span>
  42. <el-date-picker v-model="basicForm.activityEndTime" @change="e=>handleActivityDateChange(e,'activityEndTime')" type="datetime" format="yyyy-MM-dd HH:mm" placeholder="年/月/日 - -:- -" style="width: 205px;"></el-date-picker>
  43. </div>
  44. </el-form-item>
  45. <el-form-item label="活动地点" prop="">
  46. <el-select v-model="basicForm.provinceId" placeholder="省" clearable style="width: 14.5%" @change="val=>regionChange(val, 'province')">
  47. <el-option v-for="item in provinceOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
  48. </el-select>
  49. <el-select v-model="basicForm.cityId" placeholder="市" clearable style="width: 14.5%; margin: 0 1%" @change="val=>regionChange(val, 'city')">
  50. <el-option v-for="item in cityOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
  51. </el-select>
  52. <el-select v-model="basicForm.districtId" placeholder="区" clearable style="width: 14.5%; margin: 0 1% 0 0" @change="val=>regionChange(val, 'area')">
  53. <el-option v-for="item in areaOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
  54. </el-select>
  55. <el-input v-model="basicForm.address" placeholder="详细地址" style="width: 53.5%"></el-input>
  56. </el-form-item>
  57. <el-form-item label="渠道方" prop="channelId">
  58. <el-select v-model="basicForm.channelId" placeholder="请选择渠道方" class="select-box" style="width: 440px;">
  59. <el-option v-for="item in channelOptions" :key="item.id" :label="item.channelName" :value="item.id"></el-option>
  60. </el-select>
  61. </el-form-item>
  62. <!-- <el-form-item label="举办方" prop="organizerId">
  63. <el-select v-model="basicForm.organizerId" placeholder="请选择举办方" class="select-box" style="width: 440px;">
  64. <el-option v-for="item in organizerOptions" :key="item.id" :label="item.channelName" :value="item.id"></el-option>
  65. </el-select>
  66. </el-form-item> -->
  67. <el-form-item label="举办方" prop="organizerName">
  68. <el-input v-model="basicForm.organizerName" placeholder="请输入举办方名称"></el-input>
  69. </el-form-item>
  70. <el-form-item label="活动封面图片" prop="coverFile" class="redLabel">
  71. <el-upload
  72. ref="coverUploadRef"
  73. :action="url"
  74. :file-list="fileListCover"
  75. :before-upload="file => beforeUploadHandle(file, 1, [158, 214], 'coverFile')"
  76. list-type="picture-card"
  77. :on-success="successHandleCover"
  78. :on-remove="handleRemoveCover"
  79. class="upload-demo"
  80. >
  81. <i class="el-icon-plus"></i>
  82. </el-upload>
  83. <div class="upload_tip">尺寸:158*214px,上传1张,支持图片格式:jpg、png、jpeg</div>
  84. </el-form-item>
  85. <el-form-item label="活动详情banner" prop="imageFiles" class="redLabel">
  86. <el-upload
  87. ref="bannerUploadRef"
  88. :action="url"
  89. :file-list="fileListBanner"
  90. :limit="6"
  91. :before-upload="file => beforeUploadHandle(file, 2, [702, 440], 'banner')"
  92. list-type="picture-card"
  93. :on-success="successHandleBanner"
  94. :on-remove="handleRemoveBanner"
  95. class="upload-demo"
  96. >
  97. <i class="el-icon-plus"></i>
  98. </el-upload>
  99. <div class="upload_tip">尺寸:702*440px,最多上传6张,图片格式:jpg、png、jpeg(可拖动调整顺序)</div>
  100. </el-form-item>
  101. <el-form-item label="活动详情" prop="activityDetails" class="redLabel">
  102. <editor-vue style="margin-top: -30px;" :content="basicForm.activityDetails" @EditorChange="getEditor" ref="infoIntroduceRef"></editor-vue>
  103. </el-form-item>
  104. </el-form>
  105. <div class="btns adfac">
  106. <el-button type="primary" @click="handleNext">下一步</el-button>
  107. <el-button type="default" @click="handleTempSave" style="margin-left: 16px;">暂存<span v-if="autoSaving" style="margin-left: 4px; color: #909399;">保存中...</span></el-button>
  108. <!-- <el-button type="default" @click="handleBack" style="margin-left: 16px;">返回</el-button> -->
  109. </div>
  110. </div>
  111. <div class="form" v-else-if="tidx===2">
  112. <el-form ref="moreRef" :model="moreForm" :rules="moreRules" label-width="125px">
  113. <el-form-item label="参与方式" prop="joinMode">
  114. <ren-select
  115. v-model="moreForm.joinMode"
  116. dict-type="join_mode"
  117. placeholder="请选择参与方式"
  118. style="width: 820px"
  119. ></ren-select>
  120. </el-form-item>
  121. <el-form-item label="报名人数" class="redLabel">
  122. <div class="adfac">
  123. <el-radio-group v-model="moreForm.recruitmentFlag" @change="recruitmentChange">
  124. <el-radio :label="1" :value="1">限制</el-radio>
  125. <el-radio :label="2" :value="2">不限</el-radio>
  126. </el-radio-group>
  127. <div class="f-text">最多报名人数</div>
  128. <el-input type="number" min="0" v-model="moreForm.recruitmentMax" style="width: 157px;" :disabled="moreForm.recruitmentFlag==2" placeholder="请输入"></el-input>
  129. <div class="f-text">最少报名人数</div>
  130. <el-input type="number" min="0" v-model="moreForm.recruitmentMin" style="width: 157px;" :disabled="moreForm.recruitmentFlag==2" placeholder="请输入"></el-input>
  131. <div class="f-tip">活动报名人数超出报名人数后,将不能继续报名。活动报名人数低于最少报名人数时,将会取消活动</div>
  132. </div>
  133. </el-form-item>
  134. <el-form-item label="会员等级" class="redLabel">
  135. <div class="adfac">
  136. <el-radio-group v-model="moreForm.userLevelFlag" @change="userLevelChange">
  137. <el-radio :label="1" :value="1">限制</el-radio>
  138. <el-radio :label="0" :value="0">不限</el-radio>
  139. </el-radio-group>
  140. <el-select v-model="moreForm.userLevel" style="width: 157px;margin-left: 30px;" :disabled="moreForm.userLevelFlag==0">
  141. <el-option v-for="item in 6" :key="item" :label="item" :value="item"></el-option>
  142. </el-select>
  143. <div class="f-text2">级</div>
  144. </div>
  145. </el-form-item>
  146. <el-form-item label="会员年龄" class="redLabel">
  147. <div class="adfac">
  148. <el-radio-group v-model="moreForm.userAgeFlag" @change="userAgeChange">
  149. <el-radio :label="1" :value="1">限制</el-radio>
  150. <el-radio :label="2" :value="2">不限</el-radio>
  151. </el-radio-group>
  152. <div class="f-text">最小参加年龄</div>
  153. <el-input min="0" type="number" v-model="moreForm.userAgeMin" style="width: 157px;" :disabled="moreForm.userAgeFlag==2" placeholder="请输入"></el-input>
  154. <div class="f-text">最大参加年龄</div>
  155. <el-input min="0" type="number" v-model="moreForm.userAgeMax" style="width: 157px;" :disabled="moreForm.userAgeFlag==2" placeholder="请输入"></el-input>
  156. </div>
  157. </el-form-item>
  158. <el-form-item label="公益支持" class="redLabel">
  159. <div class="adfac">
  160. <el-select v-model="moreForm.activityLimit" placeholder="请选择" style="width: 123px;" class="select-box">
  161. <el-option label="专享券" :value="2" v-if="basicForm.typeId=='1977564813261750273'"></el-option>
  162. <template v-else>
  163. <el-option label="需要爱心值" :value="1"></el-option>
  164. <el-option label="免费" :value="3"></el-option>
  165. </template>
  166. </el-select>
  167. <div v-if="moreForm.activityLimit==1" class="adfac">
  168. <el-input min="1" type="number" v-model="moreForm.valueLimit" style="width: 252px;margin-left: 17px;" placeholder="请输入"></el-input>
  169. <div class="f-text2">爱心值</div>
  170. </div>
  171. <!-- <div v-if="moreForm.activityLimit==2" class="adfac">
  172. <el-input type="number" v-model="moreForm.valueLimit" style="width: 252px;margin-left: 17px;" placeholder="请输入"></el-input>
  173. <div class="f-text2">专享券</div>
  174. </div> -->
  175. </div>
  176. </el-form-item>
  177. <el-form-item label="爱心值对应内容" v-if="moreForm.activityLimit==1">
  178. <el-input v-model="moreForm.loveValueContent" style="width: 820px;" placeholder="请输入"></el-input>
  179. </el-form-item>
  180. <el-form-item label="可获得义工时长" prop="volunteerHours">
  181. <div class="adfac">
  182. <el-input min="1" type="number" v-model="moreForm.volunteerHours" style="width: 391px;" placeholder="请输入"></el-input>
  183. <div class="f-text2">小时</div>
  184. </div>
  185. </el-form-item>
  186. <el-form-item label="报名次数限制">
  187. <div class="adfac">
  188. <el-input min="1" type="number" v-model="moreForm.timesLimit" style="width: 391px;" placeholder="请输入"></el-input>
  189. <div class="f-tip">不填默认不限制</div>
  190. </div>
  191. </el-form-item>
  192. <el-form-item label="负责人" prop="contact">
  193. <el-input v-model="moreForm.contact" style="width: 820px;" placeholder="请输入"></el-input>
  194. </el-form-item>
  195. <el-form-item label="负责人电话" prop="contactPhone">
  196. <el-input v-model="moreForm.contactPhone" style="width: 820px;" placeholder="请输入"></el-input>
  197. </el-form-item>
  198. <el-form-item label="上架活动" prop="state">
  199. <el-switch v-model="moreForm.state" active-color="#13ce66" inactive-color="#a4a4a4"></el-switch>
  200. </el-form-item>
  201. </el-form>
  202. <div class="btns adfac">
  203. <el-button type="default" @click="handlePrevious">上一步</el-button>
  204. <el-button type="default" @click="handleTempSave" style="margin-left: 16px;">暂存<span v-if="autoSaving" style="margin-left: 4px; color: #909399;">保存中...</span></el-button>
  205. <el-button type="primary" @click="handleSave" style="margin-left: 16px;">发布</el-button>
  206. </div>
  207. </div>
  208. </div>
  209. <!-- 裁剪框 -->
  210. <cropper-dlg
  211. v-if="showCropper"
  212. :dialogVisible.sync="showCropper"
  213. :img-file="currentFile"
  214. ref="vueCropper"
  215. @upload="cropperUpload"
  216. ></cropper-dlg>
  217. <cropper-dlg2
  218. v-if="showCropper2"
  219. :dialogVisible.sync="showCropper2"
  220. :img-file="currentFile2"
  221. ref="vueCropper2"
  222. @upload="cropperUpload2"
  223. ></cropper-dlg2>
  224. </el-card>
  225. </template>
  226. <script>
  227. import moment from 'moment'
  228. import Cookies from 'js-cookie'
  229. import Sortable from 'sortablejs'
  230. import editorVue from '@/components/editor'
  231. import mixinRegionModule from '@/mixins/region-module.js'
  232. const getInitialBasicForm = () => ({
  233. id: '',
  234. typeId: '',
  235. categoryId: '',
  236. activityName: '',
  237. signupStartEnd: [],
  238. signupStartTime: '',
  239. signupEndTime: '',
  240. activityStartEnd: [],
  241. activityStartTime: '',
  242. activityEndTime: '',
  243. provinceId: '',
  244. cityId: '',
  245. districtId: '',
  246. address: '',
  247. channelId: '',
  248. organizerId: '',
  249. organizerName: '',
  250. coverFile: '',
  251. imageFiles: '',
  252. activityDetails: ''
  253. })
  254. const getInitialMoreForm = () => ({
  255. joinMode: '',
  256. recruitmentFlag: 1,
  257. recruitmentMax: '',
  258. recruitmentMin: '',
  259. userLevelFlag: 1,
  260. userLevel: 1,
  261. userAgeFlag: 1,
  262. userAgeMin: '',
  263. userAgeMax: '',
  264. activityLimit: 1,
  265. valueLimit: '',
  266. loveValueContent: '',
  267. volunteerHours: 0,
  268. timesLimit: '',
  269. contact: '',
  270. contactPhone: '',
  271. state: false
  272. })
  273. export default {
  274. components: { editorVue },
  275. mixins: [mixinRegionModule],
  276. data () {
  277. return {
  278. autoSaveTimer: null,
  279. autoSaving: false,
  280. currentFile: '',
  281. cropperIndex: null,
  282. showCropper: false,
  283. isCropper: true,
  284. currentFile2: '',
  285. cropperIndex2: null,
  286. showCropper2: false,
  287. isCropper2: true,
  288. updType: 1,
  289. typeOptions: [],
  290. categoryOptions: [],
  291. channelOptions: [],
  292. organizerOptions: [],
  293. tidx: 1,
  294. basicForm: getInitialBasicForm(),
  295. activityDetailsTemp: '',
  296. basicRules: {
  297. typeId: [
  298. { required: true, message: '请选择活动类型', trigger: 'change' }
  299. ],
  300. categoryId: [
  301. { required: true, message: '请选择活动分类', trigger: 'change' }
  302. ],
  303. activityName: [
  304. { required: true, message: '请输入活动名称', trigger: 'blur' }
  305. ],
  306. signupEndTime: [
  307. { required: true, message: '请选择报名时间', trigger: 'change' }
  308. ],
  309. activityEndTime: [
  310. { required: true, message: '请选择活动时间', trigger: 'change' }
  311. ],
  312. provinceId: [
  313. { required: true, message: '请选择省市区', trigger: 'change' }
  314. ],
  315. cityId: [
  316. { required: true, message: '请选择省市区', trigger: 'change' }
  317. ],
  318. districtId: [
  319. { required: true, message: '请选择省市区', trigger: 'change' }
  320. ],
  321. channelId: [
  322. { required: true, message: '请选择渠道方', trigger: 'change' }
  323. ],
  324. // organizerId: [
  325. // { required: true, message: '请选择举办方', trigger: 'change' }
  326. // ],
  327. organizerName: [
  328. { required: true, message: '请输入举办方名称', trigger: 'blur' }
  329. ]
  330. },
  331. url: `${window.SITE_CONFIG['apiURL']}/sys/oss/upload?token=${Cookies.get('token')}`,
  332. fileListCover: [],
  333. fileListBanner: [],
  334. moreForm: getInitialMoreForm(),
  335. moreForm2: {
  336. joinMode: ''
  337. },
  338. moreRules: {
  339. joinMode: [
  340. { required: true, message: '请选择参与方式', trigger: 'change' }
  341. ],
  342. volunteerHours: [
  343. { required: true, message: '请输入可获得的义工时长', trigger: 'blur' }
  344. ],
  345. contact: [
  346. { required: true, message: '请输入负责人', trigger: 'blur' }
  347. ],
  348. contactPhone: [
  349. { required: true, message: '请输入负责人电话', trigger: 'blur' },
  350. { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的电话号码', trigger: 'blur' }
  351. ]
  352. }
  353. }
  354. },
  355. created () {
  356. this.getTypeOptions()
  357. this.getCategoryOptions()
  358. },
  359. mounted () {
  360. this.provincAreaDetailInfoList()
  361. this.getSupplierOptions()
  362. this.$nextTick(() => {
  363. setTimeout(() => {
  364. this.$refs.infoIntroduceRef.init()
  365. }, 500)
  366. if (this.$route.query.id) {
  367. this.getDetail(this.$route.query.id)
  368. }
  369. })
  370. this.$nextTick(() => {
  371. this.initUploadSortable()
  372. })
  373. // 自动暂存:每分钟保存一次(仅编辑模式)
  374. this.autoSaveTimer = setInterval(() => {
  375. this.autoSave()
  376. }, 60000)
  377. },
  378. beforeDestroy () {
  379. if (this.autoSaveTimer) {
  380. clearInterval(this.autoSaveTimer)
  381. this.autoSaveTimer = null
  382. }
  383. },
  384. methods: {
  385. recruitmentChange (e) {
  386. if (e === 1) {
  387. this.moreForm.recruitmentMax = 1
  388. this.moreForm.recruitmentMin = 1
  389. } else {
  390. this.moreForm.recruitmentMax = 0
  391. this.moreForm.recruitmentMin = 0
  392. }
  393. },
  394. userLevelChange (e) {
  395. if (e === 1) this.moreForm.userLevel = 1
  396. else this.moreForm.userLevel = 0
  397. },
  398. userAgeChange (e) {
  399. if (e === 1) {
  400. this.moreForm.userAgeMax = 1
  401. this.moreForm.userAgeMin = 1
  402. } else {
  403. this.moreForm.userAgeMax = 0
  404. this.moreForm.userAgeMin = 0
  405. }
  406. },
  407. regionChange (val, type) {
  408. if (type === 'province') {
  409. this.basicForm.cityId = ''
  410. this.basicForm.districtId = ''
  411. this.cityAreaDetailInfoList(val)
  412. } else if (type === 'city') {
  413. this.basicForm.districtId = ''
  414. this.countyAreaDetailInfoList(val)
  415. }
  416. },
  417. getDetail (id) {
  418. this.$http.get('/core/activity/' + id).then(({ data: res }) => {
  419. if (res.code !== 0) return this.$message.error(res.msg)
  420. // this.basicForm = { ...this.basicForm, ...res.data }
  421. this.updateObjectFromSource(this.basicForm, res.data)
  422. this.activityDetailsTemp = res.data.activityDetails
  423. this.basicForm.signupStartEnd = [this.basicForm.signupStartTime, this.basicForm.signupEndTime]
  424. this.basicForm.activityStartEnd = [this.basicForm.activityStartTime, this.basicForm.activityEndTime]
  425. this.basicForm.channelId = this.basicForm.channelId
  426. this.basicForm.organizerId = this.basicForm.organizerId
  427. this.basicForm.organizerName = this.basicForm.organizerName
  428. if (res.data.coverFile) this.fileListCover = [{ name: '', url: res.data.coverFile }]
  429. if (res.data.imageFiles) this.fileListBanner = res.data.imageFiles.split(',').map(item => ({ name: '', url: item }))
  430. if (this.basicForm.cityId) {
  431. this.cityAreaDetailInfoList(this.basicForm.provinceId)
  432. }
  433. if (this.basicForm.districtId) {
  434. this.countyAreaDetailInfoList(this.basicForm.cityId)
  435. }
  436. this.updateObjectFromSource(this.moreForm, res.data)
  437. this.moreForm.recruitmentFlag = +this.moreForm.recruitmentMax === 0 ? 2 : 1
  438. this.moreForm.userLevelFlag = +this.moreForm.userLevel === 0 ? 0 : 1
  439. this.moreForm.userAgeFlag = +this.moreForm.userAgeMax === 0 ? 2 : 1
  440. this.moreForm.state = !!res.data.state
  441. this.$nextTick(() => {
  442. if (this.$refs.basicRef) {
  443. this.$refs.basicRef.clearValidate()
  444. }
  445. if (this.$refs.moreRef) {
  446. this.$refs.moreRef.clearValidate()
  447. }
  448. })
  449. })
  450. },
  451. updateObjectFromSource (target, source) {
  452. for (const key of Object.keys(target)) {
  453. if (Object.prototype.hasOwnProperty.call(source, key)) {
  454. target[key] = source[key]
  455. }
  456. }
  457. },
  458. getEditor (val) {
  459. this.basicForm.activityDetails = val
  460. },
  461. getTypeOptions () {
  462. this.$http.get('/core/activity/type/page', { params: { page: 1, limit: -1 } }).then(res => {
  463. this.typeOptions = res.data.data.list.filter(l => l.enable === 1) || []
  464. })
  465. },
  466. getCategoryOptions () {
  467. this.$http.get('/core/activity/category/page', { params: { page: 1, limit: -1 } }).then(res => {
  468. this.categoryOptions = res.data.data.list.filter(l => l.enable === 1) || []
  469. })
  470. },
  471. getSupplierOptions () {
  472. this.$http.get('/core/channel/page', { params: { page: 1, limit: -1 } }).then(res => {
  473. this.channelOptions = res.data.data.list.filter(item => item.type === 1) || []
  474. this.organizerOptions = res.data.data.list.filter(item => item.type === 2) || []
  475. })
  476. },
  477. handleSignupDateChange (val, key) {
  478. this.basicForm[key] = moment(val).format('YYYY-MM-DD HH:mm')
  479. },
  480. handleActivityDateChange (val, key) {
  481. this.basicForm[key] = moment(val).format('YYYY-MM-DD HH:mm')
  482. },
  483. beforeUploadHandle (file, index, fixedNumber, name) {
  484. this.updType = index
  485. if (file.type !== 'image/jpg' && file.type !== 'image/jpeg' && file.type !== 'image/png') {
  486. this.$message.error(
  487. this.$t('upload.tip', { format: 'jpg、png、gif' })
  488. )
  489. return false
  490. }
  491. if (this.updType === 1) {
  492. if (this.isCropper) {
  493. this.currentFile = file
  494. this.cropperIndex = name
  495. this.showCropper = true
  496. this.$nextTick(() => {
  497. this.$refs.vueCropper.init()
  498. })
  499. return false
  500. }
  501. } else if (this.updType === 2) {
  502. if (this.isCropper2) {
  503. this.currentFile2 = file
  504. this.cropperIndex2 = name
  505. this.showCropper2 = true
  506. this.$nextTick(() => {
  507. this.$refs.vueCropper2.init()
  508. })
  509. return false
  510. }
  511. }
  512. },
  513. cropperUpload (data) {
  514. this.showCropper = false
  515. if (data !== 'close') {
  516. this.$http.post('/sys/oss/upload', data).then((res) => {
  517. if (res.data.code === 0) {
  518. this.successHandleCover(res.data)
  519. }
  520. })
  521. }
  522. },
  523. cropperUpload2 (data) {
  524. this.showCropper2 = false
  525. if (data !== 'close') {
  526. this.$http.post('/sys/oss/upload', data).then((res) => {
  527. if (res.data.code === 0) {
  528. this.successHandleBanner(res.data)
  529. }
  530. })
  531. }
  532. },
  533. handleRemoveCover (file, fileList) {
  534. this.fileListCover = fileList.map((item) => item) || []
  535. this.basicForm.coverFile = ''
  536. },
  537. // 初始化上传列表的拖拽排序
  538. initUploadSortable () {
  539. const setup = (ref, listKey, onChange) => {
  540. const listEl = this.$refs[ref] && this.$refs[ref].$el && this.$refs[ref].$el.querySelector('.el-upload-list--picture-card')
  541. if (!listEl) return
  542. // 如果之前绑定过,先销毁旧的,避免 element-ui 重渲染后引用陈旧
  543. if (listEl.__sortable) {
  544. listEl.__sortable.destroy()
  545. listEl.__sortable = null
  546. }
  547. listEl.__sortable = Sortable.create(listEl, {
  548. animation: 150,
  549. onEnd: (evt) => {
  550. const arr = this[listKey]
  551. if (!arr || arr.length < 2) return
  552. const { oldIndex, newIndex } = evt
  553. if (oldIndex === newIndex) return
  554. const moved = arr.splice(oldIndex, 1)[0]
  555. arr.splice(newIndex, 0, moved)
  556. this[listKey] = [...arr]
  557. onChange && onChange()
  558. }
  559. })
  560. }
  561. setup('coverUploadRef', 'fileListCover', () => {
  562. this.basicForm.coverFile = this.fileListCover[0] ? this.fileListCover[0].url : ''
  563. })
  564. setup('bannerUploadRef', 'fileListBanner', () => {
  565. this.basicForm.imageFiles = this.fileListBanner.map(item => item.url).join(',')
  566. })
  567. },
  568. successHandleCover (res, file, fileList) {
  569. if (res.code !== 0) {
  570. return this.$message.error(res.msg)
  571. }
  572. // 活动封面图片只保留一张:直接替换而不是 unshift,避免与 el-upload 内部列表不一致导致第二次上传静默失败
  573. this.fileListCover = [{ name: '', url: res.data }]
  574. this.basicForm.coverFile = res.data
  575. },
  576. handleRemoveBanner (file, fileList) {
  577. this.fileListBanner = fileList.map((item) => item)
  578. this.basicForm.imageFiles = this.fileListBanner.map(item => item.url).join(',')
  579. },
  580. successHandleBanner (res, file, fileList) {
  581. if (res.code !== 0) {
  582. return this.$message.error(res.msg)
  583. }
  584. this.fileListBanner.unshift({
  585. name: '',
  586. url: res.data
  587. })
  588. this.basicForm.imageFiles = this.fileListBanner.map(item => item.url).join(',')
  589. },
  590. handleBack () {
  591. this.basicForm = getInitialBasicForm()
  592. this.moreForm = getInitialMoreForm()
  593. this.tidx = 1
  594. this.$router.push('/activity-list')
  595. },
  596. changeTab (idx) {
  597. this.tidx = idx
  598. },
  599. handleNext () {
  600. this.$refs.basicRef.validate(valid => {
  601. if (valid) {
  602. if (this.basicForm.signupStartTime && this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupStartTime) > Date.parse(this.basicForm.signupEndTime))) return this.$message.error('报名结束时间不能小于报名开始时间')
  603. if (this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupEndTime) < Date.parse(new Date()))) return this.$message.error('报名结束时间不能小于当前时间')
  604. if (this.basicForm.signupEndTime && this.basicForm.activityStartTime && (Date.parse(this.basicForm.signupEndTime) > Date.parse(this.basicForm.activityStartTime))) return this.$message.error('活动开始时间不能小于报名结束时间')
  605. if (this.basicForm.activityStartTime && this.basicForm.activityEndTime && (Date.parse(this.basicForm.activityStartTime) > Date.parse(this.basicForm.activityEndTime))) return this.$message.error('活动结束时间不能小于活动开始时间')
  606. if (!this.basicForm.coverFile) return this.$message.error('请上传活动封面图片')
  607. if (!this.basicForm.imageFiles) return this.$message.error('请上传活动详情Banner')
  608. if (!this.basicForm.activityDetails) return this.$message.error('请输入活动详情')
  609. this.tidx = 2
  610. if (this.basicForm.typeId === '1977564813261750273') {
  611. this.moreForm.activityLimit = 2
  612. this.moreForm.valueLimit = 1
  613. }
  614. } else {
  615. return false
  616. }
  617. })
  618. },
  619. handleTempSave () {
  620. if (this.basicForm.signupStartTime && this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupStartTime) > Date.parse(this.basicForm.signupEndTime))) return this.$message.error('报名结束时间不能小于报名开始时间')
  621. if (this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupEndTime) < Date.parse(new Date()))) return this.$message.error('报名结束时间不能小于当前时间')
  622. if (this.basicForm.signupEndTime && this.basicForm.activityStartTime && (Date.parse(this.basicForm.signupEndTime) > Date.parse(this.basicForm.activityStartTime))) return this.$message.error('活动开始时间不能小于报名结束时间')
  623. if (this.basicForm.activityStartTime && this.basicForm.activityEndTime && (Date.parse(this.basicForm.activityStartTime) > Date.parse(this.basicForm.activityEndTime))) return this.$message.error('活动结束时间不能小于活动开始时间')
  624. let { signupStartEnd, activityStartEnd, ...newBasic } = JSON.parse(JSON.stringify(this.basicForm))
  625. let { recruitmentFlag, userAgeFlag, userLevelFlag, ...newMore } = JSON.parse(JSON.stringify(this.moreForm))
  626. let dto = { ...newBasic, ...newMore }
  627. dto.activeState = -2
  628. dto.state = 0
  629. this.$http[dto.id ? 'put' : 'post']('/core/activity', dto).then(res => {
  630. if (res.data.code !== 0) return this.$message.error(res.data.msg)
  631. this.$message.success(dto.id ? '编辑' : '新增' + '草稿成功')
  632. this.$router.push('/activity-list')
  633. })
  634. },
  635. handlePrevious () {
  636. this.tidx = 1
  637. this.$nextTick(() => {
  638. setTimeout(() => {
  639. this.$refs.infoIntroduceRef.init()
  640. this.basicForm.activityDetails = this.activityDetails
  641. }, 500)
  642. })
  643. },
  644. autoSave () {
  645. // 只在编辑模式(有id)时自动保存
  646. if (!this.basicForm.id) return
  647. let { signupStartEnd, activityStartEnd, ...newBasic } = JSON.parse(JSON.stringify(this.basicForm))
  648. let { recruitmentFlag, userAgeFlag, userLevelFlag, ...newMore } = JSON.parse(JSON.stringify(this.moreForm))
  649. let dto = { ...newBasic, ...newMore }
  650. dto.state = dto.state ? 1 : 0
  651. this.autoSaving = true
  652. this.$http.put('/core/activity', dto).then(res => {
  653. if (res.data.code === 0) {
  654. this.autoSaving = false
  655. }
  656. }).catch(() => { this.autoSaving = false })
  657. },
  658. handleSave () {
  659. this.$refs.moreRef.validate(valid => {
  660. if (valid) {
  661. if (this.moreForm.recruitmentFlag === 1) {
  662. if (!this.moreForm.recruitmentMax) return this.$message.error('请输入最多报名人数')
  663. if (!this.moreForm.recruitmentMin) return this.$message.error('请输入最少报名人数')
  664. if (+this.moreForm.recruitmentMin > +this.moreForm.recruitmentMax) return this.$message.error('最少报名人数不能大于最多报名人数')
  665. } else {
  666. this.moreForm.recruitmentMax = 0
  667. this.moreForm.recruitmentMin = 0
  668. }
  669. if (this.moreForm.userLevelFlag === 1) {
  670. if (!this.moreForm.userLevel) return this.$message.error('请输入会员最小限制等级')
  671. } else {
  672. this.moreForm.userLevel = 0
  673. }
  674. if (this.moreForm.userAgeFlag === 1) {
  675. if (!this.moreForm.userAgeMin) return this.$message.error('请输入最小参加年龄')
  676. if (!this.moreForm.userAgeMax) return this.$message.error('请输入最大参加年龄')
  677. if (+this.moreForm.userAgeMin > +this.moreForm.userAgeMax) return this.$message.error('最小参加年龄不能大于最大参加年龄')
  678. } else {
  679. this.moreForm.userAgeMin = 0
  680. this.moreForm.userAgeMax = 0
  681. }
  682. if (this.moreForm.activityLimit === 1) {
  683. if (!this.moreForm.valueLimit) return this.$message.error('请输入爱心值数量')
  684. } else if (this.moreForm.activityLimit === 2) {
  685. if (!this.moreForm.valueLimit) return this.$message.error('请输入专享券数量')
  686. }
  687. let { signupStartEnd, activityStartEnd, ...newBasic } = JSON.parse(JSON.stringify(this.basicForm))
  688. let { recruitmentFlag, userAgeFlag, userLevelFlag, ...newMore } = JSON.parse(JSON.stringify(this.moreForm))
  689. let dto = { ...newBasic, ...newMore }
  690. dto.state = dto.state ? 1 : 0
  691. this.$http[dto.id ? 'put' : 'post']('/core/activity', dto).then(res => {
  692. if (res.data.code !== 0) return this.$message.error(res.data.msg)
  693. this.$message.success('发布成功')
  694. this.$router.push('/activity-list')
  695. })
  696. } else {
  697. return false
  698. }
  699. })
  700. }
  701. }
  702. }
  703. </script>
  704. <style scoped lang="scss">
  705. .top {
  706. padding-bottom: 13px;
  707. box-shadow: inset 0px -1px 0px 0px #F0F1F7;
  708. .back {
  709. cursor: pointer;
  710. img {
  711. width: 22px;
  712. height: 22px;
  713. }
  714. span {
  715. font-family: PingFang-SC, PingFang-SC;
  716. font-weight: bold;
  717. font-size: 14px;
  718. color: #00AE57;
  719. line-height: 20px;
  720. margin-left: 6px;
  721. }
  722. }
  723. .line {
  724. width: 1px;
  725. height: 16px;
  726. background: #F0F1F7;
  727. margin-left: 14px;
  728. }
  729. .text {
  730. font-family: PingFangSC, PingFang SC;
  731. font-weight: 400;
  732. font-size: 14px;
  733. color: #657588;
  734. line-height: 20px;
  735. margin-left: 10px;
  736. }
  737. }
  738. .tab{
  739. height: 48px;
  740. box-shadow: inset 0px -1px 0px 0px #F0F1F7;
  741. &-pre{
  742. width: 120px;
  743. height: 48px;
  744. font-family: PingFangSC, PingFang SC;
  745. font-weight: 400;
  746. font-size: 14px;
  747. color: #393939;
  748. line-height: 20px;
  749. // cursor: pointer;
  750. position: relative;
  751. padding-left: 7px;
  752. box-sizing: border-box;
  753. display: flex;
  754. flex-direction: column;
  755. justify-content: center;
  756. &.active{
  757. font-weight: bold;
  758. &::after{
  759. content: '';
  760. position: absolute;
  761. left: 7px;
  762. bottom: 2px;
  763. width: 55px;
  764. height: 2px;
  765. background: #00AE57;
  766. }
  767. }
  768. }
  769. }
  770. .form{
  771. padding: 20px 0 1px;
  772. }
  773. .btns{
  774. border-top: 1px solid #F0F1F7;
  775. padding: 24px 0 0 126px;
  776. }
  777. .f-text{
  778. font-family: PingFangSC, PingFang SC;
  779. font-weight: 400;
  780. font-size: 14px;
  781. color: #393939;
  782. line-height: 20px;
  783. margin: 0 10px 0 56px;
  784. }
  785. .f-text2{
  786. font-family: PingFangSC, PingFang SC;
  787. font-weight: 400;
  788. font-size: 14px;
  789. color: #393939;
  790. line-height: 20px;
  791. margin-left: 6px;
  792. }
  793. .f-tip{
  794. font-family: PingFangSC, PingFang SC;
  795. font-weight: 400;
  796. font-size: 12px;
  797. color: #F4657A;
  798. line-height: 17px;
  799. margin-left: 20px;
  800. }
  801. ::v-deep .el-radio__label{
  802. line-height: 20px;
  803. }
  804. .avatar-uploader-icon {
  805. font-size: 28px;
  806. color: #8c939d;
  807. width: 84px;
  808. height: 84px;
  809. line-height: 84px;
  810. text-align: center;
  811. }
  812. ::v-deep .el-upload--picture-card{
  813. width: 84px !important;
  814. height: 84px !important;
  815. display: flex;
  816. align-items: center;
  817. justify-content: center;
  818. }
  819. .avatar {
  820. width: 84px;
  821. height: 84px;
  822. display: block;
  823. }
  824. .upload-demo{
  825. display: flex !important;
  826. flex-wrap: wrap;
  827. }
  828. ::v-deep .el-upload-list--picture-card .el-upload-list__item{
  829. width: 84px !important;
  830. height: 84px !important;
  831. }
  832. .upload_tip{
  833. font-family: PingFangSC, PingFang SC;
  834. font-weight: 400;
  835. font-size: 12px;
  836. color: #A4A4A4;
  837. line-height: 17px;
  838. margin-top: 2px;
  839. }
  840. ::v-deep .redLabel>.el-form-item__label{
  841. &::before{
  842. content: "*";
  843. color: #F56C6C;
  844. margin-right: 4px;
  845. }
  846. }
  847. </style>