|
@@ -0,0 +1,553 @@
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ v-model="dialogVisible"
|
|
|
+ title="面试邀请通知"
|
|
|
+ width="60%"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ @closed="handleClosed"
|
|
|
+ custom-class="interview-notification-dialog"
|
|
|
+ >
|
|
|
+ <div class="notification">
|
|
|
+ <div class="notification-container">
|
|
|
+ <!-- 消息头部 -->
|
|
|
+ <div class="message-header">
|
|
|
+ <div class="avatar">
|
|
|
+ <img src="/src/assets/qcLogo.png" alt="公司头像" />
|
|
|
+ </div>
|
|
|
+ <div class="contact-info">
|
|
|
+ <span class="phone">+10688028...</span>
|
|
|
+ <div class="contact-actions">
|
|
|
+ <el-button link type="primary" size="small">
|
|
|
+ <el-icon><Phone /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ <el-button link type="primary" size="small">
|
|
|
+ <el-icon><MoreFilled /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 消息内容 -->
|
|
|
+ <div class="message-content">
|
|
|
+ <!-- 邀请标题 -->
|
|
|
+ <div class="invitation-title">
|
|
|
+ <span class="highlight">[候选人姓名]</span> 您好!
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="invitation-content">
|
|
|
+ <p style="margin-left: 12px;">感谢您对 <span class="company-name">[公司名称]</span> 的关注与支持,经您简历筛选 <span class="highlight">[面试职位]</span> 的面试邀请会!</p>
|
|
|
+
|
|
|
+ <div class="interview-details">
|
|
|
+ <div class="detail-item">
|
|
|
+ <strong>面试时间:</strong><span class="editable-field">【具体面试日期及时间】</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-item">
|
|
|
+ <strong>面试方式:</strong>线上面试 <span class="editable-field">(请进入下方二维码视频接入)</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-item">
|
|
|
+ <strong>面试二维码链接:</strong><span class="editable-field">【具体二维码链接】</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="notice-section">
|
|
|
+ <div class="notice-title">注意事项:</div>
|
|
|
+ <div class="notice-content">
|
|
|
+ <p>请提前 10 分钟进入面试链接,测试设备及网络,硬保摄像头、麦克风正常使用,保持良好的面试状态。</p>
|
|
|
+
|
|
|
+ <p>面试过程中请保持守时本礼仪,诚作客答</p>
|
|
|
+
|
|
|
+ <p>如有任何疑问,请随时与我们联系,期待您的精彩表现!</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="company-signature">
|
|
|
+ <div><span class="company-name">[公司名称]</span> 人力资源部</div>
|
|
|
+ <div class="date">[发送日期]</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="notification-right-footer-action">
|
|
|
+ <el-icon><Plus /></el-icon>
|
|
|
+ <div class="input"></div>
|
|
|
+ <el-icon><Plus /></el-icon>
|
|
|
+ <el-icon><Upload /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="notification-right">
|
|
|
+ <div class="notification-right-header">
|
|
|
+ <p>[候选p人姓名] 您好!</p>
|
|
|
+ <p>感谢您对 [公司名称] 的关注与支持,经您简历筛选 [面试职位] 的面试邀请会!</p>
|
|
|
+ </div>
|
|
|
+ <div class="notification-right-content">
|
|
|
+ <p><span class="notification-right-content-title">面试时间:</span>[具体面试日期及时间]</p>
|
|
|
+ <p><span class="notification-right-content-title">面试方式:</span>线上面试(请进入下方二维码视频接入)</p>
|
|
|
+ <p><span class="notification-right-content-title">面试二维码链接:</span>[具体二维码链接]</p>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <div class="notification-right-footer">
|
|
|
+ <p class="notification-right-content-title">注意事项:</p>
|
|
|
+ <p>请提前 10 分钟进入面试链接,测试设备及网络,硬保摄像头、麦克风正常使用,保持良好的面试状态。</p>
|
|
|
+ <p>面试过程中请保持守时本礼仪,诚作客答</p>
|
|
|
+ <p>如有任何疑问,请随时与我们联系,期待您的精彩表现!</p>
|
|
|
+ <p>[公司名称] 人力资源部</p>
|
|
|
+ <p>[发送日期]</p>
|
|
|
+ </div>
|
|
|
+ <!-- 编辑按钮 -->
|
|
|
+ <!-- <div class="edit-section">
|
|
|
+ <el-button type="primary" size="small" @click="handleEdit">
|
|
|
+ <el-icon><Edit /></el-icon>
|
|
|
+ 编辑
|
|
|
+ </el-button>
|
|
|
+ </div> -->
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="dialogVisible = false">关闭</el-button>
|
|
|
+ <el-button type="primary" @click="handleSend" :loading="loading">发送通知</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 编辑表单对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="editDialogVisible"
|
|
|
+ title="编辑面试邀请"
|
|
|
+ width="600px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form :model="editForm" ref="editFormRef" :rules="editRules" label-width="100px">
|
|
|
+ <el-form-item label="候选人姓名" prop="candidateName">
|
|
|
+ <el-input v-model="editForm.candidateName" placeholder="请输入候选人姓名" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="公司名称" prop="companyName">
|
|
|
+ <el-input v-model="editForm.companyName" placeholder="请输入公司名称" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="面试职位" prop="position">
|
|
|
+ <el-input v-model="editForm.position" placeholder="请输入面试职位" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="面试时间" prop="interviewTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="editForm.interviewTime"
|
|
|
+ type="datetime"
|
|
|
+ placeholder="选择面试时间"
|
|
|
+ format="YYYY-MM-DD HH:mm"
|
|
|
+ value-format="YYYY-MM-DD HH:mm"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="面试方式" prop="interviewMethod">
|
|
|
+ <el-select v-model="editForm.interviewMethod" placeholder="请选择面试方式">
|
|
|
+ <el-option label="线上面试" value="online" />
|
|
|
+ <el-option label="线下面试" value="offline" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="面试链接" prop="interviewLink" v-if="editForm.interviewMethod === 'online'">
|
|
|
+ <el-input v-model="editForm.interviewLink" placeholder="请输入面试链接或二维码" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="面试地址" prop="interviewAddress" v-if="editForm.interviewMethod === 'offline'">
|
|
|
+ <el-input v-model="editForm.interviewAddress" type="textarea" placeholder="请输入面试地址" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="editDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleSaveEdit">保存</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref, reactive } from 'vue';
|
|
|
+import { ElMessage, FormInstance } from 'element-plus';
|
|
|
+import { Phone, MoreFilled, Edit } from '@element-plus/icons-vue';
|
|
|
+import { updateBatchStatus } from '../api';
|
|
|
+
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const editDialogVisible = ref(false);
|
|
|
+const loading = ref(false);
|
|
|
+const formRef = ref<FormInstance>();
|
|
|
+const editFormRef = ref<FormInstance>();
|
|
|
+
|
|
|
+// 原有表单数据
|
|
|
+const form = reactive({
|
|
|
+ new_status: undefined as number | undefined,
|
|
|
+ note: '',
|
|
|
+ application_ids: [] as number[]
|
|
|
+});
|
|
|
+
|
|
|
+// 编辑表单数据
|
|
|
+const editForm = reactive({
|
|
|
+ candidateName: '',
|
|
|
+ companyName: '',
|
|
|
+ position: '',
|
|
|
+ interviewTime: '',
|
|
|
+ interviewMethod: 'online',
|
|
|
+ interviewLink: '',
|
|
|
+ interviewAddress: ''
|
|
|
+});
|
|
|
+
|
|
|
+const rules = {
|
|
|
+ new_status: [{ required: true, message: '请选择新状态', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+const editRules = {
|
|
|
+ candidateName: [{ required: true, message: '请输入候选人姓名', trigger: 'blur' }],
|
|
|
+ companyName: [{ required: true, message: '请输入公司名称', trigger: 'blur' }],
|
|
|
+ position: [{ required: true, message: '请输入面试职位', trigger: 'blur' }],
|
|
|
+ interviewTime: [{ required: true, message: '请选择面试时间', trigger: 'change' }],
|
|
|
+ interviewMethod: [{ required: true, message: '请选择面试方式', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+const emit = defineEmits(['success']);
|
|
|
+const props = defineProps({
|
|
|
+ crudExpose: {
|
|
|
+ type: Object,
|
|
|
+ required: true
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 打开对话框
|
|
|
+const open = (selection: any[]) => {
|
|
|
+ dialogVisible.value = true;
|
|
|
+ form.application_ids = selection.map(item => item.id);
|
|
|
+
|
|
|
+ // 初始化编辑表单数据
|
|
|
+ editForm.candidateName = selection[0]?.name || '';
|
|
|
+ editForm.companyName = '智联招聘科技有限公司';
|
|
|
+ editForm.position = selection[0]?.position || '';
|
|
|
+ editForm.interviewTime = '';
|
|
|
+ editForm.interviewMethod = 'online';
|
|
|
+ editForm.interviewLink = '';
|
|
|
+ editForm.interviewAddress = '';
|
|
|
+};
|
|
|
+
|
|
|
+// 处理编辑
|
|
|
+const handleEdit = () => {
|
|
|
+ editDialogVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 保存编辑
|
|
|
+const handleSaveEdit = async () => {
|
|
|
+ if (!editFormRef.value) return;
|
|
|
+
|
|
|
+ await editFormRef.value.validate((valid) => {
|
|
|
+ if (valid) {
|
|
|
+ ElMessage.success('编辑保存成功');
|
|
|
+ editDialogVisible.value = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 发送通知
|
|
|
+const handleSend = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ // 这里可以调用发送通知的API
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟API调用
|
|
|
+
|
|
|
+ ElMessage.success('面试邀请通知已发送');
|
|
|
+ dialogVisible.value = false;
|
|
|
+ emit('success');
|
|
|
+ props.crudExpose.doRefresh();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('发送通知失败:', error);
|
|
|
+ ElMessage.error('发送失败,请重试');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 提交表单(保留原有逻辑)
|
|
|
+const handleSubmit = async () => {
|
|
|
+ if (!formRef.value) return;
|
|
|
+
|
|
|
+ await formRef.value.validate(async (valid) => {
|
|
|
+ if (!valid) return;
|
|
|
+
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const data = {
|
|
|
+ application_ids: form.application_ids,
|
|
|
+ new_status: form.new_status as number,
|
|
|
+ note: form.note,
|
|
|
+ tenant_id: '1'
|
|
|
+ };
|
|
|
+
|
|
|
+ const res = await updateBatchStatus(data);
|
|
|
+
|
|
|
+ if (res.code === 2000) {
|
|
|
+ ElMessage.success('批量修改状态成功');
|
|
|
+ dialogVisible.value = false;
|
|
|
+ emit('success');
|
|
|
+ props.crudExpose.doRefresh();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.msg || '操作失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('批量修改状态失败:', error);
|
|
|
+ ElMessage.error('操作失败,请重试');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 关闭对话框时重置表单
|
|
|
+const handleClosed = () => {
|
|
|
+ if (formRef.value) {
|
|
|
+ formRef.value.resetFields();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 暴露方法给父组件
|
|
|
+defineExpose({
|
|
|
+ open
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.notification {
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+}
|
|
|
+.notification-container {
|
|
|
+ position: relative;
|
|
|
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
|
+ background: #f7f7f7;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 20px;
|
|
|
+ min-height: 400px;
|
|
|
+ width: 45%;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+}
|
|
|
+.notification-right{
|
|
|
+ width: 100%;
|
|
|
+ background: #f7f7f7;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #595959;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-left: 20px;
|
|
|
+ padding: 20px;
|
|
|
+ min-height: 400px;
|
|
|
+}
|
|
|
+.notification-right-header{
|
|
|
+ margin: 30px 0;
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+.notification-right-content-title{
|
|
|
+ color: #595959;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+.notification-right-content{
|
|
|
+ margin: 50px 0;
|
|
|
+ line-height: 1.6;
|
|
|
+
|
|
|
+}
|
|
|
+.notification-right-footer{
|
|
|
+
|
|
|
+}
|
|
|
+.message-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 5px 0;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+ margin-bottom: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.contact-info {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.phone {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.contact-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.message-content {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 15px;
|
|
|
+ margin-bottom: 55px;
|
|
|
+ position: relative;
|
|
|
+/* margin-left: 50px; */
|
|
|
+ box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
|
|
+}
|
|
|
+.notification-right-footer-action{
|
|
|
+ position: absolute;
|
|
|
+ bottom: -10px;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-top: 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ background: #fff;
|
|
|
+ border-top-left-radius: 15px;
|
|
|
+ border-top-right-radius: 15px;
|
|
|
+}
|
|
|
+.input{
|
|
|
+ width: 70%;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 10px;
|
|
|
+ background: #f2f2f2;
|
|
|
+}
|
|
|
+/* .message-content::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: -8px;
|
|
|
+ top: 15px;
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ border-top: 8px solid transparent;
|
|
|
+ border-bottom: 8px solid transparent;
|
|
|
+
|
|
|
+} */
|
|
|
+
|
|
|
+.invitation-title {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ padding-left: 12px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.invitation-content {
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.invitation-content p {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.highlight {
|
|
|
+ color: #595959;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.company-name {
|
|
|
+ color: #595959;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.interview-details {
|
|
|
+ margin: 10px 0;
|
|
|
+ padding: 10px;
|
|
|
+ background: rgba(255,255,255,0.3);
|
|
|
+ border-radius: 4px;
|
|
|
+ border-left: 3px solid #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-item {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-item strong {
|
|
|
+ color: #333;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.editable-field {
|
|
|
+ color: #595959;
|
|
|
+ background: #ecf5ff;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 3px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.notice-section {
|
|
|
+ margin: 10px 0;
|
|
|
+ padding: 12px;
|
|
|
+ background: rgba(255,255,255,0.2);
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid rgba(255,255,255,0.4);
|
|
|
+}
|
|
|
+
|
|
|
+.notice-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #595959;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.notice-content {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.notice-content p {
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.company-signature {
|
|
|
+ margin-top: 15px;
|
|
|
+ text-align: left;
|
|
|
+ padding-left: 12px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.date {
|
|
|
+ margin-top: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.edit-section {
|
|
|
+ text-align: left;
|
|
|
+ margin-top: 15px;
|
|
|
+ padding-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-footer {
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.interview-notification-dialog) {
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.interview-notification-dialog .el-dialog__header) {
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px 8px 0 0;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.interview-notification-dialog .el-dialog__body) {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+</style>
|