Files
guoju0808/ry-vue3/src/api/public.js
T
郭庆泰andClaude 0f1d130397 fix(expert): 编辑保存失败 Updates:0 + 错误 toast 弹两次
两个根因:

1. 后端 Updates: 0
   - ExpertNew.vue (admin+manager) onSave 编辑分支里 delete payload.expertId
   - 后端 BizExpertMapper.updateByPrimaryKey 是 where expert_id = #{expertId}
   - 没有 expertId → WHERE 不匹配 → 0 updates → 抛 "操作失败" 500
   - 修复: 不再 delete expertId (后端 insert/update 都依赖该字段)

2. 前端操作失败弹两次
   - request.js 响应拦截器在 code != 200 时自动 ElMessage.error
   - onSave 的 catch 又 ElMessage.error 一次
   - 修复: 加 __silentError 请求配置项, 拦截器遇此 flag 不自动 toast
   - api/public.js bizAdd/bizUpdate 加第 3 参数 opts 透传给 request
   - ExpertNew.vue onSave 调用时传 __silentError: true, 由 catch 块按业务语义
     决定 黄警 (手机号重复) 还是 红错 (其它)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-17 00:43:40 +08:00

110 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 公开门户 API
import request from '@/utils/request'
// 首页:项目公示 + 公示公告 + 资源
export const getPublicIndex = () => request.get('/business/public/index')
// 项目公示分页
export const listPublicProjects = (params) => request.get('/business/public/announcements', { params })
// 公示详情
export const getPublicAnnouncement = (annId) => request.get(`/business/public/announcement/${annId}`)
// 支持函详情
export const getPublicSupportLetter = (annId) => request.get(`/business/public/supportLetter/${annId}`)
// 邀请函详情(参会邀请)
export const getPublicInvitation = (annId) => request.get(`/business/public/invitation/${annId}`)
// 业务字典
export const getDictTypes = () => request.get('/business/dict/types')
export const getDictData = (dictType) => request.get('/business/dict/data', { params: { dictType } })
// 业务认证
export const bizLogin = (data) => request.post('/business/auth/login', data)
export const registerExpert = (data) => request.post('/business/auth/registerExpert', data)
export const registerExecutor = (data) => request.post('/business/auth/registerExecutor', data)
export const registerSponsor = (data) => request.post('/business/auth/registerSponsor', data)
// 业务 CRUDgeneric
// announcement 后端接口未实现, 已改为调 RuoYi 系统通知接口 /system/notice/list
const STUB_ENTITIES = new Set(['announcement'])
export const bizList = (entity, params) => {
if (STUB_ENTITIES.has(entity)) {
return Promise.resolve({ code: 200, msg: 'ok', data: { rows: [], total: 0 } })
}
return request.get(`/business/${entity}/list`, { params })
}
// 系统通知(RuoYi sys_notice 表)
// 返回 {id, title, category, text, time, read, type, content}
export const listNotices = async (params = { pageNum: 1, pageSize: 5 }) => {
const { data } = await request.get('/system/notice/list', { params })
const rows = data?.rows || []
return rows.map(n => {
// notice_content 是 longblob HTML, 提取纯文本
const html = String(n.noticeContent || '')
const text = html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 200)
return {
id: n.noticeId,
title: n.noticeTitle,
category: n.noticeType === '2' ? '公告' : '通知',
text: text || (n.remark || '请查看通知内容'),
time: n.createTime ? new Date(n.createTime.replace(/-/g, '/')).toLocaleString('zh-CN') : '',
read: !!n.isRead,
type: n.noticeType,
content: html,
}
})
}
// 个人通知 (biz_message 表) — 给当前用户发
// 返回 {rows: [{id, title, text, time, read, type, content, bizType, bizId}], unread, total}
const MSG_TYPE_LABEL = { '1': '通知', '2': '待办', '3': '系统' }
export const listMyMessages = async (params = { limit: 5 }) => {
const { data } = await request.get('/business/message/my', { params })
const rows = data?.rows || []
return {
rows: rows.map(m => ({
id: m.msgId,
title: m.title,
category: MSG_TYPE_LABEL[m.msgType] || '通知',
text: String(m.content || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 200),
time: m.createTime ? new Date(m.createTime.replace(/-/g, '/')).toLocaleString('zh-CN') : '',
read: m.isRead === '1',
type: m.msgType,
content: m.content,
bizType: m.bizType,
bizId: m.bizId,
})),
unread: data?.unread || 0,
total: data?.total || 0,
}
}
// 标记单条已读
export const markMessageRead = (msgId) => request.put(`/business/message/read/${msgId}`)
// 全部标记已读
export const markAllMessagesRead = () => request.put('/business/message/readAll')
export const bizGet = (entity, id) => request.get(`/business/${entity}/${id}`)
// opts 透传给 request (例如 { __silentError: true } 让拦截器不自动 toast, 由调用方控制)
export const bizAdd = (entity, data, opts) => request.post(`/business/${entity}`, data, opts)
export const bizUpdate = (entity, data, opts) => request.put(`/business/${entity}`, data, opts)
export const bizDelete = (entity, ids) => request.delete(`/business/${entity}/${ids}`)
// 人员批量导入 (unitType: 'sponsor' | 'executor')
export const importPerson = (unitType, file) => {
const form = new FormData()
form.append('file', file)
return request.post(`/business/person/${unitType}Import`, form, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
// 人员导入模板下载
export const downloadImportTemplate = (unitType) =>
request.get(`/business/person/${unitType}ImportTemplate`, { responseType: 'blob' })
// 专家导入模板下载
export const downloadExpertTemplate = () =>
request.get('/business/expert/importTemplate', { responseType: 'blob' })