投诉功能开发
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled

This commit is contained in:
francis-fh
2026-07-10 17:57:31 +08:00
parent 828f0f950b
commit 0cff24aab1
11 changed files with 781 additions and 34 deletions

View File

@@ -84,6 +84,10 @@ export default [
path: '/job-portal/personal-center/interviews', // 我的面试邀约
component: './JobPortal/PersonalCenter/Interviews',
},
{
path: '/job-portal/personal-center/complaints', // 我的投诉
component: './JobPortal/PersonalCenter/Complaints',
},
{
path: '/job-portal/message', // 消息通知页面
component: './JobPortal/Message',
@@ -204,6 +208,11 @@ export default [
path: '/management/showcase-review',
component: './Management/Showcase',
},
{
name: '投诉管理',
path: '/management/complaint',
component: './Complaint',
},
],
},
{

View File

@@ -0,0 +1,182 @@
import React, { useEffect, useState } from 'react';
import {
Modal,
Form,
Select,
Input,
Button,
message,
Spin,
Space,
} from 'antd';
import { getDictValueEnum } from '@/services/system/dict';
import type { DictValueEnumObj } from '@/components/DictTag';
import { getJobPortalUserId, ensureJobPortalLogin } from '@/utils/jobPortalAuth';
import { submitJobComplaint } from '@/services/jobportal/user';
const { TextArea } = Input;
interface JobComplaintModalProps {
open: boolean;
jobId: string | number;
onCancel: () => void;
onSuccess?: () => void;
}
const JobComplaintModal: React.FC<JobComplaintModalProps> = ({
open,
jobId,
onCancel,
onSuccess,
}) => {
const [form] = Form.useForm();
const [complaintTypeEnum, setComplaintTypeEnum] = useState<DictValueEnumObj>({});
const [loadingDict, setLoadingDict] = useState(false);
const [submitting, setSubmitting] = useState(false);
// 加载投诉类型字典
useEffect(() => {
if (open) {
setLoadingDict(true);
getDictValueEnum('complaint_type', false, true)
.then((data) => {
setComplaintTypeEnum(data);
})
.catch(() => {
message.error('加载投诉类型失败');
})
.finally(() => {
setLoadingDict(false);
});
}
}, [open]);
// 打开时重置表单
useEffect(() => {
if (open) {
form.resetFields();
}
}, [open, form]);
const handleSubmit = async () => {
try {
const values = await form.validateFields();
// 检查登录状态
if (!ensureJobPortalLogin('提交投诉')) {
return;
}
const userId = getJobPortalUserId();
if (!userId) {
message.warning('无法获取用户信息,请稍后重试');
return;
}
setSubmitting(true);
const res = await submitJobComplaint({
userId: String(userId),
jobId: String(jobId),
complaintType: values.complaintType,
complaintContent: values.complaintContent,
contactPhone: values.contactPhone,
});
if (res?.code === 200) {
message.success('投诉提交成功');
form.resetFields();
onSuccess?.();
onCancel();
} else {
message.error(res?.msg || '投诉提交失败');
}
} catch (error) {
// 表单校验失败不提示
if (error && typeof error === 'object' && 'errorFields' in error) {
return;
}
console.error('提交投诉失败:', error);
message.error('投诉提交失败,请重试');
} finally {
setSubmitting(false);
}
};
// 手机号校验规则
const phoneRules = [
{ required: true, message: '请输入联系方式' },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的11位手机号码',
},
];
return (
<Modal
title="岗位投诉"
open={open}
onCancel={onCancel}
width={600}
destroyOnClose
footer={
<Space>
<Button onClick={onCancel}></Button>
<Button type="primary" loading={submitting} onClick={handleSubmit}>
</Button>
</Space>
}
>
<Spin spinning={loadingDict} tip="加载投诉类型...">
<Form
form={form}
layout="vertical"
style={{ marginTop: 16 }}
>
<Form.Item
name="complaintType"
label="投诉类型"
rules={[{ required: true, message: '请选择投诉类型' }]}
>
<Select
placeholder="请选择投诉类型"
options={Object.entries(complaintTypeEnum).map(([key, val]) => ({
value: key,
label: (val as any).label || (val as any).text || key,
}))}
showSearch
optionFilterProp="label"
/>
</Form.Item>
<Form.Item
name="complaintContent"
label="投诉描述"
rules={[{ required: true, message: '请输入投诉描述' }]}
>
<TextArea
rows={6}
placeholder="请详细描述您要投诉的内容..."
maxLength={2000}
showCount
/>
</Form.Item>
<Form.Item
name="contactPhone"
label="联系方式"
rules={phoneRules}
>
<Input
placeholder="请输入您的手机号码"
maxLength={11}
style={{ maxWidth: 300 }}
/>
</Form.Item>
</Form>
</Spin>
</Modal>
);
};
export default JobComplaintModal;

View File

@@ -0,0 +1,182 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { Switch, message, Modal } from 'antd';
import { getDictValueEnum } from '@/services/system/dict';
import DictTag from '@/components/DictTag';
import { getJobComplaintList, jobUp, jobDown, companyUp, companyDown, type JobComplaint } from '@/services/cms/jobComplaint';
const ComplaintManagement: React.FC = () => {
const actionRef = useRef<ActionType>();
const [complaintTypeEnum, setComplaintTypeEnum] = useState<any>({});
useEffect(() => {
getDictValueEnum('complaint_type', false, true).then((data) => {
setComplaintTypeEnum(data);
});
}, []);
const handleJobToggle = async (record: JobComplaint) => {
try {
const isUp = record.jobStatus == 0;
const res = isUp ? await jobDown(record.jobId) : await jobUp(record.jobId);
if (res.code === 200) {
message.success(isUp ? '岗位下架成功' : '岗位上架成功');
actionRef.current?.reload();
} else {
message.error(res.msg || '操作失败');
}
} catch {
message.error('操作失败,请重试');
}
};
const handleCompanyToggle = async (record: JobComplaint) => {
const isUp = record.companyStatus == 0;
if (isUp) {
Modal.confirm({
title: '确认禁用企业',
content: '禁用企业将禁用该企业下所有已发布的岗位,将不再对求职者展示。',
okText: '确认禁用',
cancelText: '取消',
okButtonProps: { danger: true },
onOk: () => doCompanyToggle(record, isUp),
});
} else {
await doCompanyToggle(record, isUp);
}
};
const doCompanyToggle = async (record: JobComplaint, isUp: boolean) => {
try {
const res = isUp ? await companyDown(record.companyId) : await companyUp(record.companyId);
if (res.code === 200) {
message.success(isUp ? '企业下架成功' : '企业上架成功');
actionRef.current?.reload();
} else {
message.error(res.msg || '操作失败');
}
} catch {
message.error('操作失败,请重试');
}
};
const columns: ProColumns<JobComplaint>[] = [
{
title: '序号',
dataIndex: 'index',
align: 'center',
width: 60,
hideInSearch: true,
render: (_, __, index) => (
<span style={{ color: '#1890ff' }}>{index + 1}</span>
),
},
{
title: '投诉者昵称',
dataIndex: 'name',
valueType: 'text',
align: 'center',
width: 120,
},
{
title: '被投诉的企业',
dataIndex: 'companyName',
valueType: 'text',
align: 'center',
width: 200,
ellipsis: true,
},
{
title: '被投诉的岗位',
dataIndex: 'jobTitle',
valueType: 'text',
align: 'center',
width: 150,
ellipsis: true,
},
{
title: '投诉类型',
dataIndex: 'complaintType',
valueType: 'select',
align: 'center',
width: 120,
valueEnum: complaintTypeEnum,
render: (_, record) => {
return <DictTag enums={complaintTypeEnum} value={record.complaintType} />;
},
},
{
title: '投诉描述',
dataIndex: 'complaintContent',
valueType: 'text',
align: 'center',
width: 250,
ellipsis: true,
hideInSearch: true,
},
{
title: '投诉时间',
dataIndex: 'createTime',
valueType: 'dateTime',
align: 'center',
width: 180,
hideInSearch: true,
},
{
title: '岗位启用',
dataIndex: 'jobStatus',
align: 'center',
width: 90,
hideInSearch: true,
render: (_, record) => (
<Switch
checked={record.jobStatus == 0}
onChange={() => handleJobToggle(record)}
/>
),
},
{
title: '企业启用',
dataIndex: 'companyStatus',
align: 'center',
width: 90,
hideInSearch: true,
render: (_, record) => (
<Switch
checked={record.companyStatus == 0}
onChange={() => handleCompanyToggle(record)}
/>
),
},
];
return (
<Fragment>
<div style={{ width: '100%', float: 'right' }}>
<ProTable<JobComplaint>
actionRef={actionRef}
rowKey="id"
columns={columns}
search={{ labelWidth: 100 }}
request={(params) => {
const { current, pageSize, ...rest } = params;
return getJobComplaintList({
currentPage: current,
pageSize,
...rest,
}).then((res) => {
const result = {
data: res.rows,
total: res.total,
success: true,
};
return result;
});
}}
/>
</div>
</Fragment>
);
};
export default ComplaintManagement;

View File

@@ -22,7 +22,8 @@ import {
UserOutlined,
TrophyOutlined,
FireOutlined,
StarOutlined
StarOutlined,
FlagOutlined
} from '@ant-design/icons';
import { Radar } from '@ant-design/charts';
import JobPortalHeader from '@/components/JobPortalHeader';
@@ -35,6 +36,7 @@ import { isJobPortalLoggedIn } from '@/utils/jobPortalAuth';
import { favoriteJob, unfavoriteJob, applyJob, browseJob, getJobDetail } from '@/services/jobportal/user';
import { ensureJobPortalLogin, requireJobPortalUserId } from '@/utils/jobPortalAuth';
import { getDictLabel, resolveIndustryLabel } from '@/utils/jobPortalDict';
import JobComplaintModal from '@/components/JobComplaintModal';
import { getDictValueEnum } from '@/services/system/dict';
import { getCmsIndustryTreeList } from '@/services/classify/industry';
import type { DictValueEnumObj } from '@/components/DictTag';
@@ -190,6 +192,7 @@ const JobDetailPage: React.FC = () => {
]);
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
const [industryTree, setIndustryTree] = useState<any[]>([]);
const [complaintVisible, setComplaintVisible] = useState(false);
useEffect(() => {
Promise.all([
@@ -450,7 +453,7 @@ const JobDetailPage: React.FC = () => {
{/* 职位头部信息 */}
<Card className="job-header-card">
<Row gutter={24}>
<Col span={18}>
<Col span={16}>
<div className="job-title-section">
<Space direction="vertical" size={8}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '16px', flexWrap: 'wrap' }}>
@@ -458,25 +461,6 @@ const JobDetailPage: React.FC = () => {
<Title level={2} className="job-title" style={{ marginBottom: 0 }}>{jobDetail.title}</Title>
<Text className="job-salary-display">{jobDetail.salary}</Text>
</Space>
<Button
icon={isFavorited ? <HeartFilled /> : <HeartOutlined />}
danger={isFavorited}
onClick={handleCollect}
size="middle"
style={{
borderRadius: '6px',
fontWeight: 500,
height: '32px',
padding: '0 12px',
fontSize: '14px',
...(isFavorited ? {} : {
borderColor: '#1890ff',
color: '#1890ff'
})
}}
>
{isFavorited ? '已收藏' : '收藏'}
</Button>
</div>
<Space size={16}>
<Text className="job-meta">
@@ -497,17 +481,33 @@ const JobDetailPage: React.FC = () => {
</Space>
</div>
</Col>
<Col span={6}>
<div className="job-actions">
<Col span={8}>
<div className="job-actions" style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
<Button
icon={isFavorited ? <HeartFilled /> : <HeartOutlined />}
danger={isFavorited}
onClick={handleCollect}
size="large"
style={isFavorited ? {} : { borderColor: '#1890ff', color: '#1890ff' }}
>
{isFavorited ? '已收藏' : '收藏'}
</Button>
<Button
type="primary"
size="large"
block
disabled={originalJobData?.isApply === 1}
onClick={handleApply}
>
{originalJobData?.isApply === 1 ? '已投递' : '立即申请'}
</Button>
<Button
danger
icon={<FlagOutlined />}
size="large"
onClick={() => setComplaintVisible(true)}
>
</Button>
</div>
</Col>
</Row>
@@ -643,6 +643,13 @@ const JobDetailPage: React.FC = () => {
</Col>
</Row>
</div>
{/* 岗位投诉弹窗 */}
<JobComplaintModal
open={complaintVisible}
jobId={originalJobData?.jobId || originalJobData?.id || id || ''}
onCancel={() => setComplaintVisible(false)}
/>
</div>
);
};

View File

@@ -30,6 +30,7 @@ import { favoriteJob, unfavoriteJob, applyJob, browseJob } from '@/services/jobp
import { ensureJobPortalLogin, requireJobPortalUserId } from '@/utils/jobPortalAuth';
import { getDictValueEnum } from '@/services/system/dict';
import { getDictLabel, findTreeLabelById, resolveIndustryLabel } from '@/utils/jobPortalDict';
import JobComplaintModal from '@/components/JobComplaintModal';
import { getJobTitleTreeSelect } from '@/services/common/jobTitle';
import { getCmsIndustryTreeList } from '@/services/classify/industry';
import type { DictValueEnumObj } from '@/components/DictTag';
@@ -213,6 +214,7 @@ const JobListPage: React.FC = () => {
{ item: '年龄', score: 0 },
{ item: '工作地', score: 0 }
]);
const [complaintVisible, setComplaintVisible] = useState(false);
// 格式化薪资显示
const formatSalary = (minSalary: number, maxSalary: number) => {
if (minSalary && maxSalary) {
@@ -837,10 +839,16 @@ const JobListPage: React.FC = () => {
>
{selectedJob?.isApply === 1 ? '已投递' : '立即申请'}
</Button>
<Button
danger
icon={<FlagOutlined />}
onClick={() => setComplaintVisible(true)}
>
</Button>
</Space>
<Space className="action-icons" size={16}>
<QrcodeOutlined title="微信扫码分享" />
<FlagOutlined title="举报" />
</Space>
</div>
</div>
@@ -987,6 +995,13 @@ const JobListPage: React.FC = () => {
</Col>
</Row>
</div>
{/* 岗位投诉弹窗 */}
<JobComplaintModal
open={complaintVisible}
jobId={selectedJob?.jobId || selectedJob?.id || ''}
onCancel={() => setComplaintVisible(false)}
/>
</div>
);
};

View File

@@ -0,0 +1,52 @@
@import '../../theme.less';
.complaints-page {
min-height: 100vh;
background-color: @jp-bg-page;
.page-content {
.jp-page-container();
padding: 24px 20px 40px;
.back-button .ant-btn {
color: @jp-primary;
padding: 0;
&:hover {
color: @jp-primary-dark;
}
}
.page-header .ant-typography {
color: @jp-text-primary;
}
.job-list .job-card {
.jp-card-base();
margin-bottom: 16px;
.complaint-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.complaint-title {
margin-bottom: 0;
}
}
.complaint-meta {
margin-bottom: 8px;
}
.complaint-content {
margin-bottom: 8px;
}
.complaint-footer {
color: @jp-text-muted;
}
}
}
}

View File

@@ -0,0 +1,185 @@
import React, { useState, useEffect } from 'react';
import {
Card,
Row,
Col,
Tag,
Typography,
Button,
Empty,
message,
Spin,
Modal,
Pagination,
} from 'antd';
import {
ArrowLeftOutlined,
WarningOutlined,
ClockCircleOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { history } from '@umijs/max';
import JobPortalHeader from '@/components/JobPortalHeader';
import { getMyComplaintList, deleteComplaint, type JobComplaint } from '@/services/cms/jobComplaint';
import { getDictValueEnum } from '@/services/system/dict';
import type { DictValueEnumObj } from '@/components/DictTag';
import './index.less';
const { Title, Text, Paragraph } = Typography;
const ComplaintsPage: React.FC = () => {
const [complaints, setComplaints] = useState<JobComplaint[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [total, setTotal] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
const [pageSize] = useState<number>(10);
const [complaintTypeEnum, setComplaintTypeEnum] = useState<DictValueEnumObj>({});
useEffect(() => {
getDictValueEnum('complaint_type', false, true).then((data) => {
setComplaintTypeEnum(data);
});
}, []);
const fetchComplaints = async (page: number = 1) => {
try {
setLoading(true);
const res = await getMyComplaintList({ currentPage: page, pageSize });
if (res?.code === 200) {
setComplaints(res.rows);
setTotal(res.total);
} else {
message.error(res?.msg || '获取投诉列表失败');
}
} catch {
message.error('获取投诉列表失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchComplaints();
}, []);
const handlePageChange = (page: number) => {
setCurrentPage(page);
fetchComplaints(page);
};
const handleDelete = (record: JobComplaint) => {
Modal.confirm({
title: '确认撤销投诉',
content: `确定要撤销对「${record.jobTitle}」的投诉吗?`,
okText: '确认撤销',
cancelText: '取消',
okButtonProps: { danger: true },
onOk: async () => {
try {
const res = await deleteComplaint(record.id);
if (res.code === 200) {
message.success('撤销投诉成功');
fetchComplaints(currentPage);
} else {
message.error(res.msg || '撤销失败');
}
} catch {
message.error('撤销失败,请重试');
}
},
});
};
const getComplaintTypeLabel = (type: number): string => {
const item = complaintTypeEnum[type];
return (item as any)?.label || (item as any)?.text || String(type);
};
const handleBack = () => {
history.push('/job-portal/personal-center');
};
return (
<div className="complaints-page">
<JobPortalHeader showSearch={false} showHotJobs={false} />
<Spin spinning={loading}>
<div className="page-content">
<div className="back-button">
<Button type="text" icon={<ArrowLeftOutlined />} onClick={handleBack}>
</Button>
</div>
<div className="page-header">
<Title level={2}>
<WarningOutlined style={{ color: '#ff4d4f', marginRight: 8 }} />
</Title>
<Text type="secondary"> {total} </Text>
</div>
{complaints.length === 0 && !loading ? (
<Empty description="暂无投诉记录" style={{ marginTop: '100px' }} />
) : (
<>
<div className="job-list">
{complaints.map((item) => (
<Card key={item.id} className="job-card">
<Row gutter={16} align="middle">
<Col span={20}>
<div className="complaint-info">
<div className="complaint-header">
<Title level={4} className="complaint-title">
{item.jobTitle}
</Title>
<Tag color="orange">{getComplaintTypeLabel(item.complaintType)}</Tag>
</div>
<div className="complaint-meta">
<Text className="complaint-company">{item.companyName}</Text>
</div>
<div className="complaint-content">
<Paragraph ellipsis={{ rows: 2 }} type="secondary">
{item.complaintContent}
</Paragraph>
</div>
<div className="complaint-footer">
<Text type="secondary">
<ClockCircleOutlined /> {item.createTime}
</Text>
</div>
</div>
</Col>
<Col span={4} style={{ textAlign: 'right' }}>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(item)}
>
</Button>
</Col>
</Row>
</Card>
))}
</div>
{total > pageSize && (
<div style={{ textAlign: 'center', marginTop: 24 }}>
<Pagination
current={currentPage}
pageSize={pageSize}
total={total}
onChange={handlePageChange}
showSizeChanger={false}
/>
</div>
)}
</>
)}
</div>
</Spin>
</div>
);
};
export default ComplaintsPage;

View File

@@ -136,7 +136,8 @@ const PersonalCenter: React.FC = () => {
applications: 0,
favorites: 0,
footprints: 0,
appointments: 0
appointments: 0,
complaints: 0,
});
useEffect(() => {
@@ -149,6 +150,7 @@ const PersonalCenter: React.FC = () => {
favorites: Number(response.data.ysc) || 0,
footprints: Number(response.data.yzj) || 0,
appointments: Number(response.data.yyy) || 0,
complaints: Number(response.data.yts) || 0,
});
}
} catch (error) {
@@ -268,7 +270,7 @@ const PersonalCenter: React.FC = () => {
<section className="stats-section">
<Row gutter={0}>
<Col span={8}>
<Col span={6}>
<div
className="stat-item"
onClick={() => history.push('/job-portal/personal-center/applications')}
@@ -277,7 +279,7 @@ const PersonalCenter: React.FC = () => {
<div className="stat-label"></div>
</div>
</Col>
<Col span={8}>
<Col span={6}>
<div
className="stat-item"
onClick={() => history.push('/job-portal/personal-center/favorites')}
@@ -286,7 +288,7 @@ const PersonalCenter: React.FC = () => {
<div className="stat-label"></div>
</div>
</Col>
<Col span={8}>
<Col span={6}>
<div
className="stat-item"
onClick={() => history.push('/job-portal/personal-center/footprints')}
@@ -295,12 +297,15 @@ const PersonalCenter: React.FC = () => {
<div className="stat-label"></div>
</div>
</Col>
{/* <Col span={6}>
<div className="stat-item stat-item-last">
<div className="stat-number">{statistics.appointments}</div>
<div className="stat-label">预约</div>
<Col span={6}>
<div
className="stat-item"
onClick={() => history.push('/job-portal/personal-center/complaints')}
>
<div className="stat-number">{statistics.complaints || 0}</div>
<div className="stat-label"></div>
</div>
</Col> */}
</Col>
</Row>
</section>

View File

@@ -0,0 +1,95 @@
import { request } from '@umijs/max';
/** 投诉列表查询参数 */
export interface JobComplaintListParams {
pageSize?: number;
currentPage?: number;
complaintType?: string;
complaintStatus?: string;
[key: string]: any;
}
/** 投诉记录 */
export interface JobComplaint {
createTime: string;
updateTime: string;
id: number;
userId: number;
jobId: number;
complaintType: number;
complaintContent: string;
evidenceUrl: string;
contactPhone: string;
complaintStatus: number;
handleUserId: number | null;
handleContent: string;
handleTime: string | null;
companyId: number;
companyName: string;
jobTitle: string;
name: string;
/** 岗位状态0=上架1=下架 */
jobStatus?: number;
/** 企业状态0=上架1=下架 */
companyStatus?: number;
}
/** 投诉列表分页结果 */
export interface JobComplaintPageResult {
total: number;
rows: JobComplaint[];
code: number;
msg: string;
data: null;
}
/** 查询投诉列表(管理端) */
export async function getJobComplaintList(params?: JobComplaintListParams) {
return request<JobComplaintPageResult>('/api/cms/jobComplaint/list', {
method: 'GET',
params,
});
}
/** 岗位上架 */
export async function jobUp(jobId: number) {
return request<API.Result>(`/api/cms/job/jobUp/${jobId}`, {
method: 'PUT',
});
}
/** 岗位下架 */
export async function jobDown(jobId: number) {
return request<API.Result>(`/api/cms/job/jobDown/${jobId}`, {
method: 'PUT',
});
}
/** 企业上架 */
export async function companyUp(companyId: number) {
return request<API.Result>(`/api/cms/company/companyUp/${companyId}`, {
method: 'PUT',
});
}
/** 企业下架 */
export async function companyDown(companyId: number) {
return request<API.Result>(`/api/cms/company/companyDown/${companyId}`, {
method: 'PUT',
});
}
/** 查询我的投诉列表(求职者端) */
export async function getMyComplaintList(params?: JobComplaintListParams) {
return request<JobComplaintPageResult>('/api/cms/jobComplaint/listSelf', {
method: 'GET',
params,
});
}
/** 撤销投诉 */
export async function deleteComplaint(ids: number | string) {
return request<API.Result>(`/api/cms/jobComplaint/${ids}`, {
method: 'DELETE',
});
}

View File

@@ -168,3 +168,17 @@ export async function deleteShowcaseFile(id: number) {
});
}
/** 提交岗位投诉 POST /cms/jobComplaint */
export async function submitJobComplaint(params: {
userId: string;
jobId: string;
complaintType: string;
complaintContent: string;
contactPhone: string;
}) {
return request<API.Result>('/api/cms/jobComplaint', {
method: 'POST',
data: params,
});
}

View File

@@ -72,6 +72,7 @@ declare namespace API {
ysc?: string; // 收藏
yzj?: string; // 足迹
yyy?: string; // 预约
yts?: string; // 投诉
}
export interface AppUserYhscResult {