|
|
@@ -2,62 +2,126 @@
|
|
|
<div class="table-box">
|
|
|
<ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback">
|
|
|
<template #tableHeader>
|
|
|
- <el-button type="primary" :icon="CirclePlus" :disabled="!isAdmin" @click="handleCreate"> 新增律师 </el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :icon="Download"
|
|
|
+ :loading="exportLoading"
|
|
|
+ :disabled="!isAdmin || !hasExportableData"
|
|
|
+ @click="handleExport"
|
|
|
+ >
|
|
|
+ 导出
|
|
|
+ </el-button>
|
|
|
</template>
|
|
|
<template #operation="scope">
|
|
|
- <el-button type="primary" link :icon="EditPen" :disabled="!isAdmin" @click="handleEdit(scope.row)"> 编辑 </el-button>
|
|
|
- <el-button type="danger" link :icon="Delete" :disabled="!isAdmin" @click="handleDelete(scope.row)"> 删除 </el-button>
|
|
|
+ <el-button type="primary" link :icon="View" @click="handleDetail(scope.row)"> 查看详情 </el-button>
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ link
|
|
|
+ :icon="Number(scope.row.isRecommended) === 1 ? StarFilled : Star"
|
|
|
+ :disabled="!isAdmin"
|
|
|
+ @click="handleRecommend(scope.row)"
|
|
|
+ >
|
|
|
+ {{ Number(scope.row.isRecommended) === 1 ? "取消推荐" : "设为推荐" }}
|
|
|
+ </el-button>
|
|
|
</template>
|
|
|
</ProTable>
|
|
|
- <LawyerDialog
|
|
|
- ref="lawyerDialogRef"
|
|
|
- :law-firm-options="lawFirmOptions"
|
|
|
- :law-firm-loading="lawFirmLoading"
|
|
|
- :fetch-law-firms="fetchLawFirmOptions"
|
|
|
- :scene-options="sceneOptions"
|
|
|
- :professional-options="professionalOptions"
|
|
|
- @success="refreshTable"
|
|
|
- />
|
|
|
+ <el-drawer v-model="detailVisible" title="律师详情" :size="480" append-to-body>
|
|
|
+ <el-descriptions v-if="detailData" :column="1" border>
|
|
|
+ <el-descriptions-item v-for="field in detailFields" :key="field.prop" :label="field.label">
|
|
|
+ {{ getDetailValue(field) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <div v-else class="detail-empty">暂无数据</div>
|
|
|
+ </el-drawer>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, reactive, onMounted, computed } from "vue";
|
|
|
-import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
import ProTable from "@/components/ProTable/index.vue";
|
|
|
import type { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
|
|
|
-import {
|
|
|
- getLawyerPage,
|
|
|
- addLawyerUser,
|
|
|
- deleteLawyerUser,
|
|
|
- editLawyerUser,
|
|
|
- getSelectList,
|
|
|
- getScenePage,
|
|
|
- getExpertiseAreaPage
|
|
|
-} from "@/api/modules/lawyer";
|
|
|
-import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
|
|
|
-import LawyerDialog from "./components/LawyerDialog.vue";
|
|
|
+import { getLawyerPage, recommendLawyer, exportLawyerList } from "@/api/modules/lawyer";
|
|
|
+import { useDownload } from "@/hooks/useDownload";
|
|
|
+import { Download, View, Star, StarFilled } from "@element-plus/icons-vue";
|
|
|
import { useUserStore } from "@/stores/modules/user";
|
|
|
|
|
|
const proTable = ref<ProTableInstance>();
|
|
|
-const lawyerDialogRef = ref<InstanceType<typeof LawyerDialog>>();
|
|
|
-const lawFirmOptions = ref<
|
|
|
- { label: string; value: string | number; paymentList?: { id?: string | number; paymentAccount: string }[] }[]
|
|
|
->([]);
|
|
|
-const lawFirmLoading = ref(false);
|
|
|
-const sceneOptions = ref<any[]>([]);
|
|
|
-const professionalOptions = ref<{ label: string; value: string | number }[]>([]);
|
|
|
-
|
|
|
+const exportLoading = ref(false);
|
|
|
+const tableTotal = ref(0);
|
|
|
+const hasExportableData = computed(() => tableTotal.value > 0);
|
|
|
+const detailVisible = ref(false);
|
|
|
+const detailData = ref<Record<string, any> | null>(null);
|
|
|
const userStore = useUserStore();
|
|
|
const isAdmin = computed(() => userStore.userInfo?.name === "admin");
|
|
|
const loginFirmId = computed(() => userStore.userInfo?.roleId || "");
|
|
|
|
|
|
+interface DetailField {
|
|
|
+ label: string;
|
|
|
+ prop: string;
|
|
|
+ formatter?: (row: Record<string, any>) => string;
|
|
|
+}
|
|
|
+
|
|
|
+const statusText = (status?: number | string) => {
|
|
|
+ return Number(status) === 1 ? "接单中" : "暂停接单";
|
|
|
+};
|
|
|
+
|
|
|
+const recommendText = (value?: number | string) => {
|
|
|
+ return Number(value) === 1 ? "已推荐" : "未推荐";
|
|
|
+};
|
|
|
+
|
|
|
+const detailFields: DetailField[] = [
|
|
|
+ { label: "律师姓名", prop: "name" },
|
|
|
+ { label: "联系电话", prop: "phone" },
|
|
|
+ { label: "法律场景", prop: "firstLevelScenario" },
|
|
|
+ {
|
|
|
+ label: "接单状态",
|
|
|
+ prop: "status",
|
|
|
+ formatter: row => statusText(row.status)
|
|
|
+ },
|
|
|
+ { label: "收款账号", prop: "paymentNum" },
|
|
|
+ { label: "所属律所", prop: "firmName" },
|
|
|
+ {
|
|
|
+ label: "推荐状态",
|
|
|
+ prop: "isRecommended",
|
|
|
+ formatter: row => recommendText(row.isRecommended)
|
|
|
+ }
|
|
|
+];
|
|
|
+
|
|
|
+const getDetailValue = (field: DetailField) => {
|
|
|
+ const data = detailData.value;
|
|
|
+ if (!data) return "-";
|
|
|
+ if (field.formatter) return field.formatter(data);
|
|
|
+ const value = data[field.prop];
|
|
|
+ return value === undefined || value === null || value === "" ? "-" : value;
|
|
|
+};
|
|
|
+
|
|
|
const ensureAdmin = () => {
|
|
|
if (isAdmin.value) return true;
|
|
|
ElMessage.warning("仅管理员可使用该功能");
|
|
|
return false;
|
|
|
};
|
|
|
|
|
|
+const normalizeRequestParams = (params: Record<string, any> = {}) => {
|
|
|
+ const newParams: Record<string, any> = { ...params };
|
|
|
+ if (Reflect.has(newParams, "pageNum")) {
|
|
|
+ newParams.page = newParams.pageNum;
|
|
|
+ delete newParams.pageNum;
|
|
|
+ }
|
|
|
+ if (Reflect.has(newParams, "pageSize")) {
|
|
|
+ newParams.size = newParams.pageSize;
|
|
|
+ delete newParams.pageSize;
|
|
|
+ }
|
|
|
+ if (Array.isArray(newParams.time) && newParams.time.length === 2) {
|
|
|
+ [newParams.startTime, newParams.endTime] = newParams.time;
|
|
|
+ }
|
|
|
+ delete newParams.time;
|
|
|
+ if (!isAdmin.value && loginFirmId.value) {
|
|
|
+ newParams.firmId = loginFirmId.value;
|
|
|
+ }
|
|
|
+ return newParams;
|
|
|
+};
|
|
|
+
|
|
|
const columns = reactive<ColumnProps<any>[]>([
|
|
|
{ label: "序号", type: "index", width: 60, align: "center" },
|
|
|
{ label: "律师姓名", prop: "name", width: 140, search: { el: "input", props: { placeholder: "请输入律师姓名" } } },
|
|
|
@@ -80,7 +144,7 @@ const columns = reactive<ColumnProps<any>[]>([
|
|
|
}
|
|
|
},
|
|
|
// { label: "专业领域", prop: "expertiseAreaInfo" },
|
|
|
- { label: "法律场景", prop: "firstLevelScenario" },
|
|
|
+ { label: "法律场景", prop: "scenarioNames" },
|
|
|
{
|
|
|
label: "接单状态",
|
|
|
prop: "status",
|
|
|
@@ -92,159 +156,85 @@ const columns = reactive<ColumnProps<any>[]>([
|
|
|
],
|
|
|
fieldNames: { label: "label", value: "value" }
|
|
|
},
|
|
|
+ {
|
|
|
+ label: "推荐状态",
|
|
|
+ prop: "isRecommended",
|
|
|
+ width: 120,
|
|
|
+ tag: true,
|
|
|
+ enum: [
|
|
|
+ { label: "已推荐", value: 1, tagType: "success" },
|
|
|
+ { label: "未推荐", value: 0, tagType: "info" }
|
|
|
+ ],
|
|
|
+ fieldNames: { label: "label", value: "value" }
|
|
|
+ },
|
|
|
{ label: "收款账号", prop: "paymentNum", width: 220 },
|
|
|
{ label: "所属律所", prop: "firmName", width: 220 },
|
|
|
- { label: "操作", prop: "operation", width: 200, fixed: "right" }
|
|
|
+ { label: "操作", prop: "operation", width: 240, fixed: "right" }
|
|
|
]);
|
|
|
|
|
|
const getTableList = async (params: any) => {
|
|
|
- const newParams: Record<string, any> = { ...params, page: params.pageNum, size: params.pageSize };
|
|
|
- delete newParams.pageNum;
|
|
|
- delete newParams.pageSize;
|
|
|
- if (!isAdmin.value && loginFirmId.value) {
|
|
|
- newParams.firmId = loginFirmId.value;
|
|
|
- }
|
|
|
- if (params.time) {
|
|
|
- newParams.startTime = params.time[0];
|
|
|
- newParams.endTime = params.time[1];
|
|
|
- }
|
|
|
- delete newParams.time;
|
|
|
- return getLawyerPage(newParams);
|
|
|
+ return getLawyerPage(normalizeRequestParams(params));
|
|
|
};
|
|
|
|
|
|
-const dataCallback = (data: any) => ({
|
|
|
- list: data.records,
|
|
|
- total: data.total
|
|
|
-});
|
|
|
+const dataCallback = (data: any) => {
|
|
|
+ const total = Number(data.total) || 0;
|
|
|
+ tableTotal.value = total;
|
|
|
+ return {
|
|
|
+ list: data.records,
|
|
|
+ total
|
|
|
+ };
|
|
|
+};
|
|
|
|
|
|
const refreshTable = () => {
|
|
|
proTable.value?.getTableList();
|
|
|
};
|
|
|
|
|
|
-const handleCreate = () => {
|
|
|
- if (!ensureAdmin()) return;
|
|
|
- lawyerDialogRef.value?.open({
|
|
|
- title: "新增律师",
|
|
|
- onSubmit: async payload => {
|
|
|
- let params = {
|
|
|
- name: payload.name,
|
|
|
- phone: payload.phone,
|
|
|
- practiceStartDate: payload.practiceStartDate,
|
|
|
- firstLevelScenario: payload.firstLevelScenario,
|
|
|
- status: payload.status,
|
|
|
- paymentNum: payload.paymentNum,
|
|
|
- firmId: payload.lawFirmId
|
|
|
- };
|
|
|
- await addLawyerUser(params);
|
|
|
- ElMessage.success("新增成功");
|
|
|
- refreshTable();
|
|
|
- }
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const handleEdit = (row: any) => {
|
|
|
- if (!ensureAdmin()) return;
|
|
|
- lawyerDialogRef.value?.open({
|
|
|
- title: "编辑律师",
|
|
|
- row,
|
|
|
- onSubmit: async payload => {
|
|
|
- let params = {
|
|
|
- id: payload.id,
|
|
|
- name: payload.name,
|
|
|
- phone: payload.phone,
|
|
|
- practiceStartDate: payload.practiceStartDate,
|
|
|
- firstLevelScenario: payload.firstLevelScenario,
|
|
|
- status: payload.status,
|
|
|
- paymentNum: payload.paymentNum,
|
|
|
- firmId: payload.lawFirmId
|
|
|
- };
|
|
|
- await editLawyerUser(params);
|
|
|
- ElMessage.success("编辑成功");
|
|
|
- refreshTable();
|
|
|
- }
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const handleDelete = (row: any) => {
|
|
|
- if (!ensureAdmin()) return;
|
|
|
- ElMessageBox.confirm(`确定删除律师【${row.userName}】吗?`, "提示", {
|
|
|
- type: "warning"
|
|
|
- })
|
|
|
- .then(async () => {
|
|
|
- await deleteLawyerUser({ id: row.id });
|
|
|
- ElMessage.success("删除成功");
|
|
|
- refreshTable();
|
|
|
- })
|
|
|
- .catch(() => {});
|
|
|
+const collectTableParams = () => {
|
|
|
+ const table = proTable.value;
|
|
|
+ if (!table) return {};
|
|
|
+ const params: Record<string, any> = { ...(table.searchParam || {}) };
|
|
|
+ if (table.pageable) {
|
|
|
+ params.pageNum = table.pageable.pageNum;
|
|
|
+ params.pageSize = table.pageable.pageSize;
|
|
|
+ }
|
|
|
+ return params;
|
|
|
};
|
|
|
|
|
|
-const fetchLawFirmOptions = async (keyword = "") => {
|
|
|
- lawFirmLoading.value = true;
|
|
|
+const handleExport = async () => {
|
|
|
+ if (!ensureAdmin() || !hasExportableData.value) return;
|
|
|
+ exportLoading.value = true;
|
|
|
try {
|
|
|
- const res: any = await getSelectList({ firmName: keyword });
|
|
|
- const listSource = res?.data || [];
|
|
|
- lawFirmOptions.value = listSource.map((item: any) => ({
|
|
|
- label: item.firmName,
|
|
|
- value: item.id,
|
|
|
- paymentList: (item.paymentList || []).map((payment: any) => ({
|
|
|
- ...payment,
|
|
|
- paymentAccount: payment.paymentAccount || ""
|
|
|
- }))
|
|
|
- }));
|
|
|
- } catch (error) {
|
|
|
- console.error("获取律所列表失败", error);
|
|
|
- lawFirmOptions.value = [];
|
|
|
+ const params = normalizeRequestParams(collectTableParams());
|
|
|
+ await useDownload(exportLawyerList, "律师列表", params);
|
|
|
} finally {
|
|
|
- lawFirmLoading.value = false;
|
|
|
+ exportLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-const buildSceneTree = (list: any[] = []) => {
|
|
|
- const map = new Map<string | number, any>();
|
|
|
- const roots: any[] = [];
|
|
|
- list.forEach(item => {
|
|
|
- map.set(item.id, { ...item, children: [] });
|
|
|
- });
|
|
|
- list.forEach(item => {
|
|
|
- const current = map.get(item.id);
|
|
|
- if (item.parentId && map.has(item.parentId)) {
|
|
|
- map.get(item.parentId).children.push(current);
|
|
|
- } else {
|
|
|
- roots.push(current);
|
|
|
- }
|
|
|
- });
|
|
|
- return roots;
|
|
|
+const handleDetail = (row: any) => {
|
|
|
+ detailData.value = { ...row };
|
|
|
+ detailVisible.value = true;
|
|
|
};
|
|
|
|
|
|
-const fetchSceneOptions = async () => {
|
|
|
- try {
|
|
|
- const res: any = await getScenePage({ page: 1, size: 999 });
|
|
|
- const list = res?.records || res?.data?.records || [];
|
|
|
- sceneOptions.value = buildSceneTree(list);
|
|
|
- } catch (error) {
|
|
|
- console.error("获取法律场景失败", error);
|
|
|
- sceneOptions.value = [];
|
|
|
+const handleRecommend = async (row: any) => {
|
|
|
+ if (!ensureAdmin()) return;
|
|
|
+ const targetId = row?.id;
|
|
|
+ if (!targetId) {
|
|
|
+ ElMessage.error("未获取到律师ID");
|
|
|
+ return;
|
|
|
}
|
|
|
-};
|
|
|
-
|
|
|
-const fetchProfessionalOptions = async () => {
|
|
|
+ const current = Number(row?.isRecommended) === 1 ? 1 : 0;
|
|
|
+ const nextStatus = current === 1 ? 0 : 1;
|
|
|
try {
|
|
|
- const res: any = await getExpertiseAreaPage({ page: 1, size: 999 });
|
|
|
- const list = res?.records || res?.data?.records || [];
|
|
|
- professionalOptions.value = list.map((item: any) => ({
|
|
|
- label: item.expertiseAreaInfo,
|
|
|
- value: item.id
|
|
|
- }));
|
|
|
+ await recommendLawyer({ lawyerId: targetId, isRecommended: nextStatus });
|
|
|
+ ElMessage.success(nextStatus === 1 ? "设置推荐成功" : "取消推荐成功");
|
|
|
+ refreshTable();
|
|
|
} catch (error) {
|
|
|
- console.error("获取专业领域失败", error);
|
|
|
- professionalOptions.value = [];
|
|
|
+ console.error("更新推荐状态失败", error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
- fetchLawFirmOptions();
|
|
|
- fetchSceneOptions();
|
|
|
- fetchProfessionalOptions();
|
|
|
refreshTable();
|
|
|
});
|
|
|
</script>
|