index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. <script setup lang="ts" name="Activity">
  2. import dayjs from "dayjs"
  3. import apis from "@a"
  4. import { permissionNames } from "@@/services/PermissionNames"
  5. import vueQr from "vue-qr/src/packages/vue-qr.vue"
  6. //const { activity_audit_status } = useDict("activity_audit_status")
  7. const tableRef = ref()
  8. const modalRef = ref()
  9. const detailModalRef = ref()
  10. const applyModalRef = ref()
  11. const opts = reactive({
  12. columns: [
  13. { field: "activityId", name: "活动id", visible: false },
  14. { field: "title", name: "活动标题", visible: true, tooltip: true },
  15. { field: "categoryName", name: "活动类别", visible: true, width: 100 },
  16. { field: "cost", name: "活动费用", visible: true, tooltip: true },
  17. { field: "peopleMax", name: "人数上限", visible: true, isSort: true, width: 80 },
  18. { field: "activityDate", name: "活动日期", visible: true, isSort: true, width: 120 },
  19. { field: "expiryDate", name: "截止日期", visible: true, isSort: true, width: 120 },
  20. // { field: "area", name: "活动区域", visible: true, width: 80 },
  21. { field: "address", name: "活动地点", visible: true },
  22. { field: "auditStatus", name: "活动状态", visible: true, isSort: true, width: 150 },
  23. { field: "auditUser", name: "审核人", visible: true },
  24. { field: "auditTime", name: "审核时间", visible: true, isSort: true, width: 185 },
  25. { field: "isHead", name: "是否置顶", visible: true, width: 80 },
  26. { field: "isHot", name: "是否热门", visible: true, width: 80 },
  27. { field: "actions", name: `操作`, width: 150 }
  28. ],
  29. queryParams: {
  30. title: undefined,
  31. category: undefined,
  32. dateRangeActivityDate: undefined,
  33. dateRangeExpiryDate: undefined,
  34. area: undefined,
  35. address: undefined,
  36. status: undefined
  37. },
  38. searchFormItems: [
  39. {
  40. field: "auditStatus",
  41. span: 6,
  42. label: "活动状态",
  43. class: "w-100",
  44. required: false,
  45. component: "Dict",
  46. props: {
  47. showAll: true,
  48. placeholder: "请选择状态",
  49. dictType: "activity_audit_status",
  50. type: "radio"
  51. },
  52. listeners: {
  53. change: (v: any) => {
  54. handleQuery()
  55. }
  56. }
  57. },
  58. {
  59. field: "isClose",
  60. span: 6,
  61. label: "结束状态",
  62. class: "w-100",
  63. required: false,
  64. component: "Dict",
  65. props: {
  66. showAll: true,
  67. placeholder: "请选择状态",
  68. dictType: "sys_close_status",
  69. type: "radio"
  70. },
  71. listeners: {
  72. change: (v: any) => {
  73. handleQuery()
  74. }
  75. }
  76. },
  77. {
  78. field: "dateRangeActivityDate",
  79. label: "活动日期",
  80. class: "w-100",
  81. span: 6,
  82. required: false,
  83. component: "D",
  84. placeholder: "请选择活动日期",
  85. props: {
  86. type: "daterange",
  87. valueFormat: "YYYY-MM-DD",
  88. rangeSeparator: "-",
  89. startPlaceholder: "开始日期",
  90. endPlaceholder: "结束日期"
  91. },
  92. listeners: {
  93. change: (v: any) => {
  94. queryParams.value.dateRangeActivityDate = v
  95. }
  96. }
  97. },
  98. {
  99. field: "dateRangeExpiryDate",
  100. label: "截止日期",
  101. span: 6,
  102. class: "w-100",
  103. required: false,
  104. component: "D",
  105. placeholder: "请选择截止日期",
  106. props: {
  107. type: "daterange",
  108. valueFormat: "YYYY-MM-DD",
  109. rangeSeparator: "-",
  110. startPlaceholder: "开始日期",
  111. endPlaceholder: "结束日期"
  112. },
  113. listeners: {
  114. change: (v: any) => {
  115. queryParams.value.dateRangeExpiryDate = v
  116. }
  117. }
  118. },
  119. {
  120. field: "title",
  121. label: "活动标题",
  122. span: 6,
  123. class: "w-100",
  124. required: false,
  125. placeholder: "请输入活动标题",
  126. component: "I",
  127. listeners: {
  128. keyup: (e: KeyboardEvent) => {
  129. if (e.code == "Enter") {
  130. handleQuery()
  131. }
  132. }
  133. }
  134. },
  135. {
  136. field: "category",
  137. label: "活动类别",
  138. class: "w-100",
  139. span: 6,
  140. required: false,
  141. placeholder: "请选择活动类别",
  142. data: () => categoryColumns.value,
  143. component: "vs",
  144. props: {
  145. showAll: true
  146. },
  147. listeners: {
  148. change: () => {
  149. handleQuery()
  150. }
  151. }
  152. },
  153. {
  154. field: "address",
  155. span: 6,
  156. label: "活动地点",
  157. class: "w-100",
  158. required: false,
  159. placeholder: "请输入活动地点",
  160. component: "I",
  161. listeners: {
  162. keyup: (e: KeyboardEvent) => {
  163. if (e.code == "Enter") {
  164. handleQuery()
  165. }
  166. }
  167. }
  168. }
  169. ] as any,
  170. permission: permissionNames.AmActivityActivity,
  171. handleBtns: [],
  172. handleFuns: {
  173. handleUpdate: () => {
  174. const row = tableRef.value.getSelected()
  175. handleUpdate(row)
  176. },
  177. handleDelete: () => {
  178. const rows = tableRef.value.getSelecteds()
  179. handleDelete(rows)
  180. }
  181. },
  182. customBtns: [],
  183. tableListFun: apis.amActivity.activityApi.listActivity,
  184. getEntityFun: apis.amActivity.activityApi.getActivity,
  185. deleteEntityFun: apis.amActivity.activityApi.delActivity,
  186. exportUrl: apis.amActivity.activityApi.exportUrl,
  187. exportName: "Activity",
  188. modalTitle: "活动信息",
  189. formItems: [
  190. {
  191. field: "title",
  192. label: "活动标题",
  193. class: "w-100",
  194. required: true,
  195. placeholder: "请输入活动标题",
  196. component: "I",
  197. span: 12
  198. },
  199. {
  200. field: "category",
  201. label: "活动类别",
  202. class: "w-100",
  203. required: true,
  204. placeholder: "请选择活动类别",
  205. data: () => categoryColumns.value,
  206. component: "vs",
  207. span: 12
  208. },
  209. {
  210. field: "peopleMax",
  211. type: "number",
  212. label: "人数上限",
  213. class: "w-100",
  214. required: true,
  215. placeholder: "请输入活动人数上限",
  216. component: "I",
  217. span: 12
  218. },
  219. {
  220. field: "contact",
  221. label: "联系方式",
  222. class: "w-100",
  223. required: true,
  224. placeholder: "请输入联系方式",
  225. component: "I",
  226. span: 12
  227. },
  228. {
  229. field: "activityDate",
  230. label: "活动日期",
  231. class: "w-100",
  232. required: true,
  233. component: "D",
  234. props: {
  235. placeholder: "请选择活动日期",
  236. type: "date",
  237. valueFormat: "YYYY-MM-DD"
  238. },
  239. span: 12
  240. },
  241. {
  242. field: "expiryDate",
  243. label: "截止日期",
  244. class: "w-100",
  245. required: true,
  246. component: "D",
  247. props: {
  248. placeholder: "请选择截止报名日期",
  249. type: "date",
  250. valueFormat: "YYYY-MM-DD"
  251. },
  252. span: 12
  253. },
  254. {
  255. field: "area",
  256. label: "活动方式",
  257. class: "w-100",
  258. required: true,
  259. component: "vs",
  260. data: () => {
  261. return [
  262. { label: "线上活动", value: "线上活动" },
  263. { label: "线下活动", value: "线下活动" }
  264. ]
  265. },
  266. props: {
  267. type: "radio"
  268. },
  269. span: 12
  270. },
  271. {
  272. show: (v) => {
  273. return v.area === "线下活动"
  274. },
  275. field: "address",
  276. label: "详细地点",
  277. class: "w-100",
  278. required: true,
  279. placeholder: "请输入活动地点",
  280. component: "I",
  281. span: 24
  282. },
  283. {
  284. field: "needCost",
  285. label: "是否付费",
  286. class: "w-100",
  287. required: true,
  288. component: "vs",
  289. data: () => {
  290. return [
  291. { label: "活动不需要缴费", value: "0" },
  292. { label: "需要缴费(填写费用说明)", value: "1" }
  293. ]
  294. },
  295. props: {
  296. type: "radio"
  297. },
  298. span: 24
  299. },
  300. {
  301. show: (v) => {
  302. return v.needCost === "1"
  303. },
  304. field: "cost",
  305. label: "费用说明",
  306. class: "w-100",
  307. required: true,
  308. placeholder: "活动费用说明,例如免费、AA制或者多少元",
  309. component: "I",
  310. type: "textarea",
  311. props: {
  312. rows: 5
  313. }
  314. },
  315. {
  316. field: "content",
  317. label: "活动详情",
  318. placeholder: "请介绍活动详情",
  319. class: "w-100",
  320. required: true,
  321. component: "I",
  322. type: "textarea",
  323. props: {
  324. rows: 5
  325. }
  326. },
  327. {
  328. field: "images",
  329. label: "图片上传",
  330. class: "w-100",
  331. component: "VU",
  332. props: {
  333. uploadUrl: "/oss/upload/activity",
  334. prefixUrl: "/oss/preview/",
  335. limit: 8
  336. }
  337. }
  338. ] as any,
  339. resetForm: () => {
  340. form.value = emptyFormData.value
  341. },
  342. labelWidth: "80px",
  343. emptyFormData: {
  344. activityId: undefined,
  345. title: undefined,
  346. category: undefined,
  347. content: undefined,
  348. needCost: 0,
  349. cost: undefined,
  350. peopleMax: 50,
  351. isHead: 0,
  352. isHot: 0,
  353. isSys: 1,
  354. activityDate: undefined,
  355. expiryDate: undefined,
  356. auditStatus: undefined,
  357. contact: "",
  358. area: "线上活动",
  359. address: "",
  360. images: ""
  361. }
  362. })
  363. const { queryParams, emptyFormData } = toRefs(opts)
  364. const form = ref<any>(emptyFormData.value)
  365. const categoryColumns = ref<any[]>([])
  366. /** 搜索按钮操作 */
  367. function handleQuery() {
  368. addDateRange(queryParams.value, queryParams.value.dateRangeActivityDate, "ActivityDate")
  369. addDateRange(queryParams.value, queryParams.value.dateRangeExpiryDate, "ExpiryDate")
  370. tableRef.value?.search()
  371. }
  372. /** 重置按钮操作 */
  373. function resetQuery() {
  374. queryParams.value.dateRangeActivityDate = [] as any
  375. addDateRange(queryParams.value, queryParams.value.dateRangeActivityDate, "ActivityDate")
  376. queryParams.value.dateRangeExpiryDate = [] as any
  377. addDateRange(queryParams.value, queryParams.value.dateRangeExpiryDate, "ExpiryDate")
  378. //
  379. }
  380. /** 修改按钮操作 */
  381. function handleUpdate(row: any) {
  382. tableRef.value.defaultHandleFuns.handleUpdate("", row)
  383. }
  384. /** 删除按钮操作 */
  385. function handleDelete(rows: any[]) {
  386. tableRef.value.defaultHandleFuns.handleDelete("", rows)
  387. }
  388. /** 提交按钮 */
  389. function submitForm() {
  390. apis.amActivity.activityApi.addOrUpdateActivity(form.value).then(() => {
  391. handleQuery()
  392. })
  393. }
  394. const detailType = ref<"A" | "D">("A")
  395. const detailModalTitle = computed(() => {
  396. return detailType.value === "A" ? "活动审核" : "活动详情"
  397. })
  398. const detailData = ref<any>({})
  399. const auditStatus = ref("1")
  400. function handleAudit(row: any) {
  401. detailType.value = "A"
  402. apis.amActivity.activityApi.getActivity(row.activityId).then((res) => {
  403. detailData.value = res.data
  404. auditStatus.value = detailData.value.auditStatus == 0 ? "1" : "2"
  405. detailModalRef.value.show()
  406. })
  407. }
  408. const applyDetailTotal = ref(0)
  409. const applyDetailList = ref<any[]>([])
  410. const qrCode = ref({
  411. logo: "/media/yzxyh.png",
  412. size: 200
  413. })
  414. function handleDetail(row: any) {
  415. detailType.value = "D"
  416. apis.amActivity.activityApi.getActivity(row.activityId).then((res) => {
  417. detailData.value = res.data
  418. auditStatus.value = detailData.value.auditStatus == 0 ? "1" : "2"
  419. if (detailData.value.auditStatus == "1" || detailData.value.auditStatus == "3") {
  420. }
  421. detailModalRef.value.show()
  422. })
  423. apis.amActivity.activityApi
  424. .listApply({
  425. activityId: row.activityId,
  426. auditStatus: "1",
  427. pageNum: 1,
  428. pageSize: 10000,
  429. orderByColumn: "auditStatus",
  430. isAsc: "ascending"
  431. })
  432. .then((res: any) => {
  433. applyDetailTotal.value = res.total
  434. applyDetailList.value = res.rows
  435. })
  436. }
  437. function submitDetail() {
  438. if (detailType.value === "D") {
  439. return
  440. }
  441. if (detailType.value === "A") {
  442. apis.amActivity.activityApi
  443. .auditActivity(detailData.value.activityId, auditStatus.value)
  444. .then(() => {
  445. detailModalRef.value.hide()
  446. handleQuery()
  447. })
  448. }
  449. }
  450. function handleDownloadQr(id: string) {
  451. let myImg = document.querySelector("#" + id + " img") as HTMLImageElement
  452. let url = myImg?.src
  453. let a = document.createElement("a")
  454. a.href = url
  455. a.download = `${detailData.value?.title || ""}——${id == "apply-qr" ? "报名" : "签到"}二维码` //下载图名称
  456. a.click()
  457. }
  458. function handleClose(row: any) {
  459. message
  460. .confirm("是否确认关闭 [" + row.title + "] 的活动,结束后不可恢复?")
  461. .then(() => {
  462. apis.amActivity.activityApi.closeActivity(row.activityId).then(() => {
  463. handleQuery()
  464. message.msgSuccess("关闭成功")
  465. })
  466. })
  467. .catch(() => {
  468. //
  469. })
  470. }
  471. function handleTop(row: any) {
  472. if (row.auditStatus == "0" || row.auditStatus == "2") {
  473. message.msgWarning("活动未审核通过")
  474. return
  475. }
  476. if (row.isClose == "1") {
  477. message.msgWarning("活动已关闭")
  478. return
  479. }
  480. const text = row.isHead === "0" ? "取消置顶" : "置顶"
  481. message
  482. .confirm("确认要" + text + " [" + row.title + "] 的活动?")
  483. .then(() => {
  484. apis.amActivity.activityApi.topActivity(row.activityId, row.isHead).then(() => {
  485. message.msgSuccess(text + "成功")
  486. handleQuery()
  487. })
  488. })
  489. .catch(() => {
  490. row.isHead = row.isHead === "0" ? "1" : "0"
  491. })
  492. }
  493. function handleHot(row: any) {
  494. if (row.auditStatus == "0" || row.auditStatus == "2") {
  495. message.msgWarning("活动未审核通过")
  496. return
  497. }
  498. if (row.isClose == "1") {
  499. message.msgWarning("活动已关闭")
  500. return
  501. }
  502. const text = row.isHot === "0" ? "取消热门" : "热门"
  503. message
  504. .confirm("确认要" + text + " [" + row.title + "] 的活动?")
  505. .then(() => {
  506. apis.amActivity.activityApi.topActivity(row.activityId, row.isHot).then(() => {
  507. message.msgSuccess(text + "成功")
  508. handleQuery()
  509. })
  510. })
  511. .catch(() => {
  512. row.isHot = row.isHot === "0" ? "1" : "0"
  513. })
  514. }
  515. const activity = ref<any>({})
  516. const currrentApplyId = ref()
  517. const applyAuditList = ref<any[]>([])
  518. function handleAplly(row: any) {
  519. activity.value = row
  520. currrentApplyId.value = row.activityId
  521. handleQueryApply()
  522. }
  523. const applyAudit = ref("")
  524. const applyCost = ref("")
  525. const applyAttend = ref("")
  526. function handleQueryApply() {
  527. const params = {
  528. activityId: currrentApplyId.value,
  529. auditStatus: applyAudit.value,
  530. costStatus: applyCost.value,
  531. isAttend: applyAttend.value,
  532. pageNum: 1,
  533. pageSize: 10000,
  534. orderByColumn: "auditStatus",
  535. isAsc: "ascending"
  536. }
  537. apis.amActivity.activityApi.listApply(params).then((res: any) => {
  538. applyAuditList.value = res.rows
  539. applyModalRef.value.show()
  540. })
  541. }
  542. function handleApllyAudit(item: any, status: string) {
  543. const text = status === "1" ? "通过" : "拒绝"
  544. message.confirm("确认要" + text + " [" + item.name + "] 的报名?").then(() => {
  545. apis.amActivity.activityApi.auditApply(item.applyId, status).then(() => {
  546. message.msgSuccess(text + "报名成功")
  547. handleQueryApply()
  548. handleQuery()
  549. })
  550. })
  551. }
  552. function handleApllyCost(item: any) {
  553. message.prompt("请输入费用(0表示未付费)", "确认付费").then((val: any) => {
  554. console.log("val", val, item.applyId)
  555. apis.amActivity.activityApi.auditCost(item.applyId, val.value).then(() => {
  556. message.msgSuccess("付费审核成功")
  557. handleQueryApply()
  558. handleQuery()
  559. })
  560. })
  561. }
  562. const formatUrl = (url: string) => {
  563. if (url) {
  564. url = import.meta.env.VITE_APP_BASE_API + url
  565. }
  566. return url
  567. }
  568. function loadCategory() {
  569. apis.system.categoryApi.getActivityCategory().then((res) => {
  570. categoryColumns.value = []
  571. if (res.data.length > 0) {
  572. res.data.forEach((v: any) => {
  573. categoryColumns.value.push({ label: v.categoryName, value: v.categoryId })
  574. })
  575. }
  576. })
  577. }
  578. function init() {
  579. loadCategory()
  580. }
  581. onMounted(init)
  582. </script>
  583. <template>
  584. <div class="app-container">
  585. <VbDataTable
  586. ref="tableRef"
  587. keyField="activityId"
  588. :columns="opts.columns"
  589. :handle-perm="opts.permission"
  590. :handle-btns="opts.handleBtns"
  591. :handle-funs="opts.handleFuns"
  592. :search-form-items="opts.searchFormItems"
  593. :custom-btns="opts.customBtns"
  594. :remote-fun="opts.tableListFun"
  595. :get-entity-fun="opts.getEntityFun"
  596. :delete-entity-fun="opts.deleteEntityFun"
  597. :export-url="opts.exportUrl"
  598. :export-name="opts.exportName"
  599. :modal="modalRef"
  600. :reset-form-fun="opts.resetForm"
  601. v-model:form-data="form"
  602. :query-params="queryParams"
  603. :check-multiple="true"
  604. :reset-search-form-fun="resetQuery"
  605. :custom-search-fun="handleQuery">
  606. <template #auditStatus="{ row }">
  607. <div class="d-flex justify-content-around">
  608. <DictTag type="activity_audit_status" :value="row.auditStatus"></DictTag>
  609. <DictTag class="ml-2" type="sys_close_status" :value="row.isClose"></DictTag>
  610. </div>
  611. </template>
  612. <template #activityDate="{ row }">
  613. <span :class="{ 'text-danger': dayjs(row.activityDate).isBefore(dayjs(new Date())) }">
  614. {{ dayjs(row.activityDate).format("YYYY-MM-DD") }}
  615. </span>
  616. </template>
  617. <template #expiryDate="{ row }">
  618. <span :class="{ 'text-danger': dayjs(row.expiryDate).isBefore(dayjs(new Date())) }">
  619. {{ dayjs(row.expiryDate).format("YYYY-MM-DD") }}
  620. </span>
  621. </template>
  622. <template #address="{ row }">
  623. <span v-if="row.area == '线上活动'" class="text-info">{{ row.area }}</span>
  624. <span v-else>{{ row.address }}</span>
  625. </template>
  626. <template #isHead="{ row }">
  627. <el-switch
  628. v-model="row.isHead"
  629. active-value="1"
  630. inactive-value="0"
  631. @change="handleTop(row)"></el-switch>
  632. </template>
  633. <template #isHot="{ row }">
  634. <el-switch
  635. v-model="row.isHot"
  636. active-value="1"
  637. inactive-value="0"
  638. @change="handleHot(row)"></el-switch>
  639. </template>
  640. <template #actions="{ row }">
  641. <vb-tooltip v-if="row.auditStatus == 1" content="详情 " placement="top">
  642. <el-button
  643. link
  644. type="primary"
  645. @click="handleDetail(row)"
  646. v-hasPermission="permissionNames.AmActivityActivityClose">
  647. <template #icon>
  648. <VbIcon icon-name="notepad" icon-type="duotone" class="fs-3"></VbIcon>
  649. </template>
  650. </el-button>
  651. </vb-tooltip>
  652. <vb-tooltip v-if="row.auditStatus == 1" content="报名审核 " placement="top">
  653. <el-button
  654. link
  655. type="warning"
  656. @click="handleAplly(row)"
  657. v-hasPermission="permissionNames.AmActivityActivityApply">
  658. <template #icon>
  659. <VbIcon icon-name="check-circle" icon-type="duotone" class="fs-3"></VbIcon>
  660. </template>
  661. </el-button>
  662. </vb-tooltip>
  663. <vb-tooltip v-else content="审核" placement="top">
  664. <el-button
  665. link
  666. :type="row.auditStatus == 0 ? 'success' : 'danger'"
  667. @click="handleAudit(row)"
  668. v-hasPermission="permissionNames.AmActivityActivityAudit">
  669. <template #icon>
  670. <VbIcon icon-name="check-square" icon-type="duotone" class="fs-3"></VbIcon>
  671. </template>
  672. </el-button>
  673. </vb-tooltip>
  674. <vb-tooltip v-if="row.isClose == 0" content="关闭 " placement="top">
  675. <el-button
  676. link
  677. type="info"
  678. @click="handleClose(row)"
  679. v-hasPermission="permissionNames.AmActivityActivityClose">
  680. <template #icon>
  681. <VbIcon icon-name="cross-square" icon-type="duotone" class="fs-3"></VbIcon>
  682. </template>
  683. </el-button>
  684. </vb-tooltip>
  685. <vb-tooltip v-if="row.isClose == 0" content="修改" placement="top">
  686. <el-button
  687. link
  688. type="primary"
  689. @click="handleUpdate(row)"
  690. v-hasPermission="permissionNames.AmActivityActivityEdit">
  691. <template #icon>
  692. <VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
  693. </template>
  694. </el-button>
  695. </vb-tooltip>
  696. <vb-tooltip v-if="row.auditStatus == 0 || row.isClose == 1" content="删除" placement="top">
  697. <el-button
  698. link
  699. type="primary"
  700. @click="handleDelete([row])"
  701. v-hasPermission="permissionNames.AmActivityActivityRemove">
  702. <template #icon>
  703. <VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
  704. </template>
  705. </el-button>
  706. </vb-tooltip>
  707. </template>
  708. </VbDataTable>
  709. <VbModal
  710. v-model:modal="modalRef"
  711. :title="opts.modalTitle"
  712. :form-data="form"
  713. :form-items="opts.formItems"
  714. :label-width="opts.labelWidth"
  715. append-to-body
  716. @confirm="submitForm"></VbModal>
  717. <VbModal
  718. modalBodyClass="detail-modal"
  719. v-model:modal="detailModalRef"
  720. :title="detailModalTitle"
  721. @confirm="submitDetail"
  722. append-to-body>
  723. <template #body>
  724. <el-row :gutter="20">
  725. <el-col :span="24">
  726. <dl>
  727. <dt>活动标题:</dt>
  728. <dd>{{ detailData.title }}</dd>
  729. </dl>
  730. </el-col>
  731. <el-col :span="12">
  732. <dl>
  733. <dt>活动类别:</dt>
  734. <dd>{{ detailData.categoryName }}</dd>
  735. </dl>
  736. </el-col>
  737. <el-col :span="12">
  738. <dl>
  739. <dt>人数上限</dt>
  740. <dd>{{ detailData.peopleMax }}</dd>
  741. </dl>
  742. </el-col>
  743. <el-col :span="24">
  744. <dl>
  745. <dt>费用说明:</dt>
  746. <dd>{{ detailData.cost }}</dd>
  747. </dl>
  748. </el-col>
  749. <el-col :span="12">
  750. <dl>
  751. <dt>活动日期:</dt>
  752. <dd>{{ detailData.activityDate }}</dd>
  753. </dl>
  754. </el-col>
  755. <el-col :span="12">
  756. <dl>
  757. <dt>截止日期:</dt>
  758. <dd>{{ detailData.expiryDate }}</dd>
  759. </dl>
  760. </el-col>
  761. <el-col :span="24">
  762. <dl>
  763. <dt>活动地点:</dt>
  764. <dd>
  765. <span v-if="detailData.area == '线下活动'">
  766. {{ detailData.address }}
  767. </span>
  768. <span v-else>线上活动</span>
  769. </dd>
  770. </dl>
  771. </el-col>
  772. <el-col :span="24">
  773. <dl>
  774. <dt>活动详情:</dt>
  775. <dd>{{ detailData.content }}</dd>
  776. </dl>
  777. </el-col>
  778. </el-row>
  779. <div class="d-flex justify-content-center my-3" v-if="detailData.images">
  780. <VbImagePreview :src="detailData.images" :prefixSrc="'/oss/preview/'"></VbImagePreview>
  781. </div>
  782. <div v-if="detailType == 'A'" class="d-flex justify-content-center">
  783. <vb-select
  784. v-model="auditStatus"
  785. label="审核结果"
  786. type="radio"
  787. :data="[
  788. { label: '通过', value: '1' },
  789. { label: '拒绝', value: '2' }
  790. ]"></vb-select>
  791. </div>
  792. <div
  793. v-else-if="
  794. detailType == 'D' && detailData.auditStatus != '0' && applyDetailList.length > 0
  795. "
  796. class="d-flex flex-column align-item-center py-5">
  797. <div class="d-flex justify-content-center mb-3">
  798. <div class="px-15 d-flex flex-column" id="apply-qr">
  799. <div class="w-100 text-center text-primary fs-4">
  800. <span class="fw-bold">报名</span>
  801. 二维码
  802. </div>
  803. <vue-qr
  804. ref="qrCode"
  805. :text="`AD@${detailData.activityId}`"
  806. :logoSrc="qrCode.logo"
  807. :logoCornerRadius="`50%`"
  808. color-dark="#0e9489"
  809. :width="qrCode.size"
  810. :height="qrCode.size"></vue-qr>
  811. <el-button type="primary" @click="handleDownloadQr('apply-qr')">保存</el-button>
  812. </div>
  813. <div class="px-15 d-flex flex-column" id="attend-qr">
  814. <div class="w-100 text-center text-primary fs-4">
  815. <span class="fw-bold">签到</span>
  816. 二维码
  817. </div>
  818. <vue-qr
  819. ref="qrCode"
  820. :text="`AA@${detailData.activityId}`"
  821. :logoSrc="qrCode.logo"
  822. color-dark="#0e9489"
  823. :width="qrCode.size"
  824. :height="qrCode.size"></vue-qr>
  825. <el-button type="primary" @click="handleDownloadQr('attend-qr')">保存</el-button>
  826. </div>
  827. </div>
  828. <div class="text-danger text-center mb-3">注意:二维码需要小程序内部扫码功能扫码</div>
  829. <div class="d-flex align-items-center justify-content-between mb-2">
  830. <dl class="mb-0">
  831. <dt>报名详情:</dt>
  832. <dd>{{ applyDetailTotal }} 人报名</dd>
  833. </dl>
  834. <el-button class="me-2" type="primary" @click="handleAplly(detailData)">
  835. 审核报名
  836. </el-button>
  837. </div>
  838. <div class="d-flex flex-wrap px-20">
  839. <VbSymbol
  840. class="position-relative m-1"
  841. :size="50"
  842. shape="circle"
  843. :src="formatUrl(v.avatar)"
  844. :text="v.name"
  845. v-for="(v, i) in applyDetailList"
  846. :key="i"></VbSymbol>
  847. </div>
  848. </div>
  849. </template>
  850. </VbModal>
  851. <VbModal
  852. modalBodyClass="apply-modal"
  853. v-model:modal="applyModalRef"
  854. title="报名详情"
  855. :shown-event="
  856. () => {
  857. detailModalRef.hide()
  858. }
  859. "
  860. append-to-body>
  861. <template #body>
  862. <div class="w-100 px-10">
  863. <div v-if="applyAuditList.length">人员数量:{{ applyAuditList.length }} 人</div>
  864. <el-row :gutter="20" class="apply-detail">
  865. <el-col :span="8" class="justify-content-center">
  866. <vb-select
  867. v-model="applyAudit"
  868. type="radio"
  869. @change="handleQueryApply"
  870. :data="[
  871. { label: '全部', value: '' },
  872. { label: '未审核', value: '0' },
  873. { label: '已审核', value: '1' }
  874. ]"></vb-select>
  875. </el-col>
  876. <el-col :span="8" class="justify-content-center">
  877. <vb-select
  878. v-model="applyCost"
  879. type="radio"
  880. @change="handleQueryApply"
  881. :data="[
  882. { label: '全部', value: '' },
  883. { label: '未付费', value: '0' },
  884. { label: '已付费', value: '1' }
  885. ]"></vb-select>
  886. </el-col>
  887. <el-col :span="8" class="justify-content-center">
  888. <vb-select
  889. v-model="applyAttend"
  890. type="radio"
  891. @change="handleQueryApply"
  892. :data="[
  893. { label: '全部', value: '' },
  894. { label: '未签到', value: '0' },
  895. { label: '已签到', value: '1' }
  896. ]"></vb-select>
  897. </el-col>
  898. </el-row>
  899. <el-row :gutter="15">
  900. <el-col :span="12" class="p-2" v-for="(v, i) in applyAuditList" :key="i">
  901. <div
  902. class="d-flex align-items-center p-2"
  903. style="border-radius: 8px; background-color: #f2f2f2">
  904. <div class="w-80px d-flex justify-content-center me-2">
  905. <VbSymbol
  906. class="position-relative m-1"
  907. :size="50"
  908. shape="circle"
  909. :src="formatUrl(v.avatar)"
  910. :text="v.name"></VbSymbol>
  911. </div>
  912. <div class="w-100 d-flex flex-column">
  913. <span class="d-flex align-items-center">
  914. <span class="me-2">{{ v.name }}</span>
  915. <el-tag v-if="v.auditStatus == '0'">待审核</el-tag>
  916. <template v-else-if="v.auditStatus == '1'">
  917. <el-tag type="success" v-if="activity.needCost == '1' && v.costStatus == '1'">
  918. 付费:{{ v.cost }}
  919. </el-tag>
  920. <el-tag
  921. type="danger"
  922. v-else-if="activity.needCost == '1' && v.costStatus == '0'">
  923. 未付费
  924. </el-tag>
  925. <el-tag type="success" v-else>已审核</el-tag>
  926. <el-tag type="success" class="ms-1" v-if="v.isAttend == '1'">已签到</el-tag>
  927. </template>
  928. <el-tag type="danger" v-else-if="v.auditStatus == '2'">已拒绝</el-tag>
  929. </span>
  930. <span class="my-1">{{ v.createTime }}</span>
  931. </div>
  932. <div
  933. v-if="activity.isClose == '0'"
  934. class="d-flex w-80px pe-2 flex-column justify-content-around">
  935. <el-button
  936. type="primary"
  937. size="small"
  938. class="my-1 mx-0 w-100"
  939. @click="handleApllyAudit(v, '1')"
  940. v-if="v.auditStatus != '1'">
  941. 通过
  942. </el-button>
  943. <el-button
  944. type="danger"
  945. size="small"
  946. class="my-1 mx-0 w-100"
  947. @click="handleApllyAudit(v, '2')"
  948. v-if="v.auditStatus != '2'">
  949. 拒绝
  950. </el-button>
  951. <el-button
  952. type="success"
  953. size="small"
  954. class="my-1 mx-0 w-100"
  955. @click="handleApllyCost(v)"
  956. v-if="activity.needCost == '1' && v.auditStatus == '1'">
  957. 付费
  958. </el-button>
  959. </div>
  960. </div>
  961. </el-col>
  962. </el-row>
  963. </div>
  964. </template>
  965. </VbModal>
  966. </div>
  967. </template>
  968. <style>
  969. .apply-detail .el-radio {
  970. margin-right: 12px;
  971. }
  972. </style>