You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

536 lines
19 KiB

3 weeks ago
3 weeks ago
3 weeks ago
1 week ago
3 weeks ago
1 week ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
1 week ago
3 weeks ago
1 week ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
1 week ago
2 weeks ago
1 week ago
3 weeks ago
2 days ago
3 weeks ago
2 days ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
1 week ago
3 weeks ago
2 days ago
2 weeks ago
3 weeks ago
1 week ago
2 weeks ago
3 weeks ago
2 days ago
3 weeks ago
2 weeks ago
1 week ago
2 weeks ago
2 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
  1. <template>
  2. <el-card class="card1" style="margin-bottom: 1vh;">
  3. <div class="condition">
  4. <div class="condition-item1">
  5. <el-text size="large">活动名称</el-text>
  6. <el-input v-model="searchForm.activityName" style="width: 10vw" placeholder="请输入活动名称" clearable />
  7. </div>
  8. <div class="condition-item1">
  9. <el-text size="large">业绩归属</el-text>
  10. <!-- <el-cascader v-model="searchForm.businessBelong" :options="marketOptions" placeholder="请选择所属地区" clearable
  11. style="width: 10vw" /> -->
  12. <el-select v-model="searchForm.businessBelong" placeholder="请选择业绩归属" style="width: 10vw" clearable>
  13. <el-option label="客户归属地" value="客户归属地" />
  14. <el-option label="活动归属地" value="活动归属地" />
  15. </el-select>
  16. </div>
  17. <div class="condition-item2">
  18. <el-text size="large">开始时间</el-text>
  19. <el-date-picker v-model="searchForm.startTime" type="datetime" placeholder="请选择开始时间"
  20. format="YYYY-MM-DD HH:mm:ss" :default-time="defaultStartTime" clearable />
  21. </div>
  22. <div class="condition-item2">
  23. <el-text size="large">结束时间</el-text>
  24. <el-date-picker v-model="searchForm.endTime" type="datetime" placeholder="请选择结束时间"
  25. format="YYYY-MM-DD HH:mm:ss" :default-time="defaultEndTime" clearable />
  26. </div>
  27. <el-button type="primary" @click="getActivity">查询</el-button>
  28. <el-button type="success" @click="reset">重置</el-button>
  29. </div>
  30. </el-card>
  31. <el-card class="card2">
  32. <div class="add-item">
  33. <el-button type="success" @click="showAdd = true" style="margin-top: 1vh;">新增活动</el-button>
  34. </div>
  35. <div>
  36. <el-table :data="tableData" style="width: 82vw;height:70vh;" :row-style="{ height: '50px' }">
  37. <el-table-column type="index" label="序号" width="100px" fixed="left">
  38. <template #default="scope">
  39. <span>{{ scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize }}</span>
  40. </template>
  41. </el-table-column>
  42. <el-table-column prop="activityName" label="活动名称" width="150px" show-overflow-tooltip />
  43. <el-table-column prop="businessBelong" label="业绩归属地" width="150px" />
  44. <el-table-column prop="areaName" label="归属地" width="150px" />
  45. <el-table-column prop="startTime" label="开始时间" width="200px">
  46. <template #default="scope">
  47. {{ moment(scope.row.startTime).format('YYYY-MM-DD HH:mm:ss') }}
  48. </template>
  49. </el-table-column>
  50. <el-table-column prop="endTime" label="结束时间" width="200px">
  51. <template #default="scope">
  52. {{
  53. moment(scope.row.endTime).format('YYYY-MM-DD HH:mm:ss')
  54. }}
  55. </template>
  56. </el-table-column>
  57. <el-table-column prop="status" label="状态" width="150px">
  58. <template #default="scope">
  59. {{ scope.row.status === '0' ? '未开始' :
  60. scope.row.status === '1' ? '进行中' :
  61. scope.row.status === '2' ? '已结束' : scope.row.status
  62. }}
  63. </template>
  64. </el-table-column>
  65. <el-table-column prop="creatorName" label="添加人" width="150px" />
  66. <el-table-column prop="operation" label="操作" width="220px">
  67. <template #default="scope">
  68. <el-button type="primary" text @click="editOpen(scope.row)">编辑</el-button>
  69. <!-- <el-button type="danger" text @click="openDel(scope.row)">删除</el-button> -->
  70. </template>
  71. </el-table-column>
  72. </el-table>
  73. </div>
  74. <div style="margin-top: 20px;display: flex;">
  75. <el-pagination background v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
  76. layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" style="margin-top: 1vh;"
  77. @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  78. </div>
  79. </el-card>
  80. <el-dialog v-model="showAdd" width="20vw" draggable align-center style="background-color: rgb(243,250,254);">
  81. <div class="add-item">
  82. <el-text size="large">活动名称</el-text>
  83. <el-input v-model="addForm.activityName" style="width: 12vw" placeholder="请输入活动名称" maxlength="200" clearable />
  84. </div>
  85. <div class="add-item">
  86. <el-text size="large">业绩归属</el-text>
  87. <el-radio-group v-model="addForm.businessBelong" style="width: 12vw">
  88. <el-radio size="large" value="客户归属地">客户归属地</el-radio>
  89. <el-radio size="large" value="活动归属地">活动归属地</el-radio>
  90. </el-radio-group>
  91. </div>
  92. <div class="add-item" v-show="addForm.businessBelong === '活动归属地'">
  93. <el-text size="large">所属地区</el-text>
  94. <el-cascader v-model="addForm.area" :options="marketOptions" placeholder="请选择所属地区" clearable
  95. style="width: 12vw" />
  96. </div>
  97. <div class="add-item">
  98. <el-text size="large">开始时间</el-text>
  99. <el-date-picker v-model="addForm.startTime" type="datetime" placeholder="请选择开始时间"
  100. :default-time="defaultStartTime" style="width: 12vw" />
  101. </div>
  102. <div class="add-item">
  103. <el-text size="large">结束时间</el-text>
  104. <el-date-picker v-model="addForm.endTime" type="datetime" placeholder="请选择结束时间"
  105. :default-time="defaultEndTime" style="width: 12vw" />
  106. </div>
  107. <div style="display: flex; justify-content: center; margin-top: 5vh;">
  108. <el-button type="primary" @click="hideAdd">取消</el-button>
  109. <el-button type="primary" @click="throttleGetActivity">确定</el-button>
  110. </div>
  111. </el-dialog>
  112. <el-dialog v-model="showEdit" width="20vw" draggable align-center style="background-color: rgb(243,250,254);">
  113. <div class="edit-item">
  114. <el-text size="large">活动名称</el-text>
  115. <el-input v-model="editForm.activityName" style="width: 12vw" placeholder="请输入活动名称" maxlength="200" clearable />
  116. </div>
  117. <div class="edit-item">
  118. <el-text size="large">业绩归属</el-text>
  119. <el-radio-group v-model="editForm.businessBelong" style="width: 12vw">
  120. <el-radio size="large" value="客户归属地">客户归属地</el-radio>
  121. <el-radio size="large" value="活动归属地">活动归属地</el-radio>
  122. </el-radio-group>
  123. </div>
  124. <div class="edit-item" v-show="editForm.businessBelong === '活动归属地'">
  125. <el-text size="large">所属地区</el-text>
  126. <el-cascader v-model="editForm.area" :options="marketOptions" placeholder="请选择所属地区" clearable
  127. style="width: 12vw" />
  128. </div>
  129. <div class="edit-item">
  130. <el-text size="large">开始时间</el-text>
  131. <el-date-picker v-model="editForm.startTime" type="datetime" placeholder="请选择开始时间"
  132. :default-time="defaultStartTime" style="width: 12vw" />
  133. </div>
  134. <div class="edit-item">
  135. <el-text size="large">结束时间</el-text>
  136. <el-date-picker v-model="editForm.endTime" type="datetime" placeholder="请选择结束时间"
  137. :default-time="defaultEndTime" style="width: 12vw" />
  138. </div>
  139. <div style="display: flex; justify-content: center; margin-top: 5vh;">
  140. <el-button type="primary" @click="hideEdit">取消</el-button>
  141. <el-button type="primary" @click="handleEdit">确定</el-button>
  142. </div>
  143. </el-dialog>
  144. <ConfirmDialog v-model="showDel" message="删除该活动数据" @confirm="handleDel()" @cancel="hideDel" @close="hideDel" />
  145. </template>
  146. <script setup>
  147. import { ElMessage, ElPagination } from 'element-plus';
  148. import { onMounted, ref } from 'vue'
  149. import API from "@/util/http.js"
  150. import moment from 'moment'
  151. import { useAdminStore } from "@/store/index.js"
  152. import { storeToRefs } from "pinia"
  153. import { permissionMapping, hasMenuPermission } from "@/utils/menuTreePermission.js"
  154. const adminStore = useAdminStore();
  155. const { adminData, menuTree } = storeToRefs(adminStore)
  156. import ConfirmDialog from '@/components/dialogs/ConfirmDialog.vue'
  157. import _ from 'lodash'
  158. // 活动名称正则表达式
  159. const activityNameReg = /^[\u4e00-\u9fa5a-zA-Z0-9,。!?、;:“”()‘’《》【】{}——~,.!?:;'--()""\[\]_&+=]+$/;
  160. // 为什么一定要两个--才能成功?????????
  161. const tableData = ref([])
  162. const pagination = ref({
  163. pageNum: 1,
  164. pageSize: 10,
  165. total: 0
  166. })
  167. const searchForm = ref({
  168. activityName: ''
  169. })
  170. const showAdd = ref(false)
  171. const showEdit = ref(false)
  172. const showDel = ref(false)
  173. const currentDelRow = ref(null)
  174. const addForm = ref({
  175. activityName: '',
  176. area: []
  177. })
  178. const editForm = ref({
  179. activityName: '',
  180. businessBelong: '',
  181. area: [],
  182. startTime: null,
  183. endTime: null,
  184. id: ''
  185. })
  186. const marketOptions = ref([])
  187. const getActivity = async function () {
  188. if (!hasMenuPermission(permissionMapping.view_activity)) {
  189. ElMessage.error('无此权限')
  190. return
  191. }
  192. const rechargeActivity = {
  193. activityName: searchForm.value.activityName,
  194. businessBelong: searchForm.value.businessBelong,
  195. }
  196. if (searchForm.value.startTime && moment(searchForm.value.startTime).isValid()) {
  197. rechargeActivity.startTime = moment(searchForm.value.startTime).format('YYYY-MM-DD HH:mm:ss');
  198. }
  199. if (searchForm.value.endTime && moment(searchForm.value.endTime).isValid()) {
  200. rechargeActivity.endTime = moment(searchForm.value.endTime).format('YYYY-MM-DD HH:mm:ss');
  201. }
  202. const params = {
  203. pageNum: pagination.value.pageNum,
  204. pageSize: pagination.value.pageSize,
  205. rechargeActivity
  206. }
  207. const res = await API({
  208. url: '/admin/coin/rechargeActivityCenter/queryActivity',
  209. data: params
  210. })
  211. if (res.code === 200) {
  212. tableData.value = res.data.list
  213. pagination.value.total = res.data.total
  214. }
  215. }
  216. const handleAdd = async function () {
  217. if (!hasMenuPermission(permissionMapping.add_activity)) {
  218. ElMessage.error('无此权限')
  219. return
  220. }
  221. const activityName = addForm.value.activityName
  222. if (!validateActivityName(activityName)) return
  223. if (!addForm.value.businessBelong) {
  224. ElMessage.error('请选择业绩归属')
  225. return
  226. }
  227. if (addForm.value.businessBelong === '活动归属地' && addForm.value.area.length === 0) {
  228. ElMessage.error('请选择所属地区')
  229. return
  230. }
  231. if (!addForm.value.startTime) {
  232. ElMessage.error('请选择开始时间')
  233. return
  234. }
  235. if (!addForm.value.endTime) {
  236. ElMessage.error('请选择结束时间')
  237. return
  238. }
  239. if (addForm.value.businessBelong === '客户归属地') {
  240. addForm.value.area = []
  241. }
  242. const params = {
  243. activityName: addForm.value.activityName,
  244. businessBelong: addForm.value.businessBelong,
  245. area: addForm.value.area.length > 0 ? addForm.value.area.slice(-1)[0] : null,
  246. startTime: moment(addForm.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
  247. endTime: moment(addForm.value.endTime).format('YYYY-MM-DD HH:mm:ss'),
  248. creator: adminData.value.id
  249. }
  250. const res = await API({
  251. url: '/admin/coin/rechargeActivityCenter/addActivity',
  252. data: params
  253. })
  254. if (res.code === 200) {
  255. ElMessage.success('添加成功')
  256. getActivity()
  257. hideAdd()
  258. addForm.value = {
  259. activityName: '',
  260. businessBelong: '',
  261. area: [],
  262. startTime: null,
  263. endTime: null,
  264. }
  265. } else {
  266. ElMessage.error(res.msg || '添加失败')
  267. return
  268. }
  269. }
  270. // 新增节流
  271. const throttleGetActivity = _.throttle(handleAdd, 5000, { trailing: false });
  272. const handleEdit = async function () {
  273. if (!hasMenuPermission(permissionMapping.edit_activity)) {
  274. ElMessage.error('无此权限')
  275. return
  276. }
  277. const activityName = editForm.value.activityName
  278. if (!validateActivityName(activityName)) return
  279. if (!editForm.value.activityName) {
  280. ElMessage.error('请输入活动名称')
  281. return
  282. }
  283. if (!editForm.value.businessBelong) {
  284. ElMessage.error('请选择业绩归属')
  285. return
  286. }
  287. if (editForm.value.businessBelong === '活动归属地' && editForm.value.area.length === 0) {
  288. ElMessage.error('请选择所属地区')
  289. return
  290. }
  291. if (!editForm.value.startTime) {
  292. ElMessage.error('请选择开始时间')
  293. return
  294. }
  295. if (!editForm.value.endTime) {
  296. ElMessage.error('请选择结束时间')
  297. return
  298. }
  299. if(editForm.value.businessBelong === '客户归属地'){
  300. editForm.value.area = []
  301. }
  302. const params = {
  303. id: editForm.value.id,
  304. activityName: editForm.value.activityName,
  305. businessBelong: editForm.value.businessBelong,
  306. area: editForm.value.area.length > 0 ? editForm.value.area.slice(-1)[0] : null,
  307. startTime: moment(editForm.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
  308. endTime: moment(editForm.value.endTime).format('YYYY-MM-DD HH:mm:ss'),
  309. creator: adminData.value.id
  310. }
  311. console.log('看看修改params', params)
  312. const res = await API({
  313. url: '/admin/coin/rechargeActivityCenter/updateActivity',
  314. data: params
  315. })
  316. if (res.code === 200) {
  317. ElMessage.success('修改成功')
  318. getActivity()
  319. hideEdit()
  320. }
  321. }
  322. const handleDel = async function (row) {
  323. if (!hasMenuPermission(permissionMapping.delete_activity)) {
  324. ElMessage.error('无此权限')
  325. return
  326. }
  327. if (!currentDelRow.value) {
  328. ElMessage.error('当前选择无数据')
  329. return
  330. }
  331. const res = await API({
  332. url: '/admin/coin/rechargeActivityCenter/deleteActivity',
  333. data: {
  334. id: currentDelRow.value.id,
  335. }
  336. })
  337. if (res.code === 200) {
  338. ElMessage.success('删除成功')
  339. getActivity()
  340. showDel.value = false
  341. }
  342. }
  343. const getmarkets = async function () {
  344. try {
  345. const result = await API({
  346. url: '/market/selectMarket',
  347. });
  348. console.log('请求成功', result)
  349. // 递归转换树形结构为级联选择器需要的格式(跳过第一级节点)
  350. const transformTree = (nodes) => {
  351. // 直接处理第一级节点的子节点
  352. const allChildren = nodes.flatMap(node => node.children || []);
  353. return allChildren.map(child => {
  354. const grandchildren = child.children && child.children.length
  355. ? transformTree([child]) // 递归处理子节点
  356. : null;
  357. return {
  358. value: child.id,
  359. label: child.name,
  360. children: grandchildren
  361. }
  362. })
  363. }
  364. marketOptions.value = transformTree(result.data)
  365. console.log('转换后的地区树', marketOptions.value)
  366. } catch (error) {
  367. console.log('请求失败', error)
  368. }
  369. }
  370. const defaultStartTime = [
  371. new Date(2000, 1, 1, 0, 0, 0)
  372. ]
  373. const defaultEndTime = [
  374. new Date(2000, 2, 1, 23, 59, 59)
  375. ]
  376. const hideEdit = () => {
  377. showEdit.value = false
  378. editForm.value = {
  379. activityName: '',
  380. businessBelong: '',
  381. area: [],
  382. startTime: null,
  383. endTime: null,
  384. id: ''
  385. }
  386. }
  387. const editOpen = (row) => {
  388. editForm.value = {
  389. id: row.id,
  390. activityName: row.activityName,
  391. businessBelong: row.businessBelong,
  392. area: row.area ? [...row.area] : []
  393. }
  394. if (row.startTime) {
  395. editForm.value.startTime = moment(row.startTime).toDate()
  396. }
  397. if (row.endTime) {
  398. editForm.value.endTime = moment(row.endTime).toDate()
  399. }
  400. console.log('看看editForm', editForm.value)
  401. showEdit.value = true
  402. }
  403. const openDel = (row) => {
  404. currentDelRow.value = row
  405. showDel.value = true
  406. }
  407. const hideDel = () => {
  408. showDel.value = false
  409. currentDelRow.value = null
  410. }
  411. const reset = () => {
  412. searchForm.value = {
  413. activityName: '',
  414. businessBelong: '',
  415. startTime: null,
  416. endTime: null
  417. }
  418. getActivity()
  419. }
  420. const hideAdd = () => {
  421. showAdd.value = false
  422. addForm.value = {
  423. activityName: '',
  424. area: [],
  425. startTime: null,
  426. endTime: null
  427. }
  428. }
  429. const validateActivityName = (name) => {
  430. // 非空校验
  431. if (!name.trim()) {
  432. ElMessage.error('活动名称不能为空');
  433. return false;
  434. }
  435. // 长度校验(限制100字符)
  436. if (name.length > 100) {
  437. ElMessage.error('活动名称长度不能超过100字符');
  438. return false;
  439. }
  440. // 字符格式校验
  441. if (!activityNameReg.test(name)) {
  442. ElMessage.error('活动名称仅支持汉字、英文字母、数字及常见标点,中文字符,。!?、;:“ ” ‘ ’ ()《》【】——~,英文字符, . ! ? : ; " ( ) [ ] - _ & + =/')
  443. return false;
  444. }
  445. return true;
  446. };
  447. const handleSizeChange = function (val) {
  448. pagination.pageSize = val
  449. getActivity()
  450. }
  451. const handleCurrentChange = function (val) {
  452. pagination.pageNum = val
  453. getActivity()
  454. }
  455. onMounted(() => {
  456. getActivity()
  457. getmarkets()
  458. console.log('看看adminData', adminData.value)
  459. })
  460. </script>
  461. <style scoped lang="scss">
  462. // 搜索的卡片样式
  463. .card1 {
  464. background: #F3FAFE;
  465. }
  466. // 表单的卡片样式
  467. .card2 {
  468. background: #E7F4FD;
  469. }
  470. // 表头背景等
  471. :deep(.el-table__header-wrapper),
  472. :deep(.el-table__body-wrapper),
  473. :deep(.el-table__cell),
  474. /* 表格 */
  475. :deep(.el-table__body td) {
  476. background-color: #F3FAFE !important;
  477. }
  478. /* 表头 */
  479. :deep(.el-table__header th) {
  480. background-color: #F3FAFE !important;
  481. }
  482. /* 鼠标悬停 */
  483. :deep(.el-table__row:hover > .el-table__cell) {
  484. background-color: #E5EBFE !important;
  485. }
  486. .condition {
  487. display: flex;
  488. align-items: center;
  489. }
  490. .condition-item1 {
  491. display: flex;
  492. align-items: center;
  493. width: 15vw;
  494. }
  495. .condition-item2 {
  496. display: flex;
  497. align-items: center;
  498. width: 17vw;
  499. }
  500. .add-item {
  501. display: flex;
  502. align-items: center;
  503. width: 17vw;
  504. margin-bottom: 1vh;
  505. }
  506. .edit-item {
  507. display: flex;
  508. align-items: center;
  509. width: 17vw;
  510. margin-bottom: 1vh;
  511. }
  512. </style>