act-set-form.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. <template>
  2. <view class="content">
  3. <!-- 所属组织区域 -->
  4. <view class="box">
  5. <view class="box_title">
  6. <text class="text">*</text>
  7. 所属组织
  8. </view>
  9. <picker :range="orgRange" range-key="name" @change="changeOrg">
  10. <view class="box_input">
  11. <view class="picker" :class="{ pick: form.orgName }">{{ form.orgName ? form.orgName : '请选择所属组织' }}</view>
  12. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  13. </view>
  14. </picker>
  15. </view>
  16. <!-- 活动主题区域 -->
  17. <view class="box">
  18. <view class="box_title">
  19. <text class="text">*</text>
  20. 活动主题
  21. </view>
  22. <view class="box_input">
  23. <input class="input" placeholder-style="color:#A6A6A6" placeholder="请输入活动主题" v-model="form.theme" />
  24. </view>
  25. </view>
  26. <!--活动详情区域 -->
  27. <view class="box">
  28. <view class="box_title">
  29. <text class="text">*</text>
  30. 活动详情
  31. </view>
  32. <view class="box_richtext">
  33. <!-- 富文本输入框区域 -->
  34. <editor class="editor" placeholder="请输入活动详情" @ready="handleReady" @focus="handleFocus" @blur="handleBlur" @statuschange="handleStatusChange"></editor>
  35. <view class="tools" v-if="showTool">
  36. <!-- 大标题图标 -->
  37. <uni-icons :color="isTitle ? '#5278fb' : ''" fontFamily="CustomFont" :size="26" @click="handleTitle">&#xe609;</uni-icons>
  38. <!-- 加粗图标 -->
  39. <uni-icons :color="isBold ? '#5278fb' : ''" fontFamily="CustomFont" :size="26" @click="handleBold">&#xec83;</uni-icons>
  40. <!-- 倾斜图标 -->
  41. <uni-icons :color="isIncline ? '#5278fb' : ''" fontFamily="CustomFont" :size="26" @click="handleIncline">&#xe852;</uni-icons>
  42. <!-- 横线图标 -->
  43. <uni-icons fontFamily="CustomFont" :size="26" @click="handleRowLine">&#xe60a;</uni-icons>
  44. <!-- 图片图标 -->
  45. <uni-icons fontFamily="CustomFont" :size="26" @click="handleImage">&#xe8ba;</uni-icons>
  46. <!-- 确认图标 -->
  47. <uni-icons fontFamily="CustomFont" :size="26" @click="handleFinish">&#xe622;</uni-icons>
  48. </view>
  49. </view>
  50. </view>
  51. <!-- 活动开始时间区域 -->
  52. <view class="box">
  53. <view class="box_title">
  54. <text class="text">*</text>
  55. 活动开始时间
  56. </view>
  57. <uni-datetime-picker type="datetime" v-model="form.startTime" placeholder="请选择活动开始时间" :start="dayjs(Date.now()).format('YYYY-MM-DD')" />
  58. <!-- <picker mode="date" :start="dayjs(Date.now()).format('YYYY-MM-DD')" @change="changeStartTime">
  59. <view class="box_input">
  60. <view class="picker" :class="{ pick: form.startTime }">{{ form.startTime ? form.startTime : '请选择活动开始时间' }}</view>
  61. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  62. </view>
  63. </picker> -->
  64. </view>
  65. <!-- 活动结束时间区域 -->
  66. <view class="box">
  67. <view class="box_title">
  68. <text class="text">*</text>
  69. 活动结束时间
  70. </view>
  71. <uni-datetime-picker type="datetime" v-model="form.endTime" placeholder="请选择活动结束时间" :start="dayjs(Date.now()).format('YYYY-MM-DD')" />
  72. <!-- <picker mode="date" :start="dayjs(Date.now()).format('YYYY-MM-DD')" @change="changeEndTime">
  73. <view class="box_input">
  74. <view class="picker" :class="{ pick: form.endTime }">{{ form.endTime ? form.endTime : '请选择活动结束时间' }}</view>
  75. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  76. </view>
  77. </picker> -->
  78. </view>
  79. <!-- 活动地址区域 -->
  80. <view class="box">
  81. <view class="box_title">
  82. <text class="text">*</text>
  83. 活动地址
  84. </view>
  85. <view class="box_input" @click="clickAddress">
  86. <view class="picker" :class="{ pick: form.address }">{{ form.address ? form.address : '请选择活动地址' }}</view>
  87. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  88. </view>
  89. </view>
  90. <!-- 参与说明区域 -->
  91. <view class="box">
  92. <view class="box_title">
  93. <text class="text">*</text>
  94. 参与说明
  95. </view>
  96. <view class="box_textarea">
  97. <textarea class="textarea" :maxlength="300" placeholder="请输入参与说明,最多300字" @blur="inputDesc"></textarea>
  98. </view>
  99. </view>
  100. <!-- 报名开始时间区域 -->
  101. <view class="box">
  102. <view class="box_title">
  103. <text class="text">*</text>
  104. 报名开始时间
  105. </view>
  106. <uni-datetime-picker type="datetime" v-model="form.signsTime" placeholder="请选择报名开始时间" :start="dayjs(Date.now()).format('YYYY-MM-DD')" />
  107. <!-- <picker mode="date" :start="dayjs(Date.now()).format('YYYY-MM-DD')" @change="changeApplyStartTime">
  108. <view class="box_input">
  109. <view class="picker" :class="{ pick: form.signsTime }">{{ form.signsTime ? form.signsTime : '请选择报名开始时间' }}</view>
  110. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  111. </view>
  112. </picker> -->
  113. </view>
  114. <!-- 报名结束时间区域 -->
  115. <view class="box">
  116. <view class="box_title">
  117. <text class="text">*</text>
  118. 报名结束时间
  119. </view>
  120. <uni-datetime-picker type="datetime" v-model="form.signeTime" placeholder="请选择报名结束时间" :start="dayjs(Date.now()).format('YYYY-MM-DD')" />
  121. <!-- <picker mode="date" :start="dayjs(Date.now()).format('YYYY-MM-DD')" @change="changeApplyEndTime">
  122. <view class="box_input">
  123. <view class="picker" :class="{ pick: form.signeTime }">{{ form.signeTime ? form.signeTime : '请选择报名结束时间' }}</view>
  124. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  125. </view>
  126. </picker> -->
  127. </view>
  128. <!-- 报名总人数区域 -->
  129. <view class="box">
  130. <view class="box_title">
  131. <text class="text">*</text>
  132. 报名总人数
  133. </view>
  134. <view class="box_input">
  135. <input class="input" placeholder-style="color:#A6A6A6" placeholder="请输入报名总人数,0为不设限" v-model="form.totalNumber" />
  136. </view>
  137. </view>
  138. <!-- 报名范围区域 -->
  139. <view class="box">
  140. <view class="box_title">
  141. <text class="text">*</text>
  142. 报名范围
  143. </view>
  144. <picker :range="addressList" @change="changeRange">
  145. <view class="box_input">
  146. <view class="picker" :class="{ pick: form.addressRange }">{{ form.addressRange ? form.addressRange : '请选择报名范围' }}</view>
  147. <uni-icons type="down" size="24" color="#A6A6A6"></uni-icons>
  148. </view>
  149. </picker>
  150. </view>
  151. <!-- 咨询方式区域 -->
  152. <view class="box">
  153. <view class="box_title">
  154. <text class="text">*</text>
  155. 咨询方式
  156. </view>
  157. <view class="box_input">
  158. <input class="input" placeholder-style="color:#A6A6A6" placeholder="请输入咨询方式" v-model="form.phone" />
  159. </view>
  160. </view>
  161. <!-- 活动相册区域 -->
  162. <view class="box">
  163. <view class="box_title">
  164. <text class="text">*</text>
  165. 活动相册
  166. </view>
  167. <switch :checked="form.showPhoto" @change="switchChange" />
  168. </view>
  169. <view class="btn" @click="handleSubmit">提交</view>
  170. </view>
  171. </template>
  172. <script setup>
  173. import { ref, onMounted } from 'vue'
  174. import { getClubPage, getInsertActivity } from '@/api/index.js'
  175. import { uploadImage } from '@/api/uploadImage.js'
  176. import dayjs from 'dayjs'
  177. // #ifdef H5
  178. import quill from 'quill'
  179. window.Quill = quill
  180. // #endif
  181. // 组织数组
  182. const orgRange = ref([])
  183. // 报名范围数组
  184. const addressList = ['全部', '组织成员']
  185. // 富文本编辑器实例
  186. const editorCtx = ref()
  187. // 富文本工具栏显示隐藏控制
  188. const showTool = ref(false)
  189. // 是否为大标题
  190. const isTitle = ref(false)
  191. // 是否加粗
  192. const isBold = ref(false)
  193. // 是否倾斜
  194. const isIncline = ref(false)
  195. // 提交数据
  196. const form = ref({
  197. // 所属组织
  198. orgName: '',
  199. // 所属组织ID
  200. orgId: '',
  201. // 活动主题
  202. theme: '',
  203. // 活动详情
  204. themeDetail: '',
  205. // 活动开始时间
  206. startTime: '',
  207. // 活动结束时间
  208. endTime: '',
  209. // 活动地址
  210. address: '',
  211. // 纬度
  212. lat: '',
  213. // 经度
  214. lng: '',
  215. // 参与说明
  216. describes: '',
  217. // 报名开始时间
  218. signsTime: '',
  219. // 报名结束时间
  220. signeTime: '',
  221. // 报名总人数
  222. totalNumber: '',
  223. // 报名范围
  224. addressRange: '',
  225. // 组织成员:1 全部:2
  226. scope: '',
  227. // 咨询方式
  228. phone: '',
  229. // 活动相册
  230. showPhoto: false,
  231. // 是否可上传照片 是:1 否:2
  232. isImage: 2
  233. })
  234. onMounted(() => {
  235. // 获取组织分页下拉数据
  236. getOrgList()
  237. })
  238. // 获取组织分页下拉数据
  239. const getOrgList = async () => {
  240. let data = {
  241. currentPage: 1,
  242. pageCount: 100
  243. }
  244. const res = await getClubPage(data)
  245. // console.log(res)
  246. orgRange.value = res.data.list
  247. }
  248. // 选择所属组织时触发的回调
  249. const changeOrg = (e) => {
  250. // console.log(e.detail.value)
  251. let index = e.detail.value
  252. form.value.orgName = orgRange.value[index].name
  253. form.value.orgId = orgRange.value[index].id
  254. // console.log(form.value)
  255. }
  256. // 富文本编辑器初始化完成时触发
  257. const handleReady = () => {
  258. uni.createSelectorQuery()
  259. .select('.editor')
  260. .context((res) => {
  261. // 获取富文本编辑器实例
  262. editorCtx.value = res.context
  263. })
  264. .exec()
  265. }
  266. // 富文本编辑器聚焦时触发
  267. const handleFocus = () => {
  268. showTool.value = true
  269. }
  270. // 富文本编辑器失焦时触发
  271. const handleBlur = () => {
  272. // showTool.value = false
  273. editorCtx.value.getContents({
  274. success: (res) => {
  275. // console.log(res)
  276. if (res.html.trim() == '<p><br></p>') {
  277. uni.showToast({
  278. title: '活动详情不能为空',
  279. icon: 'none'
  280. })
  281. return
  282. } else {
  283. // getImgSrc(res.html)
  284. form.value.themeDetail = res.html
  285. // console.log(form.value)
  286. }
  287. }
  288. })
  289. }
  290. // 改变编辑器内样式时触发
  291. const handleStatusChange = (e) => {
  292. // console.log(e.detail)
  293. let detail = e.detail
  294. checkStatus(detail, 'header')
  295. checkStatus(detail, 'bold')
  296. checkStatus(detail, 'italic')
  297. }
  298. // 校验工具栏是否应该高亮
  299. const checkStatus = (detail, name) => {
  300. if (detail.hasOwnProperty(name)) {
  301. if (name == 'header') {
  302. isTitle.value = true
  303. }
  304. if (name == 'bold') {
  305. isBold.value = true
  306. }
  307. if (name == 'italic') {
  308. isIncline.value = true
  309. }
  310. } else {
  311. if (name == 'header') {
  312. isTitle.value = false
  313. }
  314. if (name == 'bold') {
  315. isBold.value = false
  316. }
  317. if (name == 'italic') {
  318. isIncline.value = false
  319. }
  320. }
  321. }
  322. // 点击大标题图标回调
  323. const handleTitle = () => {
  324. isTitle.value = !isTitle.value
  325. editorCtx.value.format('header', isTitle.value ? 'h2' : '')
  326. }
  327. // 点击加粗图标回调
  328. const handleBold = () => {
  329. isBold.value = !isBold.value
  330. editorCtx.value.format('bold')
  331. }
  332. // 点击倾斜图标回调
  333. const handleIncline = () => {
  334. isIncline.value = !isIncline.value
  335. editorCtx.value.format('italic')
  336. }
  337. // 点击横线图标回调
  338. const handleRowLine = () => {
  339. editorCtx.value.insertDivider()
  340. }
  341. // 点击图片图标回调
  342. const handleImage = () => {
  343. uni.chooseImage({
  344. success: async (res) => {
  345. // console.log(res)
  346. uni.showLoading({
  347. title: '上传中...',
  348. mask: true
  349. })
  350. for (let item of res.tempFilePaths) {
  351. let temp = await uploadImage(item)
  352. let result = JSON.parse(temp.data)
  353. // console.log(result)
  354. if (result.code == 200) {
  355. // 编辑器插入图片
  356. editorCtx.value.insertImage({
  357. src: result.data.fileUrl
  358. })
  359. }
  360. }
  361. uni.hideLoading()
  362. }
  363. })
  364. }
  365. // 点击确认图标回调
  366. const handleFinish = () => {
  367. showTool.value = false
  368. editorCtx.value.getContents({
  369. success: (res) => {
  370. // console.log(res)
  371. if (res.html.trim() == '<p><br></p>') {
  372. uni.showToast({
  373. title: '活动详情不能为空',
  374. icon: 'none'
  375. })
  376. return
  377. } else {
  378. // getImgSrc(res.html)
  379. form.value.themeDetail = res.html
  380. // console.log(form.value)
  381. }
  382. }
  383. })
  384. }
  385. // 选择活动开始时间回调
  386. // const changeStartTime = (e) => {
  387. // console.log(e)
  388. // console.log(form.value.startTime)
  389. // // form.value.startTime = e.detail.value + ' 00:00:00'
  390. // // console.log(form.value)
  391. // }
  392. // 选择活动结束时间回调
  393. // const changeEndTime = (e) => {
  394. // form.value.endTime = e.detail.value + ' 00:00:00'
  395. // // console.log(form.value)
  396. // }
  397. // 点击选择地址回调
  398. const clickAddress = () => {
  399. uni.chooseLocation({
  400. success: (res) => {
  401. // console.log(res)
  402. form.value.address = res.address
  403. form.value.lat = res.latitude
  404. form.value.lng = res.longitude
  405. // console.log(form.value)
  406. },
  407. fail: (err) => {
  408. console.log(err)
  409. }
  410. })
  411. }
  412. // 参与说明输入框失去焦点回调
  413. const inputDesc = (e) => {
  414. // console.log(e)
  415. form.value.describes = e.detail.value
  416. // console.log(form.value)
  417. }
  418. // 选择报名开始时间回调
  419. // const changeApplyStartTime = (e) => {
  420. // form.value.signsTime = e.detail.value + ' 00:00:00'
  421. // // console.log(form.value)
  422. // }
  423. // 选择报名结束时间回调
  424. // const changeApplyEndTime = (e) => {
  425. // form.value.signeTime = e.detail.value + ' 00:00:00'
  426. // // console.log(form.value)
  427. // }
  428. // 选择报名范围时的回调
  429. const changeRange = (e) => {
  430. let index = e.detail.value
  431. form.value.addressRange = addressList[index]
  432. form.value.scope = index == 0 ? 2 : 1
  433. // console.log(form.value)
  434. }
  435. // 活动相册切换回调
  436. const switchChange = (e) => {
  437. // console.log(e)
  438. form.value.showPhoto = e.detail.value
  439. form.value.isImage = form.value.showPhoto ? 1 : 2
  440. // console.log(form.value)
  441. }
  442. // 提交按钮回调
  443. const handleSubmit = () => {
  444. // 校验提交值是否为空
  445. let flag = checkValue()
  446. if (!flag) {
  447. uni.showModal({
  448. title: '提示',
  449. content: '确定提交吗?',
  450. success: (res) => {
  451. if (res.confirm) {
  452. submitReq()
  453. }
  454. }
  455. })
  456. }
  457. }
  458. // 提交请求
  459. const submitReq = async () => {
  460. const res = await getInsertActivity(form.value)
  461. uni.showToast({
  462. title: res.message,
  463. icon: 'success',
  464. mask: true
  465. })
  466. setTimeout(() => {
  467. uni.reLaunch({
  468. url: '/pages/activity/activity'
  469. })
  470. }, 1500)
  471. }
  472. // 校验提交值是否为空
  473. const checkValue = () => {
  474. let keyList = Object.keys(form.value)
  475. // 提示信息
  476. keyList.forEach((ele) => {
  477. // console.log(ele)
  478. if (ele == 'phone') {
  479. let regPhone = /^1[3-9]\d{9}$/
  480. if (!regPhone.test(form.value[ele])) {
  481. uni.showToast({
  482. title: `手机号码格式有误`,
  483. icon: 'none'
  484. })
  485. // 符合条件时强制打断循环
  486. keyList.length = 0
  487. }
  488. }
  489. if (form.value[ele] === '') {
  490. uni.showToast({
  491. title: `${mapValue(ele)}不能为空`,
  492. icon: 'none'
  493. })
  494. // 符合条件时强制打断循环
  495. keyList.length = 0
  496. }
  497. })
  498. // 判断是否是空值 true 为有空值
  499. let t = keyList.every((ele) => form.value[ele] === '')
  500. return t
  501. }
  502. // 映射
  503. const mapValue = (v) => {
  504. let msg = ''
  505. switch (v) {
  506. case 'orgName':
  507. case 'orgId':
  508. msg = '所属组织'
  509. break
  510. case 'theme':
  511. msg = '活动主题'
  512. break
  513. case 'themeDetail':
  514. msg = '活动详情'
  515. break
  516. case 'startTime':
  517. msg = '活动开始时间'
  518. break
  519. case 'endTime':
  520. msg = '活动结束时间'
  521. break
  522. case 'address':
  523. case 'lat':
  524. case 'lng':
  525. msg = '活动地址'
  526. break
  527. case 'describes':
  528. msg = '参与说明'
  529. break
  530. case 'signsTime':
  531. msg = '报名开始时间'
  532. break
  533. case 'signeTime':
  534. msg = '报名结束时间'
  535. break
  536. case 'totalNumber':
  537. msg = '报名总人数'
  538. break
  539. case 'addressRange':
  540. case 'scope':
  541. msg = '报名范围'
  542. break
  543. case 'phone':
  544. msg = '咨询方式'
  545. break
  546. default:
  547. msg = '内容'
  548. break
  549. }
  550. return msg
  551. }
  552. </script>
  553. <style lang="scss" scoped>
  554. .content {
  555. .box {
  556. margin-bottom: 15rpx;
  557. font-size: 28rpx;
  558. .box_title {
  559. display: flex;
  560. margin-bottom: 15rpx;
  561. .text {
  562. color: #d43030;
  563. }
  564. }
  565. .box_input {
  566. display: flex;
  567. justify-content: space-between;
  568. align-items: center;
  569. padding: 0 22rpx;
  570. width: 710rpx;
  571. height: 80rpx;
  572. border-radius: 6rpx;
  573. background-color: #f5f5f5;
  574. .input {
  575. width: 100%;
  576. height: 100%;
  577. font-size: 28rpx;
  578. }
  579. .picker {
  580. color: #a6a6a6;
  581. overflow: hidden;
  582. white-space: nowrap;
  583. text-overflow: ellipsis;
  584. }
  585. .pick {
  586. color: #000;
  587. }
  588. }
  589. .box_textarea {
  590. width: 710rpx;
  591. height: 308rpx;
  592. border-radius: 6rpx;
  593. background-color: #f5f5f5;
  594. .textarea {
  595. box-sizing: border-box;
  596. padding: 10rpx;
  597. width: 100%;
  598. height: 100%;
  599. font-size: 28rpx;
  600. }
  601. }
  602. .box_richtext {
  603. position: relative;
  604. box-sizing: border-box;
  605. padding: 15rpx 20rpx;
  606. width: 710rpx;
  607. height: 400rpx;
  608. border-radius: 6rpx;
  609. border: 2rpx solid #e6e6e6;
  610. background-color: #f5f5f5;
  611. .editor {
  612. width: 100%;
  613. height: 302rpx;
  614. min-height: 302rpx;
  615. }
  616. .tools {
  617. position: absolute;
  618. left: 0;
  619. right: 0;
  620. bottom: 0;
  621. display: flex;
  622. justify-content: space-around;
  623. align-items: center;
  624. height: 80rpx;
  625. }
  626. }
  627. }
  628. .btn {
  629. display: flex;
  630. justify-content: center;
  631. align-items: center;
  632. margin: 95rpx 0;
  633. width: 710rpx;
  634. height: 80rpx;
  635. font-size: 28rpx;
  636. color: #fff;
  637. border-radius: 8rpx;
  638. background-color: #007aff;
  639. }
  640. }
  641. ::v-deep {
  642. .uni-date-editor--x {
  643. background-color: #f5f5f5;
  644. }
  645. .uni-date-x {
  646. background-color: #f5f5f5;
  647. }
  648. }
  649. </style>