pull/804/head
YunaiV 2025-07-26 19:53:53 +08:00
commit f659899f8e
132 changed files with 20344 additions and 657 deletions

View File

@ -62,16 +62,16 @@
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[html]": { "[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[css]": { "[css]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[less]": { "[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
@ -86,8 +86,9 @@
"source.fixAll.eslint": "explicit", "source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit" "source.fixAll.stylelint": "explicit"
}, },
"editor.formatOnSave": true,
"[vue]": { "[vue]": {
"editor.defaultFormatter": "octref.vetur" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"i18n-ally.localesPaths": ["src/locales"], "i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",

17486
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "yudao-ui-admin-vue3", "name": "yudao-ui-admin-vue3",
"version": "2.4.2-snapshot", "version": "2.6.1-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript", "description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu", "author": "xingyu",
"private": false, "private": false,
@ -36,7 +36,7 @@
"@wangeditor/editor-for-vue": "^5.1.10", "@wangeditor/editor-for-vue": "^5.1.10",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.6.8", "axios": "1.9.0",
"benz-amr-recorder": "^1.1.5", "benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.36.0", "bpmn-js-token-simulation": "^0.36.0",
"camunda-bpmn-moddle": "^7.0.1", "camunda-bpmn-moddle": "^7.0.1",

View File

@ -48,6 +48,7 @@ export type ApprovalNodeInfo = {
status: number status: number
startTime?: Date startTime?: Date
endTime?: Date endTime?: Date
processInstanceId?: string
candidateUsers?: User[] candidateUsers?: User[]
tasks: ApprovalTaskInfo[] tasks: ApprovalTaskInfo[]
} }

View File

@ -105,3 +105,8 @@ export const createCodegenList = (data) => {
export const deleteCodegenTable = (id: number) => { export const deleteCodegenTable = (id: number) => {
return request.delete({ url: '/infra/codegen/delete?tableId=' + id }) return request.delete({ url: '/infra/codegen/delete?tableId=' + id })
} }
// 批量删除代码生成表定义
export const deleteCodegenTableList = (ids: number[]) => {
return request.delete({ url: '/infra/codegen/delete-list', params: { tableIds: ids.join(',') } })
}

View File

@ -42,7 +42,12 @@ export const deleteConfig = (id: number) => {
return request.delete({ url: '/infra/config/delete?id=' + id }) return request.delete({ url: '/infra/config/delete?id=' + id })
} }
// 批量删除参数
export const deleteConfigList = (ids: number[]) => {
return request.delete({ url: '/infra/config/delete-list', params: { ids: ids.join(',') } })
}
// 导出参数 // 导出参数
export const exportConfig = (params) => { export const exportConfig = (params) => {
return request.download({ url: '/infra/config/export', params }) return request.download({ url: '/infra/config/export-excel', params })
} }

View File

@ -24,6 +24,11 @@ export const deleteDataSourceConfig = (id: number) => {
return request.delete({ url: '/infra/data-source-config/delete?id=' + id }) return request.delete({ url: '/infra/data-source-config/delete?id=' + id })
} }
// 批量删除数据源配置
export const deleteDataSourceConfigList = (ids: number[]) => {
return request.delete({ url: '/infra/data-source-config/delete-list', params: { ids: ids.join(',') } })
}
// 查询数据源配置详情 // 查询数据源配置详情
export const getDataSourceConfig = (id: number) => { export const getDataSourceConfig = (id: number) => {
return request.get({ url: '/infra/data-source-config/get?id=' + id }) return request.get({ url: '/infra/data-source-config/get?id=' + id })

View File

@ -1,40 +1,50 @@
import request from '@/config/axios' import request from '@/config/axios'
import type { Dayjs } from 'dayjs'
export interface Demo01ContactVO { /** 示例联系人信息 */
id: number export interface Demo01Contact {
name: string id: number // 编号
sex: number name?: string // 名字
birthday: Date sex?: number // 性别
description: string birthday?: string | Dayjs // 出生年
avatar: string description?: string // 简介
avatar: string // 头像
} }
// 查询示例联系人分页 // 示例联系人 API
export const getDemo01ContactPage = async (params) => { export const Demo01ContactApi = {
// 查询示例联系人分页
getDemo01ContactPage: async (params: any) => {
return await request.get({ url: `/infra/demo01-contact/page`, params }) return await request.get({ url: `/infra/demo01-contact/page`, params })
} },
// 查询示例联系人详情 // 查询示例联系人详情
export const getDemo01Contact = async (id: number) => { getDemo01Contact: async (id: number) => {
return await request.get({ url: `/infra/demo01-contact/get?id=` + id }) return await request.get({ url: `/infra/demo01-contact/get?id=` + id })
} },
// 新增示例联系人 // 新增示例联系人
export const createDemo01Contact = async (data: Demo01ContactVO) => { createDemo01Contact: async (data: Demo01Contact) => {
return await request.post({ url: `/infra/demo01-contact/create`, data }) return await request.post({ url: `/infra/demo01-contact/create`, data })
} },
// 修改示例联系人 // 修改示例联系人
export const updateDemo01Contact = async (data: Demo01ContactVO) => { updateDemo01Contact: async (data: Demo01Contact) => {
return await request.put({ url: `/infra/demo01-contact/update`, data }) return await request.put({ url: `/infra/demo01-contact/update`, data })
} },
// 删除示例联系人 // 删除示例联系人
export const deleteDemo01Contact = async (id: number) => { deleteDemo01Contact: async (id: number) => {
return await request.delete({ url: `/infra/demo01-contact/delete?id=` + id }) return await request.delete({ url: `/infra/demo01-contact/delete?id=` + id })
} },
// 导出示例联系人 Excel /** 批量删除示例联系人 */
export const exportDemo01Contact = async (params) => { deleteDemo01ContactList: async (ids: number[]) => {
return await request.delete({ url: `/infra/demo01-contact/delete-list?ids=${ids.join(',')}` })
},
// 导出示例联系人 Excel
exportDemo01Contact: async (params) => {
return await request.download({ url: `/infra/demo01-contact/export-excel`, params }) return await request.download({ url: `/infra/demo01-contact/export-excel`, params })
}
} }

View File

@ -1,91 +1,127 @@
import request from '@/config/axios' import request from '@/config/axios'
import type { Dayjs } from 'dayjs';
export interface Demo03StudentVO { /** 学生课程信息 */
id: number export interface Demo03Course {
name: string id: number; // 编号
sex: number studentId?: number; // 学生编号
birthday: Date name?: string; // 名字
description: string score?: number; // 分数
} }
// 查询学生分页 /** 学生班级信息 */
export const getDemo03StudentPage = async (params) => { export interface Demo03Grade {
return await request.get({ url: `/infra/demo03-student/page`, params }) id: number; // 编号
studentId?: number; // 学生编号
name?: string; // 名字
teacher?: string; // 班主任
} }
// 查询学生详情 /** 学生信息 */
export const getDemo03Student = async (id: number) => { export interface Demo03Student {
return await request.get({ url: `/infra/demo03-student/get?id=` + id }) id: number; // 编号
name?: string; // 名字
sex?: number; // 性别
birthday?: string | Dayjs; // 出生日期
description?: string; // 简介
} }
// 新增学生 // 学生 API
export const createDemo03Student = async (data: Demo03StudentVO) => { export const Demo03StudentApi = {
return await request.post({ url: `/infra/demo03-student/create`, data }) // 查询学生分页
} getDemo03StudentPage: async (params: any) => {
return await request.get({ url: `/infra/demo03-student-erp/page`, params })
},
// 修改学生 // 查询学生详情
export const updateDemo03Student = async (data: Demo03StudentVO) => { getDemo03Student: async (id: number) => {
return await request.put({ url: `/infra/demo03-student/update`, data }) return await request.get({ url: `/infra/demo03-student-erp/get?id=` + id })
} },
// 删除学生 // 新增学生
export const deleteDemo03Student = async (id: number) => { createDemo03Student: async (data: Demo03Student) => {
return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) return await request.post({ url: `/infra/demo03-student-erp/create`, data })
} },
// 导出学生 Excel // 修改学生
export const exportDemo03Student = async (params) => { updateDemo03Student: async (data: Demo03Student) => {
return await request.download({ url: `/infra/demo03-student/export-excel`, params }) return await request.put({ url: `/infra/demo03-student-erp/update`, data })
} },
// 删除学生
deleteDemo03Student: async (id: number) => {
return await request.delete({ url: `/infra/demo03-student-erp/delete?id=` + id })
},
/** 批量删除学生 */
deleteDemo03StudentList: async (ids: number[]) => {
return await request.delete({ url: `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}` })
},
// 导出学生 Excel
exportDemo03Student: async (params) => {
return await request.download({ url: `/infra/demo03-student-erp/export-excel`, params })
},
// ==================== 子表(学生课程) ==================== // ==================== 子表(学生课程) ====================
// 获得学生课程分页 // 获得学生课程分页
export const getDemo03CoursePage = async (params) => { getDemo03CoursePage: async (params) => {
return await request.get({ url: `/infra/demo03-student/demo03-course/page`, params }) return await request.get({ url: `/infra/demo03-student-erp/demo03-course/page`, params })
} },
// 新增学生课程 // 新增学生课程
export const createDemo03Course = async (data) => { createDemo03Course: async (data: Demo03Course) => {
return await request.post({ url: `/infra/demo03-student/demo03-course/create`, data }) return await request.post({ url: `/infra/demo03-student-erp/demo03-course/create`, data })
} },
// 修改学生课程 // 修改学生课程
export const updateDemo03Course = async (data) => { updateDemo03Course: async (data: Demo03Course) => {
return await request.put({ url: `/infra/demo03-student/demo03-course/update`, data }) return await request.put({ url: `/infra/demo03-student-erp/demo03-course/update`, data })
} },
// 删除学生课程 // 删除学生课程
export const deleteDemo03Course = async (id: number) => { deleteDemo03Course: async (id: number) => {
return await request.delete({ url: `/infra/demo03-student/demo03-course/delete?id=` + id }) return await request.delete({ url: `/infra/demo03-student-erp/demo03-course/delete?id=` + id })
} },
// 获得学生课程 /** 批量删除学生课程 */
export const getDemo03Course = async (id: number) => { deleteDemo03CourseList: async (ids: number[]) => {
return await request.get({ url: `/infra/demo03-student/demo03-course/get?id=` + id }) return await request.delete({ url: `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}` })
} },
// 获得学生课程
getDemo03Course: async (id: number) => {
return await request.get({ url: `/infra/demo03-student-erp/demo03-course/get?id=` + id })
},
// ==================== 子表(学生班级) ==================== // ==================== 子表(学生班级) ====================
// 获得学生班级分页 // 获得学生班级分页
export const getDemo03GradePage = async (params) => { getDemo03GradePage: async (params) => {
return await request.get({ url: `/infra/demo03-student/demo03-grade/page`, params }) return await request.get({ url: `/infra/demo03-student-erp/demo03-grade/page`, params })
} },
// 新增学生班级 // 新增学生班级
export const createDemo03Grade = async (data) => { createDemo03Grade: async (data: Demo03Grade) => {
return await request.post({ url: `/infra/demo03-student/demo03-grade/create`, data }) return await request.post({ url: `/infra/demo03-student-erp/demo03-grade/create`, data })
} },
// 修改学生班级 // 修改学生班级
export const updateDemo03Grade = async (data) => { updateDemo03Grade: async (data: Demo03Grade) => {
return await request.put({ url: `/infra/demo03-student/demo03-grade/update`, data }) return await request.put({ url: `/infra/demo03-student-erp/demo03-grade/update`, data })
} },
// 删除学生班级 // 删除学生班级
export const deleteDemo03Grade = async (id: number) => { deleteDemo03Grade: async (id: number) => {
return await request.delete({ url: `/infra/demo03-student/demo03-grade/delete?id=` + id }) return await request.delete({ url: `/infra/demo03-student-erp/demo03-grade/delete?id=` + id })
} },
// 获得学生班级 /** 批量删除学生班级 */
export const getDemo03Grade = async (id: number) => { deleteDemo03GradeList: async (ids: number[]) => {
return await request.get({ url: `/infra/demo03-student/demo03-grade/get?id=` + id }) return await request.delete({ url: `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}` })
},
// 获得学生班级
getDemo03Grade: async (id: number) => {
return await request.get({ url: `/infra/demo03-student-erp/demo03-grade/get?id=` + id })
},
} }

View File

@ -1,57 +1,81 @@
import request from '@/config/axios' import request from '@/config/axios'
import type { Dayjs } from 'dayjs';
export interface Demo03StudentVO { /** 学生课程信息 */
id: number export interface Demo03Course {
name: string id: number; // 编号
sex: number studentId?: number; // 学生编号
birthday: Date name?: string; // 名字
description: string score?: number; // 分数
} }
// 查询学生分页 /** 学生班级信息 */
export const getDemo03StudentPage = async (params) => { export interface Demo03Grade {
return await request.get({ url: `/infra/demo03-student/page`, params }) id: number; // 编号
studentId?: number; // 学生编号
name?: string; // 名字
teacher?: string; // 班主任
} }
// 查询学生详情 /** 学生信息 */
export const getDemo03Student = async (id: number) => { export interface Demo03Student {
return await request.get({ url: `/infra/demo03-student/get?id=` + id }) id: number; // 编号
name?: string; // 名字
sex?: number; // 性别
birthday?: string | Dayjs; // 出生日期
description?: string; // 简介
demo03courses?: Demo03Course[]
demo03grade?: Demo03Grade
} }
// 新增学生 // 学生 API
export const createDemo03Student = async (data: Demo03StudentVO) => { export const Demo03StudentApi = {
return await request.post({ url: `/infra/demo03-student/create`, data }) // 查询学生分页
} getDemo03StudentPage: async (params: any) => {
return await request.get({ url: `/infra/demo03-student-inner/page`, params })
},
// 修改学生 // 查询学生详情
export const updateDemo03Student = async (data: Demo03StudentVO) => { getDemo03Student: async (id: number) => {
return await request.put({ url: `/infra/demo03-student/update`, data }) return await request.get({ url: `/infra/demo03-student-inner/get?id=` + id })
} },
// 删除学生 // 新增学生
export const deleteDemo03Student = async (id: number) => { createDemo03Student: async (data: Demo03Student) => {
return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) return await request.post({ url: `/infra/demo03-student-inner/create`, data })
} },
// 导出学生 Excel // 修改学生
export const exportDemo03Student = async (params) => { updateDemo03Student: async (data: Demo03Student) => {
return await request.download({ url: `/infra/demo03-student/export-excel`, params }) return await request.put({ url: `/infra/demo03-student-inner/update`, data })
} },
// 删除学生
deleteDemo03Student: async (id: number) => {
return await request.delete({ url: `/infra/demo03-student-inner/delete?id=` + id })
},
/** 批量删除学生 */
deleteDemo03StudentList: async (ids: number[]) => {
return await request.delete({ url: `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}` })
},
// 导出学生 Excel
exportDemo03Student: async (params) => {
return await request.download({ url: `/infra/demo03-student-inner/export-excel`, params })
},
// ==================== 子表(学生课程) ==================== // ==================== 子表(学生课程) ====================
// 获得学生课程列表 // 获得学生课程列表
export const getDemo03CourseListByStudentId = async (studentId) => { getDemo03CourseListByStudentId: async (studentId) => {
return await request.get({ return await request.get({ url: `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=` + studentId })
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId },
})
}
// ==================== 子表(学生班级) ==================== // ==================== 子表(学生班级) ====================
// 获得学生班级 // 获得学生班级
export const getDemo03GradeByStudentId = async (studentId) => { getDemo03GradeByStudentId: async (studentId) => {
return await request.get({ return await request.get({ url: `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=` + studentId })
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId },
})
} }

View File

@ -1,57 +1,81 @@
import request from '@/config/axios' import request from '@/config/axios'
import type { Dayjs } from 'dayjs';
export interface Demo03StudentVO { /** 学生课程信息 */
id: number export interface Demo03Course {
name: string id: number; // 编号
sex: number studentId?: number; // 学生编号
birthday: Date name?: string; // 名字
description: string score?: number; // 分数
} }
// 查询学生分页 /** 学生班级信息 */
export const getDemo03StudentPage = async (params) => { export interface Demo03Grade {
return await request.get({ url: `/infra/demo03-student/page`, params }) id: number; // 编号
studentId?: number; // 学生编号
name?: string; // 名字
teacher?: string; // 班主任
} }
// 查询学生详情 /** 学生信息 */
export const getDemo03Student = async (id: number) => { export interface Demo03Student {
return await request.get({ url: `/infra/demo03-student/get?id=` + id }) id: number; // 编号
name?: string; // 名字
sex?: number; // 性别
birthday?: string | Dayjs; // 出生日期
description?: string; // 简介
demo03courses?: Demo03Course[]
demo03grade?: Demo03Grade
} }
// 新增学生 // 学生 API
export const createDemo03Student = async (data: Demo03StudentVO) => { export const Demo03StudentApi = {
return await request.post({ url: `/infra/demo03-student/create`, data }) // 查询学生分页
} getDemo03StudentPage: async (params: any) => {
return await request.get({ url: `/infra/demo03-student-normal/page`, params })
},
// 修改学生 // 查询学生详情
export const updateDemo03Student = async (data: Demo03StudentVO) => { getDemo03Student: async (id: number) => {
return await request.put({ url: `/infra/demo03-student/update`, data }) return await request.get({ url: `/infra/demo03-student-normal/get?id=` + id })
} },
// 删除学生 // 新增学生
export const deleteDemo03Student = async (id: number) => { createDemo03Student: async (data: Demo03Student) => {
return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) return await request.post({ url: `/infra/demo03-student-normal/create`, data })
} },
// 导出学生 Excel // 修改学生
export const exportDemo03Student = async (params) => { updateDemo03Student: async (data: Demo03Student) => {
return await request.download({ url: `/infra/demo03-student/export-excel`, params }) return await request.put({ url: `/infra/demo03-student-normal/update`, data })
} },
// 删除学生
deleteDemo03Student: async (id: number) => {
return await request.delete({ url: `/infra/demo03-student-normal/delete?id=` + id })
},
/** 批量删除学生 */
deleteDemo03StudentList: async (ids: number[]) => {
return await request.delete({ url: `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}` })
},
// 导出学生 Excel
exportDemo03Student: async (params) => {
return await request.download({ url: `/infra/demo03-student-normal/export-excel`, params })
},
// ==================== 子表(学生课程) ==================== // ==================== 子表(学生课程) ====================
// 获得学生课程列表 // 获得学生课程列表
export const getDemo03CourseListByStudentId = async (studentId) => { getDemo03CourseListByStudentId: async (studentId) => {
return await request.get({ return await request.get({ url: `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=` + studentId })
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId },
})
}
// ==================== 子表(学生班级) ==================== // ==================== 子表(学生班级) ====================
// 获得学生班级 // 获得学生班级
export const getDemo03GradeByStudentId = async (studentId) => { getDemo03GradeByStudentId: async (studentId) => {
return await request.get({ return await request.get({ url: `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=` + studentId })
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId },
})
} }

View File

@ -22,6 +22,11 @@ export const deleteFile = (id: number) => {
return request.delete({ url: '/infra/file/delete?id=' + id }) return request.delete({ url: '/infra/file/delete?id=' + id })
} }
// 批量删除文件
export const deleteFileList = (ids: number[]) => {
return request.delete({ url: '/infra/file/delete-list', params: { ids: ids.join(',') } })
}
// 获取文件预签名地址 // 获取文件预签名地址
export const getFilePresignedUrl = (name: string, directory?: string) => { export const getFilePresignedUrl = (name: string, directory?: string) => {
return request.get<FilePresignedUrlRespVO>({ return request.get<FilePresignedUrlRespVO>({

View File

@ -56,6 +56,11 @@ export const deleteFileConfig = (id: number) => {
return request.delete({ url: '/infra/file-config/delete?id=' + id }) return request.delete({ url: '/infra/file-config/delete?id=' + id })
} }
// 批量删除文件配置
export const deleteFileConfigList = (ids: number[]) => {
return request.delete({ url: '/infra/file-config/delete-list', params: { ids: ids.join(',') } })
}
// 测试文件配置 // 测试文件配置
export const testFileConfig = (id: number) => { export const testFileConfig = (id: number) => {
return request.get({ url: '/infra/file-config/test?id=' + id }) return request.get({ url: '/infra/file-config/test?id=' + id })

View File

@ -38,6 +38,11 @@ export const deleteJob = (id: number) => {
return request.delete({ url: '/infra/job/delete?id=' + id }) return request.delete({ url: '/infra/job/delete?id=' + id })
} }
// 批量删除定时任务调度
export const deleteJobList = (ids: number[]) => {
return request.delete({ url: '/infra/job/delete-list', params: { ids: ids.join(',') } })
}
// 导出定时任务调度 // 导出定时任务调度
export const exportJob = (params) => { export const exportJob = (params) => {
return request.download({ url: '/infra/job/export-excel', params }) return request.download({ url: '/infra/job/export-excel', params })

View File

@ -102,7 +102,7 @@ export const deleteSpu = (id: number) => {
// 导出商品 Spu Excel // 导出商品 Spu Excel
export const exportSpu = async (params: any) => { export const exportSpu = async (params: any) => {
return await request.download({ url: '/product/spu/export', params }) return await request.download({ url: '/product/spu/export-excel', params })
} }
// 获得商品 SPU 精简列表 // 获得商品 SPU 精简列表

View File

@ -1,7 +1,7 @@
import request from '@/config/axios' import request from '@/config/axios'
export interface DeptVO { export interface DeptVO {
id?: number id: number
name: string name: string
parentId: number parentId: number
status: number status: number
@ -13,31 +13,41 @@ export interface DeptVO {
} }
// 查询部门(精简)列表 // 查询部门(精简)列表
export const getSimpleDeptList = async (): Promise<DeptVO[]> => { export const getSimpleDeptList = (): Promise<DeptVO[]> => {
return await request.get({ url: '/system/dept/simple-list' }) return request.get({ url: '/system/dept/simple-list' })
} }
// 查询部门列表 // 查询部门列表
export const getDeptList = (params: any) => {
return request.get({ url: '/system/dept/list', params })
}
// 查询部门分页
export const getDeptPage = async (params: PageParam) => { export const getDeptPage = async (params: PageParam) => {
return await request.get({ url: '/system/dept/list', params }) return await request.get({ url: '/system/dept/list', params })
} }
// 查询部门详情 // 查询部门详情
export const getDept = async (id: number) => { export const getDept = (id: number) => {
return await request.get({ url: '/system/dept/get?id=' + id }) return request.get({ url: '/system/dept/get?id=' + id })
} }
// 新增部门 // 新增部门
export const createDept = async (data: DeptVO) => { export const createDept = (data: DeptVO) => {
return await request.post({ url: '/system/dept/create', data: data }) return request.post({ url: '/system/dept/create', data })
} }
// 修改部门 // 修改部门
export const updateDept = async (params: DeptVO) => { export const updateDept = (data: DeptVO) => {
return await request.put({ url: '/system/dept/update', data: params }) return request.put({ url: '/system/dept/update', data })
} }
// 删除部门 // 删除部门
export const deleteDept = async (id: number) => { export const deleteDept = async (id: number) => {
return await request.delete({ url: '/system/dept/delete?id=' + id }) return await request.delete({ url: '/system/dept/delete?id=' + id })
} }
// 批量删除部门
export const deleteDeptList = async (ids: number[]) => {
return await request.delete({ url: '/system/dept/delete-list', params: { ids: ids.join(',') } })
}

View File

@ -1,8 +1,8 @@
import request from '@/config/axios' import request from '@/config/axios'
export type DictDataVO = { export interface DictDataVO {
id: number | undefined id: number
sort: number | undefined sort: number
label: string label: string
value: string value: string
dictType: string dictType: string
@ -28,6 +28,11 @@ export const getDictData = (id: number) => {
return request.get({ url: '/system/dict-data/get?id=' + id }) return request.get({ url: '/system/dict-data/get?id=' + id })
} }
// 根据字典类型查询字典数据
export const getDictDataByType = (dictType: string) => {
return request.get({ url: '/system/dict-data/type?type=' + dictType })
}
// 新增字典数据 // 新增字典数据
export const createDictData = (data: DictDataVO) => { export const createDictData = (data: DictDataVO) => {
return request.post({ url: '/system/dict-data/create', data }) return request.post({ url: '/system/dict-data/create', data })
@ -43,7 +48,12 @@ export const deleteDictData = (id: number) => {
return request.delete({ url: '/system/dict-data/delete?id=' + id }) return request.delete({ url: '/system/dict-data/delete?id=' + id })
} }
// 导出字典类型数据 // 批量删除字典数据
export const exportDictData = (params) => { export const deleteDictDataList = (ids: number[]) => {
return request.download({ url: '/system/dict-data/export', params }) return request.delete({ url: '/system/dict-data/delete-list', params: { ids: ids.join(',') } })
}
// 导出字典数据
export const exportDictData = (params: any) => {
return request.download({ url: '/system/dict-data/export-excel', params })
} }

View File

@ -1,7 +1,7 @@
import request from '@/config/axios' import request from '@/config/axios'
export type DictTypeVO = { export interface DictTypeVO {
id: number | undefined id: number
name: string name: string
type: string type: string
status: number status: number
@ -10,8 +10,8 @@ export type DictTypeVO = {
} }
// 查询字典(精简)列表 // 查询字典(精简)列表
export const getSimpleDictTypeList = () => { export const getSimpleDictTypeList = (): Promise<DictTypeVO[]> => {
return request.get({ url: '/system/dict-type/list-all-simple' }) return request.get({ url: '/system/dict-type/simple-list' })
} }
// 查询字典列表 // 查询字典列表
@ -38,7 +38,16 @@ export const updateDictType = (data: DictTypeVO) => {
export const deleteDictType = (id: number) => { export const deleteDictType = (id: number) => {
return request.delete({ url: '/system/dict-type/delete?id=' + id }) return request.delete({ url: '/system/dict-type/delete?id=' + id })
} }
// 导出字典类型
export const exportDictType = (params) => { // 批量删除字典类型
return request.download({ url: '/system/dict-type/export', params }) export const deleteDictTypeList = (ids: number[]) => {
return request.delete({ url: '/system/dict-type/delete-list', params: { ids: ids.join(',') } })
}
// 导出字典
export const exportDictType = (params) => {
return request.download({
url: '/system/dict-type/export-excel',
params
})
} }

View File

@ -21,5 +21,5 @@ export const getLoginLogPage = (params: PageParam) => {
// 导出登录日志 // 导出登录日志
export const exportLoginLog = (params) => { export const exportLoginLog = (params) => {
return request.download({ url: '/system/login-log/export', params }) return request.download({ url: '/system/login-log/export-excel', params })
} }

View File

@ -36,6 +36,11 @@ export const deleteMailAccount = async (id: number) => {
return await request.delete({ url: '/system/mail-account/delete?id=' + id }) return await request.delete({ url: '/system/mail-account/delete?id=' + id })
} }
// 批量删除邮箱账号
export const deleteMailAccountList = async (ids: number[]) => {
return await request.delete({ url: '/system/mail-account/delete-list', params: { ids: ids.join(',') } })
}
// 获得邮箱账号精简列表 // 获得邮箱账号精简列表
export const getSimpleMailAccountList = async () => { export const getSimpleMailAccountList = async () => {
return request.get({ url: '/system/mail-account/simple-list' }) return request.get({ url: '/system/mail-account/simple-list' })

View File

@ -44,6 +44,11 @@ export const deleteMailTemplate = async (id: number) => {
return await request.delete({ url: '/system/mail-template/delete?id=' + id }) return await request.delete({ url: '/system/mail-template/delete?id=' + id })
} }
// 批量删除邮件模版
export const deleteMailTemplateList = async (ids: number[]) => {
return await request.delete({ url: '/system/mail-template/delete-list', params: { ids: ids.join(',') } })
}
// 发送邮件 // 发送邮件
export const sendMail = (data: MailSendReqVO) => { export const sendMail = (data: MailSendReqVO) => {
return request.post({ url: '/system/mail-template/send-mail', data }) return request.post({ url: '/system/mail-template/send-mail', data })

View File

@ -36,6 +36,11 @@ export const deleteNotice = (id: number) => {
return request.delete({ url: '/system/notice/delete?id=' + id }) return request.delete({ url: '/system/notice/delete?id=' + id })
} }
// 批量删除公告
export const deleteNoticeList = (ids: number[]) => {
return request.delete({ url: '/system/notice/delete-list', params: { ids: ids.join(',') } })
}
// 推送公告 // 推送公告
export const pushNotice = (id: number) => { export const pushNotice = (id: number) => {
return request.post({ url: '/system/notice/push?id=' + id }) return request.post({ url: '/system/notice/push?id=' + id })

View File

@ -43,6 +43,11 @@ export const deleteNotifyTemplate = async (id: number) => {
return await request.delete({ url: '/system/notify-template/delete?id=' + id }) return await request.delete({ url: '/system/notify-template/delete?id=' + id })
} }
// 批量删除站内信模板
export const deleteNotifyTemplateList = async (ids: number[]) => {
return await request.delete({ url: '/system/notify-template/delete-list', params: { ids: ids.join(',') } })
}
// 发送站内信 // 发送站内信
export const sendNotify = (data: NotifySendReqVO) => { export const sendNotify = (data: NotifySendReqVO) => {
return request.post({ url: '/system/notify-template/send-notify', data }) return request.post({ url: '/system/notify-template/send-notify', data })

View File

@ -45,3 +45,8 @@ export const updateOAuth2Client = (data: OAuth2ClientVO) => {
export const deleteOAuth2Client = (id: number) => { export const deleteOAuth2Client = (id: number) => {
return request.delete({ url: '/system/oauth2-client/delete?id=' + id }) return request.delete({ url: '/system/oauth2-client/delete?id=' + id })
} }
// 批量删除 OAuth2 客户端
export const deleteOAuth2ClientList = (ids: number[]) => {
return request.delete({ url: '/system/oauth2-client/delete-list', params: { ids: ids.join(',') } })
}

View File

@ -26,5 +26,5 @@ export const getOperateLogPage = (params: PageParam) => {
} }
// 导出操作日志 // 导出操作日志
export const exportOperateLog = (params: any) => { export const exportOperateLog = (params: any) => {
return request.download({ url: '/system/operate-log/export', params }) return request.download({ url: '/system/operate-log/export-excel', params })
} }

View File

@ -40,7 +40,12 @@ export const deletePost = async (id: number) => {
return await request.delete({ url: '/system/post/delete?id=' + id }) return await request.delete({ url: '/system/post/delete?id=' + id })
} }
// 批量删除岗位
export const deletePostList = async (ids: number[]) => {
return await request.delete({ url: '/system/post/delete-list', params: { ids: ids.join(',') } })
}
// 导出岗位 // 导出岗位
export const exportPost = async (params) => { export const exportPost = async (params) => {
return await request.download({ url: '/system/post/export', params }) return await request.download({ url: '/system/post/export-excel', params })
} }

View File

@ -52,6 +52,11 @@ export const deleteRole = async (id: number) => {
return await request.delete({ url: '/system/role/delete?id=' + id }) return await request.delete({ url: '/system/role/delete?id=' + id })
} }
// 批量删除角色
export const deleteRoleList = async (ids: number[]) => {
return await request.delete({ url: '/system/role/delete-list', params: { ids: ids.join(',') } })
}
// 导出角色 // 导出角色
export const exportRole = (params) => { export const exportRole = (params) => {
return request.download({ return request.download({

View File

@ -41,3 +41,8 @@ export const updateSmsChannel = (data: SmsChannelVO) => {
export const deleteSmsChannel = (id: number) => { export const deleteSmsChannel = (id: number) => {
return request.delete({ url: '/system/sms-channel/delete?id=' + id }) return request.delete({ url: '/system/sms-channel/delete?id=' + id })
} }
// 批量删除短信渠道
export const deleteSmsChannelList = (ids: number[]) => {
return request.delete({ url: '/system/sms-channel/delete-list', params: { ids: ids.join(',') } })
}

View File

@ -46,6 +46,11 @@ export const deleteSmsTemplate = (id: number) => {
return request.delete({ url: '/system/sms-template/delete?id=' + id }) return request.delete({ url: '/system/sms-template/delete?id=' + id })
} }
// 批量删除短信模板
export const deleteSmsTemplateList = (ids: number[]) => {
return request.delete({ url: '/system/sms-template/delete-list', params: { ids: ids.join(',') } })
}
// 导出短信模板 // 导出短信模板
export const exportSmsTemplate = (params) => { export const exportSmsTemplate = (params) => {
return request.download({ return request.download({

View File

@ -14,7 +14,7 @@ export interface SocialUserVO {
} }
// 查询社交用户列表 // 查询社交用户列表
export const getSocialUserPage = async (params) => { export const getSocialUserPage = async (params: any) => {
return await request.get({ url: `/system/social-user/page`, params }) return await request.get({ url: `/system/social-user/page`, params })
} }
@ -22,3 +22,8 @@ export const getSocialUserPage = async (params) => {
export const getSocialUser = async (id: number) => { export const getSocialUser = async (id: number) => {
return await request.get({ url: `/system/social-user/get?id=` + id }) return await request.get({ url: `/system/social-user/get?id=` + id })
} }
// 获得绑定社交用户列表
export const getBindSocialUserList = async () => {
return await request.get({ url: '/system/social-user/get-bind-list' })
}

View File

@ -61,6 +61,11 @@ export const deleteTenant = (id: number) => {
return request.delete({ url: '/system/tenant/delete?id=' + id }) return request.delete({ url: '/system/tenant/delete?id=' + id })
} }
// 批量删除租户
export const deleteTenantList = (ids: number[]) => {
return request.delete({ url: '/system/tenant/delete-list', params: { ids: ids.join(',') } })
}
// 导出租户 // 导出租户
export const exportTenant = (params: TenantExportReqVO) => { export const exportTenant = (params: TenantExportReqVO) => {
return request.download({ url: '/system/tenant/export-excel', params }) return request.download({ url: '/system/tenant/export-excel', params })

View File

@ -36,6 +36,12 @@ export const updateTenantPackage = (data: TenantPackageVO) => {
export const deleteTenantPackage = (id: number) => { export const deleteTenantPackage = (id: number) => {
return request.delete({ url: '/system/tenant-package/delete?id=' + id }) return request.delete({ url: '/system/tenant-package/delete?id=' + id })
} }
// 批量删除租户套餐
export const deleteTenantPackageList = (ids: number[]) => {
return request.delete({ url: '/system/tenant-package/delete-list', params: { ids: ids.join(',') } })
}
// 获取租户套餐精简信息列表 // 获取租户套餐精简信息列表
export const getTenantPackageList = () => { export const getTenantPackageList = () => {
return request.get({ url: '/system/tenant-package/simple-list' }) return request.get({ url: '/system/tenant-package/simple-list' })

View File

@ -42,9 +42,14 @@ export const deleteUser = (id: number) => {
return request.delete({ url: '/system/user/delete?id=' + id }) return request.delete({ url: '/system/user/delete?id=' + id })
} }
// 批量删除用户
export const deleteUserList = (ids: number[]) => {
return request.delete({ url: '/system/user/delete-list', params: { ids: ids.join(',') } })
}
// 导出用户 // 导出用户
export const exportUser = (params: any) => { export const exportUser = (params: any) => {
return request.download({ url: '/system/user/export', params }) return request.download({ url: '/system/user/export-excel', params })
} }
// 下载用户导入模板 // 下载用户导入模板

View File

@ -16,10 +16,6 @@ export interface ProfileVO {
id: number id: number
name: string name: string
}[] }[]
socialUsers: {
type: number
openid: string
}[]
email: string email: string
mobile: string mobile: string
sex: number sex: number

View File

@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1676209854312" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3033" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M173.077333 362.666667l91.114667-214.677334a65.6 65.6 0 0 1 86.016-34.773333c11.584 4.906667 24.96 10.282667 40.896 16.448 8.277333 3.2 16.789333 6.464 27.904 10.666667 28.202667 10.709333 39.296 14.933333 46.144 17.642666l51.477333-51.669333c28.181333-28.16 74.112-27.946667 102.570667 0.533333l195.925333 195.925334c16.426667 16.426667 23.445333 38.634667 21.056 59.904H896a42.666667 42.666667 0 0 1 42.666667 42.666666v490.666667a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V405.333333a42.666667 42.666667 0 0 1 42.666667-42.666666h45.077333z m48.96 0h39.104l169.194667-169.770667-27.328-10.389333c-11.2-4.245333-19.818667-7.530667-28.224-10.794667a1459.2 1459.2 0 0 1-42.197333-17.002667 20.522667 20.522667 0 0 0-26.901334 10.88L222.037333 362.666667z m108.842667 0h454.954667a23.509333 23.509333 0 0 0-5.290667-25.322667l-195.925333-195.925333a23.36 23.36 0 0 0-33.024-0.213334L330.88 362.666667zM128 405.333333v490.666667h768V405.333333H128z m597.333333 320a85.333333 85.333333 0 1 1 0-170.666666 85.333333 85.333333 0 0 1 0 170.666666z m0-42.666666a42.666667 42.666667 0 1 0 0-85.333334 42.666667 42.666667 0 0 0 0 85.333334z" fill="#4296d5" p-id="3034"></path></svg> <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747409043186" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4834" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M44.416 853.333333v-85.205333a170.666667 170.666667 0 0 1 170.666667-170.666667h170.837333a37637.589333 37637.589333 0 0 1 0-206.165333C324.309333 352.170667 281.6 285.141333 281.6 211.968c0-116.906667 90.197333-211.072 231.168-211.072 140.970667 0 230.741333 94.208 230.741333 211.072 0 73.216-40.96 140.245333-102.528 179.328 0.256 0.170667 0.256 68.906667 0 206.165333h171.989334a170.666667 170.666667 0 0 1 170.666666 170.666667V853.333333a170.666667 170.666667 0 0 1-170.666666 170.666667H215.082667a170.666667 170.666667 0 0 1-170.666667-170.666667z m84.266667-84.650666v104.277333a85.333333 85.333333 0 0 0 85.333333 85.333333H811.52a85.333333 85.333333 0 0 0 85.333333-85.333333v-104.277333a85.333333 85.333333 0 0 0-85.333333-85.333334h-256.64l8.96-342.698666c66.944-21.333333 100.394667-64.256 100.394667-128.682667 0-61.952-57.344-129.322667-151.466667-129.322667-94.122667 0-146.645333 61.610667-146.645333 129.322667 0 71.466667 34.816 114.346667 104.362666 128.682667v342.698666H214.016a85.333333 85.333333 0 0 0-85.333333 85.333334z m167.125333 138.368c-50.432 0-91.434667-41.557333-91.434667-92.586667s41.002667-92.586667 91.434667-92.586667c50.389333 0 91.434667 41.557333 91.434667 92.586667 0 24.832-9.6 48.170667-27.008 65.706667-17.237333 17.322667-40.106667 26.88-64.426667 26.88z m0-119.466667a27.093333 27.093333 0 0 0-27.306667 26.88c0 14.805333 12.245333 26.88 27.306667 26.88a27.306667 27.306667 0 0 0 19.498667-8.106667 26.453333 26.453333 0 0 0 7.808-18.773333 27.093333 27.093333 0 0 0-27.306667-26.88z" fill="#1296db" p-id="4835"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1676209854312" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3033" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M173.077333 362.666667l91.114667-214.677334a65.6 65.6 0 0 1 86.016-34.773333c11.584 4.906667 24.96 10.282667 40.896 16.448 8.277333 3.2 16.789333 6.464 27.904 10.666667 28.202667 10.709333 39.296 14.933333 46.144 17.642666l51.477333-51.669333c28.181333-28.16 74.112-27.946667 102.570667 0.533333l195.925333 195.925334c16.426667 16.426667 23.445333 38.634667 21.056 59.904H896a42.666667 42.666667 0 0 1 42.666667 42.666666v490.666667a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V405.333333a42.666667 42.666667 0 0 1 42.666667-42.666666h45.077333z m48.96 0h39.104l169.194667-169.770667-27.328-10.389333c-11.2-4.245333-19.818667-7.530667-28.224-10.794667a1459.2 1459.2 0 0 1-42.197333-17.002667 20.522667 20.522667 0 0 0-26.901334 10.88L222.037333 362.666667z m108.842667 0h454.954667a23.509333 23.509333 0 0 0-5.290667-25.322667l-195.925333-195.925333a23.36 23.36 0 0 0-33.024-0.213334L330.88 362.666667zM128 405.333333v490.666667h768V405.333333H128z m597.333333 320a85.333333 85.333333 0 1 1 0-170.666666 85.333333 85.333333 0 0 1 0 170.666666z m0-42.666666a42.666667 42.666667 0 1 0 0-85.333334 42.666667 42.666667 0 0 0 0 85.333334z" fill="#4296d5" p-id="3034"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -80,7 +80,8 @@ const activeAppLink = ref({} as AppLink)
/** 打开弹窗 */ /** 打开弹窗 */
const dialogVisible = ref(false) const dialogVisible = ref(false)
const open = (link: string) => { const open = (link: string) => {
activeAppLink.value.path = link // activeAppLink
activeAppLink.value = { name: '', path: '' }
dialogVisible.value = true dialogVisible.value = true
// //
@ -102,8 +103,11 @@ defineExpose({ open })
// APP // APP
const handleAppLinkSelected = (appLink: AppLink) => { const handleAppLinkSelected = (appLink: AppLink) => {
//
if (!isSameLink(appLink.path, activeAppLink.value.path)) { if (!isSameLink(appLink.path, activeAppLink.value.path)) {
activeAppLink.value = appLink // path 沿 activeAppLink path
const path = appLink.path || activeAppLink.value.path
activeAppLink.value = { ...appLink, path: path }
} }
switch (appLink.type) { switch (appLink.type) {
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST: case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
@ -170,7 +174,7 @@ const groupBtnRefs = ref<ButtonInstance[]>([])
const scrollToGroupBtn = (group: string) => { const scrollToGroupBtn = (group: string) => {
const groupBtn = groupBtnRefs.value const groupBtn = groupBtnRefs.value
.map((btn: ButtonInstance) => btn['ref']) .map((btn: ButtonInstance) => btn['ref'])
.find((ref: Node) => ref.textContent === group) .find((ref: HTMLButtonElement) => ref.textContent === group)
if (groupBtn) { if (groupBtn) {
groupScrollbar.value?.setScrollTop(groupBtn.offsetTop) groupScrollbar.value?.setScrollTop(groupBtn.offsetTop)
} }

View File

@ -181,7 +181,6 @@ function openModal() {
} }
function closeModal() { function closeModal() {
debugger
dialogVisible.value = false dialogVisible.value = false
} }

View File

@ -1,9 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
defineOptions({ name: 'Dialog' }) defineOptions({ name: 'Dialog' })
const slots = useSlots() const slots = useSlots()
const emits = defineEmits(['update:modelValue'])
const props = defineProps({ const props = defineProps({
modelValue: propTypes.bool.def(false), modelValue: propTypes.bool.def(false),
@ -55,6 +57,17 @@ const dialogStyle = computed(() => {
height: unref(dialogHeight) height: unref(dialogHeight)
} }
}) })
const closing = ref(false)
function closeHandler() {
emits('update:modelValue', false)
closing.value = true
}
function closedHandler() {
closing.value = false
}
</script> </script>
<template> <template>
@ -68,7 +81,8 @@ const dialogStyle = computed(() => {
draggable draggable
class="com-dialog" class="com-dialog"
:show-close="false" :show-close="false"
@close="$emit('update:modelValue', false)" @close="closeHandler"
@closed="closedHandler"
> >
<template #header="{ close }"> <template #header="{ close }">
<div class="relative h-54px flex items-center justify-between pl-15px pr-15px"> <div class="relative h-54px flex items-center justify-between pl-15px pr-15px">
@ -102,7 +116,9 @@ const dialogStyle = computed(() => {
</ElScrollbar> </ElScrollbar>
<slot v-else></slot> <slot v-else></slot>
<template v-if="slots.footer" #footer> <template v-if="slots.footer" #footer>
<div :style="{ 'pointer-events': closing ? 'none' : 'auto' }">
<slot name="footer"></slot> <slot name="footer"></slot>
</div>
</template> </template>
</ElDialog> </ElDialog>
</template> </template>

View File

@ -10,6 +10,8 @@ export interface CarouselProperty {
autoplay: boolean autoplay: boolean
// 播放间隔 // 播放间隔
interval: number interval: number
// 轮播高度
height: number
// 轮播内容 // 轮播内容
items: CarouselItemProperty[] items: CarouselItemProperty[]
// 组件样式 // 组件样式
@ -37,6 +39,7 @@ export const component = {
indicator: 'dot', indicator: 'dot',
autoplay: false, autoplay: false,
interval: 3, interval: 3,
height: 174,
items: [ items: [
{ type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-01.jpg', videoUrl: '' }, { type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-01.jpg', videoUrl: '' },
{ type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-02.jpg', videoUrl: '' } { type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-02.jpg', videoUrl: '' }

View File

@ -8,7 +8,7 @@
</div> </div>
<div v-else class="relative"> <div v-else class="relative">
<el-carousel <el-carousel
height="174px" :height="property.height + 'px'"
:type="property.type === 'card' ? 'card' : ''" :type="property.type === 'card' ? 'card' : ''"
:autoplay="property.autoplay" :autoplay="property.autoplay"
:interval="property.interval * 1000" :interval="property.interval * 1000"

View File

@ -16,6 +16,9 @@
</el-tooltip> </el-tooltip>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="高度" prop="height">
<el-input-number class="!w-50% mr-10px" controls-position="right" v-model="formData.height" /> px
</el-form-item>
<el-form-item label="指示器" prop="indicator"> <el-form-item label="指示器" prop="indicator">
<el-radio-group v-model="formData.indicator"> <el-radio-group v-model="formData.indicator">
<el-radio value="dot">小圆点</el-radio> <el-radio value="dot">小圆点</el-radio>

View File

@ -13,7 +13,7 @@
<template v-for="(cell, cellIndex) in cellList" :key="cellIndex"> <template v-for="(cell, cellIndex) in cellList" :key="cellIndex">
<template v-if="selectedHotAreaIndex === cellIndex"> <template v-if="selectedHotAreaIndex === cellIndex">
<el-form-item :prop="`cell[${cellIndex}].type`" label="类型"> <el-form-item :prop="`cell[${cellIndex}].type`" label="类型">
<el-radio-group v-model="cell.type"> <el-radio-group v-model="cell.type" @change="handleHotAreaSelected(cell, cellIndex)">
<el-radio value="text">文字</el-radio> <el-radio value="text">文字</el-radio>
<el-radio value="image">图片</el-radio> <el-radio value="image">图片</el-radio>
<el-radio value="search">搜索框</el-radio> <el-radio value="search">搜索框</el-radio>
@ -44,9 +44,32 @@
</template> </template>
<!-- 3. 搜索框 --> <!-- 3. 搜索框 -->
<template v-else> <template v-else>
<el-form-item label="框体颜色" prop="backgroundColor">
<ColorInput v-model="cell.backgroundColor" />
</el-form-item>
<el-form-item class="lef" label="文本颜色" prop="textColor">
<ColorInput v-model="cell.textColor" />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].placeholder`" label="提示文字"> <el-form-item :prop="`cell[${cellIndex}].placeholder`" label="提示文字">
<el-input v-model="cell.placeholder" maxlength="10" show-word-limit /> <el-input v-model="cell.placeholder" maxlength="10" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item label="文本位置" prop="placeholderPosition">
<el-radio-group v-model="cell!.placeholderPosition">
<el-tooltip content="居左" placement="top">
<el-radio-button value="left">
<Icon icon="ant-design:align-left-outlined" />
</el-radio-button>
</el-tooltip>
<el-tooltip content="居中" placement="top">
<el-radio-button value="center">
<Icon icon="ant-design:align-center-outlined" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="扫一扫" prop="showScan">
<el-switch v-model="cell!.showScan" />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].borderRadius`" label="圆角"> <el-form-item :prop="`cell[${cellIndex}].borderRadius`" label="圆角">
<el-slider <el-slider
v-model="cell.borderRadius" v-model="cell.borderRadius"
@ -88,10 +111,17 @@ const cellCount = computed(() => (props.isMp ? 6 : 8))
const selectedHotAreaIndex = ref(0) const selectedHotAreaIndex = ref(0)
const handleHotAreaSelected = (cellValue: NavigationBarCellProperty, index: number) => { const handleHotAreaSelected = (cellValue: NavigationBarCellProperty, index: number) => {
selectedHotAreaIndex.value = index selectedHotAreaIndex.value = index
//
if (!cellValue.type) { if (!cellValue.type) {
cellValue.type = 'text' cellValue.type = 'text'
cellValue.textColor = '#111111' cellValue.textColor = '#111111'
} }
//
if (cellValue.type === 'search') {
cellValue.placeholderPosition = 'left'
cellValue.backgroundColor = '#EEEEEE'
cellValue.textColor = '#969799'
}
} }
</script> </script>

View File

@ -45,8 +45,14 @@ export interface NavigationBarCellProperty {
imgUrl: string imgUrl: string
// 图片链接 // 图片链接
url: string url: string
// 搜索框:框体颜色
backgroundColor: string
// 搜索框:提示文字 // 搜索框:提示文字
placeholder: string placeholder: string
// 搜索框:提示文字位置
placeholderPosition: string
// 搜索框:是否显示扫一扫
showScan: boolean
// 搜索框:边框圆角半径 // 搜索框:边框圆角半径
borderRadius: number borderRadius: number
} }

View File

@ -54,9 +54,12 @@ const getCellStyle = (cell: NavigationBarCellProperty) => {
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => { const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
return { return {
height: 30, height: 30,
showScan: false, backgroundColor: cell.backgroundColor,
showScan: cell.showScan,
placeholder: cell.placeholder, placeholder: cell.placeholder,
borderRadius: cell.borderRadius borderRadius: cell.borderRadius,
textColor: cell.textColor,
placeholderPosition: cell.placeholderPosition
} as SearchProperty } as SearchProperty
}) })
</script> </script>

View File

@ -269,6 +269,11 @@ watch(
if (!val || selectedComponentIndex.value === -1) { if (!val || selectedComponentIndex.value === -1) {
return return
} }
// -1
// https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/792
if (props.showTabBar) {
selectedComponentIndex.value = -1
}
pageComponents.value[selectedComponentIndex.value] = selectedComponent.value! pageComponents.value[selectedComponentIndex.value] = selectedComponent.value!
}, },
{ deep: true } { deep: true }

View File

@ -72,6 +72,7 @@ watch(
(options) => { (options) => {
if (echartRef) { if (echartRef) {
echartRef?.setOption(options) echartRef?.setOption(options)
echartRef?.resize()
} }
}, },
{ {

View File

@ -1014,6 +1014,16 @@
"name": "fields", "name": "fields",
"type": "Field", "type": "Field",
"isMany": true "isMany": true
},
{
"name": "id",
"type": "String",
"isAttr": true
},
{
"name": "eventDefinitions",
"type": "bpmn:TimerEventDefinition",
"isMany": true
} }
] ]
}, },

View File

@ -23,22 +23,20 @@
export default function customTranslate(translations) { export default function customTranslate(translations) {
return function (template, replacements) { return function (template, replacements) {
replacements = replacements || {} replacements = replacements || {};
// Translate // 将模板和翻译字典的键统一转换为小写进行匹配
template = translations[template] || template const lowerTemplate = template.toLowerCase();
const translation = Object.keys(translations).find(key => key.toLowerCase() === lowerTemplate);
// Replace // 如果找到匹配的翻译,使用翻译后的模板
if (translation) {
template = translations[translation];
}
// 替换模板中的占位符
return template.replace(/{([^}]+)}/g, function (_, key) { return template.replace(/{([^}]+)}/g, function (_, key) {
let str = replacements[key] // 如果替换值存在,返回替换值;否则返回原始占位符
if ( return replacements[key] !== undefined ? replacements[key] : `{${key}}`;
translations[replacements[key]] !== null && });
translations[replacements[key]] !== undefined };
) {
// eslint-disable-next-line no-mixed-spaces-and-tabs
str = translations[replacements[key]]
// eslint-disable-next-line no-mixed-spaces-and-tabs
}
return str || '{' + key + '}'
})
}
} }

View File

@ -45,8 +45,8 @@ export default {
'Service Task': '服务任务', 'Service Task': '服务任务',
'Script Task': '脚本任务', 'Script Task': '脚本任务',
'Call Activity': '调用活动', 'Call Activity': '调用活动',
'Sub Process (collapsed)': '子流程(折叠的)', 'Sub-Process (collapsed)': '子流程(折叠的)',
'Sub Process (expanded)': '子流程(展开的)', 'Sub-Process (expanded)': '子流程(展开的)',
'Start Event': '开始事件', 'Start Event': '开始事件',
StartEvent: '开始事件', StartEvent: '开始事件',
'Intermediate Throw Event': '中间事件', 'Intermediate Throw Event': '中间事件',

View File

@ -68,6 +68,11 @@
:business-object="elementBusinessObject" :business-object="elementBusinessObject"
/> />
</el-collapse-item> </el-collapse-item>
<!-- 新增的时间事件配置项 -->
<el-collapse-item v-if="elementType === 'IntermediateCatchEvent'" name="timeEvent">
<template #title><Icon icon="ep:timer" />时间事件</template>
<TimeEventConfig :businessObject="bpmnElement.value?.businessObject" :key="elementId" />
</el-collapse-item>
</el-collapse> </el-collapse>
</div> </div>
</template> </template>
@ -83,6 +88,8 @@ import ElementProperties from './properties/ElementProperties.vue'
// import ElementForm from './form/ElementForm.vue' // import ElementForm from './form/ElementForm.vue'
import UserTaskListeners from './listeners/UserTaskListeners.vue' import UserTaskListeners from './listeners/UserTaskListeners.vue'
import { getTaskCollapseItemName, isTaskCollapseItemShow } from './task/data' import { getTaskCollapseItemName, isTaskCollapseItemShow } from './task/data'
import TimeEventConfig from './time-event-config/TimeEventConfig.vue'
import { ref, watch, onMounted } from 'vue'
defineOptions({ name: 'MyPropertiesPanel' }) defineOptions({ name: 'MyPropertiesPanel' })
@ -121,6 +128,8 @@ const formVisible = ref(false) // 表单配置
const bpmnElement = ref() const bpmnElement = ref()
const isReady = ref(false) const isReady = ref(false)
const type = ref('time')
const condition = ref('')
provide('prefix', props.prefix) provide('prefix', props.prefix)
provide('width', props.width) provide('width', props.width)
@ -141,7 +150,7 @@ const initBpmnInstances = () => {
} }
// //
const allInstancesExist = Object.values(instances).every(instance => instance) const allInstancesExist = Object.values(instances).every((instance) => instance)
if (allInstancesExist) { if (allInstancesExist) {
const w = window as any const w = window as any
w.bpmnInstances = instances w.bpmnInstances = instances
@ -255,4 +264,48 @@ watch(
activeTab.value = 'base' activeTab.value = 'base'
} }
) )
function updateNode() {
const moddle = window.bpmnInstances?.moddle
const modeling = window.bpmnInstances?.modeling
const elementRegistry = window.bpmnInstances?.elementRegistry
if (!moddle || !modeling || !elementRegistry) return
const element = elementRegistry.get(props.businessObject.id)
if (!element) return
let timerDef = moddle.create('bpmn:TimerEventDefinition', {})
if (type.value === 'time') {
timerDef.timeDate = moddle.create('bpmn:FormalExpression', { body: condition.value })
} else if (type.value === 'duration') {
timerDef.timeDuration = moddle.create('bpmn:FormalExpression', { body: condition.value })
} else if (type.value === 'cycle') {
timerDef.timeCycle = moddle.create('bpmn:FormalExpression', { body: condition.value })
}
modeling.updateModdleProperties(element, element.businessObject, {
eventDefinitions: [timerDef]
})
}
//
function syncFromBusinessObject() {
if (props.businessObject) {
const timerDef = (props.businessObject.eventDefinitions || [])[0]
if (timerDef) {
if (timerDef.timeDate) {
type.value = 'time'
condition.value = timerDef.timeDate.body
} else if (timerDef.timeDuration) {
type.value = 'duration'
condition.value = timerDef.timeDuration.body
} else if (timerDef.timeCycle) {
type.value = 'cycle'
condition.value = timerDef.timeCycle.body
}
}
}
}
onMounted(syncFromBusinessObject)
watch(() => props.businessObject, syncFromBusinessObject, { deep: true })
</script> </script>

View File

@ -28,7 +28,12 @@
v-model="timeDuration" v-model="timeDuration"
:min="1" :min="1"
controls-position="right" controls-position="right"
@change="() => updateTimeModdle()" @change="
() => {
updateTimeModdle()
updateElementExtensions()
}
"
/> />
</el-form-item> </el-form-item>
<el-select <el-select
@ -55,7 +60,12 @@
v-model="maxRemindCount" v-model="maxRemindCount"
:min="1" :min="1"
:max="10" :max="10"
@change="() => updateTimeModdle()" @change="
() => {
updateTimeModdle()
updateElementExtensions()
}
"
/> />
</el-form-item> </el-form-item>
</div> </div>
@ -65,7 +75,7 @@
import { import {
TimeUnitType, TimeUnitType,
TIME_UNIT_TYPES, TIME_UNIT_TYPES,
TIMEOUT_HANDLER_TYPES, TIMEOUT_HANDLER_TYPES
} from '@/components/SimpleProcessDesignerV2/src/consts' } from '@/components/SimpleProcessDesignerV2/src/consts'
import { convertTimeUnit } from '@/components/SimpleProcessDesignerV2/src/utils' import { convertTimeUnit } from '@/components/SimpleProcessDesignerV2/src/utils'
@ -195,6 +205,7 @@ const onTimeUnitChange = () => {
timeDuration.value = 1 timeDuration.value = 1
} }
updateTimeModdle() updateTimeModdle()
updateElementExtensions()
} }
const updateTimeModdle = () => { const updateTimeModdle = () => {

View File

@ -354,12 +354,13 @@ const resetTaskForm = () => {
const changeCandidateStrategy = () => { const changeCandidateStrategy = () => {
userTaskForm.value.candidateParam = [] userTaskForm.value.candidateParam = []
deptLevel.value = 1 deptLevel.value = 1
if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) { // by https://t.zsxq.com/xNmas
// // if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) { // //
userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER // if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
} // userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
} // }
// }
updateElementTask() updateElementTask()
} }

View File

@ -0,0 +1,285 @@
<template>
<el-tabs v-model="tab">
<el-tab-pane label="CRON表达式" name="cron">
<div style="margin-bottom: 10px">
<el-input
v-model="cronStr"
readonly
style="width: 400px; font-weight: bold"
:key="'cronStr'"
/>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 8px">
<el-input v-model="fields.second" placeholder="秒" style="width: 80px" :key="'second'" />
<el-input v-model="fields.minute" placeholder="分" style="width: 80px" :key="'minute'" />
<el-input v-model="fields.hour" placeholder="时" style="width: 80px" :key="'hour'" />
<el-input v-model="fields.day" placeholder="天" style="width: 80px" :key="'day'" />
<el-input v-model="fields.month" placeholder="月" style="width: 80px" :key="'month'" />
<el-input v-model="fields.week" placeholder="周" style="width: 80px" :key="'week'" />
<el-input v-model="fields.year" placeholder="年" style="width: 80px" :key="'year'" />
</div>
<el-tabs v-model="activeField" type="card" style="margin-bottom: 8px">
<el-tab-pane v-for="f in cronFieldList" :label="f.label" :name="f.key" :key="f.key">
<div style="margin-bottom: 8px">
<el-radio-group v-model="cronMode[f.key]" :key="'radio-' + f.key">
<el-radio label="every" :key="'every-' + f.key">{{ f.label }}</el-radio>
<el-radio label="range" :key="'range-' + f.key"
>
<el-input-number
v-model="cronRange[f.key][0]"
:min="f.min"
:max="f.max"
size="small"
style="width: 60px"
:key="'range0-' + f.key"
/>
<el-input-number
v-model="cronRange[f.key][1]"
:min="f.min"
:max="f.max"
size="small"
style="width: 60px"
:key="'range1-' + f.key"
/>
之间每{{ f.label }}</el-radio
>
<el-radio label="step" :key="'step-' + f.key"
>从第
<el-input-number
v-model="cronStep[f.key][0]"
:min="f.min"
:max="f.max"
size="small"
style="width: 60px"
:key="'step0-' + f.key"
/>
开始每
<el-input-number
v-model="cronStep[f.key][1]"
:min="1"
:max="f.max"
size="small"
style="width: 60px"
:key="'step1-' + f.key"
/>
{{ f.label }}</el-radio
>
<el-radio label="appoint" :key="'appoint-' + f.key">指定</el-radio>
</el-radio-group>
</div>
<div v-if="cronMode[f.key] === 'appoint'">
<el-checkbox-group v-model="cronAppoint[f.key]" :key="'group-' + f.key">
<el-checkbox
v-for="n in f.max + 1"
:label="pad(n - 1)"
:key="'cb-' + f.key + '-' + (n - 1)"
>{{ pad(n - 1) }}</el-checkbox
>
</el-checkbox-group>
</div>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
<el-tab-pane label="标准格式" name="iso" :key="'iso-tab'">
<div style="margin-bottom: 10px">
<el-input
v-model="isoStr"
placeholder="如R1/2025-05-21T21:59:54/P3DT30M30S"
style="width: 400px; font-weight: bold"
:key="'isoStr'"
/>
</div>
<div style="margin-bottom: 10px"
>循环次数<el-input-number v-model="repeat" :min="1" style="width: 100px" :key="'repeat'"
/></div>
<div style="margin-bottom: 10px"
>日期时间<el-date-picker
v-model="isoDate"
type="datetime"
placeholder="选择日期时间"
style="width: 200px"
:key="'isoDate'"
/></div>
<div style="margin-bottom: 10px"
>当前时长<el-input
v-model="isoDuration"
placeholder="如P3DT30M30S"
style="width: 200px"
:key="'isoDuration'"
/></div>
<div>
<div
><el-button
v-for="s in [5, 10, 30, 50]"
@click="setDuration('S', s)"
:key="'sec-' + s"
>{{ s }}</el-button
>自定义</div
>
<div
><el-button
v-for="m in [5, 10, 30, 50]"
@click="setDuration('M', m)"
:key="'min-' + m"
>{{ m }}</el-button
>自定义</div
>
<div
>小时<el-button
v-for="h in [4, 8, 12, 24]"
@click="setDuration('H', h)"
:key="'hour-' + h"
>{{ h }}</el-button
>自定义</div
>
<div
><el-button
v-for="d in [1, 2, 3, 4]"
@click="setDuration('D', d)"
:key="'day-' + d"
>{{ d }}</el-button
>自定义</div
>
<div
><el-button
v-for="mo in [1, 2, 3, 4]"
@click="setDuration('M', mo)"
:key="'mon-' + mo"
>{{ mo }}</el-button
>自定义</div
>
<div
><el-button
v-for="y in [1, 2, 3, 4]"
@click="setDuration('Y', y)"
:key="'year-' + y"
>{{ y }}</el-button
>自定义</div
>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import { ref, watch, computed } from 'vue'
const props = defineProps({ value: String })
const emit = defineEmits(['change'])
const tab = ref('cron')
const cronStr = ref(props.value || '* * * * * ?')
const fields = ref({
second: '*',
minute: '*',
hour: '*',
day: '*',
month: '*',
week: '?',
year: ''
})
const cronFieldList = [
{ key: 'second', label: '秒', min: 0, max: 59 },
{ key: 'minute', label: '分', min: 0, max: 59 },
{ key: 'hour', label: '时', min: 0, max: 23 },
{ key: 'day', label: '天', min: 1, max: 31 },
{ key: 'month', label: '月', min: 1, max: 12 },
{ key: 'week', label: '周', min: 1, max: 7 },
{ key: 'year', label: '年', min: 1970, max: 2099 }
]
const activeField = ref('second')
const cronMode = ref({
second: 'appoint',
minute: 'every',
hour: 'every',
day: 'every',
month: 'every',
week: 'every',
year: 'every'
})
const cronAppoint = ref({
second: ['00', '01'],
minute: [],
hour: [],
day: [],
month: [],
week: [],
year: []
})
const cronRange = ref({
second: [0, 1],
minute: [0, 1],
hour: [0, 1],
day: [1, 2],
month: [1, 2],
week: [1, 2],
year: [1970, 1971]
})
const cronStep = ref({
second: [1, 1],
minute: [1, 1],
hour: [1, 1],
day: [1, 1],
month: [1, 1],
week: [1, 1],
year: [1970, 1]
})
function pad(n) {
return n < 10 ? '0' + n : '' + n
}
watch(
[fields, cronMode, cronAppoint, cronRange, cronStep],
() => {
// cron
let arr = cronFieldList.map((f) => {
if (cronMode.value[f.key] === 'every') return '*'
if (cronMode.value[f.key] === 'appoint') return cronAppoint.value[f.key].join(',') || '*'
if (cronMode.value[f.key] === 'range')
return `${cronRange.value[f.key][0]}-${cronRange.value[f.key][1]}`
if (cronMode.value[f.key] === 'step')
return `${cronStep.value[f.key][0]}/${cronStep.value[f.key][1]}`
return fields.value[f.key] || '*'
})
// weekyear
arr[5] = arr[5] || '?'
cronStr.value = arr.join(' ')
if (tab.value === 'cron') emit('change', cronStr.value)
},
{ deep: true }
)
//
const isoStr = ref('')
const repeat = ref(1)
const isoDate = ref('')
const isoDuration = ref('')
function setDuration(type, val) {
// ISO 8601
let d = isoDuration.value
if (!d.includes(type)) d += val + type
else d = d.replace(new RegExp(`\\d+${type}`), val + type)
isoDuration.value = d
updateIsoStr()
}
function updateIsoStr() {
let str = `R${repeat.value}`
if (isoDate.value)
str +=
'/' +
(typeof isoDate.value === 'string' ? isoDate.value : new Date(isoDate.value).toISOString())
if (isoDuration.value) str += '/' + isoDuration.value
isoStr.value = str
if (tab.value === 'iso') emit('change', isoStr.value)
}
watch([repeat, isoDate, isoDuration], updateIsoStr)
watch(
() => props.value,
(val) => {
if (!val) return
if (tab.value === 'cron') cronStr.value = val
if (tab.value === 'iso') isoStr.value = val
},
{ immediate: true }
)
</script>

View File

@ -0,0 +1,86 @@
<template>
<div>
<div style="margin-bottom: 10px"
>当前选择<el-input v-model="isoString" readonly style="width: 300px"
/></div>
<div v-for="unit in units" :key="unit.key" style="margin-bottom: 8px">
<span>{{ unit.label }}</span>
<el-button-group>
<el-button
v-for="val in unit.presets"
:key="val"
size="mini"
@click="setUnit(unit.key, val)"
>{{ val }}</el-button
>
<el-input
v-model.number="custom[unit.key]"
size="mini"
style="width: 60px; margin-left: 8px"
placeholder="自定义"
@change="setUnit(unit.key, custom[unit.key])"
/>
</el-button-group>
</div>
</div>
</template>
<script setup>
import { ref, watch, computed } from 'vue'
const props = defineProps({ value: String })
const emit = defineEmits(['change'])
const units = [
{ key: 'Y', label: '年', presets: [1, 2, 3, 4] },
{ key: 'M', label: '月', presets: [1, 2, 3, 4] },
{ key: 'D', label: '天', presets: [1, 2, 3, 4] },
{ key: 'H', label: '时', presets: [4, 8, 12, 24] },
{ key: 'm', label: '分', presets: [5, 10, 30, 50] },
{ key: 'S', label: '秒', presets: [5, 10, 30, 50] }
]
const custom = ref({ Y: '', M: '', D: '', H: '', m: '', S: '' })
const isoString = ref('')
function setUnit(key, val) {
if (!val || isNaN(val)) {
custom.value[key] = ''
return
}
custom.value[key] = val
updateIsoString()
}
function updateIsoString() {
let str = 'P'
if (custom.value.Y) str += custom.value.Y + 'Y'
if (custom.value.M) str += custom.value.M + 'M'
if (custom.value.D) str += custom.value.D + 'D'
if (custom.value.H || custom.value.m || custom.value.S) str += 'T'
if (custom.value.H) str += custom.value.H + 'H'
if (custom.value.m) str += custom.value.m + 'M'
if (custom.value.S) str += custom.value.S + 'S'
isoString.value = str === 'P' ? '' : str
emit('change', isoString.value)
}
watch(
() => props.value,
(val) => {
if (!val) return
// ISO 8601custom
const match = val.match(
/^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/
)
if (match) {
custom.value.Y = match[1] || ''
custom.value.M = match[2] || ''
custom.value.D = match[3] || ''
custom.value.H = match[4] || ''
custom.value.m = match[5] || ''
custom.value.S = match[6] || ''
updateIsoString()
}
},
{ immediate: true }
)
</script>

View File

@ -0,0 +1,312 @@
<template>
<div class="panel-tab__content">
<div style="margin-top: 10px">
<span>类型</span>
<el-button-group>
<el-button size="mini" :type="type === 'time' ? 'primary' : ''" @click="setType('time')"
>时间</el-button
>
<el-button
size="mini"
:type="type === 'duration' ? 'primary' : ''"
@click="setType('duration')"
>持续</el-button
>
<el-button size="mini" :type="type === 'cycle' ? 'primary' : ''" @click="setType('cycle')"
>循环</el-button
>
</el-button-group>
<el-icon v-if="valid" color="green" style="margin-left: 8px"><CircleCheckFilled /></el-icon>
</div>
<div style="margin-top: 10px; display: flex; align-items: center">
<span>条件</span>
<el-input
v-model="condition"
:placeholder="placeholder"
style="width: calc(100% - 100px)"
:readonly="type !== 'duration' && type !== 'cycle'"
@focus="handleInputFocus"
@blur="updateNode"
>
<template #suffix>
<el-tooltip v-if="!valid" content="格式错误" placement="top">
<el-icon color="orange"><WarningFilled /></el-icon>
</el-tooltip>
<el-tooltip :content="helpText" placement="top">
<el-icon color="#409EFF" style="cursor: pointer" @click="showHelp = true"
><QuestionFilled
/></el-icon>
</el-tooltip>
<el-button
v-if="type === 'time'"
@click="showDatePicker = true"
style="margin-left: 4px"
circle
size="small"
>
<Icon icon="ep:calendar" />
</el-button>
<el-button
v-if="type === 'duration'"
@click="showDurationDialog = true"
style="margin-left: 4px"
circle
size="small"
>
<Icon icon="ep:timer" />
</el-button>
<el-button
v-if="type === 'cycle'"
@click="showCycleDialog = true"
style="margin-left: 4px"
circle
size="small"
>
<Icon icon="ep:setting" />
</el-button>
</template>
</el-input>
</div>
<!-- 时间选择器 -->
<el-dialog
v-model="showDatePicker"
title="选择时间"
width="400px"
@close="showDatePicker = false"
>
<el-date-picker
v-model="dateValue"
type="datetime"
placeholder="选择日期时间"
style="width: 100%"
@change="onDateChange"
/>
<template #footer>
<el-button @click="showDatePicker = false">取消</el-button>
<el-button type="primary" @click="onDateConfirm"></el-button>
</template>
</el-dialog>
<!-- 持续时长选择器 -->
<el-dialog
v-model="showDurationDialog"
title="时间配置"
width="600px"
@close="showDurationDialog = false"
>
<DurationConfig :value="condition" @change="onDurationChange" />
<template #footer>
<el-button @click="showDurationDialog = false">取消</el-button>
<el-button type="primary" @click="onDurationConfirm"></el-button>
</template>
</el-dialog>
<!-- 循环配置器 -->
<el-dialog
v-model="showCycleDialog"
title="时间配置"
width="800px"
@close="showCycleDialog = false"
>
<CycleConfig :value="condition" @change="onCycleChange" />
<template #footer>
<el-button @click="showCycleDialog = false">取消</el-button>
<el-button type="primary" @click="onCycleConfirm"></el-button>
</template>
</el-dialog>
<!-- 帮助说明 -->
<el-dialog v-model="showHelp" title="格式说明" width="600px" @close="showHelp = false">
<div v-html="helpHtml"></div>
<template #footer>
<el-button @click="showHelp = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { CircleCheckFilled, WarningFilled, QuestionFilled } from '@element-plus/icons-vue'
import DurationConfig from './DurationConfig.vue'
import CycleConfig from './CycleConfig.vue'
import { createListenerObject, updateElementExtensions } from '../../utils'
const bpmnInstances = () => (window as any).bpmnInstances
const props = defineProps({ businessObject: Object })
const type = ref('time')
const condition = ref('')
const valid = ref(true)
const showDatePicker = ref(false)
const showDurationDialog = ref(false)
const showCycleDialog = ref(false)
const showHelp = ref(false)
const dateValue = ref(null)
const bpmnElement = ref(null)
const placeholder = computed(() => {
if (type.value === 'time') return '请输入时间'
if (type.value === 'duration') return '请输入持续时长'
if (type.value === 'cycle') return '请输入循环表达式'
return ''
})
const helpText = computed(() => {
if (type.value === 'time') return '选择具体时间'
if (type.value === 'duration') return 'ISO 8601格式如PT1H'
if (type.value === 'cycle') return 'CRON表达式或ISO 8601周期'
return ''
})
const helpHtml = computed(() => {
if (type.value === 'duration') {
return `指定定时器之前要等待多长时间。S表示秒M表示分D表示天P表示时间段T表示精确到时间的时间段。<br>
时间格式依然为ISO 8601格式一年两个月三天四小时五分六秒内可以写成P1Y2M3DT4H5M6S<br>
P是开始标记T是时间和日期分割标记没有日期只有时间T是不能省去的比如1小时执行一次应写成PT1H`
}
if (type.value === 'cycle') {
return `支持CRON表达式如0 0/30 * * * ?或ISO 8601周期如R3/PT10M`
}
return ''
})
//
function syncFromBusinessObject() {
if (props.businessObject) {
const timerDef = (props.businessObject.eventDefinitions || [])[0]
if (timerDef) {
if (timerDef.timeDate) {
type.value = 'time'
condition.value = timerDef.timeDate.body
} else if (timerDef.timeDuration) {
type.value = 'duration'
condition.value = timerDef.timeDuration.body
} else if (timerDef.timeCycle) {
type.value = 'cycle'
condition.value = timerDef.timeCycle.body
}
}
}
}
onMounted(syncFromBusinessObject)
//
function setType(t) {
type.value = t
condition.value = ''
updateNode()
}
//
watch([type, condition], () => {
valid.value = validate()
// updateNode() //
})
function validate() {
if (type.value === 'time') {
return !!condition.value && !isNaN(Date.parse(condition.value))
}
if (type.value === 'duration') {
return /^P.*$/.test(condition.value)
}
if (type.value === 'cycle') {
return /^([0-9*\/?, ]+|R\d*\/P.*)$/.test(condition.value)
}
return true
}
//
function onDateChange(val) {
dateValue.value = val
}
function onDateConfirm() {
if (dateValue.value) {
condition.value = new Date(dateValue.value).toISOString()
showDatePicker.value = false
updateNode()
}
}
//
function onDurationChange(val) {
condition.value = val
}
function onDurationConfirm() {
showDurationDialog.value = false
updateNode()
}
//
function onCycleChange(val) {
condition.value = val
}
function onCycleConfirm() {
showCycleDialog.value = false
updateNode()
}
//
function handleInputFocus() {
if (type.value === 'time') showDatePicker.value = true
if (type.value === 'duration') showDurationDialog.value = true
if (type.value === 'cycle') showCycleDialog.value = true
}
//
function updateNode() {
const moddle = window.bpmnInstances?.moddle
const modeling = window.bpmnInstances?.modeling
const elementRegistry = window.bpmnInstances?.elementRegistry
if (!moddle || !modeling || !elementRegistry) return
//
if (!props.businessObject || !props.businessObject.id) return
const element = elementRegistry.get(props.businessObject.id)
if (!element) return
// 1. timerDef
let timerDef =
element.businessObject.eventDefinitions && element.businessObject.eventDefinitions[0]
if (!timerDef) {
timerDef = bpmnInstances().bpmnFactory.create('bpmn:TimerEventDefinition', {})
modeling.updateProperties(element, {
eventDefinitions: [timerDef]
})
}
// 2.
delete timerDef.timeDate
delete timerDef.timeDuration
delete timerDef.timeCycle
// 3.
if (type.value === 'time' && condition.value) {
timerDef.timeDate = bpmnInstances().bpmnFactory.create('bpmn:FormalExpression', {
body: condition.value
})
} else if (type.value === 'duration' && condition.value) {
timerDef.timeDuration = bpmnInstances().bpmnFactory.create('bpmn:FormalExpression', {
body: condition.value
})
} else if (type.value === 'cycle' && condition.value) {
timerDef.timeCycle = bpmnInstances().bpmnFactory.create('bpmn:FormalExpression', {
body: condition.value
})
}
bpmnInstances().modeling.updateProperties(toRaw(element), {
eventDefinitions: [timerDef]
})
}
watch(
() => props.businessObject,
(val) => {
if (val) {
nextTick(() => {
syncFromBusinessObject()
})
}
},
{ immediate: true }
)
</script>
<style scoped>
/* 相关样式 */
</style>

View File

@ -55,6 +55,7 @@ import {
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem,
ElCard, ElCard,
ElTreeSelect
// ElFormItem, // ElFormItem,
// ElOption // ElOption
} from 'element-plus' } from 'element-plus'
@ -97,6 +98,7 @@ const components = [
ElTableColumn, ElTableColumn,
ElTabPane, ElTabPane,
ElTabs, ElTabs,
ElTreeSelect,
ElDropdown, ElDropdown,
ElDropdownMenu, ElDropdownMenu,
ElDropdownItem, ElDropdownItem,
@ -119,7 +121,7 @@ const components = [
Editor, Editor,
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem,
ElCard, ElCard
] ]
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档 // 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档

View File

@ -12,7 +12,7 @@ const router = createRouter({
}) })
export const resetRouter = (): void => { export const resetRouter = (): void => {
const resetWhiteNameList = ['Redirect', 'Login', 'NoFind', 'Root'] const resetWhiteNameList = ['Redirect', 'Login', 'NoFound', 'Home']
router.getRoutes().forEach((route) => { router.getRoutes().forEach((route) => {
const { name } = route const { name } = route
if (name && !resetWhiteNameList.includes(name as string)) { if (name && !resetWhiteNameList.includes(name as string)) {

View File

@ -1,11 +1,12 @@
import { defineStore } from 'pinia'
import { store } from '../index'
import { humpToUnderline, setCssVar } from '@/utils'
import { ElMessage } from 'element-plus'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { ElementPlusSize } from '@/types/elementPlus' import { ElementPlusSize } from '@/types/elementPlus'
import { LayoutType } from '@/types/layout' import { LayoutType } from '@/types/layout'
import { ThemeTypes } from '@/types/theme' import { ThemeTypes } from '@/types/theme'
import { humpToUnderline, setCssVar } from '@/utils'
import { getCssColorVariable, hexToRGB, mix } from '@/utils/color'
import { ElMessage } from 'element-plus'
import { defineStore } from 'pinia'
import { store } from '../index'
const { wsCache } = useCache() const { wsCache } = useCache()
@ -183,6 +184,40 @@ export const useAppStore = defineStore('app', {
} }
}, },
actions: { actions: {
setPrimaryLight() {
if (this.theme.elColorPrimary) {
const elColorPrimary = this.theme.elColorPrimary
const color = this.isDark ? '#000000' : '#ffffff'
const lightList = [3, 5, 7, 8, 9]
lightList.forEach((v) => {
setCssVar(`--el-color-primary-light-${v}`, mix(color, elColorPrimary, v / 10))
})
setCssVar(`--el-color-primary-dark-2`, mix(color, elColorPrimary, 0.2))
this.setAllColorRgbVars()
}
},
// 处理element自带的主题色和辅助色的-rgb切换主题变化--el-color-primary-rgb
setAllColorRgbVars() {
// 需要处理的颜色类型列表
const colorTypes = ['primary', 'success', 'warning', 'danger', 'error', 'info']
colorTypes.forEach((type) => {
// 获取当前颜色值
const colorValue = getCssColorVariable(`--el-color-${type}`)
if (colorValue) {
// 转换为rgba并提取RGB部分
const rgbaString = hexToRGB(colorValue, 1)
const rgbValues = rgbaString.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i)
if (rgbValues) {
const [, r, g, b] = rgbValues
// 设置对应的RGB变量
setCssVar(`--el-color-${type}-rgb`, `${r}, ${g}, ${b}`)
}
}
})
},
setBreadcrumb(breadcrumb: boolean) { setBreadcrumb(breadcrumb: boolean) {
this.breadcrumb = breadcrumb this.breadcrumb = breadcrumb
}, },
@ -256,6 +291,7 @@ export const useAppStore = defineStore('app', {
document.documentElement.classList.remove('dark') document.documentElement.classList.remove('dark')
} }
wsCache.set(CACHE_KEY.IS_DARK, this.isDark) wsCache.set(CACHE_KEY.IS_DARK, this.isDark)
this.setPrimaryLight()
}, },
setCurrentSize(currentSize: ElementPlusSize) { setCurrentSize(currentSize: ElementPlusSize) {
this.currentSize = currentSize this.currentSize = currentSize
@ -272,6 +308,7 @@ export const useAppStore = defineStore('app', {
for (const key in this.theme) { for (const key in this.theme) {
setCssVar(`--${humpToUnderline(key)}`, this.theme[key]) setCssVar(`--${humpToUnderline(key)}`, this.theme[key])
} }
this.setPrimaryLight()
}, },
setFooter(footer: boolean) { setFooter(footer: boolean) {
this.footer = footer this.footer = footer

View File

@ -62,7 +62,7 @@ export const useUserStore = defineStore('admin-user', {
userInfo = await getInfo() userInfo = await getInfo()
} catch (error) {} } catch (error) {}
} }
this.permissions = new Set(userInfo.permissions) this.permissions = new Set(userInfo.permissions || []) // 兜底为 [] https://t.zsxq.com/xCJew
this.roles = userInfo.roles this.roles = userInfo.roles
this.user = userInfo.user this.user = userInfo.user
this.isSetUser = true this.isSetUser = true

View File

@ -172,3 +172,46 @@ export const PREDEFINE_COLORS = [
'#1f73c3', '#1f73c3',
'#711f57' '#711f57'
] ]
/**
* Mixes two colors.
*
* @param {string} color1 - The first color, should be a 6-digit hexadecimal color code starting with `#`.
* @param {string} color2 - The second color, should be a 6-digit hexadecimal color code starting with `#`.
* @param {number} [weight=0.5] - The weight of color1 in the mix, should be a number between 0 and 1, where 0 represents 100% of color2, and 1 represents 100% of color1.
* @returns {string} The mixed color, a 6-digit hexadecimal color code starting with `#`.
*/
export const mix = (color1: string, color2: string, weight: number = 0.5): string => {
let color = '#'
for (let i = 0; i <= 2; i++) {
const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16)
const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16)
const c = Math.round(c1 * weight + c2 * (1 - weight))
color += c.toString(16).padStart(2, '0')
}
return color
}
/**
* getCssColorVariable
* @description css
* @param colorVariable css
* @param opacity
* @returns {string}
* @example getCssColorVariable('--el-color-primary', 0.5)
* @example getCssColorVariable('--el-color-primary')
* @example getCssColorVariable()
*/
export const getCssColorVariable = (
colorVariable: string = '--el-color-primary',
opacity?: number
) => {
const colorValue = getComputedStyle(document.documentElement)
.getPropertyValue(colorVariable)
.trim()
if (colorValue) {
return opacity ? hexToRGB(colorValue, opacity) : colorValue
}
return ''
}

View File

@ -1,6 +1,7 @@
/** /**
* https://github.com/xaboy/form-create-designer 封装的工具类 * https://github.com/xaboy/form-create-designer 封装的工具类
*/ */
import { isRef } from 'vue'
// 编码表单 Conf // 编码表单 Conf
export const encodeConf = (designerRef: object) => { export const encodeConf = (designerRef: object) => {
@ -47,10 +48,156 @@ export const setConfAndFields2 = (
// @ts-ignore // @ts-ignore
detailPreview = detailPreview.value detailPreview = detailPreview.value
} }
// 修复所有函数类型(解决设计器保存后函数变成字符串的问题)。例如说:
// https://t.zsxq.com/rADff
// https://t.zsxq.com/ZfbGt
// https://t.zsxq.com/mHOoj
// https://t.zsxq.com/BSylB
const option = JSON.parse(conf)
const rule = decodeFields(fields)
// 🔧 修复所有函数类型 - 解决设计器保存后函数变成字符串的问题
const fixFunctions = (obj: any) => {
if (obj && typeof obj === 'object') {
Object.keys(obj).forEach((key) => {
// 检查是否是函数相关的属性
if (isFunctionProperty(key)) {
// 如果不是函数类型,重新构建为函数
if (typeof obj[key] !== 'function') {
obj[key] = createDefaultFunction(key)
}
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
// 递归处理嵌套对象
fixFunctions(obj[key])
}
})
}
}
// 判断是否是函数属性
const isFunctionProperty = (key: string): boolean => {
const functionKeys = [
'beforeFetch', // 请求前处理
'afterFetch', // 请求后处理
'onSubmit', // 表单提交
'onReset', // 表单重置
'onChange', // 值变化
'onInput', // 输入事件
'onClick', // 点击事件
'onFocus', // 获取焦点
'onBlur', // 失去焦点
'onMounted', // 组件挂载
'onCreated', // 组件创建
'onReload', // 重新加载
'remoteMethod', // 远程搜索方法
'parseFunc', // 解析函数
'validator', // 验证器
'asyncValidator', // 异步验证器
'formatter', // 格式化函数
'parser', // 解析函数
'beforeUpload', // 上传前处理
'onSuccess', // 成功回调
'onError', // 错误回调
'onProgress', // 进度回调
'onPreview', // 预览回调
'onRemove', // 移除回调
'onExceed', // 超出限制回调
'filterMethod', // 过滤方法
'sortMethod', // 排序方法
'loadData', // 加载数据
'renderContent', // 渲染内容
'render' // 渲染函数
]
// 检查是否以函数相关前缀开头
const functionPrefixes = ['on', 'before', 'after', 'handle']
return functionKeys.includes(key) || functionPrefixes.some((prefix) => key.startsWith(prefix))
}
// 根据函数名创建默认函数
const createDefaultFunction = (key: string): Function => {
switch (key) {
case 'beforeFetch':
return (config: any) => {
// 添加 Token 认证头。例如说:
// https://t.zsxq.com/hK3FO
const token = localStorage.getItem('token')
if (token) {
config.headers = {
...config.headers,
Authorization: 'Bearer ' + token
}
}
// 添加通用请求头
config.headers = {
...config.headers,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
// 添加时间戳防止缓存
config.params = {
...config.params,
_t: Date.now()
}
return config
}
case 'afterFetch':
return (data: any) => {
return data
}
case 'onSubmit':
return (_formData: any) => {
return true
}
case 'onReset':
return () => {
return true
}
case 'onChange':
return (_value: any, _oldValue: any) => {}
case 'remoteMethod':
return (query: string) => {
console.log('remoteMethod被调用:', query)
}
case 'parseFunc':
return (data: any) => {
// 默认解析逻辑如果是数组直接返回否则尝试获取list属性
if (Array.isArray(data)) {
return data
}
return data?.list || data?.data || []
}
case 'validator':
return (_rule: any, _value: any, callback: Function) => {
callback()
}
case 'beforeUpload':
return (_file: any) => {
return true
}
default:
// 通用默认函数
return (...args: any[]) => {
// 对于事件处理函数返回true表示继续执行
if (key.startsWith('on') || key.startsWith('handle')) {
return true
}
// 对于其他函数,返回第一个参数(通常是数据传递)
return args[0]
}
}
}
// 修复 option 中的所有函数
fixFunctions(option)
// 修复 rule 中的所有函数(包括组件的 props
if (Array.isArray(rule)) {
rule.forEach((item: any) => {
fixFunctions(item)
})
}
// @ts-ignore // @ts-ignore
detailPreview.option = JSON.parse(conf) detailPreview.option = option
// @ts-ignore // @ts-ignore
detailPreview.rule = decodeFields(fields) detailPreview.rule = rule
if (value) { if (value) {
// @ts-ignore // @ts-ignore
detailPreview.value = value detailPreview.value = value

View File

@ -101,7 +101,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
if (!route.children && route.parentId == 0 && route.component) { if (!route.children && route.parentId == 0 && route.component) {
data.component = Layout data.component = Layout
data.meta = { data.meta = {
hidden: meta.hidden, hidden: meta.hidden
} }
data.name = toCamelCase(route.path, true) + 'Parent' data.name = toCamelCase(route.path, true) + 'Parent'
data.redirect = '' data.redirect = ''
@ -170,8 +170,9 @@ const generateRoutePath = (parentPath: string, path: string) => {
} }
export const pathResolve = (parentPath: string, path: string) => { export const pathResolve = (parentPath: string, path: string) => {
if (isUrl(path)) return path if (isUrl(path)) return path
const childPath = path.startsWith('/') || !path ? path : `/${path}` if (!path) return parentPath // 修复 path 为空时返回 parentPath避免拼接出错 https://t.zsxq.com/QVr6b
return `${parentPath}${childPath}`.replace(/\/\//g, '/') const childPath = path.startsWith('/') ? path : `/${path}`
return `${parentPath}${childPath}`.replace(/\/+/g, '/')
} }
// 路由降级 // 路由降级

View File

@ -89,7 +89,12 @@
@click="handleProjectClick(item.message)" @click="handleProjectClick(item.message)"
> >
<div class="flex items-center"> <div class="flex items-center">
<Icon :icon="item.icon" :size="25" class="mr-8px" :style="{ color: item.color }" /> <Icon
:icon="item.icon"
:size="25"
class="mr-8px"
:style="{ color: item.color }"
/>
<span class="text-16px">{{ item.name }}</span> <span class="text-16px">{{ item.name }}</span>
</div> </div>
<div class="mt-12px text-12px text-gray-400">{{ t(item.message) }}</div> <div class="mt-12px text-12px text-gray-400">{{ t(item.message) }}</div>
@ -181,17 +186,17 @@ import { EChartsOption } from 'echarts'
import { formatTime } from '@/utils' import { formatTime } from '@/utils'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useWatermark } from '@/hooks/web/useWatermark' // import { useWatermark } from '@/hooks/web/useWatermark'
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types' import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
import { pieOptions, barOptions } from './echarts-data' import { pieOptions, barOptions } from './echarts-data'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
defineOptions({ name: 'Home' }) defineOptions({ name: 'Index' })
const { t } = useI18n() const { t } = useI18n()
const router = useRouter() const router = useRouter()
const userStore = useUserStore() const userStore = useUserStore()
const { setWatermark } = useWatermark() // const { setWatermark } = useWatermark()
const loading = ref(true) const loading = ref(true)
const avatar = userStore.getUser.avatar const avatar = userStore.getUser.avatar
const username = userStore.getUser.nickname const username = userStore.getUser.nickname

View File

@ -9,14 +9,14 @@
label-width="120px" label-width="120px"
size="large" size="large"
> >
<el-row style="margin-right: -10px; margin-left: -10px"> <el-row class="mx-[-10px]">
<!-- 租户名 --> <!-- 租户名 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item v-if="resetPasswordData.tenantEnable === 'true'" prop="tenantName"> <el-form-item v-if="resetPasswordData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
v-model="resetPasswordData.tenantName" v-model="resetPasswordData.tenantName"
@ -28,7 +28,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 手机号 --> <!-- 手机号 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="mobile"> <el-form-item prop="mobile">
<el-input <el-input
v-model="resetPasswordData.mobile" v-model="resetPasswordData.mobile"
@ -45,7 +45,7 @@
@success="getSmsCode" @success="getSmsCode"
/> />
<!-- 验证码 --> <!-- 验证码 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="code"> <el-form-item prop="code">
<el-row :gutter="5" justify="space-between" style="width: 100%"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="24"> <el-col :span="24">
@ -73,44 +73,44 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="password"> <el-form-item prop="password">
<InputPassword <InputPassword
v-model="resetPasswordData.password" v-model="resetPasswordData.password"
:placeholder="t('login.passwordPlaceholder')" :placeholder="t('login.passwordPlaceholder')"
style="width: 100%" class="w-full"
strength="true" :strength="true"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="check_password"> <el-form-item prop="check_password">
<InputPassword <InputPassword
v-model="resetPasswordData.check_password" v-model="resetPasswordData.check_password"
:placeholder="t('login.checkPassword')" :placeholder="t('login.checkPassword')"
style="width: 100%" class="w-full"
strength="true" :strength="true"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 登录按钮 / 返回按钮 --> <!-- 登录按钮 / 返回按钮 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.resetPassword')" :title="t('login.resetPassword')"
class="w-[100%]" class="w-full"
type="primary" type="primary"
@click="resetPassword()" @click="resetPassword()"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.backLogin')" :title="t('login.backLogin')"
class="w-[100%]" class="w-full"
@click="handleBackLogin()" @click="handleBackLogin()"
/> />
</el-form-item> </el-form-item>
@ -134,7 +134,7 @@ const verify = ref()
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()
const { currentRoute, push } = useRouter() const { currentRoute } = useRouter()
const formSmsResetPassword = ref() const formSmsResetPassword = ref()
const loginLoading = ref(false) const loginLoading = ref(false)
const iconHouse = useIcon({ icon: 'ep:house' }) const iconHouse = useIcon({ icon: 'ep:house' })
@ -145,7 +145,7 @@ const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD) const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
const captchaType = ref('blockPuzzle') // blockPuzzle clickWord const captchaType = ref('blockPuzzle') // blockPuzzle clickWord
const validatePass2 = (rule, value, callback) => { const validatePass2 = (_rule, value, callback) => {
if (value === '') { if (value === '') {
callback(new Error('请再次输入密码')) callback(new Error('请再次输入密码'))
} else if (value !== resetPasswordData.password) { } else if (value !== resetPasswordData.password) {

View File

@ -9,13 +9,13 @@
label-width="120px" label-width="120px"
size="large" size="large"
> >
<el-row style="margin-right: -10px; margin-left: -10px"> <el-row class="mx-[-10px]">
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName"> <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
@ -26,7 +26,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="loginData.loginForm.username" v-model="loginData.loginForm.username"
@ -35,7 +35,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="loginData.loginForm.password" v-model="loginData.loginForm.password"
@ -49,7 +49,7 @@
</el-col> </el-col>
<el-col <el-col
:span="24" :span="24"
style="padding-right: 10px; padding-left: 10px; margin-top: -20px; margin-bottom: -20px" class="px-10px mt-[-20px] mb-[-20px]"
> >
<el-form-item> <el-form-item>
<el-row justify="space-between" style="width: 100%"> <el-row justify="space-between" style="width: 100%">
@ -60,7 +60,7 @@
</el-col> </el-col>
<el-col :offset="6" :span="12"> <el-col :offset="6" :span="12">
<el-link <el-link
style="float: right" class="float-right"
type="primary" type="primary"
@click="setLoginState(LoginStateEnum.RESET_PASSWORD)" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"
> >
@ -70,12 +70,12 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.login')" :title="t('login.login')"
class="w-[100%]" class="w-full"
type="primary" type="primary"
@click="getCode()" @click="getCode()"
/> />
@ -89,27 +89,27 @@
mode="pop" mode="pop"
@success="handleLogin" @success="handleLogin"
/> />
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<el-row :gutter="5" justify="space-between" style="width: 100%"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
:title="t('login.btnMobile')" :title="t('login.btnMobile')"
class="w-[100%]" class="w-full"
@click="setLoginState(LoginStateEnum.MOBILE)" @click="setLoginState(LoginStateEnum.MOBILE)"
/> />
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
:title="t('login.btnQRCode')" :title="t('login.btnQRCode')"
class="w-[100%]" class="w-full"
@click="setLoginState(LoginStateEnum.QR_CODE)" @click="setLoginState(LoginStateEnum.QR_CODE)"
/> />
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
:title="t('login.btnRegister')" :title="t('login.btnRegister')"
class="w-[100%]" class="w-full"
@click="setLoginState(LoginStateEnum.REGISTER)" @click="setLoginState(LoginStateEnum.REGISTER)"
/> />
</el-col> </el-col>
@ -117,9 +117,9 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider> <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<div class="w-[100%] flex justify-between"> <div class="w-full flex justify-between">
<Icon <Icon
v-for="(item, key) in socialList" v-for="(item, key) in socialList"
:key="key" :key="key"
@ -133,9 +133,9 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-divider content-position="center">萌新必读</el-divider> <el-divider content-position="center">萌新必读</el-divider>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<div class="w-[100%] flex justify-between"> <div class="w-full flex justify-between">
<el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link> <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
<el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link> <el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
<el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank"> <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
@ -239,12 +239,14 @@ const getLoginFormCache = () => {
} }
// //
const getTenantByWebsite = async () => { const getTenantByWebsite = async () => {
if (loginData.tenantEnable === 'true') {
const website = location.host const website = location.host
const res = await LoginApi.getTenantByWebsite(website) const res = await LoginApi.getTenantByWebsite(website)
if (res) { if (res) {
loginData.loginForm.tenantName = res.name loginData.loginForm.tenantName = res.name
authUtil.setTenantId(res.id) authUtil.setTenantId(res.id)
} }
}
} }
const loading = ref() // ElLoading.service const loading = ref() // ElLoading.service
// //

View File

@ -9,14 +9,14 @@
label-width="120px" label-width="120px"
size="large" size="large"
> >
<el-row style="margin-right: -10px; margin-left: -10px"> <el-row class="mx-[-10px]">
<!-- 租户名 --> <!-- 租户名 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName"> <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
@ -28,7 +28,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 手机号 --> <!-- 手机号 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="mobileNumber"> <el-form-item prop="mobileNumber">
<el-input <el-input
v-model="loginData.loginForm.mobileNumber" v-model="loginData.loginForm.mobileNumber"
@ -38,7 +38,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 验证码 --> <!-- 验证码 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="code"> <el-form-item prop="code">
<el-row :gutter="5" justify="space-between" style="width: 100%"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="24"> <el-col :span="24">
@ -68,23 +68,23 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 登录按钮 / 返回按钮 --> <!-- 登录按钮 / 返回按钮 -->
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.login')" :title="t('login.login')"
class="w-[100%]" class="w-full"
type="primary" type="primary"
@click="signIn()" @click="signIn()"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.backLogin')" :title="t('login.backLogin')"
class="w-[100%]" class="w-full"
@click="handleBackLogin()" @click="handleBackLogin()"
/> />
</el-form-item> </el-form-item>

View File

@ -1,17 +1,17 @@
<template> <template>
<el-row v-show="getShow" class="login-form" style="margin-right: -10px; margin-left: -10px"> <el-row v-show="getShow" class="login-form mx-[-10px]">
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-card class="mb-10px text-center" shadow="hover"> <el-card class="mb-10px text-center" shadow="hover">
<Qrcode :logo="logoImg" /> <Qrcode :logo="logoImg" />
</el-card> </el-card>
</el-col> </el-col>
<el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider> <el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<div class="mt-15px w-[100%]"> <div class="mt-4 w-full">
<XButton :title="t('login.backLogin')" class="w-[100%]" @click="handleBackLogin()" /> <XButton :title="t('login.backLogin')" class="w-full" @click="handleBackLogin()" />
</div> </div>
</el-col> </el-col>
</el-row> </el-row>

View File

@ -9,13 +9,13 @@
label-width="120px" label-width="120px"
size="large" size="large"
> >
<el-row style="margin-right: -10px; margin-left: -10px"> <el-row class="mx-[-10px]">
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item v-if="registerData.tenantEnable === 'true'" prop="tenantName"> <el-form-item v-if="registerData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
v-model="registerData.registerForm.tenantName" v-model="registerData.registerForm.tenantName"
@ -27,7 +27,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="registerData.registerForm.username" v-model="registerData.registerForm.username"
@ -37,7 +37,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="registerData.registerForm.nickname" v-model="registerData.registerForm.nickname"
@ -47,7 +47,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="registerData.registerForm.password" v-model="registerData.registerForm.password"
@ -60,7 +60,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item prop="confirmPassword"> <el-form-item prop="confirmPassword">
<el-input <el-input
v-model="registerData.registerForm.confirmPassword" v-model="registerData.registerForm.confirmPassword"
@ -73,12 +73,12 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" class="px-10px">
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
:title="t('login.register')" :title="t('login.register')"
class="w-[100%]" class="w-full"
type="primary" type="primary"
@click="getCode()" @click="getCode()"
/> />
@ -93,7 +93,7 @@
@success="handleRegister" @success="handleRegister"
/> />
</el-row> </el-row>
<XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" /> <XButton :title="t('login.hasUser')" class="w-full" @click="handleBackLogin()" />
</el-form> </el-form>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -123,7 +123,7 @@ const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER) const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
const equalToPassword = (rule, value, callback) => { const equalToPassword = (_rule, value, callback) => {
if (registerData.registerForm.password !== value) { if (registerData.registerForm.password !== value) {
callback(new Error('两次输入的密码不一致')) callback(new Error('两次输入的密码不一致'))
} else { } else {
@ -233,12 +233,14 @@ const getTenantId = async () => {
// //
const getTenantByWebsite = async () => { const getTenantByWebsite = async () => {
if (registerData.tenantEnable === 'true') {
const website = location.host const website = location.host
const res = await LoginApi.getTenantByWebsite(website) const res = await LoginApi.getTenantByWebsite(website)
if (res) { if (res) {
registerData.registerForm.tenantName = res.name registerData.registerForm.tenantName = res.name
authUtil.setTenantId(res.id) authUtil.setTenantId(res.id)
} }
}
} }
const loading = ref() // ElLoading.service const loading = ref() // ElLoading.service

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-show="ssoVisible" class="form-cont"> <div v-show="ssoVisible" class="form-cont">
<!-- 应用名 --> <!-- 应用名 -->
<LoginFormTitle style="width: 100%" /> <LoginFormTitle class="w-full" />
<el-tabs class="form" style="float: none" value="uname"> <el-tabs class="form" style="float: none" value="uname">
<el-tab-pane :label="client.name" name="uname" /> <el-tab-pane :label="client.name" name="uname" />
</el-tabs> </el-tabs>
@ -15,17 +15,17 @@
v-for="scope in queryParams.scopes" v-for="scope in queryParams.scopes"
:key="scope" :key="scope"
:value="scope" :value="scope"
style="display: block; margin-bottom: -10px" class="block mb-[-10px]"
> >
{{ formatScope(scope) }} {{ formatScope(scope) }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
<!-- 下方的登录按钮 --> <!-- 下方的登录按钮 -->
<el-form-item class="w-1/1"> <el-form-item class="w-full">
<el-button <el-button
:loading="formLoading" :loading="formLoading"
class="w-6/10" class="w-3/5"
type="primary" type="primary"
@click.prevent="handleAuthorize(true)" @click.prevent="handleAuthorize(true)"
> >

View File

@ -18,7 +18,6 @@ import { useUserStore } from '@/store/modules/user'
import { useUpload } from '@/components/UploadFile/src/useUpload' import { useUpload } from '@/components/UploadFile/src/useUpload'
import { UploadRequestOptions } from 'element-plus/es/components/upload/src/upload' import { UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
// TODO @ ProfileUser
defineOptions({ name: 'UserAvatar' }) defineOptions({ name: 'UserAvatar' })
defineProps({ defineProps({
@ -30,10 +29,12 @@ const userStore = useUserStore()
const cropperRef = ref() const cropperRef = ref()
const handelUpload = async ({ data }) => { const handelUpload = async ({ data }) => {
const { httpRequest } = useUpload() const { httpRequest } = useUpload()
const avatar = ((await httpRequest({ const avatar = (
(await httpRequest({
file: data, file: data,
filename: 'avatar.png', filename: 'avatar.png'
} as UploadRequestOptions)) as unknown as { data: string }).data } as UploadRequestOptions)) as unknown as { data: string }
).data
await updateUserProfile({ avatar }) await updateUserProfile({ avatar })
// userStore // userStore

View File

@ -23,7 +23,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { SystemUserSocialTypeEnum } from '@/utils/constants' import { SystemUserSocialTypeEnum } from '@/utils/constants'
import { getUserProfile, ProfileVO } from '@/api/system/user/profile' import { getBindSocialUserList } from '@/api/system/social/user'
import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser' import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser'
defineOptions({ name: 'UserSocial' }) defineOptions({ name: 'UserSocial' })
@ -32,19 +32,19 @@ defineProps<{
}>() }>()
const message = useMessage() const message = useMessage()
const socialUsers = ref<any[]>([]) const socialUsers = ref<any[]>([])
const userInfo = ref<ProfileVO>()
const initSocial = async () => { const initSocial = async () => {
socialUsers.value = [] // socialUsers.value = [] //
const res = await getUserProfile() //
userInfo.value = res const bindSocialUserList = await getBindSocialUserList()
//
for (const i in SystemUserSocialTypeEnum) { for (const i in SystemUserSocialTypeEnum) {
const socialUser = { ...SystemUserSocialTypeEnum[i] } const socialUser = { ...SystemUserSocialTypeEnum[i] }
socialUsers.value.push(socialUser) socialUsers.value.push(socialUser)
if (userInfo.value?.socialUsers) { if (bindSocialUserList && bindSocialUserList.length > 0) {
for (const j in userInfo.value.socialUsers) { for (const bindUser of bindSocialUserList) {
if (socialUser.type === userInfo.value.socialUsers[j].type) { if (socialUser.type === bindUser.type) {
socialUser.openid = userInfo.value.socialUsers[j].openid socialUser.openid = bindUser.openid
break break
} }
} }

View File

@ -391,7 +391,8 @@ onMounted(async () => {
line-height: 30px; line-height: 30px;
&.active { &.active {
background-color: #e6e6e6; background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-7);
.button { .button {
display: inline-block; display: inline-block;
@ -409,7 +410,7 @@ onMounted(async () => {
max-width: 220px; max-width: 220px;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
color: rgba(0, 0, 0, 0.77); color: var(--el-text-color-regular);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -430,7 +431,7 @@ onMounted(async () => {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-items: center; justify-items: center;
color: #606266; color: var(--el-text-color-regular);
.btn { .btn {
margin: 0; margin: 0;
@ -447,8 +448,8 @@ onMounted(async () => {
right: 0; right: 0;
//width: 100%; //width: 100%;
padding: 0 20px; padding: 0 20px;
background-color: #f4f4f4; background-color: var(--el-fill-color-extra-light);
box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8); box-shadow: 0 0 1px 1px var(--el-border-color-lighter);
line-height: 35px; line-height: 35px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -458,7 +459,7 @@ onMounted(async () => {
> div { > div {
display: flex; display: flex;
align-items: center; align-items: center;
color: #606266; color: var(--el-text-color-regular);
padding: 0; padding: 0;
margin: 0; margin: 0;
cursor: pointer; cursor: pointer;

View File

@ -215,13 +215,13 @@ onMounted(async () => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-wrap: break-word; overflow-wrap: break-word;
background-color: rgba(228, 228, 228, 0.8); background-color: var(--el-fill-color-light);
box-shadow: 0 0 0 1px rgba(228, 228, 228, 0.8); box-shadow: 0 0 0 1px var(--el-border-color-light);
border-radius: 10px; border-radius: 10px;
padding: 10px 10px 5px 10px; padding: 10px 10px 5px 10px;
.left-text { .left-text {
color: #393939; color: var(--el-text-color-primary);
font-size: 0.95rem; font-size: 0.95rem;
} }
} }
@ -232,10 +232,10 @@ onMounted(async () => {
.right-text { .right-text {
font-size: 0.95rem; font-size: 0.95rem;
color: #fff; color: var(--el-color-white);
display: inline; display: inline;
background-color: #267fff; background-color: var(--el-color-primary);
box-shadow: 0 0 0 1px #267fff; box-shadow: 0 0 0 1px var(--el-color-primary);
border-radius: 10px; border-radius: 10px;
padding: 10px; padding: 10px;
width: auto; width: auto;
@ -270,7 +270,7 @@ onMounted(async () => {
.btn-cus:hover { .btn-cus:hover {
cursor: pointer; cursor: pointer;
background-color: #f6f6f6; background-color: var(--el-fill-color-lighter);
} }
} }

View File

@ -29,14 +29,14 @@ defineProps({
padding: 0 10px; padding: 0 10px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
background-color: #ececec; background-color: var(--el-bg-color-page);
width: 100%; width: 100%;
.title { .title {
font-size: 20px; font-size: 20px;
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
color: #3e3e3e; color: var(--el-text-color-primary);
max-width: 220px; max-width: 220px;
} }

View File

@ -13,10 +13,10 @@
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item :command="['edit', role]"> <el-dropdown-item :command="['edit', role]">
<Icon icon="ep:edit" color="#787878" />编辑 <Icon icon="ep:edit" color="var(--el-text-color-placeholder)" />编辑
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item :command="['delete', role]" style="color: red"> <el-dropdown-item :command="['delete', role]" style="color: var(--el-color-danger)">
<Icon icon="ep:delete" color="red" />删除 <Icon icon="ep:delete" color="var(--el-color-danger)" />删除
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@ -153,13 +153,13 @@ const handleTabsScroll = async () => {
.title { .title {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
color: #3e3e3e; color: var(--el-text-color-primary);
} }
.description { .description {
margin-top: 10px; margin-top: 10px;
font-size: 14px; font-size: 14px;
color: #6a6a6a; color: var(--el-text-color-regular);
} }
} }

View File

@ -23,7 +23,7 @@
@click="handlerAddRole" @click="handlerAddRole"
class="ml-20px" class="ml-20px"
> >
<Icon icon="ep:user" style="margin-right: 5px;" /> <Icon icon="ep:user" style="margin-right: 5px" />
添加角色 添加角色
</el-button> </el-button>
</div> </div>
@ -64,15 +64,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref} from 'vue' import { ref } from 'vue'
import RoleHeader from './RoleHeader.vue' import RoleHeader from './RoleHeader.vue'
import RoleList from './RoleList.vue' import RoleList from './RoleList.vue'
import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue' import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue'
import RoleCategoryList from './RoleCategoryList.vue' import RoleCategoryList from './RoleCategoryList.vue'
import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole' import { ChatRoleApi, ChatRolePageReqVO, ChatRoleVO } from '@/api/ai/model/chatRole'
import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
import {Search} from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import {TabsPaneContext} from 'element-plus' import { TabsPaneContext } from 'element-plus'
const router = useRouter() // const router = useRouter() //
@ -244,7 +244,7 @@ onMounted(async () => {
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
background-color: #ffffff; background-color: var(--el-bg-color);
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -22,13 +22,16 @@
<Icon icon="ep:setting" class="ml-10px" /> <Icon icon="ep:setting" class="ml-10px" />
</el-button> </el-button>
<el-button size="small" class="btn" @click="handlerMessageClear"> <el-button size="small" class="btn" @click="handlerMessageClear">
<Icon icon="heroicons-outline:archive-box-x-mark" color="#787878" /> <Icon
icon="heroicons-outline:archive-box-x-mark"
color="var(--el-text-color-placeholder)"
/>
</el-button> </el-button>
<el-button size="small" class="btn"> <el-button size="small" class="btn">
<Icon icon="ep:download" color="#787878" /> <Icon icon="ep:download" color="var(--el-text-color-placeholder)" />
</el-button> </el-button>
<el-button size="small" class="btn" @click="handleGoTopMessage"> <el-button size="small" class="btn" @click="handleGoTopMessage">
<Icon icon="ep:top" color="#787878" /> <Icon icon="ep:top" color="var(--el-text-color-placeholder)" />
</el-button> </el-button>
</div> </div>
</el-header> </el-header>
@ -613,7 +616,8 @@ onMounted(async () => {
line-height: 30px; line-height: 30px;
&.active { &.active {
background-color: #e6e6e6; background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-7);
.button { .button {
display: inline-block; display: inline-block;
@ -649,7 +653,7 @@ onMounted(async () => {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-items: center; justify-items: center;
color: #606266; color: var(--el-text-color-regular);
.el-icon { .el-icon {
margin-right: 5px; margin-right: 5px;
@ -669,7 +673,7 @@ onMounted(async () => {
> div { > div {
display: flex; display: flex;
align-items: center; align-items: center;
color: #606266; color: var(--el-text-color-regular);
padding: 0; padding: 0;
margin: 0; margin: 0;
cursor: pointer; cursor: pointer;
@ -683,15 +687,15 @@ onMounted(async () => {
// //
.detail-container { .detail-container {
background: #ffffff; background: var(--el-bg-color);
.header { .header {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
background: #fbfbfb; background: var(--el-bg-color-page);
box-shadow: 0 0 0 0 #dcdfe6; box-shadow: 0 0 0 0 var(--el-border-color-light);
.title { .title {
font-size: 18px; font-size: 18px;
@ -744,7 +748,7 @@ onMounted(async () => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: auto; height: auto;
border: 1px solid #e3e3e3; border: 1px solid var(--el-border-color);
border-radius: 10px; border-radius: 10px;
margin: 10px 20px 20px 20px; margin: 10px 20px 20px 20px;
padding: 9px 10px; padding: 9px 10px;

View File

@ -510,10 +510,15 @@ const isManagerUser = (row: any) => {
/** 处理模型的排序 **/ /** 处理模型的排序 **/
const handleModelSort = () => { const handleModelSort = () => {
if (isModelSorting.value) {
//
handleModelSortCancel()
} else {
// //
originalData.value = cloneDeep(props.categoryInfo.modelList) originalData.value = cloneDeep(props.categoryInfo.modelList)
isModelSorting.value = true isModelSorting.value = true
initSort() initSort()
}
} }
/** 处理模型的排序提交 */ /** 处理模型的排序提交 */

View File

@ -186,7 +186,23 @@ const currentSelectType = ref<'start' | 'manager'>('start')
const rules = { const rules = {
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }], key: [
{ required: true, message: '流程标识不能为空', trigger: 'blur' },
{
validator: (_rule: any, value: string, callback: any) => {
if (!value) {
callback()
return
}
if (!/^[a-zA-Z_][\-_.0-9_a-zA-Z$]*$/.test(value)) {
callback(new Error('只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头'))
return
}
callback()
},
trigger: 'blur'
}
],
category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }], category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],

View File

@ -219,6 +219,16 @@ const initData = async () => {
// //
if (route.params.type === 'copy') { if (route.params.type === 'copy') {
delete formData.value.id delete formData.value.id
if (formData.value.bpmnXml) {
formData.value.bpmnXml = formData.value.bpmnXml.replaceAll(
formData.value.name,
formData.value.name + '副本'
)
formData.value.bpmnXml = formData.value.bpmnXml.replaceAll(
formData.value.key,
formData.value.key + '_copy'
)
}
formData.value.name += '副本' formData.value.name += '副本'
formData.value.key += '_copy' formData.value.key += '_copy'
tagsView.setTitle('复制流程') tagsView.setTitle('复制流程')

View File

@ -685,6 +685,7 @@ watch(
/** 弹出气泡卡 */ /** 弹出气泡卡 */
const openPopover = async (type: string) => { const openPopover = async (type: string) => {
if (popOverVisible.value[type] === true) return
if (type === 'approve') { if (type === 'approve') {
// //
const valid = await validateNormalForm() const valid = await validateNormalForm()

View File

@ -38,7 +38,13 @@
</div> </div>
</div> </div>
<div v-if="activity.nodeType === NodeType.CHILD_PROCESS_NODE"> <div v-if="activity.nodeType === NodeType.CHILD_PROCESS_NODE">
<el-button type="primary" plain size="small" @click="handleChildProcess(activity)"> <el-button
type="primary"
plain
size="small"
@click="handleChildProcess(activity)"
:disabled="!activity.processInstanceId"
>
查看子流程 查看子流程
</el-button> </el-button>
</div> </div>
@ -319,7 +325,9 @@ const handleUserSelectConfirm = (activityId: string, userList: any[]) => {
/** 跳转子流程 */ /** 跳转子流程 */
const handleChildProcess = (activity: any) => { const handleChildProcess = (activity: any) => {
// TODO @lesan if (!activity.processInstanceId) {
return
}
push({ push({
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
query: { query: {

View File

@ -131,7 +131,7 @@
:formatter="dateFormatter" :formatter="dateFormatter"
align="center" align="center"
label="发起时间" label="发起时间"
prop="createTime" prop="processInstance.createTime"
width="180" width="180"
/> />
<el-table-column align="center" label="当前任务" prop="name" width="180" /> <el-table-column align="center" label="当前任务" prop="name" width="180" />

View File

@ -56,7 +56,7 @@ import type { UploadUserFile } from 'element-plus'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
defineOptions({ name: 'SystemUserImportForm' }) defineOptions({ name: 'CrmCustomerImportForm' })
const message = useMessage() // const message = useMessage() //

View File

@ -24,6 +24,38 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="跟进内容" prop="content" /> <el-table-column align="center" label="跟进内容" prop="content" />
<el-table-column label="图片" align="center">
<template #default="scope">
<div v-if="scope.row.picUrls && scope.row.picUrls.length > 0" class="flex">
<el-image
v-for="(url, index) in scope.row.picUrls"
:key="index"
:src="url"
:preview-src-list="scope.row.picUrls"
class="w-10 h-10 mr-1"
:initial-index="index"
fit="cover"
preview-teleported
/>
</div>
</template>
</el-table-column>
<el-table-column label="附件" align="center">
<template #default="scope">
<div v-if="scope.row.fileUrls && scope.row.fileUrls.length > 0" class="flex flex-col">
<el-link
v-for="(url, index) in scope.row.fileUrls"
:key="index"
:href="url"
type="primary"
target="_blank"
download
>
{{ getFileName(url) }}
</el-link>
</div>
</template>
</el-table-column>
<el-table-column <el-table-column
:formatter="dateFormatter" :formatter="dateFormatter"
align="center" align="center"
@ -97,6 +129,14 @@ import { BizTypeEnum } from '@/api/crm/permission'
/** 跟进记录列表 */ /** 跟进记录列表 */
defineOptions({ name: 'FollowUpRecord' }) defineOptions({ name: 'FollowUpRecord' })
const getFileName = (url: string) => {
if (!url) {
return ''
}
return url.substring(url.lastIndexOf('/') + 1)
}
const props = defineProps<{ const props = defineProps<{
bizType: number bizType: number
bizId: number bizId: number

View File

@ -95,7 +95,20 @@
</el-table-column> </el-table-column>
<el-table-column label="字典类型" min-width="12%"> <el-table-column label="字典类型" min-width="12%">
<template #default="scope"> <template #default="scope">
<el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择"> <el-select v-model="scope.row.dictType" :value-on-clear="''" clearable filterable placeholder="请选择">
<template #header>
<div class="flex justify-end">
<el-popover
class="box-item"
content="加载最新字典"
placement="top-start"
>
<template #reference>
<el-button :icon="Refresh" size="small" circle @click="getDictOptions" class=""/>
</template>
</el-popover>
</div>
</template>
<el-option <el-option
v-for="dict in dictOptions" v-for="dict in dictOptions"
:key="dict.id" :key="dict.id"
@ -114,6 +127,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { Refresh } from '@element-plus/icons-vue'
import * as CodegenApi from '@/api/infra/codegen' import * as CodegenApi from '@/api/infra/codegen'
import * as DictDataApi from '@/api/system/dict/dict.type' import * as DictDataApi from '@/api/system/dict/dict.type'

View File

@ -55,13 +55,23 @@
<Icon class="mr-5px" icon="ep:zoom-in" /> <Icon class="mr-5px" icon="ep:zoom-in" />
导入 导入
</el-button> </el-button>
<el-button
v-hasPermi="['infra:codegen:delete']"
type="danger"
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch"
>
<Icon class="mr-5px" icon="ep:delete" />
批量删除
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
<el-table-column type="selection" width="55" />
<el-table-column align="center" label="数据源"> <el-table-column align="center" label="数据源">
<template #default="scope"> <template #default="scope">
{{ {{
@ -232,6 +242,24 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 批量删除操作 */
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (rows: CodegenApi.CodegenTableVO[]) => {
checkedIds.value = rows.map((row) => row.id)
}
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
//
await CodegenApi.deleteCodegenTableList(checkedIds.value)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 同步操作 */ /** 同步操作 */
const handleSyncDB = async (row: CodegenApi.CodegenTableVO) => { const handleSyncDB = async (row: CodegenApi.CodegenTableVO) => {
// DB // DB

View File

@ -65,6 +65,15 @@
> >
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
<el-button
type="danger"
plain
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch"
v-hasPermi="['infra:config:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
<el-button <el-button
type="success" type="success"
plain plain
@ -80,7 +89,8 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
<el-table-column type="selection" width="55" />
<el-table-column label="参数主键" align="center" prop="id" /> <el-table-column label="参数主键" align="center" prop="id" />
<el-table-column label="参数分类" align="center" prop="category" /> <el-table-column label="参数分类" align="center" prop="category" />
<el-table-column label="参数名称" align="center" prop="name" :show-overflow-tooltip="true" /> <el-table-column label="参数名称" align="center" prop="name" :show-overflow-tooltip="true" />
@ -206,6 +216,24 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 批量删除按钮操作 */
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (rows: ConfigApi.ConfigVO[]) => {
checkedIds.value = rows.map((row) => row.id!).filter(Boolean)
}
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
//
await ConfigApi.deleteConfigList(checkedIds.value)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = async () => { const handleExport = async () => {
try { try {

View File

@ -11,13 +11,23 @@
> >
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
<el-button
type="danger"
plain
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch"
v-hasPermi="['infra:data-source-config:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
<el-table-column type="selection" width="55" />
<el-table-column label="主键编号" align="center" prop="id" /> <el-table-column label="主键编号" align="center" prop="id" />
<el-table-column label="数据源名称" align="center" prop="name" /> <el-table-column label="数据源名称" align="center" prop="name" />
<el-table-column label="数据源连接" align="center" prop="url" :show-overflow-tooltip="true" /> <el-table-column label="数据源连接" align="center" prop="url" :show-overflow-tooltip="true" />
@ -99,6 +109,25 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 批量删除按钮操作 */
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (rows: DataSourceConfigApi.DataSourceConfigVO[]) => {
// id 0
checkedIds.value = rows.map((row) => row.id!).filter((id) => id !== 0 && Boolean(id))
}
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
//
await DataSourceConfigApi.deleteDataSourceConfigList(checkedIds.value)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
:value="dict.value" :label="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
@ -44,7 +44,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import * as Demo01ContactApi from '@/api/infra/demo/demo01' import { Demo01ContactApi, Demo01Contact } from '@/api/infra/demo/demo01'
/** 示例联系人 表单 */
defineOptions({ name: 'Demo01ContactForm' })
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -59,13 +62,13 @@ const formData = ref({
sex: undefined, sex: undefined,
birthday: undefined, birthday: undefined,
description: undefined, description: undefined,
avatar: undefined avatar: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生年不能为空', trigger: 'blur' }], birthday: [{ required: true, message: '出生年不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
@ -95,7 +98,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as Demo01ContactApi.Demo01ContactVO const data = formData.value as unknown as Demo01Contact
if (formType.value === 'create') { if (formType.value === 'create') {
await Demo01ContactApi.createDemo01Contact(data) await Demo01ContactApi.createDemo01Contact(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
@ -119,7 +122,7 @@ const resetForm = () => {
sex: undefined, sex: undefined,
birthday: undefined, birthday: undefined,
description: undefined, description: undefined,
avatar: undefined avatar: undefined,
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }

View File

@ -1,6 +1,4 @@
<template> <template>
<doc-alert title="代码生成(单表)" url="https://doc.iocoder.cn/new-feature/" />
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form <el-form
@ -20,7 +18,12 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="性别" prop="sex"> <el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px"> <el-select
v-model="queryParams.sex"
placeholder="请选择性别"
clearable
class="!w-240px"
>
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
@ -37,7 +40,7 @@
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px" class="!w-220px"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -60,13 +63,30 @@
> >
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> 导出
</el-button> </el-button>
<el-button
type="danger"
plain
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['infra:demo01-contact:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="编号" align="center" prop="id" /> <el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" /> <el-table-column label="名字" align="center" prop="name" />
<el-table-column label="性别" align="center" prop="sex"> <el-table-column label="性别" align="center" prop="sex">
@ -90,7 +110,7 @@
:formatter="dateFormatter" :formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center" min-width="120px">
<template #default="scope"> <template #default="scope">
<el-button <el-button
link link
@ -126,25 +146,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import * as Demo01ContactApi from '@/api/infra/demo/demo01' import { Demo01ContactApi, Demo01Contact } from '@/api/infra/demo/demo01'
import Demo01ContactForm from './Demo01ContactForm.vue' import Demo01ContactForm from './Demo01ContactForm.vue'
/** 示例联系人 列表 */
defineOptions({ name: 'Demo01Contact' }) defineOptions({ name: 'Demo01Contact' })
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
const loading = ref(true) // const loading = ref(true) //
const list = ref([]) // const list = ref<Demo01Contact[]>([]) //
const total = ref(0) // const total = ref(0) //
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: null, name: undefined,
sex: null, sex: undefined,
createTime: [] createTime: [],
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
const exportLoading = ref(false) // const exportLoading = ref(false) //
@ -192,6 +214,22 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 批量删除示例联系人 */
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
await Demo01ContactApi.deleteDemo01ContactList(checkedIds.value);
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: Demo01Contact[]) => {
checkedIds.value = records.map((item) => item.id);
}
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = async () => { const handleExport = async () => {
try { try {

View File

@ -41,7 +41,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { Demo03StudentApi, Demo03Student } from '@/api/infra/demo/demo03/erp'
/** 学生 表单 */
defineOptions({ name: 'Demo03StudentForm' })
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -55,13 +58,13 @@ const formData = ref({
name: undefined, name: undefined,
sex: undefined, sex: undefined,
birthday: undefined, birthday: undefined,
description: undefined description: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
@ -91,7 +94,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO const data = formData.value as unknown as Demo03Student
if (formType.value === 'create') { if (formType.value === 'create') {
await Demo03StudentApi.createDemo03Student(data) await Demo03StudentApi.createDemo03Student(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
@ -114,7 +117,7 @@ const resetForm = () => {
name: undefined, name: undefined,
sex: undefined, sex: undefined,
birthday: undefined, birthday: undefined,
description: undefined description: undefined,
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }

View File

@ -21,7 +21,7 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { Demo03StudentApi, Demo03Course } from '@/api/infra/demo/demo03/erp'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -34,22 +34,22 @@ const formData = ref({
id: undefined, id: undefined,
studentId: undefined, studentId: undefined,
name: undefined, name: undefined,
score: undefined score: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }] score: [{ required: true, message: '分数不能为空', trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number, studentId: number) => { const open = async (type: string, id?: number, studentId?: number) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
formType.value = type formType.value = type
resetForm() resetForm()
formData.value.studentId = studentId formData.value.studentId = studentId as any
// //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
@ -70,7 +70,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value const data = formData.value as unknown as Demo03Course
if (formType.value === 'create') { if (formType.value === 'create') {
await Demo03StudentApi.createDemo03Course(data) await Demo03StudentApi.createDemo03Course(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
@ -92,7 +92,7 @@ const resetForm = () => {
id: undefined, id: undefined,
studentId: undefined, studentId: undefined,
name: undefined, name: undefined,
score: undefined score: undefined,
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }

View File

@ -2,40 +2,56 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-button <el-button
v-hasPermi="['infra:demo03-student:create']"
plain
type="primary" type="primary"
plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['infra:demo03-student:create']"
> >
<Icon class="mr-5px" icon="ep:plus" /> <Icon icon="ep:plus" class="mr-5px" /> 新增
新增
</el-button> </el-button>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <el-button
<el-table-column align="center" label="编号" prop="id" /> type="danger"
<el-table-column align="center" label="名字" prop="name" /> plain
<el-table-column align="center" label="分数" prop="score" /> :disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['infra:demo03-student:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="分数" align="center" prop="score" />
<el-table-column <el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间" label="创建时间"
align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column align="center" label="操作"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-hasPermi="['infra:demo03-student:update']"
link link
type="primary" type="primary"
@click="openForm('update', scope.row.id)" @click="openForm('update', scope.row.id)"
v-hasPermi="['infra:demo03-student:update']"
> >
编辑 编辑
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['infra:demo03-student:delete']"
link link
type="danger" type="danger"
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['infra:demo03-student:delete']"
> >
删除 删除
</el-button> </el-button>
@ -44,19 +60,19 @@
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total" :total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<Demo03CourseForm ref="formRef" @success="getList" /> <Demo03CourseForm ref="formRef" @success="getList" />
</template> </template>
<script setup lang="ts">
<script lang="ts" setup>
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { isEmpty } from '@/utils/is'
import {Demo03Course, Demo03StudentApi} from '@/api/infra/demo/demo03/erp'
import Demo03CourseForm from './Demo03CourseForm.vue' import Demo03CourseForm from './Demo03CourseForm.vue'
const { t } = useI18n() // const { t } = useI18n() //
@ -127,4 +143,20 @@ const handleDelete = async (id: number) => {
await getList() await getList()
} catch {} } catch {}
} }
/** 批量删除学生课程 */
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
await Demo03StudentApi.deleteDemo03CourseList(checkedIds.value);
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: Demo03Course[]) => {
checkedIds.value = records.map((item) => item.id);
}
</script> </script>

View File

@ -21,7 +21,7 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { Demo03StudentApi, Demo03Grade } from '@/api/infra/demo/demo03/erp'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -34,22 +34,22 @@ const formData = ref({
id: undefined, id: undefined,
studentId: undefined, studentId: undefined,
name: undefined, name: undefined,
teacher: undefined teacher: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }] teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number, studentId: number) => { const open = async (type: string, id?: number, studentId?: number) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
formType.value = type formType.value = type
resetForm() resetForm()
formData.value.studentId = studentId formData.value.studentId = studentId as any
// //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
@ -70,7 +70,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value const data = formData.value as unknown as Demo03Grade
if (formType.value === 'create') { if (formType.value === 'create') {
await Demo03StudentApi.createDemo03Grade(data) await Demo03StudentApi.createDemo03Grade(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
@ -92,7 +92,7 @@ const resetForm = () => {
id: undefined, id: undefined,
studentId: undefined, studentId: undefined,
name: undefined, name: undefined,
teacher: undefined teacher: undefined,
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }

View File

@ -2,40 +2,56 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-button <el-button
v-hasPermi="['infra:demo03-student:create']"
plain
type="primary" type="primary"
plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['infra:demo03-student:create']"
> >
<Icon class="mr-5px" icon="ep:plus" /> <Icon icon="ep:plus" class="mr-5px" /> 新增
新增
</el-button> </el-button>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <el-button
<el-table-column align="center" label="编号" prop="id" /> type="danger"
<el-table-column align="center" label="名字" prop="name" /> plain
<el-table-column align="center" label="班主任" prop="teacher" /> :disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['infra:demo03-student:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="班主任" align="center" prop="teacher" />
<el-table-column <el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间" label="创建时间"
align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column align="center" label="操作"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-hasPermi="['infra:demo03-student:update']"
link link
type="primary" type="primary"
@click="openForm('update', scope.row.id)" @click="openForm('update', scope.row.id)"
v-hasPermi="['infra:demo03-student:update']"
> >
编辑 编辑
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['infra:demo03-student:delete']"
link link
type="danger" type="danger"
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['infra:demo03-student:delete']"
> >
删除 删除
</el-button> </el-button>
@ -44,19 +60,19 @@
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total" :total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<Demo03GradeForm ref="formRef" @success="getList" /> <Demo03GradeForm ref="formRef" @success="getList" />
</template> </template>
<script setup lang="ts">
<script lang="ts" setup>
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { isEmpty } from '@/utils/is'
import {Demo03Grade, Demo03StudentApi} from '@/api/infra/demo/demo03/erp'
import Demo03GradeForm from './Demo03GradeForm.vue' import Demo03GradeForm from './Demo03GradeForm.vue'
const { t } = useI18n() // const { t } = useI18n() //
@ -127,4 +143,20 @@ const handleDelete = async (id: number) => {
await getList() await getList()
} catch {} } catch {}
} }
/** 批量删除学生班级 */
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
await Demo03StudentApi.deleteDemo03GradeList(checkedIds.value);
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: Demo03Grade[]) => {
checkedIds.value = records.map((item) => item.id);
}
</script> </script>

View File

@ -1,26 +1,29 @@
<template> <template>
<doc-alert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form <el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef" ref="queryFormRef"
:inline="true" :inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px" label-width="68px"
> >
<el-form-item label="名字" prop="name"> <el-form-item label="名字" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入名字" placeholder="请输入名字"
clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="性别" prop="sex"> <el-form-item label="性别" prop="sex">
<el-select v-model="queryParams.sex" class="!w-240px" clearable placeholder="请选择性别"> <el-select
v-model="queryParams.sex"
placeholder="请选择性别"
clearable
class="!w-240px"
>
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
@ -32,41 +35,42 @@
<el-form-item label="创建时间" prop="createTime"> <el-form-item label="创建时间" prop="createTime">
<el-date-picker <el-date-picker
v-model="queryParams.createTime" v-model="queryParams.createTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<Icon class="mr-5px" icon="ep:search" /> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button <el-button
v-hasPermi="['infra:demo03-student:create']"
plain
type="primary" type="primary"
plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['infra:demo03-student:create']"
> >
<Icon class="mr-5px" icon="ep:plus" /> <Icon icon="ep:plus" class="mr-5px" /> 新增
新增
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['infra:demo03-student:export']"
:loading="exportLoading"
plain
type="success" type="success"
plain
@click="handleExport" @click="handleExport"
:loading="exportLoading"
v-hasPermi="['infra:demo03-student:export']"
> >
<Icon class="mr-5px" icon="ep:download" /> <Icon icon="ep:download" class="mr-5px" /> 导出
导出 </el-button>
<el-button
type="danger"
plain
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['infra:demo03-student:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -75,50 +79,53 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table <el-table
row-key="id"
v-loading="loading" v-loading="loading"
:data="list" :data="list"
:show-overflow-tooltip="true"
:stripe="true" :stripe="true"
:show-overflow-tooltip="true"
highlight-current-row highlight-current-row
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
@selection-change="handleRowCheckboxChange"
> >
<el-table-column align="center" label="编号" prop="id" /> <el-table-column type="selection" width="55" />
<el-table-column align="center" label="名字" prop="name" /> <el-table-column label="编号" align="center" prop="id" />
<el-table-column align="center" label="性别" prop="sex"> <el-table-column label="名字" align="center" prop="name" />
<el-table-column label="性别" align="center" prop="sex">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
:formatter="dateFormatter"
align="center"
label="出生日期" label="出生日期"
prop="birthday"
width="180px"
/>
<el-table-column align="center" label="简介" prop="description" />
<el-table-column
:formatter="dateFormatter"
align="center" align="center"
label="创建时间" prop="birthday"
prop="createTime" :formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column align="center" label="操作"> <el-table-column label="简介" align="center" prop="description" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-hasPermi="['infra:demo03-student:update']"
link link
type="primary" type="primary"
@click="openForm('update', scope.row.id)" @click="openForm('update', scope.row.id)"
v-hasPermi="['infra:demo03-student:update']"
> >
编辑 编辑
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['infra:demo03-student:delete']"
link link
type="danger" type="danger"
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['infra:demo03-student:delete']"
> >
删除 删除
</el-button> </el-button>
@ -127,9 +134,9 @@
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total" :total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
@ -140,39 +147,41 @@
<ContentWrap> <ContentWrap>
<el-tabs model-value="demo03Course"> <el-tabs model-value="demo03Course">
<el-tab-pane label="学生课程" name="demo03Course"> <el-tab-pane label="学生课程" name="demo03Course">
<Demo03CourseList :student-id="currentRow?.id" /> <Demo03CourseList :student-id="currentRow.id" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="学生班级" name="demo03Grade"> <el-tab-pane label="学生班级" name="demo03Grade">
<Demo03GradeList :student-id="currentRow?.id" /> <Demo03GradeList :student-id="currentRow.id" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</ContentWrap> </ContentWrap>
</template> </template>
<script lang="ts" setup> <script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' import { Demo03StudentApi, Demo03Student } from '@/api/infra/demo/demo03/erp'
import Demo03StudentForm from './Demo03StudentForm.vue' import Demo03StudentForm from './Demo03StudentForm.vue'
import Demo03CourseList from './components/Demo03CourseList.vue' import Demo03CourseList from './components/Demo03CourseList.vue'
import Demo03GradeList from './components/Demo03GradeList.vue' import Demo03GradeList from './components/Demo03GradeList.vue'
/** 学生 列表 */
defineOptions({ name: 'Demo03Student' }) defineOptions({ name: 'Demo03Student' })
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
const loading = ref(true) // const loading = ref(true) //
const list = ref([]) // const list = ref<Demo03Student[]>([]) //
const total = ref(0) // const total = ref(0) //
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: null, name: undefined,
sex: null, sex: undefined,
description: null, description: undefined,
createTime: [] createTime: [],
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
const exportLoading = ref(false) // const exportLoading = ref(false) //
@ -220,6 +229,22 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 批量删除学生 */
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
await Demo03StudentApi.deleteDemo03StudentList(checkedIds.value);
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: Demo03Student[]) => {
checkedIds.value = records.map((item) => item.id);
}
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = async () => { const handleExport = async () => {
try { try {

View File

@ -49,11 +49,14 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' import { Demo03Student, Demo03StudentApi } from '@/api/infra/demo/demo03/inner'
import Demo03CourseForm from './components/Demo03CourseForm.vue' import Demo03CourseForm from './components/Demo03CourseForm.vue'
import Demo03GradeForm from './components/Demo03GradeForm.vue' import Demo03GradeForm from './components/Demo03GradeForm.vue'
/** 学生 表单 */
defineOptions({ name: 'Demo03StudentForm' })
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -120,7 +123,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO const data = formData.value as unknown as Demo03Student
// //
data.demo03Courses = demo03CourseFormRef.value.getData() data.demo03Courses = demo03CourseFormRef.value.getData()
data.demo03Grade = demo03GradeFormRef.value.getData() data.demo03Grade = demo03GradeFormRef.value.getData()

View File

@ -35,13 +35,13 @@
</el-row> </el-row>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' import { Demo03StudentApi } from '@/api/infra/demo/demo03/inner'
const props = defineProps<{ const props = defineProps<{
studentId: undefined // studentId: number //
}>() }>()
const formLoading = ref(false) // const formLoading = ref(false) //
const formData = ref([]) const formData = ref<any[]>([])
const formRules = reactive({ const formRules = reactive({
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
@ -77,7 +77,7 @@ const handleAdd = () => {
name: undefined, name: undefined,
score: undefined score: undefined
} }
row.studentId = props.studentId row.studentId = props.studentId as any
formData.value.push(row) formData.value.push(row)
} }

Some files were not shown because too many files have changed in this diff Show More