|
@@ -13,6 +13,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="apply-toolbar-actions">
|
|
<div class="apply-toolbar-actions">
|
|
|
|
|
+ <el-button type="primary" plain @click="handleBack">返回</el-button>
|
|
|
<el-button type="primary" plain @click="handleAddEmployee">添加员工</el-button>
|
|
<el-button type="primary" plain @click="handleAddEmployee">添加员工</el-button>
|
|
|
<el-button type="primary" @click="handleExport">导出列表</el-button>
|
|
<el-button type="primary" @click="handleExport">导出列表</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -21,7 +22,8 @@
|
|
|
<el-table :data="pagedApplyList" class="apply-list-table" header-row-class-name="apply-table-header">
|
|
<el-table :data="pagedApplyList" class="apply-list-table" header-row-class-name="apply-table-header">
|
|
|
<el-table-column label="姓名" prop="name" min-width="160">
|
|
<el-table-column label="姓名" prop="name" min-width="160">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
- <el-button link type="primary" @click="handleOpenDetail(row)">{{ row.name }}</el-button>
|
|
|
|
|
|
|
+ <el-button v-if="row.statusText !== '待测评'" link type="primary" @click="handleOpenDetail(row)">{{ row.name }}</el-button>
|
|
|
|
|
+ <span v-else>{{ row.name }}</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column label="性别" prop="gender" width="80" align="center" />
|
|
<el-table-column label="性别" prop="gender" width="80" align="center" />
|
|
@@ -56,16 +58,24 @@
|
|
|
<el-dialog v-model="syncDialog.visible" title="同步测评" width="460px" append-to-body>
|
|
<el-dialog v-model="syncDialog.visible" title="同步测评" width="460px" append-to-body>
|
|
|
<div class="sync-dialog-content">
|
|
<div class="sync-dialog-content">
|
|
|
<div class="sync-dialog-row">
|
|
<div class="sync-dialog-row">
|
|
|
- <span class="sync-dialog-label">同步公司</span>
|
|
|
|
|
- <el-select v-model="syncDialog.companies" multiple collapse-tags collapse-tags-tooltip placeholder="请选择" class="sync-dialog-select">
|
|
|
|
|
- <el-option v-for="item in syncCompanyOptions" :key="item" :label="item" :value="item" />
|
|
|
|
|
|
|
+ <span class="sync-dialog-label">同步员工</span>
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="syncDialog.employeeIds"
|
|
|
|
|
+ multiple
|
|
|
|
|
+ collapse-tags
|
|
|
|
|
+ collapse-tags-tooltip
|
|
|
|
|
+ placeholder="请选择"
|
|
|
|
|
+ class="sync-dialog-select"
|
|
|
|
|
+ :loading="syncOptionLoading"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option v-for="item in employeeOptions" :key="item.id" :label="item.label" :value="item.id" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<div class="sync-dialog-footer">
|
|
<div class="sync-dialog-footer">
|
|
|
<el-button @click="syncDialog.visible = false">取消</el-button>
|
|
<el-button @click="syncDialog.visible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="handleConfirmSync">确定</el-button>
|
|
|
|
|
|
|
+ <el-button type="primary" :loading="syncLoading" @click="handleConfirmSync">确定</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
@@ -74,31 +84,27 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup name="EvaluationApplyList" lang="ts">
|
|
<script setup name="EvaluationApplyList" lang="ts">
|
|
|
-import { computed, getCurrentInstance, reactive, ref, type ComponentInternalInstance } from 'vue';
|
|
|
|
|
|
|
+import { computed, getCurrentInstance, onMounted, reactive, ref, type ComponentInternalInstance } from 'vue';
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
import PageShell from '@/components/PageShell/index.vue';
|
|
import PageShell from '@/components/PageShell/index.vue';
|
|
|
|
|
+import { getEvaluationSyncEmployeeOptions, listEvaluationApply, removeEvaluationApply, syncEvaluationToEmployees } from '@/api/main/evaluation';
|
|
|
|
|
+import type { EvaluationApplyRow, EvaluationSyncEmployeeOption } from '@/api/main/evaluation/types';
|
|
|
|
|
|
|
|
-type ApplyRow = {
|
|
|
|
|
- id: number;
|
|
|
|
|
- name: string;
|
|
|
|
|
- gender: string;
|
|
|
|
|
- department: string;
|
|
|
|
|
- phone: string;
|
|
|
|
|
- statusText: string;
|
|
|
|
|
- statusType: 'success' | 'danger' | 'warning' | 'info';
|
|
|
|
|
- evaluateTime: string;
|
|
|
|
|
- actionType: 'detail' | 'remove';
|
|
|
|
|
-};
|
|
|
|
|
|
|
+type SyncEmployeeOption = EvaluationSyncEmployeeOption & { label: string };
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
|
const modal = proxy?.$modal as any;
|
|
const modal = proxy?.$modal as any;
|
|
|
-const syncCompanyOptions = ['张三', '李四', '王五', '赵六'];
|
|
|
|
|
|
|
+const loading = ref(false);
|
|
|
|
|
+const syncLoading = ref(false);
|
|
|
|
|
+const syncOptionLoading = ref(false);
|
|
|
|
|
+const employeeOptions = ref<SyncEmployeeOption[]>([]);
|
|
|
const syncDialog = reactive({
|
|
const syncDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
- companies: ['张三', '李四'] as string[]
|
|
|
|
|
|
|
+ employeeIds: [] as Array<string | number>
|
|
|
});
|
|
});
|
|
|
|
|
+const evaluationId = computed(() => String(route.query.evaluationId || ''));
|
|
|
|
|
|
|
|
const queryParams = reactive({
|
|
const queryParams = reactive({
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
@@ -106,109 +112,126 @@ const queryParams = reactive({
|
|
|
keyword: ''
|
|
keyword: ''
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const allApplyList = ref<ApplyRow[]>([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- name: '小张小张白有主张',
|
|
|
|
|
- gender: '男',
|
|
|
|
|
- department: 'A',
|
|
|
|
|
- phone: '18438573743',
|
|
|
|
|
- statusText: '通过',
|
|
|
|
|
- statusType: 'success',
|
|
|
|
|
- evaluateTime: '2025-03-21 10:27:56',
|
|
|
|
|
- actionType: 'detail'
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- name: '小张小张白有主张',
|
|
|
|
|
- gender: '男',
|
|
|
|
|
- department: 'A',
|
|
|
|
|
- phone: '18438573743',
|
|
|
|
|
- statusText: '未通过',
|
|
|
|
|
- statusType: 'danger',
|
|
|
|
|
- evaluateTime: '2025-03-21 10:27:56',
|
|
|
|
|
- actionType: 'detail'
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- name: '小张小张白有主张',
|
|
|
|
|
- gender: '男',
|
|
|
|
|
- department: 'A',
|
|
|
|
|
- phone: '18438573743',
|
|
|
|
|
- statusText: '待评分',
|
|
|
|
|
- statusType: 'warning',
|
|
|
|
|
- evaluateTime: '2025-03-21 10:27:56',
|
|
|
|
|
- actionType: 'detail'
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- name: '小张小张白有主张',
|
|
|
|
|
- gender: '男',
|
|
|
|
|
- department: 'A',
|
|
|
|
|
- phone: '18438573743',
|
|
|
|
|
- statusText: '待测评',
|
|
|
|
|
- statusType: 'info',
|
|
|
|
|
- evaluateTime: '2025-03-21 10:27:56',
|
|
|
|
|
- actionType: 'remove'
|
|
|
|
|
|
|
+const allApplyList = ref<EvaluationApplyRow[]>([]);
|
|
|
|
|
+const total = ref(0);
|
|
|
|
|
+const pagedApplyList = computed(() => allApplyList.value);
|
|
|
|
|
+
|
|
|
|
|
+const loadSyncEmployeeOptions = async () => {
|
|
|
|
|
+ syncOptionLoading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getEvaluationSyncEmployeeOptions();
|
|
|
|
|
+ const rows = res.data || [];
|
|
|
|
|
+ employeeOptions.value = rows.map((item) => ({
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ label: [item.name, item.mobile, item.studentNo].filter(Boolean).join(' / ')
|
|
|
|
|
+ }));
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ syncOptionLoading.value = false;
|
|
|
}
|
|
}
|
|
|
-]);
|
|
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
-const filteredApplyList = computed(() => {
|
|
|
|
|
- const keyword = queryParams.keyword.trim();
|
|
|
|
|
- if (!keyword) {
|
|
|
|
|
- return allApplyList.value;
|
|
|
|
|
|
|
+const getList = async () => {
|
|
|
|
|
+ if (!evaluationId.value) {
|
|
|
|
|
+ allApplyList.value = [];
|
|
|
|
|
+ total.value = 0;
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- return allApplyList.value.filter((item) => [item.name, item.phone, item.department, item.statusText].some((field) => field.includes(keyword)));
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const total = computed(() => filteredApplyList.value.length);
|
|
|
|
|
-
|
|
|
|
|
-const pagedApplyList = computed(() => {
|
|
|
|
|
- const start = (queryParams.pageNum - 1) * queryParams.pageSize;
|
|
|
|
|
- const end = start + queryParams.pageSize;
|
|
|
|
|
- return filteredApplyList.value.slice(start, end);
|
|
|
|
|
-});
|
|
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listEvaluationApply({
|
|
|
|
|
+ evaluationId: evaluationId.value,
|
|
|
|
|
+ keyword: queryParams.keyword.trim(),
|
|
|
|
|
+ pageNum: queryParams.pageNum,
|
|
|
|
|
+ pageSize: queryParams.pageSize
|
|
|
|
|
+ });
|
|
|
|
|
+ allApplyList.value = res.rows || [];
|
|
|
|
|
+ total.value = res.total || 0;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
const handleQuery = () => {
|
|
const handleQuery = () => {
|
|
|
queryParams.pageNum = 1;
|
|
queryParams.pageNum = 1;
|
|
|
|
|
+ getList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handlePagination = () => {
|
|
const handlePagination = () => {
|
|
|
- // Pagination component already updates pageNum/pageSize via v-model.
|
|
|
|
|
|
|
+ getList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleAddEmployee = () => {
|
|
const handleAddEmployee = () => {
|
|
|
|
|
+ syncDialog.employeeIds = [];
|
|
|
syncDialog.visible = true;
|
|
syncDialog.visible = true;
|
|
|
|
|
+ if (!employeeOptions.value.length) {
|
|
|
|
|
+ loadSyncEmployeeOptions();
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleExport = () => {
|
|
const handleExport = () => {
|
|
|
modal?.msgSuccess('导出列表功能待接入');
|
|
modal?.msgSuccess('导出列表功能待接入');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleConfirmSync = () => {
|
|
|
|
|
- syncDialog.visible = false;
|
|
|
|
|
- modal?.msgSuccess('同步成功');
|
|
|
|
|
|
|
+const handleConfirmSync = async () => {
|
|
|
|
|
+ if (!syncDialog.employeeIds.length) {
|
|
|
|
|
+ modal?.msgWarning('请选择员工');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ syncLoading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ await syncEvaluationToEmployees({
|
|
|
|
|
+ evaluationIds: [evaluationId.value],
|
|
|
|
|
+ studentIds: syncDialog.employeeIds
|
|
|
|
|
+ });
|
|
|
|
|
+ syncDialog.visible = false;
|
|
|
|
|
+ modal?.msgSuccess('同步成功');
|
|
|
|
|
+ await getList();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ syncLoading.value = false;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleRemove = (row: ApplyRow) => {
|
|
|
|
|
- allApplyList.value = allApplyList.value.filter((item) => item.id !== row.id);
|
|
|
|
|
|
|
+const handleRemove = async (row: EvaluationApplyRow) => {
|
|
|
|
|
+ await removeEvaluationApply(row.id);
|
|
|
modal?.msgSuccess('已移除');
|
|
modal?.msgSuccess('已移除');
|
|
|
- const maxPage = Math.max(1, Math.ceil(total.value / queryParams.pageSize));
|
|
|
|
|
- if (queryParams.pageNum > maxPage) {
|
|
|
|
|
- queryParams.pageNum = maxPage;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ await getList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleOpenDetail = (row: ApplyRow) => {
|
|
|
|
|
|
|
+const handleOpenDetail = (row: EvaluationApplyRow) => {
|
|
|
router.push({
|
|
router.push({
|
|
|
path: '/evaluation/evaluation-view',
|
|
path: '/evaluation/evaluation-view',
|
|
|
query: {
|
|
query: {
|
|
|
applyId: String(row.id),
|
|
applyId: String(row.id),
|
|
|
- name: row.name,
|
|
|
|
|
|
|
+ name: row.name || '',
|
|
|
evaluationId: String(route.query.evaluationId || '')
|
|
evaluationId: String(route.query.evaluationId || '')
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+const handleBack = () => {
|
|
|
|
|
+ const raw = route.query.backPath;
|
|
|
|
|
+ const backPath = Array.isArray(raw) ? raw[0] : raw || '';
|
|
|
|
|
+ if (!backPath) {
|
|
|
|
|
+ router.back();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const url = String(backPath + 'Manage');
|
|
|
|
|
+ // 如果是完整的 http(s) 链接,直接跳转到该地址
|
|
|
|
|
+ if (/^https?:\/\//i.test(url)) {
|
|
|
|
|
+ window.location.href = url;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 否则使用 vue-router 跳转(相对或绝对路径)
|
|
|
|
|
+ try {
|
|
|
|
|
+ router.push({ path: url });
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ router.push(url as any);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getList();
|
|
|
|
|
+});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|