index.vue 14 KB

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