index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. <template>
  2. <div class="app-container">
  3. <!-- 搜索表单 -->
  4. <SearchForm
  5. :config="searchConfig"
  6. v-model="queryParams"
  7. @search="handleQuery"
  8. @reset="resetQuery"
  9. :loading="loading"
  10. />
  11. <!-- 操作按钮 -->
  12. <el-row :gutter="10" class="mb8">
  13. <el-col :span="1.5">
  14. <el-button
  15. type="primary"
  16. plain
  17. icon="Plus"
  18. @click="handleAdd"
  19. v-hasPermi="['dz:open:add']"
  20. >新增</el-button
  21. >
  22. </el-col>
  23. <el-col :span="1.5">
  24. <el-button
  25. type="warning"
  26. plain
  27. icon="Download"
  28. @click="handleExport"
  29. v-hasPermi="['dz:open:export']"
  30. >导出</el-button
  31. >
  32. </el-col>
  33. <right-toolbar
  34. v-model:showSearch="showSearch"
  35. @queryTable="getList"
  36. ></right-toolbar>
  37. </el-row>
  38. <!-- 数据表格 -->
  39. <Table
  40. :data="openList"
  41. :columns="tableConfigData.columns"
  42. :actions="tableConfigData.actions"
  43. :loading="loading"
  44. :total="total"
  45. :query-params="queryParams"
  46. v-model:selected-rows="selectedRows"
  47. @selection-change="handleSelectionChange"
  48. @action="handleAction"
  49. @get-list="getList"
  50. v-bind="tableConfigData.tableProps"
  51. >
  52. <!-- 卡号段插槽 -->
  53. <template #cardRange="{ row }">
  54. <span>{{ row.startNo }}~{{ row.endNo }}</span>
  55. </template>
  56. <!-- 申请数量插槽 -->
  57. <template #quantity="{ row }">
  58. <span>{{ calculateQuantity(row.startNo, row.endNo) }}</span>
  59. </template>
  60. <!-- 申请状态插槽 -->
  61. <template #status="{ row }">
  62. <el-tag :type="getStatusType(row.status)">
  63. {{ getStatusText(row.status) }}
  64. </el-tag>
  65. </template>
  66. <!-- 原因插槽 -->
  67. <template #auditDesc="{ row }">
  68. <span>{{ row.auditDesc || "-" }}</span>
  69. </template>
  70. </Table>
  71. <!-- 添加或修改开卡申请对话框 -->
  72. <el-dialog :title="title" v-model="open" width="500px" append-to-body>
  73. <el-form ref="openRef" :model="form" :rules="rules" label-width="80px">
  74. <el-form-item label="起始卡号" prop="startNo">
  75. <el-input v-model="form.startNo" placeholder="请输入起始卡号" />
  76. </el-form-item>
  77. <el-form-item label="截止卡号" prop="endNo">
  78. <el-input v-model="form.endNo" placeholder="请输入截至卡号" />
  79. </el-form-item>
  80. <!-- 省份 -->
  81. <el-form-item label="省份" prop="province">
  82. <el-select
  83. v-model="form.province"
  84. placeholder="请选择省份"
  85. style="width: 100%"
  86. @change="getCampusListData(this.value)"
  87. clearable
  88. >
  89. <el-option
  90. v-for="province in provinceList"
  91. :key="province.dictValue"
  92. :label="province.dictLabel"
  93. :value="province.dictValue"
  94. ></el-option>
  95. </el-select>
  96. </el-form-item>
  97. <!-- 学校 -->
  98. <el-form-item label="学校" prop="schoolId">
  99. <el-select
  100. v-model="form.schoolId"
  101. placeholder="请选择学校"
  102. style="width: 100%"
  103. >
  104. <el-option
  105. v-for="school in schoolList"
  106. :key="school.id"
  107. :label="school.name"
  108. :value="school.id"
  109. ></el-option>
  110. </el-select>
  111. </el-form-item>
  112. <!-- <el-form-item label="原因" prop="reason">-->
  113. <!-- <el-input v-model="form.reason" placeholder="请输入原因" />-->
  114. <!-- </el-form-item>-->
  115. </el-form>
  116. <template #footer>
  117. <div class="dialog-footer">
  118. <el-button type="primary" @click="submitForm">确 定</el-button>
  119. <el-button @click="cancel">取 消</el-button>
  120. </div>
  121. </template>
  122. </el-dialog>
  123. <!-- 审核对话框 -->
  124. <el-dialog
  125. title="审核开卡申请"
  126. v-model="reviewOpen"
  127. width="400px"
  128. append-to-body
  129. >
  130. <div class="detail">
  131. <div class="item">
  132. <span class="label"> 卡号段: </span>
  133. <span class="value">
  134. {{ reviewForm.startNo }}~{{ reviewForm.endNo }}
  135. </span>
  136. </div>
  137. <div class="item">
  138. <span class="label"> 申请人: </span>
  139. <span class="value">
  140. {{ reviewForm.applicant }}
  141. </span>
  142. </div>
  143. <div class="item">
  144. <span class="label"> 申请时间: </span>
  145. <span class="value">
  146. {{ reviewForm.createTime }}
  147. </span>
  148. </div>
  149. <div class="item">
  150. <span class="label"> 学校: </span>
  151. <span class="value">
  152. {{ reviewForm.schoolName }}
  153. </span>
  154. </div>
  155. </div>
  156. <el-form
  157. ref="reviewRef"
  158. :model="reviewForm"
  159. :rules="reviewRules"
  160. label-width="80px"
  161. >
  162. <el-form-item label="审核结果" prop="status">
  163. <el-radio-group v-model="reviewForm.status">
  164. <el-radio :value="2">审核通过</el-radio>
  165. <el-radio :value="3">已拒绝</el-radio>
  166. </el-radio-group>
  167. </el-form-item>
  168. <el-form-item label="原因" prop="auditDesc">
  169. <el-input
  170. v-model="reviewForm.auditDesc"
  171. placeholder="请输入审核原因"
  172. type="textarea"
  173. :rows="3"
  174. />
  175. </el-form-item>
  176. </el-form>
  177. <template #footer>
  178. <div class="dialog-footer">
  179. <el-button type="primary" @click="submitReview">确 定</el-button>
  180. <el-button @click="cancelReview">取 消</el-button>
  181. </div>
  182. </template>
  183. </el-dialog>
  184. </div>
  185. </template>
  186. <script setup name="Open">
  187. import { listOpen, getOpen, addOpen, updateOpen } from "@/api/dz/open";
  188. import SearchForm from "@/components/SearchForm/index.vue";
  189. import Table from "@/components/Table/index.vue";
  190. import formConfig from "./config/form.js";
  191. import tableConfig from "./config/table.js";
  192. import {ref} from "vue";
  193. import {getPaperProvinces} from "@/api/dz/papers.js";
  194. import {getCampusSchoolList} from "@/api/dz/cards.js";
  195. const { proxy } = getCurrentInstance();
  196. const openList = ref([]);
  197. const open = ref(false);
  198. const reviewOpen = ref(false);
  199. const provinceList = ref([]); // 省份列表
  200. const schoolList = ref([]); // 学校列表
  201. const loading = ref(true);
  202. const showSearch = ref(true);
  203. const total = ref(0);
  204. const title = ref("");
  205. const selectedRows = ref([]);
  206. const { card_status, card_distribute_status, card_time_status, bool_values, card_pay_status, audit_status, } = proxy.useDict( "card_status", "card_distribute_status", "card_time_status", "bool_values", "card_pay_status", "audit_status" );
  207. // 搜索配置
  208. const searchConfig = computed(() => {
  209. return formConfig.info.filter((item) => item.search);
  210. });
  211. // 表格配置
  212. const tableConfigData = computed(() => tableConfig);
  213. const data = reactive({
  214. form: {},
  215. reviewForm: {
  216. id: null,
  217. status: null,
  218. auditDesc: null,
  219. startNo: null,
  220. endNo: null,
  221. applicant: null,
  222. createTime: null,
  223. province:null,
  224. schoolId: null,
  225. },
  226. queryParams: {
  227. pageNum: 1,
  228. pageSize: 10,
  229. applicant: null,
  230. cardNo: null,
  231. status: null,
  232. },
  233. rules: {
  234. applicant: [{ required: true, message: "申请人不能为空", trigger: "blur" }],
  235. startNo: [{ required: true, message: "起始卡号不能为空", trigger: "blur" }],
  236. endNo: [{ required: true, message: "截至卡号不能为空", trigger: "blur" }],
  237. province: [{ required: true, message: "省份不能为空", trigger: "change" }],
  238. schoolId: [{ required: true, message: "学校不能为空", trigger: "change" }],
  239. status: [
  240. { required: true, message: "申请状态不能为空", trigger: "change" },
  241. ],
  242. },
  243. reviewRules: {
  244. status: [
  245. { required: true, message: "审核结果不能为空", trigger: "change" },
  246. ],
  247. auditDesc: [],
  248. },
  249. });
  250. const { queryParams, form, rules, reviewForm, reviewRules } = toRefs(data);
  251. /** 查询开卡申请列表 */
  252. function getList() {
  253. loading.value = true;
  254. listOpen(queryParams.value).then((response) => {
  255. openList.value = response.rows;
  256. total.value = response.total;
  257. loading.value = false;
  258. });
  259. }
  260. // 获取省份列表
  261. async function getProvinceList() {
  262. try {
  263. const response = await getPaperProvinces();
  264. if (response.code === 200) {
  265. provinceList.value = response.data || [];
  266. }
  267. schoolList.value = []
  268. } catch (error) {
  269. console.error("获取省份列表失败:", error);
  270. }
  271. }
  272. // 获取校区列表
  273. async function getCampusListData() {
  274. try {
  275. const response = await getCampusSchoolList({ campus: false, location: form.value.province, pageNum: 1, pageSize: 1000 });
  276. if (response.code === 200) {
  277. schoolList.value = response.data || [];
  278. }
  279. } catch (error) {
  280. console.error("获取校区列表失败:", error);
  281. }
  282. }
  283. // 计算申请数量
  284. function calculateQuantity(startNo, endNo) {
  285. if (!startNo || !endNo) return 0;
  286. const start = parseInt(startNo);
  287. const end = parseInt(endNo);
  288. return end - start + 1;
  289. }
  290. // 获取状态文本
  291. function getStatusText(status) {
  292. const statusMap = {
  293. 1: "待审核",
  294. 2: "审核通过",
  295. 3: "已拒绝",
  296. };
  297. return statusMap[status] || "未知";
  298. }
  299. // 获取状态类型
  300. function getStatusType(status) {
  301. const typeMap = {
  302. 1: "warning",
  303. 2: "success",
  304. 3: "danger",
  305. };
  306. return typeMap[status] || "info";
  307. }
  308. // 取消按钮
  309. function cancel() {
  310. open.value = false;
  311. reset();
  312. }
  313. // 取消审核
  314. function cancelReview() {
  315. reviewOpen.value = false;
  316. resetReview();
  317. }
  318. // 表单重置
  319. function reset() {
  320. form.value = {
  321. id: null,
  322. applicant: null,
  323. startNo: null,
  324. endNo: null,
  325. schoolName: null,
  326. status: null,
  327. reason: null,
  328. createTime: null,
  329. };
  330. proxy.resetForm("openRef");
  331. }
  332. // 审核表单重置
  333. function resetReview() {
  334. reviewForm.value = {
  335. id: null,
  336. status: null,
  337. auditDesc: null,
  338. startNo: null,
  339. endNo: null,
  340. applicant: null,
  341. createTime: null,
  342. schoolName: null,
  343. };
  344. proxy.resetForm("reviewRef");
  345. }
  346. /** 搜索按钮操作 */
  347. function handleQuery() {
  348. queryParams.value.pageNum = 1;
  349. getList();
  350. }
  351. /** 重置按钮操作 */
  352. function resetQuery() {
  353. proxy.resetForm("queryRef");
  354. handleQuery();
  355. }
  356. // 多选框选中数据
  357. function handleSelectionChange(selection) {
  358. ids.value = selection.map((item) => item.id);
  359. single.value = selection.length != 1;
  360. multiple.value = !selection.length;
  361. }
  362. /** 新增按钮操作 */
  363. function handleAdd() {
  364. reset();
  365. open.value = true;
  366. title.value = "添加开卡申请";
  367. }
  368. /** 修改按钮操作 */
  369. function handleUpdate(row) {
  370. reset();
  371. const _id = row.id || ids.value;
  372. getOpen(_id).then((response) => {
  373. form.value = response.data;
  374. open.value = true;
  375. title.value = "修改开卡申请";
  376. });
  377. }
  378. /** 审核按钮操作 */
  379. function handleReview(row) {
  380. resetReview();
  381. // 从列表中获取对应的数据并填充到reviewForm中
  382. reviewForm.value = {
  383. id: row.id,
  384. status: null,
  385. auditDesc: null,
  386. startNo: row.startNo || "-",
  387. endNo: row.endNo || "-",
  388. applicant: row.applicant || "-",
  389. createTime: row.createTime || "-",
  390. schoolName: row.schoolName || "-",
  391. };
  392. reviewOpen.value = true;
  393. }
  394. /** 表格操作处理 */
  395. function handleAction(action, row) {
  396. switch (action.key) {
  397. case "review":
  398. handleReview(row);
  399. break;
  400. }
  401. }
  402. /** 提交按钮 */
  403. function submitForm() {
  404. proxy.$refs["openRef"].validate((valid) => {
  405. if (valid) {
  406. if (form.value.id != null) {
  407. updateOpen(form.value).then((response) => {
  408. proxy.$modal.msgSuccess("修改成功");
  409. open.value = false;
  410. getList();
  411. });
  412. } else {
  413. addOpen(form.value).then((response) => {
  414. proxy.$modal.msgSuccess("新增成功");
  415. open.value = false;
  416. getList();
  417. });
  418. }
  419. }
  420. });
  421. }
  422. /** 提交审核 */
  423. function submitReview() {
  424. proxy.$refs["reviewRef"].validate((valid) => {
  425. if (valid) {
  426. // 这里应该调用审核API,暂时使用更新API
  427. const updateData = {
  428. id: reviewForm.value.id,
  429. status: reviewForm.value.status,
  430. auditDesc: reviewForm.value.auditDesc,
  431. };
  432. updateOpen(updateData).then((response) => {
  433. proxy.$modal.msgSuccess("审核成功");
  434. reviewOpen.value = false;
  435. getList();
  436. });
  437. }
  438. });
  439. }
  440. /** 导出按钮操作 */
  441. function handleExport() {
  442. proxy.download(
  443. "dz/open/export",
  444. {
  445. ...queryParams.value,
  446. },
  447. `open_${new Date().getTime()}.xlsx`
  448. );
  449. }
  450. getProvinceList();
  451. getList();
  452. </script>
  453. <style>
  454. .detail {
  455. padding-left: 10px;
  456. .item {
  457. display: flex;
  458. align-items: center;
  459. margin-bottom: 10px;
  460. }
  461. }
  462. </style>