|
@@ -232,22 +232,114 @@
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 添加职位问题对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="positionQuestionDialogVisible"
|
|
|
+ title="职位问题"
|
|
|
+ width="60%"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <el-form :model="positionQuestionForm" label-width="80px">
|
|
|
+ <el-form-item label="职位">
|
|
|
+ <el-select v-model="positionQuestionForm.position" placeholder="请选择" style="width: 100%">
|
|
|
+ <el-option
|
|
|
+ v-for="item in positionOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="职位问题">
|
|
|
+ <div class="question-selection-container">
|
|
|
+ <div class="question-search">
|
|
|
+ <el-input
|
|
|
+ v-model="questionSearchKeyword"
|
|
|
+ placeholder="搜索问题"
|
|
|
+ clearable
|
|
|
+ @input="filterQuestions"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon><Search /></el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="question-table-container">
|
|
|
+ <el-table
|
|
|
+ v-loading="questionsLoading"
|
|
|
+ :data="filteredQuestionOptions"
|
|
|
+ style="width: 100%"
|
|
|
+ height="300"
|
|
|
+ @selection-change="handleQuestionSelectionChange"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="55" />
|
|
|
+ <el-table-column prop="label" label="问题" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="question_form_name" label="问题类型" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="chinese_explanation" label="描述" show-overflow-tooltip />
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="pagination-container">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="questionPagination.currentPage"
|
|
|
+ v-model:page-size="questionPagination.pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :total="questionPagination.total"
|
|
|
+ @size-change="handleQuestionSizeChange"
|
|
|
+ @current-change="handleQuestionCurrentChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="排序">
|
|
|
+ <el-input v-model="positionQuestionForm.sequence" placeholder="请输入排序值" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- <div class="question-list-container">
|
|
|
+ <h3>已选问题列表</h3>
|
|
|
+ <el-table :data="selectedQuestions" style="width: 100%">
|
|
|
+ <el-table-column prop="label" label="问题" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="question_form_name" label="问题类型" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="chinese_explanation" label="中文释义" show-overflow-tooltip />
|
|
|
+ <el-table-column label="操作" width="120">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button type="danger" link @click="removeQuestion(scope.row)">
|
|
|
+ <el-icon><Delete /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <span class="dialog-footer">
|
|
|
+ <el-button @click="positionQuestionDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitPositionQuestionForm">保存</el-button>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</fs-page>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup name="documentList">
|
|
|
-import { ref, onMounted, onBeforeUnmount } from 'vue';
|
|
|
+import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
|
|
|
import { useFs } from '@fast-crud/fast-crud';
|
|
|
import XEUtils from 'xe-utils';
|
|
|
import { ElMessageBox, ElMessage } from 'element-plus';
|
|
|
import { createCrudOptions } from './crud';
|
|
|
import { Session } from '/@/utils/storage';
|
|
|
import { getBaseURL } from '/@/utils/baseUrl';
|
|
|
-import { Document, Delete, View, Printer } from '@element-plus/icons-vue';
|
|
|
+import { Document, Delete, View, Printer, Search } from '@element-plus/icons-vue';
|
|
|
import { successNotification } from '/@/utils/message';
|
|
|
import DocumentTreeCom from './components/DocumentTreeCom/index.vue';
|
|
|
import DocumentFormCom from './components/DocumentFormCom/index.vue';
|
|
|
-import { GetDocumentTree, DeleteDocumentCategory, UpdateDocument ,UpdateSequence} from './api';
|
|
|
+import { GetDocumentTree, DeleteDocumentCategory, UpdateDocument ,UpdateSequence, GetInterviewQuestions,AddDocument } from './api';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
/* import { print } from '/@/utils/print'; */
|
|
|
|
|
@@ -274,7 +366,7 @@ const uploadedFile = ref<UploadedFileInfo | null>(null);
|
|
|
|
|
|
// 树形结构相关数据
|
|
|
const documentTreeData = ref([]);
|
|
|
-const documentTreeCacheData = ref([]);
|
|
|
+const documentTreeCacheData = ref<any[]>([]);
|
|
|
const drawerVisible = ref(false);
|
|
|
const drawerFormData = ref({});
|
|
|
const documentTreeRef = ref<DocumentTreeRef | null>(null);
|
|
@@ -374,6 +466,211 @@ interface QuestionRow {
|
|
|
sequence_number?: number;
|
|
|
}
|
|
|
|
|
|
+// 职位问题对话框相关状态
|
|
|
+const positionQuestionDialogVisible = ref(false);
|
|
|
+const positionQuestionForm = ref({
|
|
|
+ position: '',
|
|
|
+ questions: [] as string[],
|
|
|
+ sequence: '0'
|
|
|
+});
|
|
|
+
|
|
|
+// 职位选项和问题选项
|
|
|
+const positionOptions = ref<{ value: string, label: string }[]>([]);
|
|
|
+const questionOptions = ref<{ value: string, label: string, chinese_explanation: string, question_form_name: string }[]>([]);
|
|
|
+
|
|
|
+// 计算属性:已选问题列表
|
|
|
+const selectedQuestions = computed(() => {
|
|
|
+ // 根据已选ID查找完整的问题对象
|
|
|
+ return positionQuestionForm.value.questions.map(questionId => {
|
|
|
+ // 先在当前页面的问题中查找
|
|
|
+ const question = questionOptions.value.find(q => q.value === questionId);
|
|
|
+ if (question) return question;
|
|
|
+
|
|
|
+ // 如果在当前页面找不到,可能需要额外请求
|
|
|
+ // 这里简化处理,只返回ID
|
|
|
+ return { value: questionId, label: `问题 ${questionId}`, chinese_explanation: '' ,question_form_name: ''};
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+// 问题加载状态
|
|
|
+const questionsLoading = ref(false);
|
|
|
+
|
|
|
+// 问题分页参数
|
|
|
+const questionPagination = ref({
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0
|
|
|
+});
|
|
|
+
|
|
|
+// 加载问题选项
|
|
|
+const loadQuestionOptions = async () => {
|
|
|
+ try {
|
|
|
+ questionsLoading.value = true;
|
|
|
+ const response = await GetInterviewQuestions({
|
|
|
+ page: questionPagination.value.currentPage,
|
|
|
+ limit: questionPagination.value.pageSize,
|
|
|
+ tenant_id: 1,
|
|
|
+ keyword: questionSearchKeyword.value
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response && response.code === 2000 && response.data) {
|
|
|
+ // 转换数据格式
|
|
|
+ questionOptions.value = response.data.items.map((item: any) => ({
|
|
|
+ value: item.id.toString(),
|
|
|
+ label: item.question,
|
|
|
+ chinese_explanation: item.scoring_reference || '暂无释义',
|
|
|
+ question_form_name: item.question_form_name || '暂无问题类型'
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 更新总数
|
|
|
+ questionPagination.value.total = response.data.total;
|
|
|
+ } else {
|
|
|
+ ElMessage.error('获取问题列表失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载问题选项失败:', error);
|
|
|
+ ElMessage.error('加载问题选项失败');
|
|
|
+ } finally {
|
|
|
+ questionsLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 处理问题选择变化
|
|
|
+const handleQuestionSelectionChange = (selection: any[]) => {
|
|
|
+ positionQuestionForm.value.questions = selection.map(item => item.value);
|
|
|
+};
|
|
|
+
|
|
|
+// 处理问题分页大小变化
|
|
|
+const handleQuestionSizeChange = (size: number) => {
|
|
|
+ questionPagination.value.pageSize = size;
|
|
|
+ loadQuestionOptions();
|
|
|
+};
|
|
|
+
|
|
|
+// 处理问题当前页变化
|
|
|
+const handleQuestionCurrentChange = (page: number) => {
|
|
|
+ questionPagination.value.currentPage = page;
|
|
|
+ loadQuestionOptions();
|
|
|
+};
|
|
|
+
|
|
|
+// 修改过滤问题的方法
|
|
|
+const filterQuestions = () => {
|
|
|
+ // 重置到第一页
|
|
|
+ questionPagination.value.currentPage = 1;
|
|
|
+ // 重新加载数据
|
|
|
+ loadQuestionOptions();
|
|
|
+};
|
|
|
+
|
|
|
+// 修改打开职位问题对话框方法
|
|
|
+const openPositionQuestionDialog = () => {
|
|
|
+ // 加载职位选项
|
|
|
+ loadPositionOptions();
|
|
|
+ // 加载问题选项
|
|
|
+ loadQuestionOptions();
|
|
|
+ positionQuestionDialogVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 修改 loadPositionOptions 函数,从实际API获取职位数据
|
|
|
+const loadPositionOptions = async () => {
|
|
|
+ try {
|
|
|
+ // 调用实际的API获取职位列表
|
|
|
+ const response = await fetch(getBaseURL() + 'api/system/job/list?tenant_id=1', {
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'Authorization': 'JWT ' + Session.get('token'),
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('获取职位列表失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = await response.json();
|
|
|
+
|
|
|
+ if (data && data.code === 2000 && data.data) {
|
|
|
+ // 转换数据格式
|
|
|
+ positionOptions.value = data.data.map((item: any) => ({
|
|
|
+ value: item.id.toString(),
|
|
|
+ label: item.title,
|
|
|
+ // 如果有中文释义字段,可以在这里添加
|
|
|
+ description: item.description || ''
|
|
|
+ }));
|
|
|
+ } else {
|
|
|
+ ElMessage.error('获取职位列表失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载职位选项失败:', error);
|
|
|
+ ElMessage.error('加载职位选项失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 添加一个函数来获取职位名称
|
|
|
+const getPositionName = (positionId: string | number) => {
|
|
|
+ if (!positionId) return '';
|
|
|
+ const position = positionOptions.value.find(p => p.value === positionId.toString());
|
|
|
+ return position ? position.label : positionId.toString();
|
|
|
+};
|
|
|
+
|
|
|
+// 修改 submitPositionQuestionForm 函数,添加中文释义处理
|
|
|
+const submitPositionQuestionForm = async () => {
|
|
|
+ try {
|
|
|
+ if (!positionQuestionForm.value.position) {
|
|
|
+ ElMessage.warning('请选择职位');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (positionQuestionForm.value.questions.length === 0) {
|
|
|
+ ElMessage.warning('请选择至少一个问题');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取职位名称,用于显示成功消息
|
|
|
+ const positionName = getPositionName(positionQuestionForm.value.position);
|
|
|
+
|
|
|
+ // 构建API请求数据 - 修改为符合API期望的格式
|
|
|
+ const requestData = {
|
|
|
+ position_id: parseInt(positionQuestionForm.value.position),
|
|
|
+ question_id: {
|
|
|
+ question_id: positionQuestionForm.value.questions.map(questionId => parseInt(questionId))
|
|
|
+ },
|
|
|
+ duration: parseInt(positionQuestionForm.value.sequence) || 60,
|
|
|
+ tenant_id: 1
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('提交的数据:', requestData);
|
|
|
+
|
|
|
+ // 调用实际的API保存职位问题关联
|
|
|
+ const response = await AddDocument(requestData);
|
|
|
+
|
|
|
+ if (response && response.code === 2000) {
|
|
|
+ ElMessage.success(`${positionName}职位问题保存成功`);
|
|
|
+ positionQuestionDialogVisible.value = false;
|
|
|
+
|
|
|
+ // 重置表单
|
|
|
+ positionQuestionForm.value = {
|
|
|
+ position: '',
|
|
|
+ questions: [],
|
|
|
+ sequence: '0'
|
|
|
+ };
|
|
|
+
|
|
|
+ // 刷新列表
|
|
|
+ crudExpose.doRefresh();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(response?.msg || '保存失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存职位问题失败:', error);
|
|
|
+ ElMessage.error('保存失败,请重试');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 移除已选问题
|
|
|
+const removeQuestion = (question: { value: string, label: string }) => {
|
|
|
+ positionQuestionForm.value.questions = positionQuestionForm.value.questions.filter(
|
|
|
+ id => id !== question.value
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
// 获取文档树数据
|
|
|
const getTreeData = () => {
|
|
|
GetDocumentTree({}).then((ret: any) => {
|
|
@@ -477,9 +774,7 @@ const handleUploadSuccess = (response: any, uploadFile: any, scope: any) => {
|
|
|
uploadedFile.value = fileInfo;
|
|
|
|
|
|
// 刷新列表时传递category参数
|
|
|
- crudExpose.doRefresh({
|
|
|
- category: selectedCategoryId.value
|
|
|
- });
|
|
|
+ crudExpose.doRefresh();
|
|
|
};
|
|
|
|
|
|
// 处理文件删除
|
|
@@ -921,6 +1216,7 @@ onMounted(() => {
|
|
|
window.addEventListener('viewDocumentDetail', handleViewDetail);
|
|
|
window.addEventListener('openVideoUploadDialog', handleOpenVideoUploadDialog);
|
|
|
window.addEventListener('openSortDialog', handleOpenSortDialog);
|
|
|
+ window.addEventListener('openPositionQuestionDialog', openPositionQuestionDialog);
|
|
|
});
|
|
|
|
|
|
// 添加 onBeforeUnmount 钩子移除事件监听器
|
|
@@ -928,6 +1224,20 @@ onBeforeUnmount(() => {
|
|
|
window.removeEventListener('viewDocumentDetail', handleViewDetail);
|
|
|
window.removeEventListener('openVideoUploadDialog', handleOpenVideoUploadDialog);
|
|
|
window.removeEventListener('openSortDialog', handleOpenSortDialog);
|
|
|
+ window.removeEventListener('openPositionQuestionDialog', openPositionQuestionDialog);
|
|
|
+});
|
|
|
+
|
|
|
+// 新增 questionSearchKeyword
|
|
|
+const questionSearchKeyword = ref('');
|
|
|
+const filteredQuestionOptions = computed(() => {
|
|
|
+ if (!questionSearchKeyword.value) {
|
|
|
+ return questionOptions.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ const keyword = questionSearchKeyword.value.toLowerCase();
|
|
|
+ return questionOptions.value.filter(item =>
|
|
|
+ item.label.toLowerCase().includes(keyword)
|
|
|
+ );
|
|
|
});
|
|
|
</script>
|
|
|
|
|
@@ -1067,4 +1377,63 @@ onBeforeUnmount(() => {
|
|
|
.mt-2 {
|
|
|
margin-top: 8px;
|
|
|
}
|
|
|
+
|
|
|
+.question-list-container {
|
|
|
+ margin-top: 20px;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 15px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+}
|
|
|
+
|
|
|
+.question-list-container h3 {
|
|
|
+ margin-top: 0;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ font-size: 16px;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.question-selection-container {
|
|
|
+ width: 100%;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.question-search {
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
+}
|
|
|
+
|
|
|
+.question-table-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 350px;
|
|
|
+}
|
|
|
+
|
|
|
+.pagination-container {
|
|
|
+ margin-top: 10px;
|
|
|
+ padding: 5px 0;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-top: 1px solid #ebeef5;
|
|
|
+}
|
|
|
+
|
|
|
+.question-item {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.no-data {
|
|
|
+ padding: 20px 0;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
</style>
|