index.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <template>
  2. <div class="table-box">
  3. <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback">
  4. <template #tableHeader>
  5. <el-button type="primary" :icon="CirclePlus" @click="handleCreate"> 新增账号 </el-button>
  6. </template>
  7. <template #operation="scope">
  8. <el-button type="primary" link :icon="EditPen" @click="handleEdit(scope.row)"> 编辑 </el-button>
  9. <el-button type="danger" link :icon="Delete" @click="handleDelete(scope.row)"> 删除 </el-button>
  10. </template>
  11. </ProTable>
  12. <UserDialog
  13. ref="userDialogRef"
  14. :law-firm-options="lawFirmOptions"
  15. :law-firm-loading="lawFirmLoading"
  16. :fetch-law-firms="fetchLawFirmOptions"
  17. @success="refreshTable"
  18. />
  19. </div>
  20. </template>
  21. <script setup lang="ts">
  22. import { ref, reactive, onMounted } from "vue";
  23. import { ElMessage, ElMessageBox } from "element-plus";
  24. import ProTable from "@/components/ProTable/index.vue";
  25. import type { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
  26. import type { User } from "@/api/interface";
  27. import { getUserList, addUser, editUser, deleteUser } from "@/api/modules/user";
  28. import { getSelectList } from "@/api/modules/lawyer";
  29. import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
  30. import UserDialog from "./components/UserDialog.vue";
  31. const proTable = ref<ProTableInstance>();
  32. const userDialogRef = ref<InstanceType<typeof UserDialog>>();
  33. const lawFirmOptions = ref<{ label: string; value: string | number; phone?: string }[]>([]);
  34. const lawFirmLoading = ref(false);
  35. const lawFirmMap = ref<Record<string, string>>({});
  36. type UserRow = Partial<User.ResUserList> & { userName?: string };
  37. const statusOptions = [
  38. { label: "启用", value: 1 },
  39. { label: "禁用", value: 0 }
  40. ];
  41. const columns = reactive<ColumnProps<User.ResUserList & { roleName?: string }>[]>([
  42. { type: "index", label: "序号", width: 60 },
  43. { prop: "userName", label: "登录账号", search: { el: "input", props: { placeholder: "请输入登录账号" } } },
  44. { prop: "userPassword", label: "登录密码" },
  45. { prop: "roleName", label: "关联律所" },
  46. {
  47. prop: "status",
  48. label: "账号状态",
  49. width: 100,
  50. enum: statusOptions,
  51. fieldNames: { label: "label", value: "value" },
  52. search: { el: "select", props: { placeholder: "请选择状态" } },
  53. tag: true
  54. },
  55. { prop: "remark", label: "备注", width: 220 },
  56. { prop: "createdTime", label: "创建时间", width: 200 },
  57. { prop: "operation", label: "操作", width: 150, fixed: "right" }
  58. ]);
  59. const getTableList = (params: any) => {
  60. const tempParams = { ...params };
  61. tempParams.page = tempParams.pageNum;
  62. tempParams.size = tempParams.pageSize;
  63. delete tempParams.pageNum;
  64. delete tempParams.pageSize;
  65. return getUserList(tempParams);
  66. };
  67. const dataCallback = (data: any) => {
  68. const list = data;
  69. const total = data?.total ?? data?.records?.length ?? 0;
  70. const mappedList = list.map((item: any) => ({
  71. ...item,
  72. roleName: item.roleName || lawFirmMap.value[String(item.roleId)] || "-"
  73. }));
  74. return {
  75. list: mappedList,
  76. total
  77. };
  78. };
  79. const refreshTable = () => {
  80. proTable.value?.getTableList();
  81. };
  82. const handleCreate = () => {
  83. userDialogRef.value?.open({
  84. title: "新增账号",
  85. mode: "add",
  86. onSubmit: async payload => {
  87. await addUser(payload);
  88. ElMessage.success("新增账号成功");
  89. }
  90. });
  91. };
  92. const handleEdit = (row: UserRow) => {
  93. const editRow = { ...row };
  94. if (!editRow.roleId && editRow.roleName) {
  95. const target = lawFirmOptions.value.find(item => item.label === editRow.roleName);
  96. if (target) editRow.roleId = String(target.value);
  97. } else if (editRow.roleId && !editRow.roleName) {
  98. editRow.roleName = lawFirmMap.value[String(editRow.roleId)];
  99. }
  100. userDialogRef.value?.open({
  101. title: "编辑账号",
  102. mode: "edit",
  103. row: editRow,
  104. onSubmit: async payload => {
  105. await editUser(payload);
  106. ElMessage.success("编辑账号成功");
  107. }
  108. });
  109. };
  110. const handleDelete = (row: UserRow) => {
  111. if (!row?.id) {
  112. ElMessage.warning("未找到该账号的唯一标识,无法删除");
  113. return;
  114. }
  115. const displayName = row.userName ?? row.username ?? "";
  116. ElMessageBox.confirm(`确定删除账号「${displayName}」吗?`, "提示", {
  117. type: "warning",
  118. confirmButtonText: "确定",
  119. cancelButtonText: "取消"
  120. })
  121. .then(async () => {
  122. await deleteUser({ id: row.id });
  123. ElMessage.success("删除账号成功");
  124. refreshTable();
  125. })
  126. .catch(() => {});
  127. };
  128. const fetchLawFirmOptions = async (keyword = "") => {
  129. lawFirmLoading.value = true;
  130. try {
  131. const res: any = await getSelectList({ firmName: keyword });
  132. const list = res?.data || [];
  133. lawFirmOptions.value = list.map((item: any) => ({
  134. label: item.firmName,
  135. value: item.id,
  136. phone: item.phone || ""
  137. }));
  138. if (list.length) {
  139. const map = { ...lawFirmMap.value };
  140. list.forEach((item: any) => {
  141. map[String(item.id)] = item.firmName;
  142. });
  143. lawFirmMap.value = map;
  144. }
  145. } catch (error) {
  146. console.error("获取律所列表失败", error);
  147. lawFirmOptions.value = [];
  148. } finally {
  149. lawFirmLoading.value = false;
  150. }
  151. };
  152. onMounted(async () => {
  153. await fetchLawFirmOptions();
  154. refreshTable();
  155. });
  156. </script>
  157. <style scoped lang="scss"></style>