index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <template>
  2. <div class="app-page">
  3. <el-form :model="queryParams" ref="queryRef" v-show="showSearch" label-width="100px">
  4. <!-- 第一行:卡号段、卡类型、卡分配日期 -->
  5. <el-row :gutter="20">
  6. <el-col :span="6">
  7. <el-form-item label="卡号段" prop="begin">
  8. <div class="flex items-center gap-x-1">
  9. <el-input v-model="queryParams.begin" type="text" maxlength="11" class="w-[85px]!" placeholder="开始卡号" />
  10. <span class="text-gray-500 text-sm">-</span>
  11. <el-input v-model="queryParams.end" type="text" maxlength="11" class="w-[85px]!" placeholder="结束卡号" />
  12. </div>
  13. </el-form-item>
  14. </el-col>
  15. <el-col :span="6">
  16. <el-form-item label="卡类型" prop="type">
  17. <ie-select v-model="queryParams.type" :options="card_type" class="w-[180px]!" clearable />
  18. </el-form-item>
  19. </el-col>
  20. <el-col :span="7">
  21. <el-form-item label="卡分配日期" prop="assignTimeRange">
  22. <el-date-picker v-model="queryParams.assignTimeRange" type="daterange" range-separator="至"
  23. start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" class="w-[180px]!" />
  24. </el-form-item>
  25. </el-col>
  26. </el-row>
  27. <!-- 其他表单项:每行4个 -->
  28. <el-row :gutter="20">
  29. <el-col :span="6">
  30. <el-form-item label="省份筛选" prop="provinceId">
  31. <el-cascader class="w-[180px]!" :options="area.list" :props="cascaderProps" v-model="area.selected" clearable />
  32. </el-form-item>
  33. </el-col>
  34. <el-col :span="6">
  35. <el-form-item label="分配学校" prop="assignSchoolId">
  36. <ie-select v-model="selectedAssignSchool" :options="schoolList" label-key="name" value-key="id" filterable
  37. clearable class="w-[180px]!" />
  38. </el-form-item>
  39. </el-col>
  40. <el-col :span="6">
  41. <el-form-item label="代理商" prop="agentId">
  42. <ie-agent-select v-model="queryParams.agentId" class="w-[180px]!" filterable clearable />
  43. </el-form-item>
  44. </el-col>
  45. <el-col :span="6">
  46. <el-form-item label="平台机构" prop="deptId">
  47. <ie-institution-select v-model="queryParams.deptId" class="w-[180px]!" clearable />
  48. </el-form-item>
  49. </el-col>
  50. </el-row>
  51. <el-row :gutter="20">
  52. <el-col :span="6">
  53. <el-form-item label="注册学校" prop="schoolId">
  54. <ie-select v-model="selectedSchool" :options="schoolList" label-key="name" value-key="id" filterable clearable
  55. class="w-[180px]!" />
  56. </el-form-item>
  57. </el-col>
  58. <el-col :span="6">
  59. <el-form-item label="注册班级" prop="classId">
  60. <ie-select v-model="selectedClass" :options="classList" label-key="name" value-key="classId" filterable
  61. clearable class="w-[180px]!" />
  62. </el-form-item>
  63. </el-col>
  64. <el-col :span="6">
  65. <el-form-item label="培训学校" prop="campusId">
  66. <ie-select v-model="selectedCampus" :options="campusList" label-key="name" value-key="id" filterable clearable
  67. class="w-[180px]!" @change="handleCampusChange" />
  68. </el-form-item>
  69. </el-col>
  70. <el-col :span="6">
  71. <el-form-item label="培训班级" prop="campusClassId">
  72. <ie-select v-model="selectedCampusClass" :options="campusClassList" label-key="name" value-key="classId"
  73. clearable filterable class="w-[180px]!" />
  74. </el-form-item>
  75. </el-col>
  76. </el-row>
  77. <el-row :gutter="20">
  78. <el-col :span="6">
  79. <el-form-item label="姓名" prop="nickName">
  80. <el-input v-model="queryParams.nickName" type="text" class="w-[180px]!" placeholder="请输入姓名" clearable />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :span="6">
  84. <el-form-item label="手机" prop="phonenumber">
  85. <el-input v-model="queryParams.phonenumber" type="text" v-number maxlength="11" class="w-[180px]!"
  86. placeholder="请输入手机号" clearable />
  87. </el-form-item>
  88. </el-col>
  89. <el-col :span="6">
  90. <el-form-item label="考生类型" prop="examType">
  91. <ie-select v-model="queryParams.examType" :options="exam_type" class="w-[180px]!" clearable />
  92. </el-form-item>
  93. </el-col>
  94. <el-col :span="6">
  95. <el-form-item label="分配状态" prop="distributeStatus">
  96. <ie-select v-model="queryParams.distributeStatus" :options="card_distribute_status" class="w-[180px]!"
  97. clearable />
  98. </el-form-item>
  99. </el-col>
  100. </el-row>
  101. <el-row :gutter="20">
  102. <el-col :span="6">
  103. <el-form-item label="使用状态" prop="status">
  104. <ie-select v-model="queryParams.status" :options="CARD_STATUS" class="w-[180px]!" clearable />
  105. </el-form-item>
  106. </el-col>
  107. <el-col :span="6">
  108. <el-form-item label="过期状态" prop="timeStatus">
  109. <ie-select v-model="queryParams.timeStatus" :options="card_time_status" class="w-[180px]!" clearable />
  110. </el-form-item>
  111. </el-col>
  112. <el-col :span="6">
  113. <el-form-item label="结算状态" prop="isSettlement">
  114. <ie-select v-model="queryParams.isSettlement" :options="card_settlement_status" class="w-[180px]!" clearable />
  115. </el-form-item>
  116. </el-col>
  117. <el-col :span="6">
  118. <el-form-item label="缴费状态" prop="payStatus">
  119. <ie-select v-model="queryParams.payStatus" :options="card_pay_status" class="w-[180px]!" clearable />
  120. </el-form-item>
  121. </el-col>
  122. </el-row>
  123. <el-row :gutter="20">
  124. <el-col :span="6">
  125. <el-form-item>
  126. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  127. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  128. </el-form-item>
  129. </el-col>
  130. </el-row>
  131. </el-form>
  132. <el-row class="mt-2">
  133. <CustomButton icon="plus" v-hasPermi="['dz:cards:issue']" @click="handleAddCard">制卡</CustomButton>
  134. <CustomButton color="#67c23a" v-hasPermi="['dz:cards:assign']" @click="handleAssign">
  135. <svg-icon icon-class="peoples" class="mr-1" style="font-size: 12px" />
  136. 分配卡
  137. </CustomButton>
  138. <CustomButton color="#E6A23C" v-hasPermi="['dz:cards:pay']" :disabled="batchDisabled" @click="handlePay">
  139. <svg-icon icon-class="money" class="mr-1" style="font-size: 12px" />
  140. 缴费
  141. </CustomButton>
  142. <CustomButton color="#F56C6C" v-hasPermi="['dz:cards:close']" :disabled="batchDisabled" @click="handleClose">
  143. <svg-icon icon-class="lock" class="mr-1" style="font-size: 12px" />
  144. 关卡
  145. </CustomButton>
  146. <CustomButton color="#67C23A" v-hasPermi="['dz:cards:reopen']" :disabled="batchDisabled" @click="handleReopen">
  147. <svg-icon icon-class="enter" class="mr-1" style="font-size: 12px" />
  148. 重开
  149. </CustomButton>
  150. <CustomButton color="#E6A23C" v-hasPermi="['dz:cards:refund']" :disabled="batchDisabled" @click="handleRefund">
  151. <svg-icon icon-class="money" class="mr-1" style="font-size: 12px" />
  152. 退费
  153. </CustomButton>
  154. <CustomButton color="#9C27B0" v-hasPermi="['dz:cards:associateCampus']" @click="handleRelateCampus">
  155. <svg-icon icon-class="link" class="mr-1" style="font-size: 12px" />
  156. 关联校区
  157. </CustomButton>
  158. <!-- <CustomButton color="#00BCD4" v-hasPermi="['dz:cards:openFinished']" @click="handleOpenCard">
  159. <svg-icon icon-class="enter" class="mr-1" style="font-size: 12px" />
  160. 直接开卡
  161. </CustomButton> -->
  162. <!-- <CustomButton icon="delete" color="#F56C6C" v-hasPermi="['dz:cards:remove']" :disabled="batchDisabled"
  163. @click="handleDeleteBatch" v-if="false">
  164. 删除
  165. </CustomButton> -->
  166. <CustomButton color="#FFC107" v-hasPermi="['dz:cards:settlement']" :disabled="batchDisabled" @click="handleSettle">
  167. <svg-icon icon-class="chart" class="mr-1" style="font-size: 12px" />
  168. 结算
  169. </CustomButton>
  170. <!-- <CustomButton color="#009688" :disabled="batchDisabled" @click="handleRenew">
  171. <svg-icon icon-class="time" class="mr-1" style="font-size: 14px" />
  172. 续期
  173. </CustomButton> -->
  174. <CustomButton color="#673AB7" v-hasPermi="['dz:cards:updateuser']" :disabled="editDisabled" @click="handleEdit">
  175. <svg-icon icon-class="edit" class="mr-1" style="font-size: 12px" />
  176. 修改
  177. </CustomButton>
  178. <CustomButton color="#606266" v-hasPermi="['dz:cards:export']" @click="handleExport">
  179. <svg-icon icon-class="download" class="mr-1" style="font-size: 14px" />
  180. 导出
  181. </CustomButton>
  182. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
  183. </el-row>
  184. <card-table :data="cardList" :loading="loading" :hide-actions="true" @selectionChange="handleSelectionChange"
  185. @delete="handleDelete" />
  186. <div class="flex justify-end">
  187. <Pagination :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
  188. @pagination="getList" />
  189. </div>
  190. <MakeDialog ref="makeDialogRef" @refresh="getList" />
  191. <OpenDialog ref="openDialogRef" @refresh="getList" />
  192. <AssignDialog ref="assignDialogRef" @refresh="getList" />
  193. <EditDialog ref="editDialogRef" @refresh="getList" />
  194. <RelateDialog ref="relateDialogRef" @refresh="getList" />
  195. </div>
  196. </template>
  197. <script setup>
  198. import useSchool from '@/hooks/useSchool';
  199. import IeSelect from '@/components/IeSelect/index.vue';
  200. import IeAgentSelect from '@/components/IeAgentSelect/index.vue';
  201. import IeInstitutionSelect from '@/components/IeInstitutionSelect/index.vue';
  202. import CardTable from './components/CardTable.vue';
  203. import Pagination from '@/components/Pagination/index.vue';
  204. import CustomButton from './components/CustomButton.vue';
  205. import MakeDialog from './components/MakeDialog.vue';
  206. import OpenDialog from './components/OpenDialog.vue';
  207. import AssignDialog from './components/AssignDialog.vue';
  208. import EditDialog from './components/EditDialog.vue';
  209. import RelateDialog from './components/RelateDialog.vue';
  210. import { listCards, delCards, payCard, closeCard, reopenCard, refundCard, settleCard, renewCard } from '@/api/dz/cards';
  211. import { CARD_STATUS, EnumCardType } from '@/common/enum';
  212. import { getCurrentInstance, nextTick } from 'vue';
  213. const { proxy } = getCurrentInstance();
  214. const {
  215. reset,
  216. area,
  217. // areaList,
  218. // selectedArea,
  219. schoolList,
  220. selectedSchool,
  221. selectedAssignSchool,
  222. classList,
  223. selectedClass,
  224. campusList,
  225. selectedCampus,
  226. campusClassList,
  227. selectedCampusClass,
  228. } = useSchool({ loadCampus: true, loadCampusClass: true, loadClass: true });
  229. const {
  230. exam_type,
  231. card_distribute_status,
  232. card_time_status,
  233. card_settlement_status,
  234. card_pay_status,
  235. card_type,
  236. } = proxy.useDict("exam_type", "card_distribute_status", "card_time_status", "card_settlement_status", "card_pay_status", "card_type");
  237. const cascaderProps = {
  238. label: "areaName",
  239. value: "areaId",
  240. checkStrictly: true,
  241. }
  242. const queryParams = ref({
  243. pageNum: 1,
  244. pageSize: 20
  245. })
  246. const showSearch = ref(true)
  247. const cardList = ref([])
  248. const total = ref(0)
  249. const selectedRows = ref([])
  250. const loading = ref(false)
  251. const ids = computed(() => {
  252. return selectedRows.value.map(item => item.cardId);
  253. })
  254. const batchDisabled = computed(() => {
  255. return selectedRows.value.length === 0;
  256. })
  257. const editDisabled = computed(() => {
  258. return selectedRows.value.length !== 1;
  259. })
  260. watchEffect(() => {
  261. queryParams.value.areaIds = area.selected;
  262. queryParams.value.schoolId = selectedSchool.value;
  263. queryParams.value.assignSchoolId = selectedAssignSchool.value;
  264. queryParams.value.classId = selectedClass.value;
  265. queryParams.value.campusId = selectedCampus.value;
  266. queryParams.value.campusClassId = selectedCampusClass.value;
  267. })
  268. const handleQuery = () => {
  269. queryParams.pageNum = 1;
  270. getList();
  271. }
  272. const resetQuery = () => {
  273. queryParams.value = {
  274. pageNum: 1,
  275. pageSize: 20
  276. };
  277. reset();
  278. handleQuery();
  279. }
  280. const getList = () => {
  281. const params = {
  282. ...queryParams.value,
  283. assignTimeBegin: queryParams.value.assignTimeRange?.[0],
  284. assignTimeEnd: queryParams.value.assignTimeRange?.[1],
  285. };
  286. delete params.assignTimeRange;
  287. listCards(params).then(res => {
  288. cardList.value = res.rows;
  289. total.value = res.total;
  290. }).finally(() => {
  291. loading.value = false;
  292. })
  293. }
  294. const handleSelectionChange = (selection) => {
  295. selectedRows.value = selection;
  296. }
  297. const makeDialogRef = ref(null);
  298. const handleAddCard = () => {
  299. makeDialogRef.value.open()
  300. }
  301. const handleDelete = (row) => {
  302. proxy.$modal.confirm(`是否确认删除学习卡号为"${row.cardNo}"的数据项?`).then(() => {
  303. delCards(row.cardId).then(() => {
  304. proxy.$modal.msgSuccess('删除成功')
  305. getList()
  306. })
  307. });
  308. }
  309. const handleDeleteBatch = () => {
  310. proxy.$modal.confirm(`是否确认删除所选数据 (${ids.value.length}项) ?`).then(() => {
  311. delCards(ids.value).then(() => {
  312. proxy.$modal.msgSuccess('删除成功')
  313. getList()
  314. })
  315. });
  316. }
  317. const relateDialogRef = ref(null);
  318. const handleRelateCampus = () => {
  319. relateDialogRef.value.open()
  320. }
  321. const openDialogRef = ref(null);
  322. const handleOpenCard = () => {
  323. openDialogRef.value.open()
  324. }
  325. const handlePay = () => {
  326. // 体验卡禁止缴费
  327. const isExperienceCard = selectedRows.value.some(item => item.type === EnumCardType.EXPERIENCE);
  328. if (isExperienceCard) {
  329. proxy.$modal.msgError('体验卡禁止缴费');
  330. return;
  331. }
  332. proxy.$modal.confirm(`是否确认缴费所选数据 (${ids.value.length}项) ?`).then(() => {
  333. payCard(ids.value).then(() => {
  334. proxy.$modal.msgSuccess('缴费成功')
  335. getList()
  336. })
  337. })
  338. }
  339. const handleClose = () => {
  340. proxy.$modal.confirm(`是否确认关卡所选数据 (${ids.value.length}项) ?`).then(() => {
  341. closeCard(ids.value).then(() => {
  342. proxy.$modal.msgSuccess('关卡成功')
  343. getList()
  344. })
  345. })
  346. }
  347. const handleReopen = () => {
  348. proxy.$modal.confirm(`是否确认重开所选数据 (${ids.value.length}项) ?`).then(() => {
  349. reopenCard(ids.value).then(() => {
  350. proxy.$modal.msgSuccess('重开成功')
  351. getList()
  352. })
  353. })
  354. }
  355. const handleRefund = () => {
  356. // 体验卡禁止退费
  357. const isExperienceCard = selectedRows.value.some(item => item.type === EnumCardType.EXPERIENCE);
  358. if (isExperienceCard) {
  359. proxy.$modal.msgError('体验卡禁止退费');
  360. return;
  361. }
  362. proxy.$modal.confirm(`是否确认退费所选数据 (${ids.value.length}项) ?`).then(() => {
  363. refundCard(ids.value).then(() => {
  364. proxy.$modal.msgSuccess('退费成功')
  365. getList()
  366. })
  367. })
  368. }
  369. const handleSettle = () => {
  370. // 体验卡禁止结算
  371. const isExperienceCard = selectedRows.value.some(item => item.type === EnumCardType.EXPERIENCE);
  372. if (isExperienceCard) {
  373. proxy.$modal.msgError('体验卡禁止结算');
  374. return;
  375. }
  376. proxy.$modal.confirm(`是否确认结算所选数据 (${ids.value.length}项) ?`).then(() => {
  377. settleCard(ids.value).then(() => {
  378. proxy.$modal.msgSuccess('结算成功')
  379. getList()
  380. })
  381. })
  382. }
  383. const handleRenew = () => {
  384. proxy.$modal.confirm(`是否确认续费所选数据 (${ids.value.length}项) ?`).then(() => {
  385. renewCard(ids.value).then(() => {
  386. proxy.$modal.msgSuccess('续费成功')
  387. getList()
  388. })
  389. })
  390. }
  391. const editDialogRef = ref(null);
  392. const handleEdit = () => {
  393. const row = selectedRows.value[0];
  394. if (row.status !== 30) {
  395. proxy.$modal.msgError('该卡未绑定用户');
  396. return;
  397. }
  398. editDialogRef.value.open({ ...row });
  399. }
  400. const assignDialogRef = ref(null);
  401. const handleAssign = () => {
  402. assignDialogRef.value.open()
  403. }
  404. const handleExport = () => {
  405. proxy.download('dz/cards/export', {
  406. ...queryParams.value
  407. }, `cards_${new Date().getTime()}.xlsx`)
  408. }
  409. onMounted(() => {
  410. handleQuery();
  411. })
  412. </script>
  413. <style lang="scss" scoped></style>