11
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-06-17 18:38:40 +08:00
parent e5a41feea9
commit 183a25f709
10 changed files with 1001 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
{
"pid": 8432,
"pid": 15780,
"version": "0.9.9",
"socketPath": "\\\\.\\pipe\\codegraph-ae68fa88b3ffb91a",
"startedAt": 1781608072582
"startedAt": 1781687361498
}

View File

@@ -92,6 +92,10 @@ export default [
path: '/job-portal/policy/detail',// 政策详情
component: './JobPortal/Policy/Detail',
},
{
path: '/job-portal/live-recruitment',// 直播带岗
component: './JobPortal/LiveRecruitment',
},
],
},
{
@@ -211,6 +215,17 @@ export default [
},
],
},
{
name: 'jobportal',
path: '/jobportal',
routes: [
{
name: '直播待岗管理',
path: '/jobportal/live-mgmt',
component: './Livemgmt',
},
],
},
{
path: '*',
layout: false,

View File

@@ -16,6 +16,7 @@ import {
ReadOutlined,
LoginOutlined,
FundProjectionScreenOutlined,
PlayCircleOutlined,
} from '@ant-design/icons';
import { history, useLocation, useModel } from '@umijs/max';
import {
@@ -102,6 +103,7 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
const isMessage = location.pathname.startsWith('/job-portal/message');
const isPolicy = location.pathname.startsWith('/job-portal/policy');
const isCareerRecommend = location.pathname.startsWith('/job-portal/career-recommendation');
const isLiveRecruitment = location.pathname.startsWith('/job-portal/live-recruitment');
// 获取未读消息数量(仅登录用户)
useEffect(() => {
@@ -295,6 +297,14 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
>
</Button>
<Button
type="text"
icon={<PlayCircleOutlined />}
onClick={() => handleNavClick('/job-portal/live-recruitment')}
className={`nav-btn${isLiveRecruitment ? ' active' : ''}`}
>
</Button>
<Badge count={unreadCount > 0 ? unreadCount : 0} offset={[8, 0]} size="small">
<Button
type="text"

View File

@@ -0,0 +1,353 @@
@import '../theme.less';
.live-recruitment-page {
min-height: 100vh;
background: @jp-bg-page;
}
// ===================== Banner =====================
.live-banner {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 40%, #0f3460 100%);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -60%;
right: -15%;
width: 600px;
height: 600px;
border-radius: 50%;
background: radial-gradient(circle, rgba(24, 144, 255, 0.15) 0%, transparent 70%);
}
&::after {
content: '';
position: absolute;
bottom: -40%;
left: -10%;
width: 400px;
height: 400px;
border-radius: 50%;
background: radial-gradient(circle, rgba(102, 126, 234, 0.12) 0%, transparent 70%);
}
.live-banner-inner {
.jp-page-container();
position: relative;
z-index: 1;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 24px;
padding-top: 36px;
padding-bottom: 36px;
}
.banner-text {
.banner-title {
color: #fff;
margin: 0 0 8px;
font-size: 30px;
font-weight: 700;
letter-spacing: 2px;
.banner-icon {
margin-right: 12px;
font-size: 32px;
color: #69b1ff;
}
}
.banner-subtitle {
color: rgba(255, 255, 255, 0.65);
font-size: 15px;
letter-spacing: 1px;
}
}
.banner-search {
width: 480px;
max-width: 100%;
.search-row {
display: flex;
align-items: stretch;
gap: 0;
height: 40px;
}
.search-type-select {
width: 90px;
flex-shrink: 0;
height: 40px;
.ant-select-selector {
height: 40px !important;
line-height: 38px !important;
border-radius: 24px 0 0 24px !important;
border: 1px solid rgba(255, 255, 255, 0.2);
border-right: none;
background: rgba(255, 255, 255, 0.08);
color: #fff;
font-size: 14px;
.ant-select-selection-item {
line-height: 38px;
color: rgba(255, 255, 255, 0.85);
}
.ant-select-selection-search-input {
height: 40px;
}
}
.ant-select-arrow {
color: rgba(255, 255, 255, 0.5);
}
}
.search-input {
height: 40px;
border-radius: 0 24px 24px 0;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.08);
color: #fff;
font-size: 14px;
.ant-input {
background: transparent;
color: #fff;
&::placeholder {
color: rgba(255, 255, 255, 0.45);
}
}
.ant-input-prefix {
color: rgba(255, 255, 255, 0.45);
margin-right: 8px;
}
.search-btn {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(24, 144, 255, 0.85);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #fff;
transition: all 0.3s;
&:hover {
background: @jp-primary;
transform: scale(1.05);
}
}
&:hover,
&:focus-within {
border-color: @jp-primary;
background: rgba(255, 255, 255, 0.12);
}
}
}
}
// ===================== Stats Bar =====================
.live-stats-bar {
background: @jp-bg-card;
border-bottom: 1px solid @jp-border;
.stats-inner {
.jp-page-container();
padding-top: 12px;
padding-bottom: 12px;
display: flex;
align-items: center;
gap: 24px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
.stat-icon {
color: #ff4d4f;
font-size: 16px;
&.pulse {
animation: livePulse 1.5s ease-in-out infinite;
}
}
.stat-label {
color: @jp-text-secondary;
}
.stat-value {
color: @jp-primary;
font-weight: 600;
margin-left: 2px;
}
}
}
// ===================== Content =====================
.live-content {
padding: 28px 20px 48px;
.live-container {
.jp-page-container();
}
}
// ===================== Card =====================
.live-card {
border-radius: @jp-radius-lg !important;
overflow: hidden;
border: 1px solid @jp-border !important;
box-shadow: @jp-shadow-card;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-6px);
box-shadow: 0 12px 32px rgba(24, 144, 255, 0.18);
border-color: @jp-primary-border !important;
.live-card-cover {
.cover-play-icon {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
.live-card-action {
.action-text {
color: @jp-primary;
gap: 8px;
}
}
}
}
// Card cover (top decorated area)
.live-card-cover {
height: 120px;
position: relative;
display: flex;
align-items: flex-start;
justify-content: flex-end;
padding: 14px 16px;
.cover-play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.6);
font-size: 48px;
color: rgba(255, 255, 255, 0.9);
opacity: 0;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
}
// Card body
.live-card-body {
padding: 18px 20px 16px;
.live-title {
margin: 0 0 12px;
font-size: 15px;
font-weight: 600;
color: @jp-text-primary;
line-height: 1.5;
min-height: 44px;
}
.live-company {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid lighten(@jp-border, 3%);
.company-icon {
color: @jp-primary;
font-size: 14px;
flex-shrink: 0;
}
.company-name {
color: @jp-text-secondary;
font-size: 13px;
}
}
.live-time-range {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 14px;
padding: 8px 12px;
background: @jp-primary-light;
border-radius: 6px;
font-size: 15px;
font-weight: 600;
color: @jp-primary;
.anticon {
font-size: 16px;
color: @jp-primary;
}
.time-separator {
color: @jp-text-muted;
margin: 0 2px;
}
}
.live-card-action {
.action-text {
display: inline-flex;
align-items: center;
gap: 4px;
color: @jp-text-secondary;
font-size: 13px;
font-weight: 500;
transition: all 0.3s;
}
}
}
// ===================== Pagination =====================
.live-pagination {
display: flex;
justify-content: center;
margin-top: 36px;
}
// Empty state
.live-empty {
margin-top: 60px;
}
// ===================== Animations =====================
@keyframes livePulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.5;
transform: scale(1.3);
}
}

View File

@@ -0,0 +1,247 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
Card,
Col,
Row,
Select,
Spin,
Typography,
Input,
Pagination,
Empty,
message,
} from 'antd';
import {
PlayCircleOutlined,
SearchOutlined,
EnvironmentOutlined,
ClockCircleOutlined,
RightOutlined,
FireOutlined,
} from '@ant-design/icons';
import JobPortalHeader from '@/components/JobPortalHeader';
import { getLiveList, type LiveStreamItem } from '@/services/jobportal/live';
import './index.less';
const { Title, Text } = Typography;
/** 卡片渐变色盘,按 id 取模分配 */
const COVER_COLORS = [
'#667eea',
'#f093fb',
'#4facfe',
'#43e97b',
'#fa709a',
'#a18cd1',
'#30cfd0',
'#f6d365',
'#f5576c',
'#667eea',
'#764ba2',
'#f093fb',
];
const PAGE_SIZE = 9;
const getCoverColor = (id: number) => COVER_COLORS[id % COVER_COLORS.length];
const LiveRecruitmentPage: React.FC = () => {
const [list, setList] = useState<LiveStreamItem[]>([]);
const [loading, setLoading] = useState(false);
const [total, setTotal] = useState(0);
const [pageNum, setPageNum] = useState(1);
const [searchValue, setSearchValue] = useState('');
const [keyword, setKeyword] = useState('');
const [searchType, setSearchType] = useState<'title' | 'companyName'>('title');
const fetchList = useCallback(async () => {
try {
setLoading(true);
const params: { title?: string; companyName?: string } = {};
if (keyword) {
params[searchType] = keyword;
}
const response = await getLiveList(params);
if (response?.code === 200) {
// 前端分页
const rows = response.rows || [];
const start = (pageNum - 1) * PAGE_SIZE;
setList(rows.slice(start, start + PAGE_SIZE));
setTotal(response.total || rows.length);
} else {
message.error(response?.msg || '获取直播列表失败');
}
} catch (error) {
console.error('获取直播列表失败:', error);
message.error('获取直播列表失败');
} finally {
setLoading(false);
}
}, [pageNum, keyword, searchType]);
useEffect(() => {
fetchList();
}, [fetchList]);
const handleSearch = () => {
setPageNum(1);
setKeyword(searchValue.trim());
};
const handleCardClick = (item: LiveStreamItem) => {
if (item.liveUrl) {
window.open(item.liveUrl, '_blank', 'noopener,noreferrer');
}
};
/** 格式化时间,只保留前 16 位 (yyyy-MM-dd HH:mm) */
const formatTime = (time?: string): string => {
if (!time) return '';
return time.length >= 16 ? time.substring(0, 16) : time;
};
return (
<div className="live-recruitment-page">
<JobPortalHeader showSearch={false} showHotJobs={false} />
{/* 顶部 Banner */}
<div className="live-banner">
<div className="live-banner-inner">
<div className="banner-text">
<Title level={2} className="banner-title">
<PlayCircleOutlined className="banner-icon" />
</Title>
<Text className="banner-subtitle">
线线
</Text>
</div>
<div className="banner-search">
<div className="search-row">
<Select
value={searchType}
onChange={(value) => setSearchType(value)}
size="large"
className="search-type-select"
options={[
{ value: 'title', label: '标题' },
{ value: 'companyName', label: '公司' },
]}
/>
<Input
placeholder={searchType === 'title' ? '搜索直播标题...' : '搜索公司名称...'}
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onPressEnter={handleSearch}
allowClear
size="large"
className="search-input"
prefix={<SearchOutlined />}
suffix={
<div className="search-btn" onClick={handleSearch}>
<RightOutlined />
</div>
}
/>
</div>
</div>
</div>
</div>
{/* 统计栏 */}
<div className="live-stats-bar">
<div className="stats-inner">
<div className="stat-item">
<FireOutlined className="stat-icon pulse" />
<span className="stat-label"></span>
<span className="stat-value">{total} </span>
</div>
</div>
</div>
{/* 卡片列表 */}
<div className="live-content">
<div className="live-container">
<Spin spinning={loading}>
{list.length > 0 ? (
<>
<Row gutter={[20, 20]}>
{list.map((item) => (
<Col xs={24} sm={12} lg={8} key={item.id}>
<Card
className="live-card"
hoverable
bodyStyle={{ padding: 0 }}
onClick={() => handleCardClick(item)}
>
{/* 卡片顶部装饰 */}
<div
className="live-card-cover"
style={{
background: `linear-gradient(135deg, ${getCoverColor(item.id)}, ${getCoverColor(item.id)}dd)`,
}}
>
<PlayCircleOutlined className="cover-play-icon" />
</div>
{/* 卡片内容 */}
<div className="live-card-body">
<Title
level={5}
className="live-title"
ellipsis={{ rows: 2 }}
>
{item.title}
</Title>
<div className="live-company">
<EnvironmentOutlined className="company-icon" />
<Text ellipsis className="company-name">
{item.companyName}
</Text>
</div>
{(item.startTime || item.endTime) && (
<div className="live-time-range">
<ClockCircleOutlined />
<span>{formatTime(item.startTime) || '待定'}</span>
<span className="time-separator"></span>
<span>{formatTime(item.endTime) || '待定'}</span>
</div>
)}
<div className="live-card-action">
<span className="action-text">
<RightOutlined />
</span>
</div>
</div>
</Card>
</Col>
))}
</Row>
<div className="live-pagination">
<Pagination
current={pageNum}
pageSize={PAGE_SIZE}
total={total}
showSizeChanger={false}
showTotal={(t) => `${t} 场直播`}
onChange={(page) => setPageNum(page)}
/>
</div>
</>
) : (
!loading && (
<Empty
className="live-empty"
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={keyword ? '没有找到相关直播' : '暂无直播安排'}
/>
)
)}
</Spin>
</div>
</div>
</div>
);
};
export default LiveRecruitmentPage;

View File

@@ -169,6 +169,15 @@ const PersonalCenter: React.FC = () => {
hasArrow: !isRealNameVerified,
color: isRealNameVerified ? '#52c41a' : '#8c8c8c'
},
{
id: 2,
title: '在线面试',
icon: <SafetyCertificateOutlined />,
status: '',
hasArrow: true,
color: '#1890ff',
link: 'https://mc.repohub.cn/pro.html'
},
{
id: 4,
title: '通知与提醒',
@@ -180,8 +189,8 @@ const PersonalCenter: React.FC = () => {
], [isRealNameVerified]);
const handleServiceClick = (service: any) => {
if (service.hasArrow) {
// 预留跳转
if (service.link) {
window.open(service.link, '_blank');
}
};

View File

@@ -0,0 +1,87 @@
import React, { useEffect } from 'react';
import {
ModalForm,
ProFormText,
ProFormTextArea,
ProFormDateTimePicker,
} from '@ant-design/pro-components';
import { Form } from 'antd';
import type { LiveStreamItem, CmsLiveForm } from '@/services/jobportal/live';
interface EditModalProps {
open: boolean;
values?: LiveStreamItem;
onCancel: () => void;
onSubmit: (values: CmsLiveForm) => Promise<void>;
}
const EditModal: React.FC<EditModalProps> = ({ open, values, onCancel, onSubmit }) => {
const [form] = Form.useForm();
useEffect(() => {
if (open) {
form.resetFields();
if (values) {
form.setFieldsValue(values);
}
}
}, [open, values, form]);
return (
<ModalForm
title={values ? '编辑直播带岗' : '新增直播带岗'}
form={form}
open={open}
width={600}
modalProps={{ destroyOnClose: true, onCancel }}
onFinish={async (formValues) => {
await onSubmit({ ...formValues, id: values?.id } as CmsLiveForm);
}}
>
<ProFormText name="id" hidden />
<ProFormText
name="title"
label="直播标题"
rules={[{ required: true, message: '请输入直播标题' }]}
placeholder="请输入直播标题"
/>
<ProFormText
name="liveUrl"
label="直播链接"
rules={[{ required: true, message: '请输入直播链接' }]}
placeholder="请输入直播链接"
/>
<ProFormText
name="companyName"
label="公司名称"
placeholder="请输入公司名称"
/>
<ProFormText
name="companyId"
label="公司ID"
placeholder="请输入公司ID"
/>
<ProFormDateTimePicker
name="startTime"
label="开始时间"
width="md"
rules={[{ required: true, message: '请选择开始时间' }]}
fieldProps={{ format: 'YYYY-MM-DD HH:mm:ss' }}
/>
<ProFormDateTimePicker
name="endTime"
label="结束时间"
width="md"
rules={[{ required: true, message: '请选择结束时间' }]}
fieldProps={{ format: 'YYYY-MM-DD HH:mm:ss' }}
/>
<ProFormTextArea
name="description"
label="直播描述"
placeholder="请输入直播描述"
/>
</ModalForm>
);
};
export default EditModal;

View File

@@ -0,0 +1,163 @@
import React, { useRef, useState } from 'react';
import { useAccess } from '@umijs/max';
import { Button, message, Modal } from 'antd';
import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
import { PlusOutlined, DeleteOutlined, FormOutlined } from '@ant-design/icons';
import {
getCmsLiveList,
addCmsLive,
updateCmsLive,
deleteCmsLive,
} from '@/services/jobportal/live';
import type { LiveStreamItem, CmsLiveListParams, CmsLiveForm } from '@/services/jobportal/live';
import EditModal from './components/EditModal';
const LiveMgmtList: React.FC = () => {
const access = useAccess();
const actionRef = useRef<ActionType>();
const [modalVisible, setModalVisible] = useState(false);
const [currentRow, setCurrentRow] = useState<LiveStreamItem>();
const handleDelete = async (id: number) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除该直播吗?',
onOk: async () => {
const res = await deleteCmsLive(id);
if (res.code === 200) {
message.success('删除成功');
actionRef.current?.reload();
} else {
message.error(res.msg || '删除失败');
}
},
});
};
const columns: ProColumns<LiveStreamItem>[] = [
{
title: '直播标题',
dataIndex: 'title',
ellipsis: true,
},
{
title: '直播链接',
dataIndex: 'liveUrl',
ellipsis: true,
hideInSearch: true,
},
{
title: '公司名称',
dataIndex: 'companyName',
ellipsis: true,
},
{
title: '开始时间',
dataIndex: 'startTime',
valueType: 'dateTime',
hideInSearch: true,
},
{
title: '结束时间',
dataIndex: 'endTime',
valueType: 'dateTime',
hideInSearch: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
valueType: 'dateTime',
hideInSearch: true,
},
{
title: '操作',
valueType: 'option',
width: 160,
fixed: 'right',
render: (_, record) => [
<Button
key="edit"
type="link"
size="small"
icon={<FormOutlined />}
hidden={!access.hasPerms('cms:live:edit')}
onClick={() => {
setCurrentRow(record);
setModalVisible(true);
}}
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
icon={<DeleteOutlined />}
hidden={!access.hasPerms('cms:live:remove')}
onClick={() => handleDelete(record.id)}
>
</Button>,
],
},
];
return (
<PageContainer>
<ProTable<LiveStreamItem>
headerTitle="直播待岗列表"
actionRef={actionRef}
rowKey="id"
columns={columns}
scroll={{ x: 'max-content' }}
request={async (params) => {
const res = await getCmsLiveList({
current: params.current,
pageSize: params.pageSize,
title: params.title,
companyName: params.companyName,
} as CmsLiveListParams);
return { data: res.rows, total: res.total, success: true };
}}
toolBarRender={() => [
<Button
key="add"
type="primary"
icon={<PlusOutlined />}
hidden={!access.hasPerms('cms:live:add')}
onClick={() => {
setCurrentRow(undefined);
setModalVisible(true);
}}
>
</Button>,
]}
/>
<EditModal
open={modalVisible}
values={currentRow}
onCancel={() => {
setModalVisible(false);
setCurrentRow(undefined);
}}
onSubmit={async (values: CmsLiveForm) => {
const res = values.id
? await updateCmsLive(values)
: await addCmsLive(values);
if (res.code === 200) {
message.success(values.id ? '修改成功' : '新增成功');
setModalVisible(false);
setCurrentRow(undefined);
actionRef.current?.reload();
} else {
message.error(res.msg || '操作失败');
}
}}
/>
</PageContainer>
);
};
export default LiveMgmtList;

View File

@@ -180,7 +180,7 @@ function ManagementList() {
size="small"
key="view-resume"
icon={<AlignLeftOutlined/>}
hidden={!access.hasPerms('area:business:List.add')}
hidden={!access.hasPerms('cms:userworkexperiences:list')}
onClick={() => {
setCurrentRow(record);
setResumeVisible(true);
@@ -193,7 +193,7 @@ function ManagementList() {
size="small"
key="edit"
icon={<FormOutlined/>}
hidden={!access.hasPerms('area:business:List.add')}
hidden={!access.hasPerms('cms:employeeConfirm:add')}
onClick={() => {
setCurrentRow(record);
setHireVisible(true);
@@ -207,7 +207,7 @@ function ManagementList() {
size="small"
key="edit"
icon={<AlignLeftOutlined/>}
hidden={!access.hasPerms('area:business:List.update')}
// hidden={!access.hasPerms('area:business:List.update')}
onClick={() => {
setModalVisible(true);
setCurrentRow(record);

View File

@@ -0,0 +1,110 @@
import { request } from '@umijs/max';
/** 直播带岗列表项 */
export interface LiveStreamItem {
id: number;
title: string;
liveUrl: string;
description?: string;
startTime?: string;
endTime?: string;
companyId?: string;
companyName: string;
createTime?: string;
updateTime?: string;
}
/** 直播带岗列表查询参数 */
export interface LiveListParams {
title?: string;
companyName?: string;
}
/** 直播带岗列表响应 */
export interface LiveListResult {
code: number;
msg?: string;
total: number;
rows: LiveStreamItem[];
data?: any;
}
/** 获取直播带岗列表 GET /app/live/list */
export async function getLiveList(params?: LiveListParams) {
return request<LiveListResult>('/api/app/live/list', {
method: 'GET',
params,
});
}
// ==================== 管理端 CMS 接口 ====================
const CMS_BASE_URL = '/api/cms/live';
/** 管理端直播列表查询参数 */
export interface CmsLiveListParams {
title?: string;
companyName?: string;
pageNum?: number;
pageSize?: number;
current?: number;
}
/** 管理端通用响应 */
export interface CmsLiveCommonResult {
code: number;
msg?: string;
data?: any;
}
/** 管理端直播列表响应 */
export interface CmsLiveListResult {
code: number;
msg?: string;
total: number;
rows: LiveStreamItem[];
data?: any;
}
/** 管理端新增/编辑直播参数 */
export interface CmsLiveForm {
id?: number;
title: string;
liveUrl: string;
description?: string;
startTime?: string;
endTime?: string;
companyId?: string;
companyName?: string;
}
/** 获取直播带岗管理列表 GET /api/cms/live/list */
export async function getCmsLiveList(params?: CmsLiveListParams) {
return request<CmsLiveListResult>(`${CMS_BASE_URL}/list`, {
method: 'GET',
params,
});
}
/** 新增直播带岗 POST /api/cms/live */
export async function addCmsLive(data: CmsLiveForm) {
return request<CmsLiveCommonResult>(CMS_BASE_URL, {
method: 'POST',
data,
});
}
/** 修改直播带岗 PUT /api/cms/live */
export async function updateCmsLive(data: CmsLiveForm) {
return request<CmsLiveCommonResult>(CMS_BASE_URL, {
method: 'PUT',
data,
});
}
/** 删除直播带岗 DELETE /api/cms/live/{id} */
export async function deleteCmsLive(id: number) {
return request<CmsLiveCommonResult>(`${CMS_BASE_URL}/${id}`, {
method: 'DELETE',
});
}