Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
31d2d04a46 | |
|
|
666c95855b |
File diff suppressed because it is too large
Load Diff
|
|
@ -5,6 +5,7 @@ export * from './tech-resources'
|
||||||
export * from './system'
|
export * from './system'
|
||||||
export * from './dashboard'
|
export * from './dashboard'
|
||||||
export * from './upload'
|
export * from './upload'
|
||||||
|
export * from './patent'
|
||||||
|
|
||||||
// 导出请求方法
|
// 导出请求方法
|
||||||
export { get, post, put, del } from './request'
|
export { get, post, put, del } from './request'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,418 @@
|
||||||
|
// 组织机构接口
|
||||||
|
export interface Organization {
|
||||||
|
id: string
|
||||||
|
orgName: string
|
||||||
|
orgType: 'SCHOOL' | 'ENTERPRISE' | 'RESEARCH_INSTITUTE' | 'GOVERNMENT' | 'OTHER'
|
||||||
|
orgCode?: string
|
||||||
|
orgStatus?: string
|
||||||
|
legalRepresentative?: string
|
||||||
|
establishmentDate?: string
|
||||||
|
province?: string
|
||||||
|
city?: string
|
||||||
|
district?: string
|
||||||
|
address?: string
|
||||||
|
contactPhone?: string
|
||||||
|
contactEmail?: string
|
||||||
|
website?: string
|
||||||
|
employeeCount?: number
|
||||||
|
introduction?: string
|
||||||
|
registeredCapital?: string
|
||||||
|
businessScope?: string
|
||||||
|
industry?: string
|
||||||
|
isHighTech?: number
|
||||||
|
schoolType?: string
|
||||||
|
schoolLevel?: string
|
||||||
|
qualificationCertificates?: string
|
||||||
|
flag: number
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
createBy?: string
|
||||||
|
updateBy?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页查询参数
|
||||||
|
export interface OrganizationPageParams {
|
||||||
|
pageNum: number
|
||||||
|
pageSize: number
|
||||||
|
orgName?: string
|
||||||
|
orgType?: string
|
||||||
|
orgStatus?: string
|
||||||
|
province?: string
|
||||||
|
city?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增/更新参数
|
||||||
|
export interface OrganizationFormData {
|
||||||
|
id?: string
|
||||||
|
orgName: string
|
||||||
|
orgType: string
|
||||||
|
orgCode?: string
|
||||||
|
orgStatus?: string
|
||||||
|
legalRepresentative?: string
|
||||||
|
establishmentDate?: string
|
||||||
|
province?: string
|
||||||
|
city?: string
|
||||||
|
district?: string
|
||||||
|
address?: string
|
||||||
|
contactPhone?: string
|
||||||
|
contactEmail?: string
|
||||||
|
website?: string
|
||||||
|
employeeCount?: number
|
||||||
|
introduction?: string
|
||||||
|
registeredCapital?: string
|
||||||
|
businessScope?: string
|
||||||
|
industry?: string
|
||||||
|
isHighTech?: number
|
||||||
|
schoolType?: string
|
||||||
|
schoolLevel?: string
|
||||||
|
qualificationCertificates?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// API响应接口
|
||||||
|
export interface ApiResponse<T = unknown> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageResponse<T> {
|
||||||
|
records: T[]
|
||||||
|
total: number
|
||||||
|
size: number
|
||||||
|
current: number
|
||||||
|
pages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Mock 数据 ====================
|
||||||
|
const mockOrganizations: Organization[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
orgName: '清华大学',
|
||||||
|
orgType: 'SCHOOL',
|
||||||
|
orgCode: '12100000400000846E',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '李校长',
|
||||||
|
establishmentDate: '1911-04-26',
|
||||||
|
province: '北京市',
|
||||||
|
city: '北京市',
|
||||||
|
district: '海淀区',
|
||||||
|
address: '清华园1号',
|
||||||
|
contactPhone: '010-62793001',
|
||||||
|
contactEmail: 'info@tsinghua.edu.cn',
|
||||||
|
website: 'https://www.tsinghua.edu.cn',
|
||||||
|
employeeCount: 15000,
|
||||||
|
introduction: '清华大学是中国著名高等学府,坐落于北京西北郊风景秀丽的清华园。',
|
||||||
|
schoolType: '985',
|
||||||
|
schoolLevel: '本科',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-01 10:00:00',
|
||||||
|
updateTime: '2024-01-15 14:30:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
orgName: '华为技术有限公司',
|
||||||
|
orgType: 'ENTERPRISE',
|
||||||
|
orgCode: '91440300279860880E',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '任正非',
|
||||||
|
establishmentDate: '1987-09-15',
|
||||||
|
province: '广东省',
|
||||||
|
city: '深圳市',
|
||||||
|
district: '龙岗区',
|
||||||
|
address: '坂田华为基地',
|
||||||
|
contactPhone: '0755-28780808',
|
||||||
|
contactEmail: 'contact@huawei.com',
|
||||||
|
website: 'https://www.huawei.com',
|
||||||
|
employeeCount: 195000,
|
||||||
|
introduction: '华为是全球领先的ICT(信息与通信)基础设施和智能终端提供商。',
|
||||||
|
registeredCapital: '403.08亿元',
|
||||||
|
businessScope: '通信设备、智能终端、云计算、人工智能等',
|
||||||
|
industry: '信息技术',
|
||||||
|
isHighTech: 1,
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-02 09:00:00',
|
||||||
|
updateTime: '2024-01-16 11:20:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
orgName: '中国科学院计算技术研究所',
|
||||||
|
orgType: 'RESEARCH_INSTITUTE',
|
||||||
|
orgCode: '12100000400001234X',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '孙凝晖',
|
||||||
|
establishmentDate: '1956-06-01',
|
||||||
|
province: '北京市',
|
||||||
|
city: '北京市',
|
||||||
|
district: '海淀区',
|
||||||
|
address: '中关村科学院南路6号',
|
||||||
|
contactPhone: '010-62600114',
|
||||||
|
contactEmail: 'office@ict.ac.cn',
|
||||||
|
website: 'http://www.ict.ac.cn',
|
||||||
|
employeeCount: 800,
|
||||||
|
introduction: '中国科学院计算技术研究所创建于1956年,是中国第一个专门从事计算机科学技术综合性研究的学术机构。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-03 08:30:00',
|
||||||
|
updateTime: '2024-01-17 16:45:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
orgName: '北京市科学技术委员会',
|
||||||
|
orgType: 'GOVERNMENT',
|
||||||
|
orgCode: '11000000MB0109876Y',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '张主任',
|
||||||
|
establishmentDate: '1978-03-01',
|
||||||
|
province: '北京市',
|
||||||
|
city: '北京市',
|
||||||
|
district: '西城区',
|
||||||
|
address: '西城区西直门南大街16号',
|
||||||
|
contactPhone: '010-66415555',
|
||||||
|
contactEmail: 'bjkw@beijing.gov.cn',
|
||||||
|
website: 'http://kw.beijing.gov.cn',
|
||||||
|
employeeCount: 300,
|
||||||
|
introduction: '北京市科学技术委员会是北京市人民政府组成部门,负责全市科技工作的统筹规划和综合协调。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-04 10:15:00',
|
||||||
|
updateTime: '2024-01-18 09:30:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
orgName: '北京大学',
|
||||||
|
orgType: 'SCHOOL',
|
||||||
|
orgCode: '12100000400000123A',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '郝平',
|
||||||
|
establishmentDate: '1898-07-03',
|
||||||
|
province: '北京市',
|
||||||
|
city: '北京市',
|
||||||
|
district: '海淀区',
|
||||||
|
address: '颐和园路5号',
|
||||||
|
contactPhone: '010-62751234',
|
||||||
|
contactEmail: 'xxb@pku.edu.cn',
|
||||||
|
website: 'https://www.pku.edu.cn',
|
||||||
|
employeeCount: 12000,
|
||||||
|
introduction: '北京大学创办于1898年,初名京师大学堂,是中国第一所国立综合性大学。',
|
||||||
|
schoolType: '985',
|
||||||
|
schoolLevel: '本科',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-05 11:00:00',
|
||||||
|
updateTime: '2024-01-19 10:15:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
orgName: '腾讯科技(深圳)有限公司',
|
||||||
|
orgType: 'ENTERPRISE',
|
||||||
|
orgCode: '91440300708461136T',
|
||||||
|
orgStatus: 'ACTIVE',
|
||||||
|
legalRepresentative: '马化腾',
|
||||||
|
establishmentDate: '1998-11-11',
|
||||||
|
province: '广东省',
|
||||||
|
city: '深圳市',
|
||||||
|
district: '南山区',
|
||||||
|
address: '科技园中区一路腾讯大厦',
|
||||||
|
contactPhone: '0755-86013388',
|
||||||
|
contactEmail: 'service@tencent.com',
|
||||||
|
website: 'https://www.tencent.com',
|
||||||
|
employeeCount: 110000,
|
||||||
|
introduction: '腾讯以技术丰富互联网用户的生活,致力成为最受尊敬的互联网企业。',
|
||||||
|
registeredCapital: '65000万元',
|
||||||
|
businessScope: '互联网服务、社交网络、游戏、金融科技等',
|
||||||
|
industry: '互联网',
|
||||||
|
isHighTech: 1,
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-06 14:20:00',
|
||||||
|
updateTime: '2024-01-20 15:40:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let mockIdCounter = 7
|
||||||
|
|
||||||
|
// 模拟延迟
|
||||||
|
const mockDelay = () => new Promise(resolve => setTimeout(resolve, 300))
|
||||||
|
|
||||||
|
// 分页查询组织机构列表
|
||||||
|
export async function getOrganizationPage(params: OrganizationPageParams) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
let filteredData = [...mockOrganizations]
|
||||||
|
|
||||||
|
// 过滤条件
|
||||||
|
if (params.orgName) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.orgName.includes(params.orgName!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (params.orgType) {
|
||||||
|
filteredData = filteredData.filter(item => item.orgType === params.orgType)
|
||||||
|
}
|
||||||
|
if (params.province) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.province?.includes(params.province!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (params.city) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.city?.includes(params.city!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const start = (params.pageNum - 1) * params.pageSize
|
||||||
|
const end = start + params.pageSize
|
||||||
|
const records = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: {
|
||||||
|
records,
|
||||||
|
total: filteredData.length,
|
||||||
|
size: params.pageSize,
|
||||||
|
current: params.pageNum,
|
||||||
|
pages: Math.ceil(filteredData.length / params.pageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取组织机构详情
|
||||||
|
export async function getOrganizationDetail(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const organization = mockOrganizations.find(item => item.id === id)
|
||||||
|
|
||||||
|
if (organization) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: organization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该机构',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增组织机构
|
||||||
|
export async function addOrganization(data: OrganizationFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const newOrganization: Organization = {
|
||||||
|
...data,
|
||||||
|
id: String(mockIdCounter++),
|
||||||
|
flag: 0,
|
||||||
|
createTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
} as Organization
|
||||||
|
|
||||||
|
mockOrganizations.unshift(newOrganization)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '新增成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改组织机构
|
||||||
|
export async function updateOrganization(data: OrganizationFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const index = mockOrganizations.findIndex(item => item.id === data.id)
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
mockOrganizations[index] = {
|
||||||
|
...mockOrganizations[index],
|
||||||
|
...data,
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '修改成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该机构',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除组织机构
|
||||||
|
export async function deleteOrganization(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const index = mockOrganizations.findIndex(item => item.id === id)
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
mockOrganizations.splice(index, 1)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '删除成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该机构',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除组织机构
|
||||||
|
export async function batchDeleteOrganization(ids: string[]) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
ids.forEach(id => {
|
||||||
|
const index = mockOrganizations.findIndex(item => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
mockOrganizations.splice(index, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '批量删除成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
// src/api/patent.ts
|
||||||
|
|
||||||
|
// 专利信息接口 (对应 patent 表和 patent_talent 关联表)
|
||||||
|
export interface Patent {
|
||||||
|
id: string
|
||||||
|
patentName: string
|
||||||
|
patentNumber: string
|
||||||
|
patentType: string // 专利类型
|
||||||
|
applicationTime: string
|
||||||
|
authorizationTime?: string | null // 允许为空
|
||||||
|
patentStatus: string // 专利状态
|
||||||
|
status?: number
|
||||||
|
flag: number
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
createBy?: string
|
||||||
|
updateBy?: string
|
||||||
|
|
||||||
|
// 关联的跨表字段(前端展示用)
|
||||||
|
talentId?: string
|
||||||
|
talentName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页查询参数
|
||||||
|
export interface PatentPageParams {
|
||||||
|
pageNum: number
|
||||||
|
pageSize: number
|
||||||
|
patentName?: string
|
||||||
|
patentNumber?: string
|
||||||
|
patentType?: string
|
||||||
|
talentName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增/更新参数
|
||||||
|
export interface PatentFormData {
|
||||||
|
id?: string
|
||||||
|
talentId: string // 关联人才
|
||||||
|
patentName: string
|
||||||
|
patentNumber: string
|
||||||
|
patentType: string
|
||||||
|
applicationTime: string
|
||||||
|
authorizationTime?: string | null
|
||||||
|
patentStatus: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:这里不加 export,防止和 index.ts 里其他文件的同名接口冲突
|
||||||
|
interface ApiResponse<T = unknown> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PageResponse<T> {
|
||||||
|
records: T[]
|
||||||
|
total: number
|
||||||
|
size: number
|
||||||
|
current: number
|
||||||
|
pages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Mock 数据 ====================
|
||||||
|
const mockPatents: Patent[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
talentId: '1',
|
||||||
|
talentName: '张伟',
|
||||||
|
patentName: '一种基于深度学习的图像识别方法及系统',
|
||||||
|
patentNumber: 'CN202110123456.7',
|
||||||
|
patentType: 'INVENTION',
|
||||||
|
applicationTime: '2021-03-15 00:00:00',
|
||||||
|
authorizationTime: '2022-08-20 00:00:00',
|
||||||
|
patentStatus: 'AUTHORIZED',
|
||||||
|
status: 1,
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-01 10:00:00',
|
||||||
|
updateTime: '2024-01-15 14:30:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
talentId: '2',
|
||||||
|
talentName: '李娜',
|
||||||
|
patentName: '一种新型散热计算机机箱',
|
||||||
|
patentNumber: 'CN202220987654.1',
|
||||||
|
patentType: 'UTILITY_MODEL',
|
||||||
|
applicationTime: '2022-05-10 00:00:00',
|
||||||
|
authorizationTime: null,
|
||||||
|
patentStatus: 'APPLYING',
|
||||||
|
status: 1,
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-02 09:00:00',
|
||||||
|
updateTime: '2024-01-16 11:20:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let mockIdCounter = 3
|
||||||
|
const mockDelay = () => new Promise(resolve => setTimeout(resolve, 300))
|
||||||
|
|
||||||
|
export async function getPatentPage(params: PatentPageParams) {
|
||||||
|
await mockDelay()
|
||||||
|
let filteredData = [...mockPatents]
|
||||||
|
|
||||||
|
if (params.patentName) {
|
||||||
|
filteredData = filteredData.filter(item => item.patentName.includes(params.patentName!))
|
||||||
|
}
|
||||||
|
if (params.patentNumber) {
|
||||||
|
filteredData = filteredData.filter(item => item.patentNumber.includes(params.patentNumber!))
|
||||||
|
}
|
||||||
|
if (params.patentType) {
|
||||||
|
filteredData = filteredData.filter(item => item.patentType === params.patentType)
|
||||||
|
}
|
||||||
|
if (params.talentName) {
|
||||||
|
filteredData = filteredData.filter(item => item.talentName?.includes(params.talentName!))
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredData.sort((a, b) => b.applicationTime.localeCompare(a.applicationTime))
|
||||||
|
|
||||||
|
const start = (params.pageNum - 1) * params.pageSize
|
||||||
|
const end = start + params.pageSize
|
||||||
|
const records = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: {
|
||||||
|
records,
|
||||||
|
total: filteredData.length,
|
||||||
|
size: params.pageSize,
|
||||||
|
current: params.pageNum,
|
||||||
|
pages: Math.ceil(filteredData.length / params.pageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPatentDetail(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
const patent = mockPatents.find(item => item.id === id)
|
||||||
|
if (patent) {
|
||||||
|
return { data: { code: 200, message: '查询成功', data: patent } }
|
||||||
|
} else {
|
||||||
|
return { data: { code: 404, message: '未找到该专利', data: null } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addPatent(data: PatentFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
const newPatent: Patent = {
|
||||||
|
...data,
|
||||||
|
id: String(mockIdCounter++),
|
||||||
|
flag: 0,
|
||||||
|
createTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin',
|
||||||
|
talentName: '关联人才' // Mock时简单占位
|
||||||
|
} as Patent
|
||||||
|
mockPatents.unshift(newPatent)
|
||||||
|
return { data: { code: 200, message: '新增成功', data: null } }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePatent(data: PatentFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
const index = mockPatents.findIndex(item => item.id === data.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
mockPatents[index] = {
|
||||||
|
...mockPatents[index],
|
||||||
|
...data,
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
return { data: { code: 200, message: '修改成功', data: null } }
|
||||||
|
} else {
|
||||||
|
return { data: { code: 404, message: '未找到该专利', data: null } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePatent(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
const index = mockPatents.findIndex(item => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
mockPatents.splice(index, 1)
|
||||||
|
return { data: { code: 200, message: '删除成功', data: null } }
|
||||||
|
} else {
|
||||||
|
return { data: { code: 404, message: '未找到该专利', data: null } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function batchDeletePatent(ids: string[]) {
|
||||||
|
await mockDelay()
|
||||||
|
ids.forEach(id => {
|
||||||
|
const index = mockPatents.findIndex(item => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
mockPatents.splice(index, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { data: { code: 200, message: '批量删除成功', data: null } }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,375 @@
|
||||||
|
// 履历信息接口
|
||||||
|
export interface Resume {
|
||||||
|
id: string
|
||||||
|
talentId: string
|
||||||
|
talentName?: string // 人才姓名(用于显示)
|
||||||
|
startTime: string
|
||||||
|
endTime?: string
|
||||||
|
unitId?: string
|
||||||
|
unitName: string
|
||||||
|
unitAddress?: string
|
||||||
|
workContent: string
|
||||||
|
flag: number
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
createBy?: string
|
||||||
|
updateBy?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页查询参数
|
||||||
|
export interface ResumePageParams {
|
||||||
|
pageNum: number
|
||||||
|
pageSize: number
|
||||||
|
talentId?: string
|
||||||
|
talentName?: string
|
||||||
|
unitName?: string
|
||||||
|
startTime?: string
|
||||||
|
endTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增/更新参数
|
||||||
|
export interface ResumeFormData {
|
||||||
|
id?: string
|
||||||
|
talentId: string
|
||||||
|
startTime: string
|
||||||
|
endTime?: string
|
||||||
|
unitId?: string
|
||||||
|
unitName: string
|
||||||
|
unitAddress?: string
|
||||||
|
workContent: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// API响应接口
|
||||||
|
export interface ApiResponse<T = unknown> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageResponse<T> {
|
||||||
|
records: T[]
|
||||||
|
total: number
|
||||||
|
size: number
|
||||||
|
current: number
|
||||||
|
pages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Mock 数据 ====================
|
||||||
|
const mockResumes: Resume[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
talentId: '1',
|
||||||
|
talentName: '张伟',
|
||||||
|
startTime: '2015-07-01 00:00:00',
|
||||||
|
endTime: '2018-06-30 00:00:00',
|
||||||
|
unitId: '1',
|
||||||
|
unitName: '清华大学',
|
||||||
|
unitAddress: '北京市海淀区清华园1号',
|
||||||
|
workContent: '担任计算机系助理教授,主要从事人工智能和机器学习方向的教学与科研工作。指导研究生5名,发表SCI论文8篇。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-01 10:00:00',
|
||||||
|
updateTime: '2024-01-15 14:30:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
talentId: '1',
|
||||||
|
talentName: '张伟',
|
||||||
|
startTime: '2018-07-01 00:00:00',
|
||||||
|
endTime: '2021-12-31 00:00:00',
|
||||||
|
unitId: '2',
|
||||||
|
unitName: '华为技术有限公司',
|
||||||
|
unitAddress: '广东省深圳市龙岗区坂田华为基地',
|
||||||
|
workContent: '担任AI研究院高级研究员,负责计算机视觉算法研发。主导多个重点项目,申请发明专利12项,其中已授权6项。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-02 09:00:00',
|
||||||
|
updateTime: '2024-01-16 11:20:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
talentId: '1',
|
||||||
|
talentName: '张伟',
|
||||||
|
startTime: '2022-01-01 00:00:00',
|
||||||
|
endTime: null,
|
||||||
|
unitId: '5',
|
||||||
|
unitName: '北京大学',
|
||||||
|
unitAddress: '北京市海淀区颐和园路5号',
|
||||||
|
workContent: '担任信息科学技术学院教授、博士生导师。主持国家自然科学基金重点项目1项,参与国家重点研发计划项目2项。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-03 08:30:00',
|
||||||
|
updateTime: '2024-01-17 16:45:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
talentId: '2',
|
||||||
|
talentName: '李娜',
|
||||||
|
startTime: '2012-09-01 00:00:00',
|
||||||
|
endTime: '2016-06-30 00:00:00',
|
||||||
|
unitId: '3',
|
||||||
|
unitName: '中国科学院计算技术研究所',
|
||||||
|
unitAddress: '北京市海淀区中关村科学院南路6号',
|
||||||
|
workContent: '担任助理研究员,从事高性能计算和并行算法研究。参与国家863计划项目,发表论文5篇。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-04 10:15:00',
|
||||||
|
updateTime: '2024-01-18 09:30:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
talentId: '2',
|
||||||
|
talentName: '李娜',
|
||||||
|
startTime: '2016-07-01 00:00:00',
|
||||||
|
endTime: null,
|
||||||
|
unitId: '6',
|
||||||
|
unitName: '腾讯科技(深圳)有限公司',
|
||||||
|
unitAddress: '广东省深圳市南山区科技园中区一路腾讯大厦',
|
||||||
|
workContent: '担任腾讯云计算部门技术总监,负责云计算平台架构设计与优化。带领团队完成多个大型云服务项目,服务客户超过1000家。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-05 11:00:00',
|
||||||
|
updateTime: '2024-01-19 10:15:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
talentId: '3',
|
||||||
|
talentName: '王强',
|
||||||
|
startTime: '2010-07-01 00:00:00',
|
||||||
|
endTime: '2015-08-31 00:00:00',
|
||||||
|
unitName: '上海交通大学',
|
||||||
|
unitAddress: '上海市闵行区东川路800号',
|
||||||
|
workContent: '担任电子信息与电气工程学院讲师,主要从事通信工程和信号处理方向的教学科研工作。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-06 14:20:00',
|
||||||
|
updateTime: '2024-01-20 15:40:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
talentId: '3',
|
||||||
|
talentName: '王强',
|
||||||
|
startTime: '2015-09-01 00:00:00',
|
||||||
|
endTime: null,
|
||||||
|
unitId: '1',
|
||||||
|
unitName: '清华大学',
|
||||||
|
unitAddress: '北京市海淀区清华园1号',
|
||||||
|
workContent: '担任电子工程系副教授,主要研究5G/6G通信技术。主持国家重点研发计划课题1项,发表高水平论文20余篇。',
|
||||||
|
flag: 0,
|
||||||
|
createTime: '2024-01-07 09:45:00',
|
||||||
|
updateTime: '2024-01-21 13:25:00',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let mockIdCounter = 8
|
||||||
|
|
||||||
|
// 模拟延迟
|
||||||
|
const mockDelay = () => new Promise(resolve => setTimeout(resolve, 300))
|
||||||
|
|
||||||
|
// 分页查询履历列表
|
||||||
|
export async function getResumePage(params: ResumePageParams) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
let filteredData = [...mockResumes]
|
||||||
|
|
||||||
|
// 过滤条件
|
||||||
|
if (params.talentId) {
|
||||||
|
filteredData = filteredData.filter(item => item.talentId === params.talentId)
|
||||||
|
}
|
||||||
|
if (params.talentName) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.talentName?.includes(params.talentName!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (params.unitName) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.unitName.includes(params.unitName!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (params.startTime) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.startTime >= params.startTime!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (params.endTime) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.endTime && item.endTime <= params.endTime!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按开始时间倒序排序
|
||||||
|
filteredData.sort((a, b) => b.startTime.localeCompare(a.startTime))
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const start = (params.pageNum - 1) * params.pageSize
|
||||||
|
const end = start + params.pageSize
|
||||||
|
const records = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: {
|
||||||
|
records,
|
||||||
|
total: filteredData.length,
|
||||||
|
size: params.pageSize,
|
||||||
|
current: params.pageNum,
|
||||||
|
pages: Math.ceil(filteredData.length / params.pageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取履历详情
|
||||||
|
export async function getResumeDetail(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const resume = mockResumes.find(item => item.id === id)
|
||||||
|
|
||||||
|
if (resume) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: resume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该履历',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增履历
|
||||||
|
export async function addResume(data: ResumeFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const newResume: Resume = {
|
||||||
|
...data,
|
||||||
|
id: String(mockIdCounter++),
|
||||||
|
flag: 0,
|
||||||
|
createTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin'
|
||||||
|
} as Resume
|
||||||
|
|
||||||
|
mockResumes.unshift(newResume)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '新增成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改履历
|
||||||
|
export async function updateResume(data: ResumeFormData) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const index = mockResumes.findIndex(item => item.id === data.id)
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
mockResumes[index] = {
|
||||||
|
...mockResumes[index],
|
||||||
|
...data,
|
||||||
|
updateTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
||||||
|
updateBy: 'admin'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '修改成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该履历',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除履历
|
||||||
|
export async function deleteResume(id: string) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const index = mockResumes.findIndex(item => item.id === id)
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
mockResumes.splice(index, 1)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '删除成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 404,
|
||||||
|
message: '未找到该履历',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除履历
|
||||||
|
export async function batchDeleteResume(ids: string[]) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
ids.forEach(id => {
|
||||||
|
const index = mockResumes.findIndex(item => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
mockResumes.splice(index, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '批量删除成功',
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据人才ID获取履历列表
|
||||||
|
export async function getResumesByTalentId(talentId: string) {
|
||||||
|
await mockDelay()
|
||||||
|
|
||||||
|
const resumes = mockResumes
|
||||||
|
.filter(item => item.talentId === talentId)
|
||||||
|
.sort((a, b) => b.startTime.localeCompare(a.startTime))
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
message: '查询成功',
|
||||||
|
data: resumes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -178,6 +178,66 @@ const routes: Array<RouteRecordRaw> = [
|
||||||
path: 'dict',
|
path: 'dict',
|
||||||
name: 'admin-dict',
|
name: 'admin-dict',
|
||||||
component: () => import('@/views/admin/system/dict.vue')
|
component: () => import('@/views/admin/system/dict.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-unit',
|
||||||
|
name: 'admin-talent-unit',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-unit.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-unit/create',
|
||||||
|
name: 'admin-talent-unit-create',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-unit-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-unit/edit/:id',
|
||||||
|
name: 'admin-talent-unit-edit',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-unit-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-unit/view/:id',
|
||||||
|
name: 'admin-talent-unit-view',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-unit-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-resume',
|
||||||
|
name: 'admin-talent-resume',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-resume.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-resume/create',
|
||||||
|
name: 'admin-talent-resume-create',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-resume-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-resume/edit/:id',
|
||||||
|
name: 'admin-talent-resume-edit',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-resume-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'talent-resume/view/:id',
|
||||||
|
name: 'admin-talent-resume-view',
|
||||||
|
component: () => import('@/views/admin/tech-resources/talent-resume-form.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tech-patents',
|
||||||
|
name: 'TechPatents',
|
||||||
|
component: () => import('@/views/admin/tech-resources/tech-patent.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tech-patents/create',
|
||||||
|
name: 'TechPatentsCreate',
|
||||||
|
component: () => import('@/views/admin/tech-resources/tech-patent-form.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tech-patents/edit/:id',
|
||||||
|
name: 'TechPatentsEdit',
|
||||||
|
component: () => import('@/views/admin/tech-resources/tech-patent-form.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tech-patents/view/:id',
|
||||||
|
name: 'TechPatentsView',
|
||||||
|
component: () => import('@/views/admin/tech-resources/tech-patent-form.vue'),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
<template>
|
||||||
|
<div class="talent-resume-form-container">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ isView ? '查看' : (formData.id ? '编辑' : '新增') }}履历信息</span>
|
||||||
|
<el-button @click="handleBack">返回</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="140px"
|
||||||
|
:disabled="isView"
|
||||||
|
>
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<el-divider content-position="left">基本信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="人才" prop="talentId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.talentId"
|
||||||
|
placeholder="请选择人才"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="talent in talentList"
|
||||||
|
:key="talent.id"
|
||||||
|
:label="talent.name"
|
||||||
|
:value="talent.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 时间信息 -->
|
||||||
|
<el-divider content-position="left">时间信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="开始时间" prop="startTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.startTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="请选择开始时间"
|
||||||
|
style="width: 100%"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="结束时间" prop="endTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.endTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="请选择结束时间(不填表示至今)"
|
||||||
|
style="width: 100%"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 单位信息 -->
|
||||||
|
<el-divider content-position="left">单位信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="关联单位" prop="unitId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.unitId"
|
||||||
|
placeholder="请选择关联单位(可选)"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
@change="handleUnitChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="unit in unitList"
|
||||||
|
:key="unit.id"
|
||||||
|
:label="unit.orgName"
|
||||||
|
:value="unit.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="单位名称" prop="unitName">
|
||||||
|
<el-input v-model="formData.unitName" placeholder="请输入单位名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="单位地址" prop="unitAddress">
|
||||||
|
<el-input v-model="formData.unitAddress" placeholder="请输入单位地址(选填)" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 工作内容 -->
|
||||||
|
<el-divider content-position="left">工作内容</el-divider>
|
||||||
|
|
||||||
|
<el-form-item label="工作内容" prop="workContent">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.workContent"
|
||||||
|
type="textarea"
|
||||||
|
:rows="6"
|
||||||
|
placeholder="请输入工作内容描述"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<el-form-item v-if="!isView">
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button>
|
||||||
|
<el-button @click="handleBack">取消</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TalentResumeForm'
|
||||||
|
})
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getResumeDetail,
|
||||||
|
addResume,
|
||||||
|
updateResume,
|
||||||
|
type ResumeFormData
|
||||||
|
} from '@/api/resume'
|
||||||
|
import { getOrganizationPage, type Organization } from '@/api/organization'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const submitting = ref(false)
|
||||||
|
const isView = ref(false)
|
||||||
|
|
||||||
|
// 人才列表(mock数据)
|
||||||
|
const talentList = ref([
|
||||||
|
{ id: '1', name: '张伟' },
|
||||||
|
{ id: '2', name: '李娜' },
|
||||||
|
{ id: '3', name: '王强' },
|
||||||
|
{ id: '4', name: '赵敏' },
|
||||||
|
{ id: '5', name: '刘洋' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 单位列表
|
||||||
|
const unitList = ref<Organization[]>([])
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = reactive<ResumeFormData>({
|
||||||
|
talentId: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
unitId: '',
|
||||||
|
unitName: '',
|
||||||
|
unitAddress: '',
|
||||||
|
workContent: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules: FormRules = {
|
||||||
|
talentId: [
|
||||||
|
{ required: true, message: '请选择人才', trigger: 'change' }
|
||||||
|
],
|
||||||
|
startTime: [
|
||||||
|
{ required: true, message: '请选择开始时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
unitName: [
|
||||||
|
{ required: true, message: '请输入单位名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
workContent: [
|
||||||
|
{ required: true, message: '请输入工作内容', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载单位列表
|
||||||
|
const loadUnitList = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await getOrganizationPage({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 1000
|
||||||
|
})
|
||||||
|
if (data.code === 200) {
|
||||||
|
unitList.value = data.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载单位列表失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单位选择变化
|
||||||
|
const handleUnitChange = (unitId: string) => {
|
||||||
|
if (unitId) {
|
||||||
|
const unit = unitList.value.find(u => u.id === unitId)
|
||||||
|
if (unit) {
|
||||||
|
formData.unitName = unit.orgName
|
||||||
|
formData.unitAddress = unit.address || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载详情
|
||||||
|
const loadDetail = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const { data } = await getResumeDetail(id)
|
||||||
|
if (data.code === 200) {
|
||||||
|
Object.assign(formData, data.data)
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '加载数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('加载数据失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
await formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
const apiFunc = formData.id ? updateResume : addResume
|
||||||
|
const { data } = await apiFunc(formData)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success(formData.id ? '修改成功' : '新增成功')
|
||||||
|
handleBack()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '操作失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('操作失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const handleBack = () => {
|
||||||
|
router.push('/admin/talent-resume')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadUnitList()
|
||||||
|
|
||||||
|
const id = route.params.id as string
|
||||||
|
isView.value = route.path.includes('/view/')
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
loadDetail(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.talent-resume-form-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-divider__text) {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
<template>
|
||||||
|
<div class="talent-resume-container">
|
||||||
|
<el-card>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item label="人才姓名">
|
||||||
|
<el-input v-model="searchForm.talentName" placeholder="请输入人才姓名" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位名称">
|
||||||
|
<el-input v-model="searchForm.unitName" placeholder="请输入单位名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开始时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchForm.startTime"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择开始时间"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||||
|
<el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">批量删除</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
style="width: 100%"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
v-loading="loading"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="talentName" label="人才姓名" width="120" />
|
||||||
|
<el-table-column prop="unitName" label="工作单位" min-width="180" />
|
||||||
|
<el-table-column label="工作时间" width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.startTime) }} ~ {{ row.endTime ? formatDate(row.endTime) : '至今' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="workContent" label="工作内容" min-width="250" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="unitAddress" label="单位地址" width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
||||||
|
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||||
|
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="pagination.pageNum"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="pagination.total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
class="pagination"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TalentResume'
|
||||||
|
})
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getResumePage,
|
||||||
|
deleteResume,
|
||||||
|
batchDeleteResume,
|
||||||
|
type Resume,
|
||||||
|
type ResumePageParams
|
||||||
|
} from '@/api/resume'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive<Partial<ResumePageParams>>({
|
||||||
|
talentName: '',
|
||||||
|
unitName: '',
|
||||||
|
startTime: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分页信息
|
||||||
|
const pagination = reactive({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref<Resume[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const selectedIds = ref<string[]>([])
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (dateStr: string) => {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
return dateStr.substring(0, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params: ResumePageParams = {
|
||||||
|
pageNum: pagination.pageNum,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
...searchForm
|
||||||
|
}
|
||||||
|
const { data } = await getResumePage(params)
|
||||||
|
if (data.code === 200) {
|
||||||
|
tableData.value = data.data.records
|
||||||
|
pagination.total = data.data.total
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '加载数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('加载数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
pagination.pageNum = 1
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(searchForm, {
|
||||||
|
talentName: '',
|
||||||
|
unitName: '',
|
||||||
|
startTime: ''
|
||||||
|
})
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
router.push('/admin/talent-resume/create')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看
|
||||||
|
const handleView = (row: Resume) => {
|
||||||
|
router.push(`/admin/talent-resume/view/${row.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: Resume) => {
|
||||||
|
router.push(`/admin/talent-resume/edit/${row.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row: Resume) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要删除该履历记录吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
const { data } = await deleteResume(row.id)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
loadData()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const handleBatchDelete = async () => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要删除选中的 ${selectedIds.value.length} 条记录吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
const { data } = await batchDeleteResume(selectedIds.value)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
selectedIds.value = []
|
||||||
|
loadData()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (selection: Resume[]) => {
|
||||||
|
selectedIds.value = selection.map(item => item.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页大小变化
|
||||||
|
const handleSizeChange = () => {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前页变化
|
||||||
|
const handleCurrentChange = () => {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.talent-resume-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,336 @@
|
||||||
|
<template>
|
||||||
|
<div class="talent-unit-form-container">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ isView ? '查看' : (formData.id ? '编辑' : '新增') }}工作单位</span>
|
||||||
|
<el-button @click="handleBack">返回</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="140px"
|
||||||
|
:disabled="isView"
|
||||||
|
>
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<el-divider content-position="left">基本信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="机构名称" prop="orgName">
|
||||||
|
<el-input v-model="formData.orgName" placeholder="请输入机构名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="组织类型" prop="orgType">
|
||||||
|
<el-select v-model="formData.orgType" placeholder="请选择组织类型" style="width: 100%">
|
||||||
|
<el-option label="高校" value="SCHOOL" />
|
||||||
|
<el-option label="企业" value="ENTERPRISE" />
|
||||||
|
<el-option label="科研院所" value="RESEARCH_INSTITUTE" />
|
||||||
|
<el-option label="政府机关" value="GOVERNMENT" />
|
||||||
|
<el-option label="其他" value="OTHER" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="机构代码" prop="orgCode">
|
||||||
|
<el-input v-model="formData.orgCode" placeholder="请输入统一社会信用代码" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="法定代表人" prop="legalRepresentative">
|
||||||
|
<el-input v-model="formData.legalRepresentative" placeholder="请输入法定代表人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="成立日期" prop="establishmentDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.establishmentDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择成立日期"
|
||||||
|
style="width: 100%"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="员工人数" prop="employeeCount">
|
||||||
|
<el-input-number v-model="formData.employeeCount" :min="0" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 联系信息 -->
|
||||||
|
<el-divider content-position="left">联系信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
|
<el-input v-model="formData.contactPhone" placeholder="请输入联系电话" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="联系邮箱" prop="contactEmail">
|
||||||
|
<el-input v-model="formData.contactEmail" placeholder="请输入联系邮箱" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="官方网址" prop="website">
|
||||||
|
<el-input v-model="formData.website" placeholder="请输入官方网址" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="所属省份" prop="province">
|
||||||
|
<el-input v-model="formData.province" placeholder="请输入省份" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="所属城市" prop="city">
|
||||||
|
<el-input v-model="formData.city" placeholder="请输入城市" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="所属区域" prop="district">
|
||||||
|
<el-input v-model="formData.district" placeholder="请输入区域" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="详细地址" prop="address">
|
||||||
|
<el-input v-model="formData.address" placeholder="请输入详细地址" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 企业信息 -->
|
||||||
|
<template v-if="formData.orgType === 'ENTERPRISE'">
|
||||||
|
<el-divider content-position="left">企业信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="注册资本" prop="registeredCapital">
|
||||||
|
<el-input v-model="formData.registeredCapital" placeholder="请输入注册资本" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所属行业" prop="industry">
|
||||||
|
<el-input v-model="formData.industry" placeholder="请输入所属行业" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="是否高新技术企业" prop="isHighTech">
|
||||||
|
<el-radio-group v-model="formData.isHighTech">
|
||||||
|
<el-radio :label="0">否</el-radio>
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="经营范围" prop="businessScope">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.businessScope"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入经营范围"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 高校信息 -->
|
||||||
|
<template v-if="formData.orgType === 'SCHOOL'">
|
||||||
|
<el-divider content-position="left">高校信息</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="高校类型" prop="schoolType">
|
||||||
|
<el-select v-model="formData.schoolType" placeholder="请选择高校类型" style="width: 100%">
|
||||||
|
<el-option label="985" value="985" />
|
||||||
|
<el-option label="211" value="211" />
|
||||||
|
<el-option label="双一流" value="双一流" />
|
||||||
|
<el-option label="普通本科" value="普通本科" />
|
||||||
|
<el-option label="其他" value="其他" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="办学层次" prop="schoolLevel">
|
||||||
|
<el-select v-model="formData.schoolLevel" placeholder="请选择办学层次" style="width: 100%">
|
||||||
|
<el-option label="本科" value="本科" />
|
||||||
|
<el-option label="专科" value="专科" />
|
||||||
|
<el-option label="本专科" value="本专科" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 其他信息 -->
|
||||||
|
<el-divider content-position="left">其他信息</el-divider>
|
||||||
|
|
||||||
|
<el-form-item label="机构简介" prop="introduction">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入机构简介"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<el-form-item v-if="!isView">
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button>
|
||||||
|
<el-button @click="handleBack">取消</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TalentUnitForm'
|
||||||
|
})
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getOrganizationDetail,
|
||||||
|
addOrganization,
|
||||||
|
updateOrganization,
|
||||||
|
type OrganizationFormData
|
||||||
|
} from '@/api/organization'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const submitting = ref(false)
|
||||||
|
const isView = ref(false)
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = reactive<OrganizationFormData>({
|
||||||
|
orgName: '',
|
||||||
|
orgType: 'ENTERPRISE',
|
||||||
|
orgCode: '',
|
||||||
|
legalRepresentative: '',
|
||||||
|
establishmentDate: '',
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
district: '',
|
||||||
|
address: '',
|
||||||
|
contactPhone: '',
|
||||||
|
contactEmail: '',
|
||||||
|
website: '',
|
||||||
|
employeeCount: undefined,
|
||||||
|
introduction: '',
|
||||||
|
registeredCapital: '',
|
||||||
|
businessScope: '',
|
||||||
|
industry: '',
|
||||||
|
isHighTech: 0,
|
||||||
|
schoolType: '',
|
||||||
|
schoolLevel: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules: FormRules = {
|
||||||
|
orgName: [
|
||||||
|
{ required: true, message: '请输入机构名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
orgType: [
|
||||||
|
{ required: true, message: '请选择组织类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
contactEmail: [
|
||||||
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
website: [
|
||||||
|
{ type: 'url', message: '请输入正确的网址', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载详情
|
||||||
|
const loadDetail = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const { data } = await getOrganizationDetail(id)
|
||||||
|
if (data.code === 200) {
|
||||||
|
Object.assign(formData, data.data)
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '加载数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('加载数据失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
await formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
const apiFunc = formData.id ? updateOrganization : addOrganization
|
||||||
|
const { data } = await apiFunc(formData)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success(formData.id ? '修改成功' : '新增成功')
|
||||||
|
handleBack()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '操作失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('操作失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const handleBack = () => {
|
||||||
|
router.push('/admin/talent-unit')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const id = route.params.id as string
|
||||||
|
isView.value = route.path.includes('/view/')
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
loadDetail(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.talent-unit-form-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-divider__text) {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,282 @@
|
||||||
|
<template>
|
||||||
|
<div class="talent-unit-container">
|
||||||
|
<el-card>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item label="机构名称">
|
||||||
|
<el-input v-model="searchForm.orgName" placeholder="请输入机构名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="组织类型">
|
||||||
|
<el-select v-model="searchForm.orgType" placeholder="请选择组织类型" clearable>
|
||||||
|
<el-option label="高校" value="SCHOOL" />
|
||||||
|
<el-option label="企业" value="ENTERPRISE" />
|
||||||
|
<el-option label="科研院所" value="RESEARCH_INSTITUTE" />
|
||||||
|
<el-option label="政府机关" value="GOVERNMENT" />
|
||||||
|
<el-option label="其他" value="OTHER" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属省份">
|
||||||
|
<el-input v-model="searchForm.province" placeholder="请输入省份" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属城市">
|
||||||
|
<el-input v-model="searchForm.city" placeholder="请输入城市" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||||
|
<el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">批量删除</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
style="width: 100%"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
v-loading="loading"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="orgName" label="机构名称" min-width="150" />
|
||||||
|
<el-table-column prop="orgType" label="组织类型" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getOrgTypeTag(row.orgType)">{{ getOrgTypeLabel(row.orgType) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="orgCode" label="机构代码" width="150" />
|
||||||
|
<el-table-column prop="legalRepresentative" label="负责人" width="100" />
|
||||||
|
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
||||||
|
<el-table-column prop="province" label="省份" width="100" />
|
||||||
|
<el-table-column prop="city" label="城市" width="100" />
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
||||||
|
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||||
|
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="pagination.pageNum"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="pagination.total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
class="pagination"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TalentUnit'
|
||||||
|
})
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getOrganizationPage,
|
||||||
|
deleteOrganization,
|
||||||
|
batchDeleteOrganization,
|
||||||
|
type Organization,
|
||||||
|
type OrganizationPageParams
|
||||||
|
} from '@/api/organization'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive<Partial<OrganizationPageParams>>({
|
||||||
|
orgName: '',
|
||||||
|
orgType: '',
|
||||||
|
province: '',
|
||||||
|
city: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分页信息
|
||||||
|
const pagination = reactive({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref<Organization[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const selectedIds = ref<string[]>([])
|
||||||
|
|
||||||
|
// 获取组织类型标签
|
||||||
|
const getOrgTypeTag = (type: string) => {
|
||||||
|
const tagMap: Record<string, string> = {
|
||||||
|
SCHOOL: 'success',
|
||||||
|
ENTERPRISE: 'primary',
|
||||||
|
RESEARCH_INSTITUTE: 'warning',
|
||||||
|
GOVERNMENT: 'danger',
|
||||||
|
OTHER: 'info'
|
||||||
|
}
|
||||||
|
return tagMap[type] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取组织类型文本
|
||||||
|
const getOrgTypeLabel = (type: string) => {
|
||||||
|
const labelMap: Record<string, string> = {
|
||||||
|
SCHOOL: '高校',
|
||||||
|
ENTERPRISE: '企业',
|
||||||
|
RESEARCH_INSTITUTE: '科研院所',
|
||||||
|
GOVERNMENT: '政府机关',
|
||||||
|
OTHER: '其他'
|
||||||
|
}
|
||||||
|
return labelMap[type] || type
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params: OrganizationPageParams = {
|
||||||
|
pageNum: pagination.pageNum,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
...searchForm
|
||||||
|
}
|
||||||
|
const { data } = await getOrganizationPage(params)
|
||||||
|
if (data.code === 200) {
|
||||||
|
tableData.value = data.data.records
|
||||||
|
pagination.total = data.data.total
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '加载数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('加载数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
pagination.pageNum = 1
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(searchForm, {
|
||||||
|
orgName: '',
|
||||||
|
orgType: '',
|
||||||
|
province: '',
|
||||||
|
city: ''
|
||||||
|
})
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
router.push('/admin/talent-unit/create')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看
|
||||||
|
const handleView = (row: Organization) => {
|
||||||
|
router.push(`/admin/talent-unit/view/${row.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: Organization) => {
|
||||||
|
router.push(`/admin/talent-unit/edit/${row.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row: Organization) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要删除机构"${row.orgName}"吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
const { data } = await deleteOrganization(row.id)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
loadData()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const handleBatchDelete = async () => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要删除选中的 ${selectedIds.value.length} 条记录吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
const { data } = await batchDeleteOrganization(selectedIds.value)
|
||||||
|
if (data.code === 200) {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
selectedIds.value = []
|
||||||
|
loadData()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(data.message || '删除失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (selection: Organization[]) => {
|
||||||
|
selectedIds.value = selection.map(item => item.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页大小变化
|
||||||
|
const handleSizeChange = () => {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前页变化
|
||||||
|
const handleCurrentChange = () => {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.talent-unit-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
<template>
|
||||||
|
<div class="tech-patents-form-container">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ isView ? '查看' : (formData.id ? '编辑' : '新增') }}专利</span>
|
||||||
|
<el-button @click="handleBack">返回</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isView">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="关联人才" prop="talentId">
|
||||||
|
<el-select v-model="formData.talentId" placeholder="请选择发明人" style="width: 100%" filterable>
|
||||||
|
<el-option v-for="t in talentList" :key="t.id" :label="t.name" :value="t.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="专利名称" prop="patentName">
|
||||||
|
<el-input v-model="formData.patentName" placeholder="请输入专利名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="专利号" prop="patentNumber">
|
||||||
|
<el-input v-model="formData.patentNumber" placeholder="请输入专利号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="专利类型" prop="patentType">
|
||||||
|
<el-select v-model="formData.patentType" placeholder="请选择类型" style="width: 100%">
|
||||||
|
<el-option label="发明专利" value="INVENTION" />
|
||||||
|
<el-option label="实用新型" value="UTILITY_MODEL" />
|
||||||
|
<el-option label="外观设计" value="DESIGN" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="申请时间" prop="applicationTime">
|
||||||
|
<el-date-picker v-model="formData.applicationTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="授权时间" prop="authorizationTime">
|
||||||
|
<el-date-picker v-model="formData.authorizationTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="专利状态" prop="patentStatus">
|
||||||
|
<el-select v-model="formData.patentStatus" style="width: 300px">
|
||||||
|
<el-option label="申请中" value="APPLYING" />
|
||||||
|
<el-option label="已授权" value="AUTHORIZED" />
|
||||||
|
<el-option label="已失效" value="INVALID" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="!isView">
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button>
|
||||||
|
<el-button @click="handleBack">取消</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { getPatentDetail, addPatent, updatePatent, type PatentFormData } from '@/api/patent'
|
||||||
|
|
||||||
|
defineOptions({ name: 'TechPatentsForm' })
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const submitting = ref(false)
|
||||||
|
const isView = ref(false)
|
||||||
|
|
||||||
|
const talentList = ref([
|
||||||
|
{ id: '1', name: '张伟' }, { id: '2', name: '李娜' }, { id: '3', name: '王强' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const formData = reactive<PatentFormData>({
|
||||||
|
talentId: '', patentName: '', patentNumber: '', patentType: 'INVENTION',
|
||||||
|
patentStatus: 'APPLYING', applicationTime: '', authorizationTime: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
talentId: [{ required: true, message: '请选择关联人才', trigger: 'change' }],
|
||||||
|
patentName: [{ required: true, message: '请输入专利名称', trigger: 'blur' }],
|
||||||
|
patentNumber: [{ required: true, message: '请输入专利号', trigger: 'blur' }],
|
||||||
|
applicationTime: [{ required: true, message: '请选择申请时间', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDetail = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const { data } = await getPatentDetail(id)
|
||||||
|
if (data.code === 200) Object.assign(formData, data.data)
|
||||||
|
} catch (error) { ElMessage.error('加载失败') }
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
const apiFunc = formData.id ? updatePatent : addPatent
|
||||||
|
const { data } = await apiFunc(formData)
|
||||||
|
if (data.code === 200) { ElMessage.success('操作成功'); handleBack() }
|
||||||
|
} catch (error) { ElMessage.error('操作失败') }
|
||||||
|
finally { submitting.value = false }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBack = () => router.push('/admin/tech-patents')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const id = route.params.id as string
|
||||||
|
isView.value = route.path.includes('/view/')
|
||||||
|
if (id) loadDetail(id)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tech-patents-form-container { padding: 20px; }
|
||||||
|
.card-header { display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
<template>
|
||||||
|
<div class="tech-patents-container">
|
||||||
|
<el-card>
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item label="专利名称">
|
||||||
|
<el-input v-model="searchForm.patentName" placeholder="请输入专利名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="专利号">
|
||||||
|
<el-input v-model="searchForm.patentNumber" placeholder="请输入专利号" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发明人">
|
||||||
|
<el-input v-model="searchForm.talentName" placeholder="请输入关联人才" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="toolbar">
|
||||||
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||||
|
<el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">批量删除</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange" v-loading="loading">
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="patentName" label="专利名称" min-width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="patentNumber" label="专利号" width="160" />
|
||||||
|
<el-table-column prop="talentName" label="发明人" width="120" />
|
||||||
|
<el-table-column label="申请时间" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.applicationTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="授权时间" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.authorizationTime) || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
||||||
|
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||||
|
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="pagination.pageNum"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="pagination.total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
class="pagination"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getPatentPage, deletePatent, batchDeletePatent, type Patent, type PatentPageParams
|
||||||
|
} from '@/api/patent'
|
||||||
|
|
||||||
|
defineOptions({ name: 'TechPatents' })
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const searchForm = reactive<Partial<PatentPageParams>>({ patentName: '', patentNumber: '', talentName: '' })
|
||||||
|
const pagination = reactive({ pageNum: 1, pageSize: 10, total: 0 })
|
||||||
|
const tableData = ref<Patent[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const selectedIds = ref<string[]>([])
|
||||||
|
|
||||||
|
const formatDate = (dateStr?: string | null) => {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
return dateStr.substring(0, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = { pageNum: pagination.pageNum, pageSize: pagination.pageSize, ...searchForm }
|
||||||
|
const { data } = await getPatentPage(params)
|
||||||
|
if (data.code === 200) {
|
||||||
|
tableData.value = data.data.records
|
||||||
|
pagination.total = data.data.total
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('加载失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => { pagination.pageNum = 1; loadData() }
|
||||||
|
const handleReset = () => { Object.assign(searchForm, { patentName: '', patentNumber: '', talentName: '' }); handleSearch() }
|
||||||
|
|
||||||
|
// 注意这里的跳转路径,全都是 tech-patents 相关的
|
||||||
|
const handleAdd = () => router.push('/admin/tech-patents/create')
|
||||||
|
const handleView = (row: Patent) => router.push(`/admin/tech-patents/view/${row.id}`)
|
||||||
|
const handleEdit = (row: Patent) => router.push(`/admin/tech-patents/edit/${row.id}`)
|
||||||
|
|
||||||
|
const handleDelete = async (row: Patent) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('确定要删除该专利吗?', '提示', { type: 'warning' })
|
||||||
|
const { data } = await deletePatent(row.id)
|
||||||
|
if (data.code === 200) { ElMessage.success('删除成功'); loadData() }
|
||||||
|
} catch (e) { /* catch cancel */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBatchDelete = async () => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('确定要删除选中记录吗?', '提示', { type: 'warning' })
|
||||||
|
const { data } = await batchDeletePatent(selectedIds.value)
|
||||||
|
if (data.code === 200) { ElMessage.success('删除成功'); selectedIds.value = []; loadData() }
|
||||||
|
} catch (e) { /* catch cancel */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelectionChange = (selection: Patent[]) => { selectedIds.value = selection.map(item => item.id) }
|
||||||
|
const handleSizeChange = () => loadData()
|
||||||
|
const handleCurrentChange = () => loadData()
|
||||||
|
|
||||||
|
onMounted(() => loadData())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tech-patents-container { padding: 20px; }
|
||||||
|
.search-form { margin-bottom: 20px; }
|
||||||
|
.toolbar { margin-bottom: 20px; }
|
||||||
|
.pagination { margin-top: 20px; justify-content: flex-end; }
|
||||||
|
</style>
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
const { defineConfig } = require("@vue/cli-service");
|
const { defineConfig } = require("@vue/cli-service");
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
transpileDependencies: true,
|
transpileDependencies: true,
|
||||||
|
lintOnSave: false,
|
||||||
devServer: {
|
devServer: {
|
||||||
|
client: {
|
||||||
|
overlay: false,
|
||||||
|
},
|
||||||
port: 8080,
|
port: 8080,
|
||||||
proxy: {
|
proxy: {
|
||||||
"/brain": {
|
"/brain": {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue