ActivityManagement.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <div class="activity-management">
  3. <el-card>
  4. <template #header>
  5. <div class="search-section">
  6. <div class="search-row">
  7. <el-form :model="searchForm" inline label-width="86px">
  8. <el-form-item label="活动 ID">
  9. <el-input v-model="searchForm.id" clearable placeholder="精确查询活动编号" style="width: 180px" />
  10. </el-form-item>
  11. <el-form-item label="活动名称">
  12. <el-input v-model="searchForm.name" clearable placeholder="对内标题/对客名称" style="width: 220px" />
  13. </el-form-item>
  14. <el-form-item label="活动类型">
  15. <el-select v-model="searchForm.category" clearable placeholder="全部" style="width: 150px">
  16. <el-option label="普通活动" value="普通活动" />
  17. <el-option label="卓越迎新礼" value="卓越迎新礼" />
  18. <el-option label="层层礼" value="层层礼" />
  19. <el-option label="MGM" value="MGM" />
  20. <el-option label="信用卡活动" value="信用卡活动" />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="活动状态">
  24. <el-select v-model="searchForm.status" clearable placeholder="全部" style="width: 140px">
  25. <el-option label="草稿" value="draft" />
  26. <el-option label="待审核" value="pending" />
  27. <el-option label="审核通过" value="approved" />
  28. <el-option label="已发布" value="published" />
  29. <el-option label="已下线" value="offline" />
  30. <el-option label="已结束" value="ended" />
  31. </el-select>
  32. </el-form-item>
  33. <el-form-item label="创建人">
  34. <el-input v-model="searchForm.creator" clearable placeholder="创建人" style="width: 130px" />
  35. </el-form-item>
  36. <el-form-item>
  37. <el-button type="primary" @click="handleSearch">查询</el-button>
  38. <el-button @click="handleReset">重置</el-button>
  39. </el-form-item>
  40. </el-form>
  41. </div>
  42. <div class="operation-row">
  43. <el-button type="primary" @click="handleCreate"><el-icon><Plus /></el-icon>新建活动</el-button>
  44. </div>
  45. </div>
  46. </template>
  47. <el-table :data="pagedTableData" stripe style="width: 100%">
  48. <el-table-column label="操作" width="240" fixed="left">
  49. <template #default="{ row, $index }">
  50. <el-button type="text" size="small" @click="handleView(row)">查看</el-button>
  51. <el-button v-if="['draft', 'rejected', 'approved'].includes(row.status)" type="text" size="small" @click="handleEdit(row)">编辑</el-button>
  52. <el-button type="text" size="small" @click="preview(row)">预览</el-button>
  53. <el-popover placement="bottom" trigger="click" :ref="`morePopover-${$index}`">
  54. <div class="more-actions-menu">
  55. <div v-if="row.status === 'draft'" class="more-actions-item" @click="submitAudit(row)">提交审核</div>
  56. <div v-if="row.status === 'pending'" class="more-actions-item" @click="handleAudit(row)">审核</div>
  57. <div v-if="row.status === 'approved'" class="more-actions-item" @click="publish(row)">发布</div>
  58. <div v-if="row.status === 'published'" class="more-actions-item more-actions-item-danger" @click="offline(row)">下线</div>
  59. </div>
  60. <template #reference>
  61. <el-button
  62. v-if="['draft', 'pending', 'approved', 'published'].includes(row.status)"
  63. type="text"
  64. size="small"
  65. class="more-button"
  66. >
  67. <el-icon><More /></el-icon>
  68. </el-button>
  69. </template>
  70. </el-popover>
  71. </template>
  72. </el-table-column>
  73. <el-table-column prop="id" label="活动 ID" width="130" />
  74. <el-table-column prop="internalTitle" label="活动标题(对内)" min-width="170" show-overflow-tooltip />
  75. <el-table-column prop="name" label="活动名称(对客)" min-width="170" show-overflow-tooltip />
  76. <el-table-column prop="category" label="活动类型" width="120" />
  77. <el-table-column label="活动时间" width="210"><template #default="{ row }">{{ rangeText(row.activityTime) }}</template></el-table-column>
  78. <el-table-column label="报名时间" width="210"><template #default="{ row }">{{ rangeText(row.signupTime) }}</template></el-table-column>
  79. <el-table-column label="展示状态" width="110">
  80. <template #default="{ row }">
  81. <el-tag :type="row.showOnHome || row.showOnAll ? 'success' : 'info'">{{ row.showOnHome || row.showOnAll ? '对客展示' : '不展示' }}</el-tag>
  82. </template>
  83. </el-table-column>
  84. <el-table-column label="活动状态" width="110"><template #default="{ row }"><el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag></template></el-table-column>
  85. <el-table-column prop="auditStatus" label="审核状态" width="110" />
  86. <el-table-column prop="creator" label="创建人" width="110" />
  87. <el-table-column prop="auditor" label="审核人" width="110" />
  88. <el-table-column prop="createdAt" label="创建时间" width="170" />
  89. <el-table-column prop="updatedAt" label="更新时间" width="170" />
  90. </el-table>
  91. <div class="pagination-section">
  92. <span>Total {{ tableData.length }}</span>
  93. <el-pagination
  94. v-model:current-page="currentPage"
  95. v-model:page-size="pageSize"
  96. layout="prev, pager, next, sizes, jumper"
  97. :page-sizes="[10, 20, 50]"
  98. :total="tableData.length"
  99. />
  100. </div>
  101. </el-card>
  102. </div>
  103. </template>
  104. <script>
  105. import { ElMessage, ElMessageBox } from 'element-plus'
  106. import activityStore from '@/store/activityStore'
  107. export default {
  108. name: 'ActivityManagement',
  109. data() {
  110. return {
  111. searchForm: { id: '', name: '', category: '', status: '', creator: '' },
  112. tableData: [],
  113. currentPage: 1,
  114. pageSize: 10
  115. }
  116. },
  117. computed: {
  118. pagedTableData() {
  119. const start = (this.currentPage - 1) * this.pageSize
  120. return this.tableData.slice(start, start + this.pageSize)
  121. }
  122. },
  123. created() { this.handleSearch() },
  124. activated() { this.handleSearch() },
  125. methods: {
  126. handleSearch() {
  127. this.tableData = activityStore.queryActivities(this.searchForm).filter((item) => {
  128. if (this.searchForm.creator && !String(item.creator || '').includes(this.searchForm.creator)) return false
  129. return true
  130. })
  131. this.currentPage = 1
  132. },
  133. handleReset() {
  134. this.searchForm = { id: '', name: '', category: '', status: '', creator: '' }
  135. this.handleSearch()
  136. },
  137. handleCreate() { this.$router.push('/customization/hsbc/activities/add') },
  138. handleEdit(row) { this.$router.push(`/customization/hsbc/activities/edit/${row.id}`) },
  139. handleView(row) { this.$router.push(`/customization/hsbc/activities/view/${row.id}`) },
  140. handleAudit(row) { this.$router.push(`/customization/hsbc/activities/audit/${row.id}`) },
  141. preview(row) { ElMessage.success(`已生成移动端预览:${row.name}`) },
  142. submitAudit(row) {
  143. const error = this.validateRequired(row)
  144. if (error) return ElMessage.warning(error)
  145. row.status = 'pending'
  146. row.auditStatus = '待审核'
  147. ElMessage.success('已提交审核')
  148. this.handleSearch()
  149. },
  150. async publish(row) {
  151. await ElMessageBox.confirm(`确定发布活动「${row.name}」吗?`, '发布活动', { type: 'warning' })
  152. activityStore.publishActivity(row.id)
  153. this.handleSearch()
  154. },
  155. async offline(row) {
  156. await ElMessageBox.confirm(`确定下线活动「${row.name}」吗?`, '下线活动', { type: 'warning' })
  157. activityStore.offlineActivity(row.id)
  158. this.handleSearch()
  159. },
  160. validateRequired(row) {
  161. if (!row.name) return '活动名称(对客)必填'
  162. if (!row.internalTitle) return '活动标题(对内)必填'
  163. if (!row.activityTime || row.activityTime.length !== 2) return '活动时间必填'
  164. if (row.needSignup && (!row.signupTime || row.signupTime.length !== 2)) return '报名时间必填'
  165. if (row.progressEnabled && !row.progressTemplateId) return '开启进度跟踪后必须选择进度模板'
  166. if (row.progressEnabled && !row.progressRuleId) return '开启进度跟踪后必须选择达标规则'
  167. if (row.rewardEnabled && !row.grantPlanId) return '需要权益发放时必须选择发放计划'
  168. if (row.mutexEnabled && !row.mutexGroup) return '参与互斥时必须选择互斥组'
  169. return ''
  170. },
  171. rangeText(range) { return range && range.length === 2 ? `${range[0]} 至 ${range[1]}` : '-' },
  172. statusText(status) {
  173. return { draft: '草稿', pending: '待审核', approved: '审核通过', published: '已发布', offline: '已下线', ended: '已结束', rejected: '已驳回' }[status] || status
  174. },
  175. statusType(status) {
  176. return { draft: 'info', pending: 'warning', approved: 'success', published: 'success', offline: 'danger', ended: 'info', rejected: 'danger' }[status] || ''
  177. }
  178. }
  179. }
  180. </script>
  181. <style scoped>
  182. .activity-management { padding:0; }
  183. .search-section { display:flex; flex-direction:column; }
  184. .search-row { margin-bottom:15px; }
  185. .search-row .el-form { display:flex; flex-wrap:wrap; }
  186. .search-row .el-form-item { margin-right:0; margin-bottom:0; }
  187. .operation-row { display:flex; align-items:center; }
  188. .pagination-section { display:flex; justify-content:flex-end; align-items:center; gap:20px; margin-top:20px; }
  189. .more-button { margin-left:8px; }
  190. </style>
  191. <style>
  192. .more-actions-menu { display:flex; flex-direction:column; margin:-12px; }
  193. .more-actions-item { padding:8px 16px; cursor:pointer; color:#606266; font-size:14px; }
  194. .more-actions-item:hover { background:#f5f7fa; color:#409eff; }
  195. .more-actions-item-danger:hover { color:#f56c6c; }
  196. </style>