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.

520 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 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
1 week ago
3 weeks ago
2 weeks ago
3 weeks ago
1 week ago
2 weeks 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">新增活动</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. const rechargeActivity = {
  189. activityName: searchForm.value.activityName,
  190. businessBelong: searchForm.value.businessBelong,
  191. }
  192. if (searchForm.value.startTime && moment(searchForm.value.startTime).isValid()) {
  193. rechargeActivity.startTime = moment(searchForm.value.startTime).format('YYYY-MM-DD HH:mm:ss');
  194. }
  195. if (searchForm.value.endTime && moment(searchForm.value.endTime).isValid()) {
  196. rechargeActivity.endTime = moment(searchForm.value.endTime).format('YYYY-MM-DD HH:mm:ss');
  197. }
  198. const params = {
  199. pageNum: pagination.value.pageNum,
  200. pageSize: pagination.value.pageSize,
  201. rechargeActivity
  202. }
  203. const res = await API({
  204. url: '/admin/coin/rechargeActivityCenter/queryActivity',
  205. data: params
  206. })
  207. if (res.code === 200) {
  208. tableData.value = res.data.list
  209. pagination.value.total = res.data.total
  210. }
  211. }
  212. const handleAdd = async function () {
  213. const activityName = addForm.value.activityName
  214. if (!validateActivityName(activityName)) return
  215. if (!addForm.value.businessBelong) {
  216. ElMessage.error('请选择业绩归属')
  217. return
  218. }
  219. if (addForm.value.businessBelong === '活动归属地' && addForm.value.area.length === 0) {
  220. ElMessage.error('请选择所属地区')
  221. return
  222. }
  223. if (!addForm.value.startTime) {
  224. ElMessage.error('请选择开始时间')
  225. return
  226. }
  227. if (!addForm.value.endTime) {
  228. ElMessage.error('请选择结束时间')
  229. return
  230. }
  231. if (addForm.value.businessBelong === '客户归属地') {
  232. addForm.value.area = []
  233. }
  234. const params = {
  235. activityName: addForm.value.activityName,
  236. businessBelong: addForm.value.businessBelong,
  237. area: addForm.value.area.length > 0 ? addForm.value.area.slice(-1)[0] : null,
  238. startTime: moment(addForm.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
  239. endTime: moment(addForm.value.endTime).format('YYYY-MM-DD HH:mm:ss'),
  240. creator: adminData.value.id
  241. }
  242. const res = await API({
  243. url: '/admin/coin/rechargeActivityCenter/addActivity',
  244. data: params
  245. })
  246. if (res.code === 200) {
  247. ElMessage.success('添加成功')
  248. getActivity()
  249. hideAdd()
  250. addForm.value = {
  251. activityName: '',
  252. businessBelong: '',
  253. area: [],
  254. startTime: null,
  255. endTime: null,
  256. }
  257. } else {
  258. ElMessage.error(res.msg || '添加失败')
  259. return
  260. }
  261. }
  262. // 新增节流
  263. const throttleGetActivity = _.throttle(handleAdd, 5000, { trailing: false });
  264. const handleEdit = async function () {
  265. const activityName = editForm.value.activityName
  266. if (!validateActivityName(activityName)) return
  267. if (!editForm.value.activityName) {
  268. ElMessage.error('请输入活动名称')
  269. return
  270. }
  271. if (!editForm.value.businessBelong) {
  272. ElMessage.error('请选择业绩归属')
  273. return
  274. }
  275. if (editForm.value.businessBelong === '活动归属地' && editForm.value.area.length === 0) {
  276. ElMessage.error('请选择所属地区')
  277. return
  278. }
  279. if (!editForm.value.startTime) {
  280. ElMessage.error('请选择开始时间')
  281. return
  282. }
  283. if (!editForm.value.endTime) {
  284. ElMessage.error('请选择结束时间')
  285. return
  286. }
  287. if(editForm.value.businessBelong === '客户归属地'){
  288. editForm.value.area = []
  289. }
  290. const params = {
  291. id: editForm.value.id,
  292. activityName: editForm.value.activityName,
  293. businessBelong: editForm.value.businessBelong,
  294. area: editForm.value.area.length > 0 ? editForm.value.area.slice(-1)[0] : null,
  295. startTime: moment(editForm.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
  296. endTime: moment(editForm.value.endTime).format('YYYY-MM-DD HH:mm:ss'),
  297. creator: adminData.value.id
  298. }
  299. console.log('看看修改params', params)
  300. const res = await API({
  301. url: '/admin/coin/rechargeActivityCenter/updateActivity',
  302. data: params
  303. })
  304. if (res.code === 200) {
  305. ElMessage.success('修改成功')
  306. getActivity()
  307. hideEdit()
  308. }
  309. }
  310. const handleDel = async function (row) {
  311. if (!currentDelRow.value) {
  312. ElMessage.error('当前选择无数据')
  313. return
  314. }
  315. const res = await API({
  316. url: '/admin/coin/rechargeActivityCenter/deleteActivity',
  317. data: {
  318. id: currentDelRow.value.id,
  319. }
  320. })
  321. if (res.code === 200) {
  322. ElMessage.success('删除成功')
  323. getActivity()
  324. showDel.value = false
  325. }
  326. }
  327. const getmarkets = async function () {
  328. try {
  329. const result = await API({
  330. url: '/market/selectMarket',
  331. });
  332. console.log('请求成功', result)
  333. // 递归转换树形结构为级联选择器需要的格式(跳过第一级节点)
  334. const transformTree = (nodes) => {
  335. // 直接处理第一级节点的子节点
  336. const allChildren = nodes.flatMap(node => node.children || []);
  337. return allChildren.map(child => {
  338. const grandchildren = child.children && child.children.length
  339. ? transformTree([child]) // 递归处理子节点
  340. : null;
  341. return {
  342. value: child.id,
  343. label: child.name,
  344. children: grandchildren
  345. }
  346. })
  347. }
  348. marketOptions.value = transformTree(result.data)
  349. console.log('转换后的地区树', marketOptions.value)
  350. } catch (error) {
  351. console.log('请求失败', error)
  352. }
  353. }
  354. const defaultStartTime = [
  355. new Date(2000, 1, 1, 0, 0, 0)
  356. ]
  357. const defaultEndTime = [
  358. new Date(2000, 2, 1, 23, 59, 59)
  359. ]
  360. const hideEdit = () => {
  361. showEdit.value = false
  362. editForm.value = {
  363. activityName: '',
  364. businessBelong: '',
  365. area: [],
  366. startTime: null,
  367. endTime: null,
  368. id: ''
  369. }
  370. }
  371. const editOpen = (row) => {
  372. editForm.value = {
  373. id: row.id,
  374. activityName: row.activityName,
  375. businessBelong: row.businessBelong,
  376. area: row.area ? [...row.area] : []
  377. }
  378. if (row.startTime) {
  379. editForm.value.startTime = moment(row.startTime).toDate()
  380. }
  381. if (row.endTime) {
  382. editForm.value.endTime = moment(row.endTime).toDate()
  383. }
  384. console.log('看看editForm', editForm.value)
  385. showEdit.value = true
  386. }
  387. const openDel = (row) => {
  388. currentDelRow.value = row
  389. showDel.value = true
  390. }
  391. const hideDel = () => {
  392. showDel.value = false
  393. currentDelRow.value = null
  394. }
  395. const reset = () => {
  396. searchForm.value = {
  397. activityName: '',
  398. businessBelong: '',
  399. startTime: null,
  400. endTime: null
  401. }
  402. getActivity()
  403. }
  404. const hideAdd = () => {
  405. showAdd.value = false
  406. addForm.value = {
  407. activityName: '',
  408. area: [],
  409. startTime: null,
  410. endTime: null
  411. }
  412. }
  413. const validateActivityName = (name) => {
  414. // 非空校验
  415. if (!name.trim()) {
  416. ElMessage.error('活动名称不能为空');
  417. return false;
  418. }
  419. // 长度校验(限制100字符)
  420. if (name.length > 100) {
  421. ElMessage.error('活动名称长度不能超过100字符');
  422. return false;
  423. }
  424. // 字符格式校验
  425. if (!activityNameReg.test(name)) {
  426. ElMessage.error('活动名称仅支持汉字、英文字母、数字及常见标点,中文字符,。!?、;:“ ” ‘ ’ ()《》【】——~,英文字符, . ! ? : ; " ( ) [ ] - _ & + =/')
  427. return false;
  428. }
  429. return true;
  430. };
  431. const handleSizeChange = function (val) {
  432. pagination.pageSize = val
  433. getActivity()
  434. }
  435. const handleCurrentChange = function (val) {
  436. pagination.pageNum = val
  437. getActivity()
  438. }
  439. onMounted(() => {
  440. getActivity()
  441. getmarkets()
  442. console.log('看看adminData', adminData.value)
  443. })
  444. </script>
  445. <style scoped lang="scss">
  446. // 搜索的卡片样式
  447. .card1 {
  448. background: #F3FAFE;
  449. }
  450. // 表单的卡片样式
  451. .card2 {
  452. background: #E7F4FD;
  453. }
  454. // 表头背景等
  455. :deep(.el-table__header-wrapper),
  456. :deep(.el-table__body-wrapper),
  457. :deep(.el-table__cell),
  458. /* 表格 */
  459. :deep(.el-table__body td) {
  460. background-color: #F3FAFE !important;
  461. }
  462. /* 表头 */
  463. :deep(.el-table__header th) {
  464. background-color: #F3FAFE !important;
  465. }
  466. /* 鼠标悬停 */
  467. :deep(.el-table__row:hover > .el-table__cell) {
  468. background-color: #E5EBFE !important;
  469. }
  470. .condition {
  471. display: flex;
  472. align-items: center;
  473. }
  474. .condition-item1 {
  475. display: flex;
  476. align-items: center;
  477. width: 15vw;
  478. }
  479. .condition-item2 {
  480. display: flex;
  481. align-items: center;
  482. width: 17vw;
  483. }
  484. .add-item {
  485. display: flex;
  486. align-items: center;
  487. width: 17vw;
  488. margin-bottom: 1vh;
  489. }
  490. .edit-item {
  491. display: flex;
  492. align-items: center;
  493. width: 17vw;
  494. margin-bottom: 1vh;
  495. }
  496. </style>