diff --git a/src/api/infra/job/index.ts b/src/api/infra/job/index.ts index 63f15da0..c1398d07 100644 --- a/src/api/infra/job/index.ts +++ b/src/api/infra/job/index.ts @@ -13,50 +13,38 @@ export interface JobVO { createTime: Date } -export interface JobPageReqVO extends PageParam { - name?: string - status?: number - handlerName?: string -} - -export interface JobExportReqVO { - name?: string - status?: number - handlerName?: string -} - // 任务列表 -export const getJobPageApi = (params: JobPageReqVO) => { +export const getJobPage = (params: PageParam) => { return request.get({ url: '/infra/job/page', params }) } // 任务详情 -export const getJobApi = (id: number) => { +export const getJob = (id: number) => { return request.get({ url: '/infra/job/get?id=' + id }) } // 新增任务 -export const createJobApi = (data: JobVO) => { +export const createJob = (data: JobVO) => { return request.post({ url: '/infra/job/create', data }) } // 修改定时任务调度 -export const updateJobApi = (data: JobVO) => { +export const updateJob = (data: JobVO) => { return request.put({ url: '/infra/job/update', data }) } // 删除定时任务调度 -export const deleteJobApi = (id: number) => { +export const deleteJob = (id: number) => { return request.delete({ url: '/infra/job/delete?id=' + id }) } // 导出定时任务调度 -export const exportJobApi = (params: JobExportReqVO) => { +export const exportJob = (params) => { return request.download({ url: '/infra/job/export-excel', params }) } // 任务状态修改 -export const updateJobStatusApi = (id: number, status: number) => { +export const updateJobStatus = (id: number, status: number) => { const params = { id, status @@ -70,6 +58,6 @@ export const runJobApi = (id: number) => { } // 获得定时任务的下 n 次执行时间 -export const getJobNextTimesApi = (id: number) => { +export const getJobNextTimes = (id: number) => { return request.get({ url: '/infra/job/get_next_times?id=' + id }) } diff --git a/src/api/infra/jobLog/index.ts b/src/api/infra/jobLog/index.ts index 84b74fbd..f429cd9e 100644 --- a/src/api/infra/jobLog/index.ts +++ b/src/api/infra/jobLog/index.ts @@ -14,34 +14,18 @@ export interface JobLogVO { createTime: string } -export interface JobLogPageReqVO extends PageParam { - jobId?: number - handlerName?: string - beginTime?: string - endTime?: string - status?: number -} - -export interface JobLogExportReqVO { - jobId?: number - handlerName?: string - beginTime?: string - endTime?: string - status?: number -} - // 任务日志列表 -export const getJobLogPageApi = (params: JobLogPageReqVO) => { +export const getJobLogPage = (params: PageParam) => { return request.get({ url: '/infra/job-log/page', params }) } // 任务日志详情 -export const getJobLogApi = (id: number) => { +export const getJobLog = (id: number) => { return request.get({ url: '/infra/job-log/get?id=' + id }) } // 导出定时任务日志 -export const exportJobLogApi = (params: JobLogExportReqVO) => { +export const exportJobLog = (params) => { return request.download({ url: '/infra/job-log/export-excel', params diff --git a/src/api/mall/product/category.ts b/src/api/mall/product/category.ts new file mode 100644 index 00000000..7ae81285 --- /dev/null +++ b/src/api/mall/product/category.ts @@ -0,0 +1,60 @@ +import request from '@/config/axios' + +/** + * 产品分类 + */ +export interface CategoryVO { + /** + * 分类编号 + */ + id?: number + /** + * 父分类编号 + */ + parentId?: number + /** + * 分类名称 + */ + name: string + /** + * 分类图片 + */ + picUrl: string + /** + * 分类排序 + */ + sort?: number + /** + * 分类描述 + */ + description?: string + /** + * 开启状态 + */ + status: number +} + +// 创建商品分类 +export const createCategory = (data: CategoryVO) => { + return request.post({ url: '/product/category/create', data }) +} + +// 更新商品分类 +export const updateCategory = (data: CategoryVO) => { + return request.put({ url: '/product/category/update', data }) +} + +// 删除商品分类 +export const deleteCategory = (id: number) => { + return request.delete({ url: `/product/category/delete?id=${id}` }) +} + +// 获得商品分类 +export const getCategory = (id: number) => { + return request.get({ url: `/product/category/get?id=${id}` }) +} + +// 获得商品分类列表 +export const getCategoryList = (params: any) => { + return request.get({ url: '/product/category/list', params }) +} diff --git a/src/api/mall/product/property.ts b/src/api/mall/product/property.ts new file mode 100644 index 00000000..01c79f9f --- /dev/null +++ b/src/api/mall/product/property.ts @@ -0,0 +1,103 @@ +import request from '@/config/axios' + +/** + * 商品属性 + */ +export interface PropertyVO { + id?: number + /** 名称 */ + name: string + /** 备注 */ + remark?: string +} + +/** + * 属性值 + */ +export interface PropertyValueVO { + id?: number + /** 属性项的编号 */ + propertyId?: number + /** 名称 */ + name: string + /** 备注 */ + remark?: string +} + +/** + * 商品属性值的明细 + */ +export interface PropertyValueDetailVO { + /** 属性项的编号 */ + propertyId: number // 属性的编号 + /** 属性的名称 */ + propertyName: string + /** 属性值的编号 */ + valueId: number + /** 属性值的名称 */ + valueName: string +} + +// ------------------------ 属性项 ------------------- + +// 创建属性项 +export const createProperty = (data: PropertyVO) => { + return request.post({ url: '/product/property/create', data }) +} + +// 更新属性项 +export const updateProperty = (data: PropertyVO) => { + return request.put({ url: '/product/property/update', data }) +} + +// 删除属性项 +export const deleteProperty = (id: number) => { + return request.delete({ url: `/product/property/delete?id=${id}` }) +} + +// 获得属性项 +export const getProperty = (id: number): Promise<PropertyVO> => { + return request.get({ url: `/product/property/get?id=${id}` }) +} + +// 获得属性项分页 +export const getPropertyPage = (params: PageParam) => { + return request.get({ url: '/product/property/page', params }) +} + +// 获得属性项列表 +export const getPropertyList = (params: any) => { + return request.get({ url: '/product/property/list', params }) +} + +// 获得属性项列表 +export const getPropertyListAndValue = (params: any) => { + return request.get({ url: '/product/property/get-value-list', params }) +} + +// ------------------------ 属性值 ------------------- + +// 获得属性值分页 +export const getPropertyValuePage = (params: PageParam & any) => { + return request.get({ url: '/product/property/value/page', params }) +} + +// 获得属性值 +export const getPropertyValue = (id: number): Promise<PropertyValueVO> => { + return request.get({ url: `/product/property/value/get?id=${id}` }) +} + +// 创建属性值 +export const createPropertyValue = (data: PropertyValueVO) => { + return request.post({ url: '/product/property/value/create', data }) +} + +// 更新属性值 +export const updatePropertyValue = (data: PropertyValueVO) => { + return request.put({ url: '/product/property/value/update', data }) +} + +// 删除属性值 +export const deletePropertyValue = (id: number) => { + return request.delete({ url: `/product/property/value/delete?id=${id}` }) +} diff --git a/src/api/pay/merchant/index.ts b/src/api/pay/merchant/index.ts index b4b6ba51..bfb8f5e4 100644 --- a/src/api/pay/merchant/index.ts +++ b/src/api/pay/merchant/index.ts @@ -29,17 +29,17 @@ export interface MerchantExportReqVO { } // 查询列表支付商户 -export const getMerchantPageApi = (params: MerchantPageReqVO) => { +export const getMerchantPage = (params: MerchantPageReqVO) => { return request.get({ url: '/pay/merchant/page', params }) } // 查询详情支付商户 -export const getMerchantApi = (id: number) => { +export const getMerchant = (id: number) => { return request.get({ url: '/pay/merchant/get?id=' + id }) } // 根据商户名称搜索商户列表 -export const getMerchantListByNameApi = (name: string) => { +export const getMerchantListByName = (name: string) => { return request.get({ url: '/pay/merchant/list-by-name?id=', params: { @@ -49,26 +49,27 @@ export const getMerchantListByNameApi = (name: string) => { } // 新增支付商户 -export const createMerchantApi = (data: MerchantVO) => { +export const createMerchant = (data: MerchantVO) => { return request.post({ url: '/pay/merchant/create', data }) } // 修改支付商户 -export const updateMerchantApi = (data: MerchantVO) => { +export const updateMerchant = (data: MerchantVO) => { return request.put({ url: '/pay/merchant/update', data }) } // 删除支付商户 -export const deleteMerchantApi = (id: number) => { +export const deleteMerchant = (id: number) => { return request.delete({ url: '/pay/merchant/delete?id=' + id }) } // 导出支付商户 -export const exportMerchantApi = (params: MerchantExportReqVO) => { +export const exportMerchant = (params: MerchantExportReqVO) => { return request.download({ url: '/pay/merchant/export-excel', params }) } + // 支付商户状态修改 -export const changeMerchantStatusApi = (id: number, status: number) => { +export const updateMerchantStatus = (id: number, status: number) => { const data = { id, status diff --git a/src/api/system/dict/dict.data.ts b/src/api/system/dict/dict.data.ts index 6d981326..87e7dce7 100644 --- a/src/api/system/dict/dict.data.ts +++ b/src/api/system/dict/dict.data.ts @@ -44,6 +44,6 @@ export const deleteDictData = (id: number) => { } // 导出字典类型数据 -export const exportDictDataApi = (params) => { +export const exportDictData = (params) => { return request.get({ url: '/system/dict-data/export', params }) } diff --git a/src/api/system/oauth2/client.ts b/src/api/system/oauth2/client.ts index 4c06386d..6f71acad 100644 --- a/src/api/system/oauth2/client.ts +++ b/src/api/system/oauth2/client.ts @@ -21,31 +21,27 @@ export interface OAuth2ClientVO { createTime: Date } -export interface OAuth2ClientPageReqVO extends PageParam { - name?: string - status?: number -} -// 查询 OAuth2列表 -export const getOAuth2ClientPageApi = (params: OAuth2ClientPageReqVO) => { +// 查询 OAuth2 客户端的列表 +export const getOAuth2ClientPage = (params: PageParam) => { return request.get({ url: '/system/oauth2-client/page', params }) } -// 查询 OAuth2详情 -export const getOAuth2ClientApi = (id: number) => { +// 查询 OAuth2 客户端的详情 +export const getOAuth2Client = (id: number) => { return request.get({ url: '/system/oauth2-client/get?id=' + id }) } -// 新增 OAuth2 -export const createOAuth2ClientApi = (data: OAuth2ClientVO) => { +// 新增 OAuth2 客户端 +export const createOAuth2Client = (data: OAuth2ClientVO) => { return request.post({ url: '/system/oauth2-client/create', data }) } -// 修改 OAuth2 -export const updateOAuth2ClientApi = (data: OAuth2ClientVO) => { +// 修改 OAuth2 客户端 +export const updateOAuth2Client = (data: OAuth2ClientVO) => { return request.put({ url: '/system/oauth2-client/update', data }) } // 删除 OAuth2 -export const deleteOAuth2ClientApi = (id: number) => { +export const deleteOAuth2Client = (id: number) => { return request.delete({ url: '/system/oauth2-client/delete?id=' + id }) } diff --git a/src/api/system/permission/index.ts b/src/api/system/permission/index.ts index c529f884..baf2805b 100644 --- a/src/api/system/permission/index.ts +++ b/src/api/system/permission/index.ts @@ -17,26 +17,26 @@ export interface PermissionAssignRoleDataScopeReqVO { } // 查询角色拥有的菜单权限 -export const listRoleMenusApi = async (roleId: number) => { +export const getRoleMenuList = async (roleId: number) => { return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId }) } // 赋予角色菜单权限 -export const assignRoleMenuApi = async (data: PermissionAssignRoleMenuReqVO) => { +export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => { return await request.post({ url: '/system/permission/assign-role-menu', data }) } // 赋予角色数据权限 -export const assignRoleDataScopeApi = async (data: PermissionAssignRoleDataScopeReqVO) => { +export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => { return await request.post({ url: '/system/permission/assign-role-data-scope', data }) } // 查询用户拥有的角色数组 -export const listUserRolesApi = async (userId: number) => { +export const getUserRoleList = async (userId: number) => { return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId }) } // 赋予用户角色 -export const assignUserRoleApi = async (data: PermissionAssignUserRoleReqVO) => { +export const assignUserRole = async (data: PermissionAssignUserRoleReqVO) => { return await request.post({ url: '/system/permission/assign-user-role', data }) } diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts index 902d5ca6..93636ff0 100644 --- a/src/api/system/role/index.ts +++ b/src/api/system/role/index.ts @@ -7,6 +7,8 @@ export interface RoleVO { sort: number status: number type: number + dataScope: number + dataScopeDeptIds: number[] createTime: Date } @@ -49,6 +51,7 @@ export const updateRoleStatus = async (data: UpdateStatusReqVO) => { export const deleteRole = async (id: number) => { return await request.delete({ url: '/system/role/delete?id=' + id }) } + // 导出角色 export const exportRole = (params) => { return request.download({ diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index e488c0d7..6224f0e8 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -17,58 +17,43 @@ export interface UserVO { createTime: Date } -export interface UserPageReqVO extends PageParam { - deptId?: number - username?: string - mobile?: string - status?: number - createTime?: Date[] -} - -export interface UserExportReqVO { - code?: string - name?: string - status?: number - createTime?: Date[] -} - // 查询用户管理列表 -export const getUserPageApi = (params: UserPageReqVO) => { +export const getUserPage = (params: PageParam) => { return request.get({ url: '/system/user/page', params }) } // 查询用户详情 -export const getUserApi = (id: number) => { +export const getUser = (id: number) => { return request.get({ url: '/system/user/get?id=' + id }) } // 新增用户 -export const createUserApi = (data: UserVO | Recordable) => { +export const createUser = (data: UserVO) => { return request.post({ url: '/system/user/create', data }) } // 修改用户 -export const updateUserApi = (data: UserVO | Recordable) => { +export const updateUser = (data: UserVO) => { return request.put({ url: '/system/user/update', data }) } // 删除用户 -export const deleteUserApi = (id: number) => { +export const deleteUser = (id: number) => { return request.delete({ url: '/system/user/delete?id=' + id }) } // 导出用户 -export const exportUserApi = (params: UserExportReqVO) => { +export const exportUser = (params) => { return request.download({ url: '/system/user/export', params }) } // 下载用户导入模板 -export const importUserTemplateApi = () => { +export const importUserTemplate = () => { return request.download({ url: '/system/user/get-import-template' }) } // 用户密码重置 -export const resetUserPwdApi = (id: number, password: string) => { +export const resetUserPwd = (id: number, password: string) => { const data = { id, password @@ -77,7 +62,7 @@ export const resetUserPwdApi = (id: number, password: string) => { } // 用户状态修改 -export const updateUserStatusApi = (id: number, status: number) => { +export const updateUserStatus = (id: number, status: number) => { const data = { id, status diff --git a/src/components/Crontab/src/Crontab.vue b/src/components/Crontab/src/Crontab.vue index fe33bd5f..0e474fb2 100644 --- a/src/components/Crontab/src/Crontab.vue +++ b/src/components/Crontab/src/Crontab.vue @@ -6,7 +6,10 @@ interface shortcutsType { value: string } const props = defineProps({ - modelValue: { type: String, default: '* * * * * ?' }, + modelValue: { + type: String, + default: '* * * * * ?' + }, shortcuts: { type: Array as PropType<shortcutsType[]>, default: () => [] } }) const defaultValue = ref('') diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 4d8c3dac..2dc9522d 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -162,7 +162,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ children: [ { path: 'job-log', - component: () => import('@/views/infra/job/JobLog.vue'), + component: () => import('@/views/infra/job/logger/index.vue'), name: 'JobLog', meta: { noCache: true, @@ -319,6 +319,22 @@ const remainingRouter: AppRouteRecordRaw[] = [ } } ] + }, + { + path: '/property', + component: Layout, + name: 'property', + meta: { + hidden: true + }, + children: [ + { + path: 'value/:propertyId(\\d+)', + component: () => import('@/views/mall/product/property/value/index.vue'), + name: 'PropertyValue', + meta: { title: '商品属性值', icon: '', activeMenu: '/product/property' } + } + ] } ] diff --git a/src/types/auto-components.d.ts b/src/types/auto-components.d.ts index 2a42203d..80a5900f 100644 --- a/src/types/auto-components.d.ts +++ b/src/types/auto-components.d.ts @@ -23,6 +23,7 @@ declare module '@vue/runtime-core' { DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default'] Echart: typeof import('./../components/Echart/src/Echart.vue')['default'] Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] + ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer'] ElBadge: typeof import('element-plus/es')['ElBadge'] ElButton: typeof import('element-plus/es')['ElButton'] ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] @@ -52,13 +53,16 @@ declare module '@vue/runtime-core' { ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElIcon: typeof import('element-plus/es')['ElIcon'] + ElImage: typeof import('element-plus/es')['ElImage'] ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] + ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElLink: typeof import('element-plus/es')['ElLink'] ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] ElPopover: typeof import('element-plus/es')['ElPopover'] ElRadio: typeof import('element-plus/es')['ElRadio'] + ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] @@ -67,9 +71,15 @@ declare module '@vue/runtime-core' { ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTableV2: typeof import('element-plus/es')['ElTableV2'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElTimeline: typeof import('element-plus/es')['ElTimeline'] + ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElTransfer: typeof import('element-plus/es')['ElTransfer'] + ElTree: typeof import('element-plus/es')['ElTree'] ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] ElUpload: typeof import('element-plus/es')['ElUpload'] Error: typeof import('./../components/Error/src/Error.vue')['default'] diff --git a/src/views/bpm/form/editor/index.vue b/src/views/bpm/form/editor/index.vue index cb7d023f..3a28697f 100644 --- a/src/views/bpm/form/editor/index.vue +++ b/src/views/bpm/form/editor/index.vue @@ -11,7 +11,7 @@ </ContentWrap> <!-- 表单保存的弹窗 --> - <Dialog title="保存表单" v-model="modelVisible" width="600"> + <Dialog title="保存表单" v-model="dialogVisible" width="600"> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px"> <el-form-item label="表单名" prop="name"> <el-input v-model="formData.name" placeholder="请输入表单名" /> @@ -33,7 +33,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -48,7 +48,7 @@ const message = useMessage() // 消息 const { query } = useRoute() // 路由 const designer = ref() // 表单设计器 -const modelVisible = ref(false) // 弹窗是否展示 +const dialogVisible = ref(false) // 弹窗是否展示 const formLoading = ref(false) // 表单的加载中:提交的按钮禁用 const formData = ref({ name: '', @@ -63,7 +63,7 @@ const formRef = ref() // 表单 Ref /** 处理保存按钮 */ const handleSave = () => { - modelVisible.value = true + dialogVisible.value = true } /** 提交表单 */ @@ -85,7 +85,7 @@ const submitForm = async () => { await FormApi.updateForm(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false } finally { formLoading.value = false } diff --git a/src/views/bpm/form/index.vue b/src/views/bpm/form/index.vue index 616e60be..f8d94317 100644 --- a/src/views/bpm/form/index.vue +++ b/src/views/bpm/form/index.vue @@ -20,7 +20,7 @@ <el-form-item> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button type="primary" @click="openForm()" v-hasPermi="['bpm:form:create']"> + <el-button type="primary" plain @click="openForm" v-hasPermi="['bpm:form:create']"> <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> </el-form-item> diff --git a/src/views/bpm/group/UserGroupForm.vue b/src/views/bpm/group/UserGroupForm.vue index 9496ad84..e8011d8c 100644 --- a/src/views/bpm/group/UserGroupForm.vue +++ b/src/views/bpm/group/UserGroupForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -37,7 +37,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -50,8 +50,8 @@ import * as UserApi from '@/api/system/user' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -72,8 +72,8 @@ const userList = ref([]) // 用户列表 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -108,7 +108,7 @@ const submitForm = async () => { await UserGroupApi.updateUserGroup(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/bpm/group/index.vue b/src/views/bpm/group/index.vue index 0b52e9a2..0c731dbe 100644 --- a/src/views/bpm/group/index.vue +++ b/src/views/bpm/group/index.vue @@ -43,6 +43,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openForm('create')" v-hasPermi="['bpm:user-group:create']" > diff --git a/src/views/bpm/model/ModelForm.vue b/src/views/bpm/model/ModelForm.vue index ac536958..24484ec6 100644 --- a/src/views/bpm/model/ModelForm.vue +++ b/src/views/bpm/model/ModelForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" width="600"> + <Dialog :title="dialogTitle" v-model="dialogVisible" width="600"> <el-form ref="formRef" :model="formData" @@ -117,7 +117,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -129,8 +129,8 @@ import * as FormApi from '@/api/bpm/form' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -154,8 +154,8 @@ const formList = ref([]) // 流程表单的下拉框的数据 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -203,7 +203,7 @@ const submitForm = async () => { await ModelApi.updateModel(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/bpm/model/ModelImportForm.vue b/src/views/bpm/model/ModelImportForm.vue index ac26ac08..76493c95 100644 --- a/src/views/bpm/model/ModelImportForm.vue +++ b/src/views/bpm/model/ModelImportForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="导入流程" v-model="modelVisible" width="400"> + <Dialog title="导入流程" v-model="dialogVisible" width="400"> <div> <el-upload ref="uploadRef" @@ -8,7 +8,7 @@ :data="formData" name="bpmnFile" v-model:file-list="fileList" - :drag="true" + drag :auto-upload="false" accept=".bpmn, .xml" :limit="1" @@ -45,7 +45,7 @@ </div> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -53,7 +53,7 @@ import { getAccessToken, getTenantId } from '@/utils/auth' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中 const formData = ref({ key: '', @@ -72,12 +72,12 @@ const fileList = ref([]) // 文件列表 /** 打开弹窗 */ const open = async () => { - modelVisible.value = true + dialogVisible.value = true resetForm() } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 -/** 重置表单 */ +/** 提交表单 */ const submitForm = async () => { // 校验表单 if (!formRef) return @@ -98,7 +98,7 @@ const submitForm = async () => { /** 文件上传成功 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 -const submitFormSuccess = async (response: any): Promise<void> => { +const submitFormSuccess = async (response: any) => { if (response.code !== 0) { message.error(response.msg) formLoading.value = false diff --git a/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue b/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue index 7f9cfd48..0f16deeb 100644 --- a/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue +++ b/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="转派审批人" v-model="modelVisible" width="500"> + <Dialog title="转派审批人" v-model="dialogVisible" width="500"> <el-form ref="formRef" :model="formData" @@ -20,7 +20,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -28,7 +28,7 @@ import * as TaskApi from '@/api/bpm/task' import * as UserApi from '@/api/system/user' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中 const formData = ref({ id: '', @@ -43,7 +43,7 @@ const userList = ref<any[]>([]) // 用户列表 /** 打开弹窗 */ const open = async (id: string) => { - modelVisible.value = true + dialogVisible.value = true resetForm() formData.value.id = id // 获得用户列表 @@ -62,7 +62,7 @@ const submitForm = async () => { formLoading.value = true try { await TaskApi.updateTaskAssignee(formData.value) - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue b/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue index a452cab9..2ddebab1 100644 --- a/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue +++ b/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="修改任务规则" v-model="modelVisible" width="600"> + <Dialog title="修改任务规则" v-model="dialogVisible" width="600"> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px"> <el-form-item label="任务名称" prop="taskDefinitionName"> <el-input v-model="formData.taskDefinitionName" placeholder="请输入流标标识" disabled /> @@ -93,7 +93,7 @@ <!-- 操作按钮 --> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -109,7 +109,7 @@ import * as UserGroupApi from '@/api/bpm/userGroup' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ type: Number(undefined), @@ -171,7 +171,7 @@ const open = async (modelId: string, row: TaskAssignRuleApi.TaskAssignVO) => { formData.value.scripts.push(...row.options) } // 打开弹窗 - modelVisible.value = true + dialogVisible.value = true // 获得角色列表 roleOptions.value = await RoleApi.getSimpleRoleList() @@ -232,7 +232,7 @@ const submitForm = async () => { await TaskAssignRuleApi.updateTaskAssignRule(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue b/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue index d046a521..4746b0c7 100644 --- a/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue +++ b/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500" width="800"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500" width="800"> <el-descriptions border :column="1"> <el-descriptions-item label="日志主键" min-width="120"> {{ detailData.id }} @@ -45,13 +45,13 @@ import { DICT_TYPE } from '@/utils/dict' import { formatDate } from '@/utils/formatTime' import * as ApiAccessLog from '@/api/infra/apiAccessLog' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单地加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const open = async (data: ApiAccessLog.ApiAccessLogVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue b/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue index 5076fe00..4b279924 100644 --- a/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue +++ b/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500" width="800"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500" width="800"> <el-descriptions border :column="1"> <el-descriptions-item label="日志主键" min-width="120"> {{ detailData.id }} @@ -60,13 +60,13 @@ import { DICT_TYPE } from '@/utils/dict' import { formatDate } from '@/utils/formatTime' import * as ApiErrorLog from '@/api/infra/apiErrorLog' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const open = async (data: ApiErrorLog.ApiErrorLogVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/infra/codegen/ImportTable.vue b/src/views/infra/codegen/ImportTable.vue index b89b2923..0146b6c3 100644 --- a/src/views/infra/codegen/ImportTable.vue +++ b/src/views/infra/codegen/ImportTable.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="导入表" v-model="modelVisible" width="800px"> + <Dialog title="导入表" v-model="dialogVisible" width="800px"> <!-- 搜索栏 --> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form-item label="数据源" prop="dataSourceConfigId"> @@ -69,7 +69,7 @@ import * as DataSourceConfigApi from '@/api/infra/dataSourceConfig' import { ElTable } from 'element-plus' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const dbTableLoading = ref(true) // 数据源的加载中 const dbTableList = ref<CodegenApi.DatabaseTableVO[]>([]) // 表的列表 const queryParams = reactive({ @@ -103,7 +103,7 @@ const open = async () => { // 加载数据源的列表 dataSourceConfigList.value = await DataSourceConfigApi.getDataSourceConfigList() queryParams.dataSourceConfigId = dataSourceConfigList.value[0].id as number - modelVisible.value = true + dialogVisible.value = true // 加载表的列表 await getList() } @@ -111,7 +111,7 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗 /** 关闭弹窗 */ const close = () => { - modelVisible.value = false + dialogVisible.value = false tableList.value = [] } diff --git a/src/views/infra/codegen/PreviewCode.vue b/src/views/infra/codegen/PreviewCode.vue index df521f5a..6a81499d 100644 --- a/src/views/infra/codegen/PreviewCode.vue +++ b/src/views/infra/codegen/PreviewCode.vue @@ -1,7 +1,7 @@ <template> <Dialog title="代码预览" - v-model="modelVisible" + v-model="dialogVisible" align-center width="80%" class="app-infra-codegen-preview-container" @@ -61,7 +61,7 @@ import * as CodegenApi from '@/api/infra/codegen' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const loading = ref(false) // 加载中的状态 const preview = reactive({ fileTree: [], // 文件树 @@ -86,7 +86,7 @@ interface filesType { /** 打开弹窗 */ const open = async (id: number) => { - modelVisible.value = true + dialogVisible.value = true try { loading.value = true // 生成代码 diff --git a/src/views/infra/codegen/index.vue b/src/views/infra/codegen/index.vue index 34c81e58..4325f702 100644 --- a/src/views/infra/codegen/index.vue +++ b/src/views/infra/codegen/index.vue @@ -163,7 +163,7 @@ const queryParams = reactive({ const queryFormRef = ref() // 搜索的表单 const dataSourceConfigList = ref<DataSourceConfigApi.DataSourceConfigVO[]>([]) // 数据源列表 -/** 查询参数列表 */ +/** 查询列表 */ const getList = async () => { loading.value = true try { diff --git a/src/views/infra/config/form.vue b/src/views/infra/config/form.vue index 8d96b629..18ecfa3d 100644 --- a/src/views/infra/config/form.vue +++ b/src/views/infra/config/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -36,7 +36,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -47,8 +47,8 @@ import * as ConfigApi from '@/api/infra/config' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -71,8 +71,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -105,7 +105,7 @@ const submitForm = async () => { await ConfigApi.updateConfig(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/infra/config/index.vue b/src/views/infra/config/index.vue index 5fcafc07..cbbabc4f 100644 --- a/src/views/infra/config/index.vue +++ b/src/views/infra/config/index.vue @@ -55,7 +55,12 @@ <el-form-item> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button type="primary" @click="openModal('create')" v-hasPermi="['infra:config:create']"> + <el-button + type="primary" + plain + @click="openModal('create')" + v-hasPermi="['infra:config:create']" + > <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> <el-button diff --git a/src/views/infra/dataSourceConfig/form.vue b/src/views/infra/dataSourceConfig/form.vue index cd79e24b..33f67c56 100644 --- a/src/views/infra/dataSourceConfig/form.vue +++ b/src/views/infra/dataSourceConfig/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -22,7 +22,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -32,8 +32,8 @@ import * as DataSourceConfigApi from '@/api/infra/dataSourceConfig' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref<DataSourceConfigApi.DataSourceConfigVO>({ @@ -53,8 +53,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -87,7 +87,7 @@ const submitForm = async () => { await DataSourceConfigApi.updateDataSourceConfig(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/infra/dataSourceConfig/index.vue b/src/views/infra/dataSourceConfig/index.vue index ab3523e7..3b5eddba 100644 --- a/src/views/infra/dataSourceConfig/index.vue +++ b/src/views/infra/dataSourceConfig/index.vue @@ -5,6 +5,7 @@ <el-form-item> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['infra:data-source-config:create']" > diff --git a/src/views/infra/file/form.vue b/src/views/infra/file/form.vue index b0a76e0e..56dbf25f 100644 --- a/src/views/infra/file/form.vue +++ b/src/views/infra/file/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-upload ref="uploadRef" :limit="1" @@ -24,7 +24,7 @@ </el-upload> <template #footer> <el-button @click="submitFileForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -36,8 +36,8 @@ import { getAccessToken } from '@/utils/auth' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const url = import.meta.env.VITE_UPLOAD_URL const headers = { Authorization: 'Bearer ' + getAccessToken() } @@ -46,8 +46,8 @@ const uploadRef = ref() /** 打开弹窗 */ const openModal = async () => { - modelVisible.value = true - modelTitle.value = t('action.fileUpload') + dialogVisible.value = true + dialogTitle.value = t('action.fileUpload') } defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 @@ -71,7 +71,7 @@ const submitFileForm = () => { /** 文件上传成功处理 */ const handleFileSuccess = () => { // 清理 - modelVisible.value = false + dialogVisible.value = false formLoading.value = false unref(uploadRef)?.clearFiles() // 提示成功,并刷新 diff --git a/src/views/infra/fileConfig/form.vue b/src/views/infra/fileConfig/form.vue index f08ba4c4..740d29ed 100644 --- a/src/views/infra/fileConfig/form.vue +++ b/src/views/infra/fileConfig/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -94,7 +94,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -105,8 +105,8 @@ import * as FileConfigApi from '@/api/infra/fileConfig' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -137,8 +137,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -171,7 +171,7 @@ const submitForm = async () => { await FileConfigApi.updateFileConfig(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/infra/fileConfig/index.vue b/src/views/infra/fileConfig/index.vue index ca0fac15..245b7f5c 100644 --- a/src/views/infra/fileConfig/index.vue +++ b/src/views/infra/fileConfig/index.vue @@ -35,6 +35,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['infra:file-config:create']" > diff --git a/src/views/infra/job/JobDetail.vue b/src/views/infra/job/JobDetail.vue new file mode 100644 index 00000000..18e4235b --- /dev/null +++ b/src/views/infra/job/JobDetail.vue @@ -0,0 +1,71 @@ +<template> + <Dialog title="任务详细" v-model="dialogVisible" width="700px"> + <el-descriptions border :column="1"> + <el-descriptions-item label="任务编号" min-width="60"> + {{ detailData.id }} + </el-descriptions-item> + <el-descriptions-item label="任务名称"> + {{ detailData.name }} + </el-descriptions-item> + <el-descriptions-item label="任务名称"> + <dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="detailData.status" /> + </el-descriptions-item> + <el-descriptions-item label="处理器的名字"> + {{ detailData.handlerName }} + </el-descriptions-item> + <el-descriptions-item label="处理器的参数"> + {{ detailData.handlerParam }} + </el-descriptions-item> + <el-descriptions-item label="Cron 表达式"> + {{ detailData.cronExpression }} + </el-descriptions-item> + <el-descriptions-item label="重试次数"> + {{ detailData.retryCount }} + </el-descriptions-item> + <el-descriptions-item label="重试间隔"> + {{ detailData.retryInterval + ' 毫秒' }} + </el-descriptions-item> + <el-descriptions-item label="监控超时时间"> + {{ detailData.monitorTimeout > 0 ? detailData.monitorTimeout + ' 毫秒' : '未开启' }} + </el-descriptions-item> + <el-descriptions-item label="后续执行时间"> + <el-timeline> + <el-timeline-item + v-for="(nextTime, index) in nextTimes" + :key="index" + :timestamp="formatDate(nextTime)" + > + 第 {{ index + 1 }} 次 + </el-timeline-item> + </el-timeline> + </el-descriptions-item> + </el-descriptions> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE } from '@/utils/dict' +import { formatDate } from '@/utils/formatTime' +import * as JobApi from '@/api/infra/job' + +const dialogVisible = ref(false) // 弹窗的是否展示 +const detailLoading = ref(false) // 表单的加载中 +const detailData = ref({}) // 详情数据 +const nextTimes = ref([]) // 下一轮执行时间的数组 + +/** 打开弹窗 */ +const open = async (id: number) => { + dialogVisible.value = true + // 查看,设置数据 + if (id) { + detailLoading.value = true + try { + detailData.value = await JobApi.getJob(id) + // 获取下一次执行时间 + nextTimes.value = await JobApi.getJobNextTimes(id) + } finally { + detailLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +</script> diff --git a/src/views/infra/job/form.vue b/src/views/infra/job/JobForm.vue similarity index 60% rename from src/views/infra/job/form.vue rename to src/views/infra/job/JobForm.vue index 24488fd7..9c03bd1c 100644 --- a/src/views/infra/job/form.vue +++ b/src/views/infra/job/JobForm.vue @@ -1,6 +1,5 @@ <template> - <!-- 添加或修改定时任务对话框 --> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -22,14 +21,7 @@ <el-input v-model="formData.handlerParam" placeholder="请输入处理器的参数" /> </el-form-item> <el-form-item label="CRON 表达式" prop="cronExpression"> - <el-input v-model="formData.cronExpression" placeholder="请输入CRON 表达式"> - <template #append> - <el-button type="primary" @click="handleShowCron"> - 生成表达式 - <i class="el-icon-time el-icon--right"></i> - </el-button> - </template> - </el-input> + <crontab v-model="formData.cronExpression" /> </el-form-item> <el-form-item label="重试次数" prop="retryCount"> <el-input @@ -47,56 +39,28 @@ <el-input v-model="formData.monitorTimeout" placeholder="请输入监控超时时间,单位:毫秒" /> </el-form-item> </el-form> - <!-- 操作按钮 --> <template #footer> - <!-- 按钮:保存 --> - <div class="dialog-footer"> - <el-button type="primary" @click="submitForm" :loading="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> - </div> + <el-button type="primary" @click="submitForm" :loading="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> - <el-dialog - title="Cron表达式生成器" - v-model="openCron" - append-to-body - class="scrollbar" - destroy-on-close - > - <crontab @hide="openCron = false" @fill="crontabFill" :expression="expression" /> - </el-dialog> </template> <script setup lang="ts" name="JobForm"> import * as JobApi from '@/api/infra/job' - -const emit = defineEmits(['success', 'crontabFill']) // 定义 success 事件,用于操作成功后的回调 - const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 -const defaultFormData = { +const formData = ref({ id: undefined, name: '', - status: 0, handlerName: '', handlerParam: '', - cronExpression: '', - retryCount: 0, - retryInterval: 0, - monitorTimeout: 0, - createTime: new Date() -} -const formData = ref({ ...defaultFormData }) - -// 是否显示Cron表达式弹出层 -const openCron = ref(false) -// 传入的表达式 -const expression = ref('') -// 表单校验 + cronExpression: '' +}) const formRules = reactive({ name: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }], handlerName: [{ required: true, message: '处理器的名字不能为空', trigger: 'blur' }], @@ -107,37 +71,25 @@ const formRules = reactive({ const formRef = ref() // 表单 Ref /** 打开弹窗 */ -const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 if (id) { formLoading.value = true try { - formData.value = await JobApi.getJobApi(id) + formData.value = await JobApi.getJob(id) } finally { formLoading.value = false } } } -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 -/** cron表达式按钮操作 */ -const handleShowCron = () => { - console.info(123333333333) - expression.value = formData.value.cronExpression - openCron.value = true -} - -// cron表达式填充 -const crontabFill = (expression: string) => { - formData.value.cronExpression = expression - emit('crontabFill', expression) -} - -// 提交按钮 +/** 提交按钮 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { // 校验表单 if (!formRef) return @@ -148,13 +100,13 @@ const submitForm = async () => { try { const data = formData.value as unknown as JobApi.JobVO if (formType.value === 'create') { - await JobApi.createJobApi(data) + await JobApi.createJob(data) message.success(t('common.createSuccess')) } else { - await JobApi.updateJobApi(data) + await JobApi.updateJob(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { @@ -165,7 +117,11 @@ const submitForm = async () => { /** 重置表单 */ const resetForm = () => { formData.value = { - ...defaultFormData + id: undefined, + name: '', + handlerName: '', + handlerParam: '', + cronExpression: '' } formRef.value?.resetFields() } diff --git a/src/views/infra/job/JobLogView.vue b/src/views/infra/job/JobLogView.vue deleted file mode 100644 index c66e0d80..00000000 --- a/src/views/infra/job/JobLogView.vue +++ /dev/null @@ -1,74 +0,0 @@ -<template> - <!-- 调度日志详细 --> - <Dialog title="调度日志详细" v-model="modelVisible" width="700px" append-to-body> - <el-form ref="form" :model="formData" label-width="120px" size="mini"> - <el-row> - <el-col :span="12"> - <el-form-item label="日志编号:">{{ formData.id }}</el-form-item> - <el-form-item label="任务编号:">{{ formData.jobId }}</el-form-item> - <el-form-item label="处理器的名字:">{{ formData.handlerName }}</el-form-item> - <el-form-item label="处理器的参数:">{{ formData.handlerParam }}</el-form-item> - <el-form-item label="第几次执行:">{{ formData.executeIndex }}</el-form-item> - <el-form-item label="执行时间:">{{ - parseTime(formData.beginTime) + ' ~ ' + parseTime(formData.endTime) - }}</el-form-item> - <el-form-item label="执行时长:">{{ formData.duration + ' 毫秒' }}</el-form-item> - <el-form-item label="任务状态:"> - <dict-tag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="formData.status" /> - </el-form-item> - <el-form-item label="执行结果:">{{ formData.result }}</el-form-item> - </el-col> - </el-row> - </el-form> - <template #footer> - <div class="dialog-footer"> - <el-button @click="close">关 闭</el-button> - </div> - </template> - </Dialog> -</template> -<script setup lang="ts" name="JobView"> -import * as JobLogApi from '@/api/infra/jobLog' -import { DICT_TYPE } from '@/utils/dict' -import { parseTime } from './utils' - -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 - -const { t } = useI18n() // 国际化 - -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const formData = ref({ - id: undefined, - jobId: undefined, - handlerParam: '', - handlerName: '', - executeIndex: '', - beginTime: undefined, - endTime: undefined, - duration: true, - result: '', - status: undefined -}) - -/** 打开弹窗 */ -const openModal = async (id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.detail') - // 查看,设置数据 - if (id) { - formLoading.value = true - try { - formData.value = await JobLogApi.getJobLogApi(id) - } finally { - formLoading.value = false - } - } -} -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 - -const close = () => { - emit('success') -} -</script> diff --git a/src/views/infra/job/index.vue b/src/views/infra/job/index.vue index 702b31fe..e113878a 100644 --- a/src/views/infra/job/index.vue +++ b/src/views/infra/job/index.vue @@ -1,19 +1,31 @@ <template> <content-wrap> - <!-- 搜索栏 --> - <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px"> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="100px" + > <el-form-item label="任务名称" prop="name"> <el-input v-model="queryParams.name" placeholder="请输入任务名称" clearable @keyup.enter="handleQuery" + class="!w-240px" /> </el-form-item> <el-form-item label="任务状态" prop="status"> - <el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable> + <el-select + v-model="queryParams.status" + placeholder="请选择任务状态" + clearable + class="!w-240px" + > <el-option - v-for="dict in getDictOptions(DICT_TYPE.INFRA_JOB_STATUS)" + v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_JOB_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value" @@ -26,6 +38,7 @@ placeholder="请输入处理器的名字" clearable @keyup.enter="handleQuery" + class="!w-240px" /> </el-form-item> <el-form-item> @@ -34,7 +47,7 @@ <el-button type="primary" plain - @click="openModal('create')" + @click="openForm('create')" v-hasPermi="['infra:job:create']" > <Icon icon="ep:plus" class="mr-5px" /> 新增 @@ -48,63 +61,66 @@ > <Icon icon="ep:download" class="mr-5px" /> 导出 </el-button> - <el-button type="info" plain @click="handleJobLog" v-hasPermi="['infra:job:query']"> <Icon icon="ep:zoom-in" class="mr-5px" /> 执行日志 </el-button> </el-form-item> </el-form> + </content-wrap> + <!-- 列表 --> + <content-wrap> <el-table v-loading="loading" :data="list"> <el-table-column label="任务编号" align="center" prop="id" /> <el-table-column label="任务名称" align="center" prop="name" /> <el-table-column label="任务状态" align="center" prop="status"> <template #default="scope"> <dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="scope.row.status" /> - </template> </el-table-column - >> + </template> + </el-table-column> <el-table-column label="处理器的名字" align="center" prop="handlerName" /> <el-table-column label="处理器的参数" align="center" prop="handlerParam" /> <el-table-column label="CRON 表达式" align="center" prop="cronExpression" /> - <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <el-table-column label="操作" align="center" width="200"> <template #default="scope"> <el-button + type="primary" link - icon="el-icon-edit" - @click="openModal('update', scope.row.id)" + @click="openForm('update', scope.row.id)" v-hasPermi="['infra:job:update']" - >修改</el-button > + 修改 + </el-button> <el-button + type="primary" link - icon="el-icon-check" @click="handleChangeStatus(scope.row)" v-hasPermi="['infra:job:update']" - >{{ scope.row.status === InfraJobStatusEnum.STOP ? '开启' : '暂停' }}</el-button > + {{ scope.row.status === InfraJobStatusEnum.STOP ? '开启' : '暂停' }} + </el-button> <el-button + type="danger" link - icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['infra:job:delete']" - >删除</el-button > + 删除 + </el-button> <el-dropdown - class="mt-1" - :teleported="true" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['infra:job:trigger', 'infra:job:query']" > - <el-button link icon="el-icon-d-arrow-right">更多</el-button> + <el-button type="primary" link><Icon icon="ep:d-arrow-right" /> 更多</el-button> <template #dropdown> <el-dropdown-menu> - <el-dropdown-item command="handleRun" v-if="hasPermi(['infra:job:trigger'])"> + <el-dropdown-item command="handleRun" v-if="checkPermi(['infra:job:trigger'])"> 执行一次 </el-dropdown-item> - <el-dropdown-item command="handleView" v-if="hasPermi(['infra:job:query'])"> + <el-dropdown-item command="openDetail" v-if="checkPermi(['infra:job:query'])"> 任务详细 </el-dropdown-item> - <el-dropdown-item command="handleJobLog" v-if="hasPermi(['infra:job:query'])"> + <el-dropdown-item command="handleJobLog" v-if="checkPermi(['infra:job:query'])"> 调度日志 </el-dropdown-item> </el-dropdown-menu> @@ -114,8 +130,7 @@ </el-table-column> </el-table> <!-- 分页组件 --> - <pagination - v-show="total > 0" + <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @@ -124,23 +139,21 @@ </content-wrap> <!-- 表单弹窗:添加/修改 --> - <job-form ref="modalRef" @success="getList" /> + <JobForm ref="formRef" @success="getList" /> <!-- 表单弹窗:查看 --> - <job-view ref="viewModalRef" @success="getList" /> + <JobDetail ref="detailRef" /> </template> - <script setup lang="ts" name="Job"> -import { DICT_TYPE, getDictOptions } from '@/utils/dict' -import JobForm from './form.vue' -import JobView from './view.vue' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { checkPermi } from '@/utils/permission' +import JobForm from './JobForm.vue' +import JobDetail from './JobDetail.vue' import download from '@/utils/download' import * as JobApi from '@/api/infra/job' import { InfraJobStatusEnum } from '@/utils/constants' -import { CACHE_KEY, useCache } from '@/hooks/web/useCache' - const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const { push } = useRouter() +const { push } = useRouter() // 路由 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 @@ -155,11 +168,11 @@ const queryParams = reactive({ const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中 -/** 查询参数列表 */ +/** 查询列表 */ const getList = async () => { loading.value = true try { - const data = await JobApi.getJobPageApi(queryParams) + const data = await JobApi.getJobPage(queryParams) list.value = data.list total.value = data.total } finally { @@ -167,30 +180,6 @@ const getList = async () => { } } -const handleChangeStatus = async (row: JobApi.JobVO) => { - const text = row.status === InfraJobStatusEnum.STOP ? '开启' : '关闭' - - const status = - row.status === InfraJobStatusEnum.STOP ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP - message - .confirm('确认要' + text + '定时任务编号为"' + row.id + '"的数据项?', t('common.reminder')) - .then(async () => { - row.status = - row.status === InfraJobStatusEnum.NORMAL - ? InfraJobStatusEnum.NORMAL - : InfraJobStatusEnum.STOP - await JobApi.updateJobStatusApi(row.id, status) - message.success(text + '成功') - await getList() - }) - .catch(() => { - row.status = - row.status === InfraJobStatusEnum.NORMAL - ? InfraJobStatusEnum.STOP - : InfraJobStatusEnum.NORMAL - }) -} - /** 搜索按钮操作 */ const handleQuery = () => { queryParams.pageNo = 1 @@ -203,10 +192,47 @@ const resetQuery = () => { handleQuery() } +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await JobApi.exportJob(queryParams) + download.excel(data, '定时任务.xls') + } catch { + } finally { + exportLoading.value = false + } +} + /** 添加/修改操作 */ -const modalRef = ref() -const openModal = (type: string, id?: number) => { - modalRef.value.openModal(type, id) +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 修改状态操作 */ +const handleChangeStatus = async (row: JobApi.JobVO) => { + try { + // 修改状态的二次确认 + const text = row.status === InfraJobStatusEnum.STOP ? '开启' : '关闭' + await message.confirm( + '确认要' + text + '定时任务编号为"' + row.id + '"的数据项?', + t('common.reminder') + ) + const status = + row.status === InfraJobStatusEnum.STOP ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP + await JobApi.updateJobStatus(row.id, status) + message.success(text + '成功') + // 刷新列表 + await getList() + } catch { + // 取消后,进行恢复按钮 + row.status = + row.status === InfraJobStatusEnum.NORMAL ? InfraJobStatusEnum.STOP : InfraJobStatusEnum.NORMAL + } } /** 删除按钮操作 */ @@ -215,43 +241,21 @@ const handleDelete = async (id: number) => { // 删除的二次确认 await message.delConfirm() // 发起删除 - await JobApi.deleteJobApi(id) + await JobApi.deleteJob(id) message.success(t('common.delSuccess')) // 刷新列表 await getList() } catch {} } -/** 查看操作 */ -const viewModalRef = ref() -const handleView = (rowId?: number) => { - viewModalRef.value.openModal(rowId) -} -// 执行日志 -const handleJobLog = (rowId?: number) => { - if (rowId) { - push('/job/job-log?id=' + rowId) - } else { - push('/job/job-log') - } -} -// 执行一次 -const handleRun = (row: JobApi.JobVO) => { - message.confirm('确认要立即执行一次' + row.name + '?', t('common.reminder')).then(async () => { - await JobApi.runJobApi(row.id) - message.success('执行成功') - await getList() - }) -} - /** '更多'操作按钮 */ const handleCommand = (command, row) => { switch (command) { case 'handleRun': handleRun(row) break - case 'handleView': - handleView(row?.id) + case 'openDetail': + openDetail(row.id) break case 'handleJobLog': handleJobLog(row?.id) @@ -261,36 +265,32 @@ const handleCommand = (command, row) => { } } -/** 导出按钮操作 */ -const handleExport = async () => { +/** 执行一次 */ +const handleRun = async (row: JobApi.JobVO) => { try { - // 导出的二次确认 - await message.exportConfirm() - // 发起导出 - exportLoading.value = true - const data = await JobApi.exportJobApi(queryParams) - download.excel(data, '定时任务.xls') - } catch { - } finally { - exportLoading.value = false - } + // 二次确认 + await message.confirm('确认要立即执行一次' + row.name + '?', t('common.reminder')) + // 提交执行 + await JobApi.runJobApi(row.id) + message.success('执行成功') + // 刷新列表 + await getList() + } catch {} } -// 权限判断:dropdown 与 v-hasPermi有冲突会造成大量的waring,改用v-if调用此方法 -const hasPermi = (permiKeys: string[]) => { - const { wsCache } = useCache() - const all_permission = '*:*:*' - const permissions = wsCache.get(CACHE_KEY.USER).permissions +/** 查看操作 */ +const detailRef = ref() +const openDetail = (id: number) => { + detailRef.value.open(id) +} - if (permiKeys && permiKeys instanceof Array && permiKeys.length > 0) { - const permissionFlag = permiKeys - - const hasPermissions = permissions.some((permission: string) => { - return all_permission === permission || permissionFlag.includes(permission) - }) - return hasPermissions +/** 跳转执行日志 */ +const handleJobLog = (id: number) => { + if (id) { + push('/job/job-log?id=' + id) + } else { + push('/job/job-log') } - return false } /** 初始化 **/ diff --git a/src/views/infra/job/logger/JobLogDetail.vue b/src/views/infra/job/logger/JobLogDetail.vue new file mode 100644 index 00000000..3b8795d9 --- /dev/null +++ b/src/views/infra/job/logger/JobLogDetail.vue @@ -0,0 +1,57 @@ +<template> + <Dialog title="任务详细" v-model="dialogVisible" width="700px"> + <el-descriptions border :column="1"> + <el-descriptions-item label="日志编号" min-width="60"> + {{ detailData.id }} + </el-descriptions-item> + <el-descriptions-item label="任务编号"> + {{ detailData.jobId }} + </el-descriptions-item> + <el-descriptions-item label="处理器的名字"> + {{ detailData.handlerName }} + </el-descriptions-item> + <el-descriptions-item label="处理器的参数"> + {{ detailData.handlerParam }} + </el-descriptions-item> + <el-descriptions-item label="第几次执行"> + {{ detailData.executeIndex }} + </el-descriptions-item> + <el-descriptions-item label="执行时间"> + {{ formatDate(detailData.beginTime) + ' ~ ' + formatDate(detailData.endTime) }} + </el-descriptions-item> + <el-descriptions-item label="执行时长"> + {{ detailData.duration + ' 毫秒' }} + </el-descriptions-item> + <el-descriptions-item label="任务状态"> + <dict-tag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="detailData.status" /> + </el-descriptions-item> + <el-descriptions-item label="执行结果"> + {{ detailData.duration + ' result' }} + </el-descriptions-item> + </el-descriptions> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE } from '@/utils/dict' +import { formatDate } from '@/utils/formatTime' +import * as JobLogApi from '@/api/infra/jobLog' + +const dialogVisible = ref(false) // 弹窗的是否展示 +const detailLoading = ref(false) // 表单的加载中 +const detailData = ref({}) // 详情数据 + +/** 打开弹窗 */ +const open = async (id: number) => { + dialogVisible.value = true + // 查看,设置数据 + if (id) { + detailLoading.value = true + try { + detailData.value = await JobLogApi.getJobLog(id) + } finally { + detailLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +</script> diff --git a/src/views/infra/job/JobLog.vue b/src/views/infra/job/logger/index.vue similarity index 74% rename from src/views/infra/job/JobLog.vue rename to src/views/infra/job/logger/index.vue index daa20046..f55a0647 100644 --- a/src/views/infra/job/JobLog.vue +++ b/src/views/infra/job/logger/index.vue @@ -1,37 +1,52 @@ <template> <content-wrap> - <!-- 搜索栏 --> - <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px"> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="120px" + > <el-form-item label="处理器的名字" prop="handlerName"> <el-input v-model="queryParams.handlerName" placeholder="请输入处理器的名字" clearable @keyup.enter="handleQuery" + class="!w-240px" /> </el-form-item> <el-form-item label="开始执行时间" prop="beginTime"> <el-date-picker - clearable v-model="queryParams.beginTime" type="date" - value-format="YYYY-MM-DD" + value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择开始执行时间" + clearable + class="!w-240px" /> </el-form-item> <el-form-item label="结束执行时间" prop="endTime"> <el-date-picker - clearable v-model="queryParams.endTime" type="date" - value-format="YYYY-MM-DD" + value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择结束执行时间" + clearable + :default-time="new Date('1 23:59:59')" + class="!w-240px" /> </el-form-item> <el-form-item label="任务状态" prop="status"> - <el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable> + <el-select + v-model="queryParams.status" + placeholder="请选择任务状态" + clearable + class="!w-240px" + > <el-option - v-for="dict in getDictOptions(DICT_TYPE.INFRA_JOB_LOG_STATUS)" + v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_JOB_LOG_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value" @@ -52,16 +67,19 @@ </el-button> </el-form-item> </el-form> + </content-wrap> + <!-- 列表 --> + <content-wrap> <el-table v-loading="loading" :data="list"> <el-table-column label="日志编号" align="center" prop="id" /> <el-table-column label="任务编号" align="center" prop="jobId" /> <el-table-column label="处理器的名字" align="center" prop="handlerName" /> <el-table-column label="处理器的参数" align="center" prop="handlerParam" /> <el-table-column label="第几次执行" align="center" prop="executeIndex" /> - <el-table-column label="执行时间" align="center" width="180"> + <el-table-column label="执行时间" align="center" width="170s"> <template #default="scope"> - <span>{{ parseTime(scope.row.beginTime) + ' ~ ' + parseTime(scope.row.endTime) }}</span> + <span>{{ formatDate(scope.row.beginTime) + ' ~ ' + formatDate(scope.row.endTime) }}</span> </template> </el-table-column> <el-table-column label="执行时长" align="center" prop="duration"> @@ -74,40 +92,39 @@ <dict-tag :type="DICT_TYPE.INFRA_JOB_LOG_STATUS" :value="scope.row.status" /> </template> </el-table-column> - <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <el-table-column label="操作" align="center"> <template #default="scope"> <el-button + type="primary" link - icon="el-icon-view" - @click="handleView(scope.row.id)" - :loading="exportLoading" + @click="openDetail(scope.row.id)" v-hasPermi="['infra:job:query']" - >详细 + > + 详细 </el-button> </template> </el-table-column> </el-table> - - <pagination - v-show="total > 0" + <!-- 分页组件 --> + <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /> </content-wrap> + <!-- 表单弹窗:查看 --> - <log-view ref="viewModalRef" @success="getList" /> + <JobLogDetail ref="detailRef" /> </template> - <script setup lang="ts" name="JobLog"> -import { DICT_TYPE, getDictOptions } from '@/utils/dict' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { formatDate } from '@/utils/formatTime' import download from '@/utils/download' -import LogView from './JobLogView.vue' +import JobLogDetail from './JobLogDetail.vue' import * as JobLogApi from '@/api/infra/jobLog' -import { parseTime } from './utils' - const message = useMessage() // 消息弹窗 +const { query } = useRoute() // 查询参数 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 @@ -115,6 +132,7 @@ const list = ref([]) // 列表的数据 const queryParams = reactive({ pageNo: 1, pageSize: 10, + jobId: query.id, handlerName: undefined, beginTime: undefined, endTime: undefined, @@ -123,15 +141,11 @@ const queryParams = reactive({ const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中 -/** 查询参数列表 */ +/** 查询列表 */ const getList = async () => { loading.value = true try { - const data = await JobLogApi.getJobLogPageApi({ - ...queryParams, - beginTime: queryParams.beginTime ? queryParams.beginTime + ' 00:00:00' : undefined, - endTime: queryParams.endTime ? queryParams.endTime + ' 23:59:59' : undefined - }) + const data = await JobLogApi.getJobLogPage(queryParams) list.value = data.list total.value = data.total } finally { @@ -152,9 +166,9 @@ const resetQuery = () => { } /** 查看操作 */ -const viewModalRef = ref() -const handleView = (rowId?: number) => { - viewModalRef.value.openModal(rowId) +const detailRef = ref() +const openDetail = (rowId?: number) => { + detailRef.value.open(rowId) } /** 导出按钮操作 */ @@ -164,7 +178,7 @@ const handleExport = async () => { await message.exportConfirm() // 发起导出 exportLoading.value = true - const data = await JobLogApi.exportJobLogApi(queryParams) + const data = await JobLogApi.exportJobLog(queryParams) download.excel(data, '定时任务执行日志.xls') } catch { } finally { diff --git a/src/views/infra/job/utils.ts b/src/views/infra/job/utils.ts deleted file mode 100644 index a3774f22..00000000 --- a/src/views/infra/job/utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const parseTime = (time) => { - if (!time) { - return null - } - const format = '{y}-{m}-{d} {h}:{i}:{s}' - let date - if (typeof time === 'object') { - date = time - } else { - if (typeof time === 'string' && /^[0-9]+$/.test(time)) { - time = parseInt(time) - } else if (typeof time === 'string') { - time = time - .replace(new RegExp(/-/gm), '/') - .replace('T', ' ') - .replace(new RegExp(/\.[\d]{3}/gm), '') - } - if (typeof time === 'number' && time.toString().length === 10) { - time = time * 1000 - } - date = new Date(time) - } - const formatObj = { - y: date.getFullYear(), - m: date.getMonth() + 1, - d: date.getDate(), - h: date.getHours(), - i: date.getMinutes(), - s: date.getSeconds(), - a: date.getDay() - } - const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { - let value = formatObj[key] - // Note: getDay() returns 0 on Sunday - if (key === 'a') { - return ['日', '一', '二', '三', '四', '五', '六'][value] - } - if (result.length > 0 && value < 10) { - value = '0' + value - } - return value || 0 - }) - return time_str -} diff --git a/src/views/infra/job/view.vue b/src/views/infra/job/view.vue deleted file mode 100644 index d195e0e3..00000000 --- a/src/views/infra/job/view.vue +++ /dev/null @@ -1,89 +0,0 @@ -<template> - <!-- 任务详细 --> - <Dialog title="任务详细" v-model="modelVisible" width="700px" append-to-body> - <el-form ref="formRef" :model="formData" label-width="200px"> - <el-row> - <el-col :span="24"> - <el-form-item label="任务编号:">{{ formData.id }}</el-form-item> - <el-form-item label="任务名称:">{{ formData.name }}</el-form-item> - <el-form-item label="任务名称:"> - <dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="formData.status" /> - </el-form-item> - <el-form-item label="处理器的名字:">{{ formData.handlerName }}</el-form-item> - <el-form-item label="处理器的参数:">{{ formData.handlerParam }}</el-form-item> - <el-form-item label="cron表达式:">{{ formData.cronExpression }}</el-form-item> - <el-form-item label="重试次数:">{{ formData.retryCount }}</el-form-item> - <el-form-item label="重试间隔:">{{ formData.retryInterval + ' 毫秒' }}</el-form-item> - <el-form-item label="监控超时时间:">{{ - formData.monitorTimeout > 0 ? formData.monitorTimeout + ' 毫秒' : '未开启' - }}</el-form-item> - <el-form-item label="后续执行时间:"> - <el-timeline class="pt-3"> - <el-timeline-item - v-for="(activity, index) in nextTimes" - :key="index" - :timestamp="parseTime(activity)" - > - 第{{ index + 1 }}次 - </el-timeline-item> - </el-timeline> - </el-form-item> - </el-col> - </el-row> - </el-form> - <template #footer> - <div class="dialog-footer"> - <el-button @click="close">关 闭</el-button> - </div> - </template> - </Dialog> -</template> -<script setup lang="ts" name="JobView"> -import * as JobApi from '@/api/infra/job' -import { parseTime } from './utils' -import { DICT_TYPE } from '@/utils/dict' - -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 - -const { t } = useI18n() // 国际化 - -const formRef = ref() // 表单 Ref -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const formData = ref({ - id: undefined, - name: '', - handlerParam: '', - handlerName: '', - cronExpression: '', - retryCount: true, - retryInterval: '', - monitorTimeout: 0, - status: 0 -}) -const nextTimes = ref([]) - -/** 打开弹窗 */ -const openModal = async (id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.detail') - // 查看,设置数据 - if (id) { - formLoading.value = true - try { - formData.value = await JobApi.getJobApi(id) - // 获取下一次执行时间 - nextTimes.value = await JobApi.getJobNextTimesApi(id) - } finally { - formLoading.value = false - } - } -} -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 - -const close = () => { - modelVisible.value = false - emit('success') -} -</script> diff --git a/src/views/mall/product/category/CategoryForm.vue b/src/views/mall/product/category/CategoryForm.vue new file mode 100644 index 00000000..db395a66 --- /dev/null +++ b/src/views/mall/product/category/CategoryForm.vue @@ -0,0 +1,147 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="80px" + v-loading="formLoading" + > + <el-form-item label="上级分类" prop="parentId"> + <el-tree-select + v-model="formData.parentId" + :data="categoryTree" + :props="{ label: 'name', value: 'id' }" + :render-after-expand="false" + placeholder="请选择上级分类" + check-strictly + default-expand-all + /> + </el-form-item> + <el-form-item label="分类名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入分类名称" /> + </el-form-item> + <el-form-item label="分类图片" prop="picUrl"> + <UploadImg v-model="formData.picUrl" :limit="1" :is-show-tip="false" /> + <div v-if="formData.parentId === 0" style="font-size: 10px">推荐 200x100 图片分辨率</div> + <div v-else style="font-size: 10px">推荐 100x100 图片分辨率</div> + </el-form-item> + <el-form-item label="分类排序" prop="sort"> + <el-input-number v-model="formData.sort" controls-position="right" :min="0" /> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="分类描述"> + <el-input v-model="formData.description" type="textarea" placeholder="请输入分类描述" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { CommonStatusEnum } from '@/utils/constants' +import { handleTree } from '@/utils/tree' +import * as ProductCategoryApi from '@/api/mall/product/category' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: '', + picUrl: '', + status: CommonStatusEnum.ENABLE, + description: '' +}) +const formRules = reactive({ + parentId: [{ required: true, message: '请选择上级分类', trigger: 'blur' }], + name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }], + picUrl: [{ required: true, message: '分类图片不能为空', trigger: 'blur' }], + sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }], + status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const categoryTree = ref<any[]>([]) // 分类树 + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ProductCategoryApi.getCategory(id) + } finally { + formLoading.value = false + } + } + // 获得分类树 + await getTree() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = formData.value as ProductCategoryApi.CategoryVO + if (formType.value === 'create') { + await ProductCategoryApi.createCategory(data) + message.success(t('common.createSuccess')) + } else { + await ProductCategoryApi.updateCategory(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: '', + picUrl: '', + status: CommonStatusEnum.ENABLE, + description: '' + } + formRef.value?.resetFields() +} + +/** 获得分类树 */ +const getTree = async () => { + const data = await ProductCategoryApi.getCategoryList({}) + const tree = handleTree(data, 'id', 'parentId') + const menu = { id: 0, name: '顶级分类', children: tree } + categoryTree.value = [menu] +} +</script> diff --git a/src/views/mall/product/category/index.vue b/src/views/mall/product/category/index.vue new file mode 100644 index 00000000..f57e35f8 --- /dev/null +++ b/src/views/mall/product/category/index.vue @@ -0,0 +1,144 @@ +<template> + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="分类名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入分类名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['product:category:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" row-key="id" default-expand-all> + <el-table-column label="分类名称" prop="name" sortable /> + <el-table-column label="分类图片" align="center" prop="picUrl"> + <template #default="scope"> + <img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" class="h-100px" /> + </template> + </el-table-column> + <el-table-column label="分类排序" align="center" prop="sort" /> + <el-table-column label="开启状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['product:category:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['product:category:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <CategoryForm ref="formRef" @success="getList" /> +</template> +<script setup lang="ts" name="ProductCategory"> +import { DICT_TYPE } from '@/utils/dict' +import { handleTree } from '@/utils/tree' +import { dateFormatter } from '@/utils/formatTime' +import * as ProductCategoryApi from '@/api/mall/product/category' +import CategoryForm from './CategoryForm.vue' +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<any[]>([]) // 列表的数据 +const queryParams = reactive({ + name: undefined +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ProductCategoryApi.getCategoryList(queryParams) + list.value = handleTree(data, 'id', 'parentId') + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ProductCategoryApi.deleteCategory(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/mall/product/property/PropertyForm.vue b/src/views/mall/product/property/PropertyForm.vue new file mode 100644 index 00000000..c9a21d64 --- /dev/null +++ b/src/views/mall/product/property/PropertyForm.vue @@ -0,0 +1,95 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="80px" + v-loading="formLoading" + > + <el-form-item label="名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名称" /> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="formData.remark" type="textarea" placeholder="请输入内容" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </div> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as PropertyApi from '@/api/mall/product/property' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: '' +}) +const formRules = reactive({ + name: [{ required: true, message: '名称不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await PropertyApi.getProperty(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = formData.value as PropertyApi.PropertyVO + if (formType.value === 'create') { + await PropertyApi.createProperty(data) + message.success(t('common.createSuccess')) + } else { + await PropertyApi.updateProperty(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: '' + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/mall/product/property/index.vue b/src/views/mall/product/property/index.vue new file mode 100644 index 00000000..6c2e6e97 --- /dev/null +++ b/src/views/mall/product/property/index.vue @@ -0,0 +1,160 @@ +<template> + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + 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-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + plain + type="primary" + @click="openForm('create')" + v-hasPermi="['product:property:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名称" align="center" /> + <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['product:property:update']" + > + 编辑 + </el-button> + <el-button link type="primary"> + <router-link :to="'/property/value/' + scope.row.id">属性值</router-link> + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['product:property:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <PropertyForm ref="formRef" @success="getList" /> +</template> +<script setup lang="ts" name="Config"> +import { dateFormatter } from '@/utils/formatTime' +import * as PropertyApi from '@/api/mall/product/property' +import PropertyForm from './PropertyForm.vue' +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await PropertyApi.getPropertyPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await PropertyApi.deleteProperty(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/mall/product/property/value/ValueForm.vue b/src/views/mall/product/property/value/ValueForm.vue new file mode 100644 index 00000000..3f6a5225 --- /dev/null +++ b/src/views/mall/product/property/value/ValueForm.vue @@ -0,0 +1,102 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="80px" + v-loading="formLoading" + > + <el-form-item label="属性编号" prop="category"> + <el-input v-model="formData.propertyId" disabled="" /> + </el-form-item> + <el-form-item label="名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名称" /> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="formData.remark" type="textarea" placeholder="请输入内容" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as PropertyApi from '@/api/mall/product/property' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + propertyId: undefined, + name: '', + remark: '' +}) +const formRules = reactive({ + propertyId: [{ required: true, message: '属性不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名称不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, propertyId: number, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + formData.value.propertyId = propertyId + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await PropertyApi.getPropertyValue(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = formData.value as PropertyApi.PropertyValueVO + if (formType.value === 'create') { + await PropertyApi.createPropertyValue(data) + message.success(t('common.createSuccess')) + } else { + await PropertyApi.updatePropertyValue(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + propertyId: undefined, + name: '', + remark: '' + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/mall/product/property/value/index.vue b/src/views/mall/product/property/value/index.vue new file mode 100644 index 00000000..6f5ae5c8 --- /dev/null +++ b/src/views/mall/product/property/value/index.vue @@ -0,0 +1,160 @@ +<template> + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="属性项" prop="propertyId"> + <el-select v-model="queryParams.propertyId" class="!w-240px"> + <el-option + v-for="item in propertyOptions" + :key="item.id" + :label="item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + plain + type="primary" + @click="openForm('create')" + v-hasPermi="['product:property:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名称" align="center" prop="name" :show-overflow-tooltip="true" /> + <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['product:property:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['product:property:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ValueForm ref="formRef" @success="getList" /> +</template> +<script setup lang="ts" name="Config"> +import { dateFormatter } from '@/utils/formatTime' +import * as PropertyApi from '@/api/mall/product/property' +import ValueForm from './ValueForm.vue' +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 +const { params } = useRoute() // 查询参数 + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + propertyId: Number(params.propertyId), + name: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const propertyOptions = ref([]) // 属性项的列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await PropertyApi.getPropertyValuePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, queryParams.propertyId, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await PropertyApi.deleteProperty(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + await getList() + // 属性项下拉框数据 + propertyOptions.value = await PropertyApi.getPropertyList({}) +}) +</script> diff --git a/src/views/mp/account/AccountForm.vue b/src/views/mp/account/AccountForm.vue index 406db8fe..a80bb48f 100644 --- a/src/views/mp/account/AccountForm.vue +++ b/src/views/mp/account/AccountForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -64,7 +64,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -73,8 +73,8 @@ import * as AccountApi from '@/api/mp/account' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -98,8 +98,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -132,7 +132,7 @@ const submitForm = async () => { await AccountApi.updateAccount(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/mp/freePublish/index.vue b/src/views/mp/freePublish/index.vue index 05f3dec6..8a3d5285 100644 --- a/src/views/mp/freePublish/index.vue +++ b/src/views/mp/freePublish/index.vue @@ -19,14 +19,8 @@ </el-select> </el-form-item> <el-form-item> - <el-button @click="handleQuery"> - <Icon icon="ep:search" class="mr-5px" /> - 搜索 - </el-button> - <el-button @click="resetQuery"> - <Icon icon="ep:refresh" class="mr-5px" /> - 重置 - </el-button> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> </el-form-item> </el-form> </content-wrap> @@ -41,7 +35,6 @@ :key="item.articleId" > <wx-news :articles="item.content.newsItem" /> - <!-- 操作 --> <el-row justify="center" class="ope-row"> <el-button type="danger" @@ -54,9 +47,8 @@ </el-row> </div> </div> - <!-- 分页组件 --> - <pagination - v-show="total > 0" + <!-- 分页 --> + <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @@ -66,24 +58,18 @@ </template> <script setup lang="ts" name="freePublish"> -import { getFreePublishPage, deleteFreePublish } from '@/api/mp/freePublish' +import * as FreePublishApi from '@/api/mp/freePublish' import * as MpAccountApi from '@/api/mp/account' import WxNews from '@/views/mp/components/wx-news/main.vue' - const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 const list = ref([]) // 列表的数据 -interface QueryParams { - currentPage: number | undefined | string - pageNo: number | undefined | string - accountId: number | undefined | string -} - -const queryParams: QueryParams = reactive({ - currentPage: 1, // 当前页数 - pageNo: 1, // 当前页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, accountId: undefined // 当前页数 }) const queryFormRef = ref() // 搜索的表单 @@ -96,25 +82,14 @@ const getList = async () => { message.error('未选中公众号,无法查询已发表图文') return false } - // TODO 改成 await 形式 - loading.value = true - getFreePublishPage(queryParams) - .then((data) => { - console.log(data) - // 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面 - data.list.forEach((item) => { - console.log(item) - const newsItem = item.content.newsItem - newsItem.forEach((article) => { - article.picUrl = article.thumbUrl - }) - }) - list.value = data.list - total.value = data.total - }) - .finally(() => { - loading.value = false - }) + try { + loading.value = true + const data = await FreePublishApi.getFreePublishPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } } /** 搜索按钮操作 */ @@ -135,21 +110,15 @@ const resetQuery = () => { /** 删除按钮操作 */ const handleDelete = async (item) => { - { - // TODO 改成 await 形式 - const articleId = item.articleId - const accountId = queryParams.accountId - message - .confirm('删除后用户将无法访问此页面,确定删除?') - .then(function () { - return deleteFreePublish(accountId, articleId) - }) - .then(() => { - getList() - message.success('删除成功') - }) - .catch(() => {}) - } + try { + // 删除的二次确认 + await message.delConfirm('删除后用户将无法访问此页面,确定删除?') + // 发起删除 + await FreePublishApi.deleteFreePublish(queryParams.accountId, item.articleId) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} } onMounted(async () => { @@ -162,15 +131,6 @@ onMounted(async () => { }) </script> <style lang="scss" scoped> -.pagination { - float: right; - margin-right: 25px; -} - -.add_but { - padding: 10px; -} - .ope-row { margin-top: 5px; text-align: center; diff --git a/src/views/mp/tag/TagForm.vue b/src/views/mp/tag/TagForm.vue index db251cdf..e190af89 100644 --- a/src/views/mp/tag/TagForm.vue +++ b/src/views/mp/tag/TagForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -13,7 +13,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -22,8 +22,8 @@ import * as MpTagApi from '@/api/mp/tag' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -37,8 +37,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const open = async (type: string, accountId: number, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() formData.value.accountId = accountId @@ -72,7 +72,7 @@ const submitForm = async () => { await MpTagApi.updateTag(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/mp/tag/index.vue b/src/views/mp/tag/index.vue index bedfbad8..8a4b731d 100644 --- a/src/views/mp/tag/index.vue +++ b/src/views/mp/tag/index.vue @@ -12,9 +12,9 @@ <el-select v-model="queryParams.accountId" placeholder="请选择公众号" class="!w-240px"> <el-option v-for="item in accountList" - :key="parseInt(item.id)" + :key="item.id" :label="item.name" - :value="parseInt(item.id)" + :value="item.id" /> </el-select> </el-form-item> @@ -28,21 +28,15 @@ /> </el-form-item> <el-form-item> - <el-button @click="handleQuery"> - <Icon icon="ep:search" class="mr-5px" /> - 搜索 - </el-button> - <el-button @click="resetQuery"> - <Icon icon="ep:refresh" class="mr-5px" /> - 重置 - </el-button> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['mp:tag:create']"> - <Icon icon="ep:plus" class="mr-5px" /> - 新增 + <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> <el-button type="success" plain @click="handleSync" v-hasPermi="['mp:tag:sync']"> - <Icon icon="ep:refresh" class="mr-5px" /> - 同步 + <Icon icon="ep:refresh" class="mr-5px" /> 同步 </el-button> </el-form-item> </el-form> @@ -99,7 +93,6 @@ import { dateFormatter } from '@/utils/formatTime' import * as MpTagApi from '@/api/mp/tag' import * as MpAccountApi from '@/api/mp/account' import TagForm from './TagForm.vue' - const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 @@ -143,7 +136,6 @@ const resetQuery = () => { queryFormRef.value.resetFields() // 默认选中第一个 if (accountList.value.length > 0) { - // @ts-ignore queryParams.accountId = accountList.value[0].id } handleQuery() @@ -184,7 +176,6 @@ onMounted(async () => { accountList.value = await MpAccountApi.getSimpleAccountList() // 选中第一个 if (accountList.value.length > 0) { - // @ts-ignore queryParams.accountId = accountList.value[0].id } await getList() diff --git a/src/views/pay/merchant/MerchantForm.vue b/src/views/pay/merchant/MerchantForm.vue new file mode 100644 index 00000000..34dea00a --- /dev/null +++ b/src/views/pay/merchant/MerchantForm.vue @@ -0,0 +1,110 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px"> + <el-form-item label="商户全称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入商户全称" /> + </el-form-item> + <el-form-item label="商户简称" prop="shortName"> + <el-input v-model="formData.shortName" placeholder="请输入商户简称" /> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-select v-model="formData.status" placeholder="请选择状态" clearable> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as MerchantApi from '@/api/pay/merchant' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { CommonStatusEnum } from '@/utils/constants' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: '', + shortName: '', + status: CommonStatusEnum.ENABLE, + remark: '' +}) +const formRules = reactive({ + name: [{ required: true, message: '商户名称不能为空', trigger: 'blur' }], + shortName: [{ required: true, message: '商户简称不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'change' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await MerchantApi.getMerchant(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as MerchantApi.MerchantVO + if (formType.value === 'create') { + await MerchantApi.createMerchant(data) + message.success(t('common.createSuccess')) + } else { + await MerchantApi.updateMerchant(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: '', + shortName: '', + status: CommonStatusEnum.ENABLE, + remark: '' + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/pay/merchant/index.vue b/src/views/pay/merchant/index.vue index 1ea460ec..d2e487c3 100644 --- a/src/views/pay/merchant/index.vue +++ b/src/views/pay/merchant/index.vue @@ -1,153 +1,242 @@ <template> - <ContentWrap> - <!-- 列表 --> - <XTable @register="registerTable"> - <template #toolbar_buttons> - <!-- 操作:新增 --> - <XButton + <content-wrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="商户号" prop="no"> + <el-input v-model="queryParams.no" placeholder="请输入商户号" clearable class="!w-240px" /> + </el-form-item> + <el-form-item label="商户全称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入商户全称" + clearable + class="!w-240px" + /> + </el-form-item> + <el-form-item label="商户简称" prop="shortName"> + <el-input + v-model="queryParams.shortName" + placeholder="请输入商户简称" + clearable + class="!w-240px" + /> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="字典状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input + v-model="queryParams.remark" + placeholder="请输入备注" + clearable + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="datetimerange" + start-placeholder="开始日期" + end-placeholder="结束日期" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + plain type="primary" - preIcon="ep:zoom-in" - :title="t('action.add')" + @click="openForm('create')" v-hasPermi="['pay:merchant:create']" - @click="handleCreate()" - /> - <!-- 操作:导出 --> - <XButton - type="warning" - preIcon="ep:download" - :title="t('action.export')" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" v-hasPermi="['pay:merchant:export']" - @click="exportList('商户列表.xls')" - /> - </template> - <template #actionbtns_default="{ row }"> - <!-- 操作:修改 --> - <XTextButton - preIcon="ep:edit" - :title="t('action.edit')" - v-hasPermi="['pay:merchant:update']" - @click="handleUpdate(row.id)" - /> - <!-- 操作:详情 --> - <XTextButton - preIcon="ep:view" - :title="t('action.detail')" - v-hasPermi="['pay:merchant:query']" - @click="handleDetail(row.id)" - /> - <!-- 操作:删除 --> - <XTextButton - preIcon="ep:delete" - :title="t('action.del')" - v-hasPermi="['pay:merchant:delete']" - @click="deleteData(row.id)" - /> - </template> - </XTable> - </ContentWrap> - <XModal v-model="dialogVisible" :title="dialogTitle"> - <!-- 对话框(添加 / 修改) --> - <Form - v-if="['create', 'update'].includes(actionType)" - :schema="allSchemas.formSchema" - :rules="rules" - ref="formRef" - /> - <!-- 对话框(详情) --> - <Descriptions - v-if="actionType === 'detail'" - :schema="allSchemas.detailSchema" - :data="detailData" - /> - <!-- 操作按钮 --> - <template #footer> - <!-- 按钮:保存 --> - <XButton - v-if="['create', 'update'].includes(actionType)" - type="primary" - :title="t('action.save')" - :loading="actionLoading" - @click="submitForm()" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </content-wrap> + + <!-- 列表 --> + <content-wrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="商户编号" align="center" prop="id" /> + <el-table-column label="商户号" align="center" prop="no" /> + <el-table-column label="商户全称" align="center" prop="name" /> + <el-table-column label="商户简称" align="center" prop="shortName" /> + <el-table-column label="开启状态" align="center" prop="status"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + @change="handleStatusChange(scope.row)" + /> + </template> + </el-table-column> + <el-table-column label="备注" align="center" prop="remark" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180" /> - <!-- 按钮:关闭 --> - <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" /> - </template> - </XModal> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['pay:merchant:update']" + > + 修改 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['pay:merchant:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </content-wrap> + + <!-- 表单弹窗:添加/修改 --> + <MerchantForm ref="formRef" @success="getList" /> </template> <script setup lang="ts" name="Merchant"> -import type { FormExpose } from '@/components/Form' -import { rules, allSchemas } from './merchant.data' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { CommonStatusEnum } from '@/utils/constants' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' import * as MerchantApi from '@/api/pay/merchant' - -const { t } = useI18n() // 国际化 +import MerchantForm from './MerchantForm.vue' const message = useMessage() // 消息弹窗 -// 列表相关的变量 -const [registerTable, { reload, deleteData, exportList }] = useXTable({ - allSchemas: allSchemas, - getListApi: MerchantApi.getMerchantPageApi, - deleteApi: MerchantApi.deleteMerchantApi, - exportListApi: MerchantApi.exportMerchantApi +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: '', + shortName: '', + status: undefined }) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 -// ========== CRUD 相关 ========== -const actionLoading = ref(false) // 遮罩层 -const actionType = ref('') // 操作按钮的类型 -const dialogVisible = ref(false) // 是否显示弹出层 -const dialogTitle = ref('edit') // 弹出层标题 -const formRef = ref<FormExpose>() // 表单 Ref -const detailData = ref() // 详情 Ref - -// 设置标题 -const setDialogTile = (type: string) => { - dialogTitle.value = t('action.' + type) - actionType.value = type - dialogVisible.value = true +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await MerchantApi.getMerchantPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } } -// 新增操作 -const handleCreate = () => { - setDialogTile('create') +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() } -// 修改操作 -const handleUpdate = async (rowId: number) => { - setDialogTile('update') - // 设置数据 - const res = await MerchantApi.getMerchantApi(rowId) - unref(formRef)?.setValues(res) +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() } -// 详情操作 -const handleDetail = async (rowId: number) => { - setDialogTile('detail') - const res = await MerchantApi.getMerchantApi(rowId) - detailData.value = res +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) } -// 提交按钮 -const submitForm = async () => { - const elForm = unref(formRef)?.getElFormRef() - if (!elForm) return - elForm.validate(async (valid) => { - if (valid) { - actionLoading.value = true - // 提交请求 - try { - const data = unref(formRef)?.formModel as MerchantApi.MerchantVO - if (actionType.value === 'create') { - await MerchantApi.createMerchantApi(data) - message.success(t('common.createSuccess')) - } else { - await MerchantApi.updateMerchantApi(data) - message.success(t('common.updateSuccess')) - } - dialogVisible.value = false - } finally { - actionLoading.value = false - // 刷新列表 - await reload() - } - } - }) +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await MerchantApi.deleteMerchant(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} } + +/** 修改状态操作 */ +const handleStatusChange = async (row: MerchantApi.MerchantVO) => { + try { + // 修改状态的二次确认 + const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用' + await message.confirm('确认要"' + text + '""' + row.name + '"商户吗?', t('common.reminder')) + // 发起修改状态 + await MerchantApi.updateMerchantStatus(row.id, row.status) + // 刷新列表 + await getList() + } catch { + // 取消后,进行恢复按钮 + row.status = + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE + } +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await MerchantApi.exportMerchant(queryParams) + download.excel(data, '商户信息.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) </script> diff --git a/src/views/pay/merchant/merchant.data.ts b/src/views/pay/merchant/merchant.data.ts deleted file mode 100644 index e0e0727d..00000000 --- a/src/views/pay/merchant/merchant.data.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' -const { t } = useI18n() // 国际化 - -// 表单校验 -export const rules = reactive({ - no: [required], - name: [required], - shortName: [required], - status: [required] -}) - -// CrudSchema -const crudSchemas = reactive<VxeCrudSchema>({ - primaryKey: 'id', - primaryType: 'seq', - primaryTitle: '商户编号', - action: true, - columns: [ - { - title: '商户号', - field: 'no', - isSearch: true - }, - { - title: '商户全称', - field: 'code', - isSearch: true - }, - { - title: '商户简称', - field: 'shortName', - isSearch: true - }, - { - title: t('common.status'), - field: 'status', - dictType: DICT_TYPE.COMMON_STATUS, - dictClass: 'number', - isSearch: true - }, - { - title: t('form.remark'), - field: 'remark', - isTable: false, - form: { - component: 'Input', - componentProps: { - type: 'textarea', - rows: 4 - }, - colProps: { - span: 24 - } - } - }, - { - title: t('common.createTime'), - field: 'createTime', - formatter: 'formatDate', - isForm: false, - search: { - show: true, - itemRender: { - name: 'XDataTimePicker' - } - } - } - ] -}) -export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/system/area/form.vue b/src/views/system/area/form.vue index f0cff434..8e1604e5 100644 --- a/src/views/system/area/form.vue +++ b/src/views/system/area/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="IP 查询" v-model="modelVisible"> + <Dialog title="IP 查询" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -16,7 +16,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -24,7 +24,7 @@ import * as AreaApi from '@/api/system/area' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:提交的按钮禁用 const formData = ref({ ip: '', @@ -37,7 +37,7 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async () => { - modelVisible.value = true + dialogVisible.value = true resetForm() } defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 diff --git a/src/views/system/area/index.vue b/src/views/system/area/index.vue index f9d4c57d..d84c769f 100644 --- a/src/views/system/area/index.vue +++ b/src/views/system/area/index.vue @@ -1,7 +1,7 @@ <template> <!-- 操作栏 --> <content-wrap> - <el-button type="primary" @click="openModal()"> + <el-button type="primary" plain @click="openModal()"> <Icon icon="ep:plus" class="mr-5px" /> IP 查询 </el-button> </content-wrap> diff --git a/src/views/system/dept/DeptForm.vue b/src/views/system/dept/DeptForm.vue index f2c3bc02..21985416 100644 --- a/src/views/system/dept/DeptForm.vue +++ b/src/views/system/dept/DeptForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -58,7 +58,7 @@ </el-form> <template #footer> <el-button type="primary" @click="submitForm">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -71,8 +71,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -101,8 +101,8 @@ const userList = ref<UserApi.UserVO[]>([]) // 用户列表 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -139,7 +139,7 @@ const submitForm = async () => { await DeptApi.updateDeptApi(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue index ead319ce..7a1db4b4 100644 --- a/src/views/system/dept/index.vue +++ b/src/views/system/dept/index.vue @@ -51,13 +51,7 @@ <!-- 列表 --> <ContentWrap> - <el-table - v-loading="loading" - :data="list" - row-key="id" - v-if="refreshTable" - :default-expand-all="isExpandAll" - > + <el-table v-loading="loading" :data="list" row-key="id" default-expand-all v-if="refreshTable"> <el-table-column prop="name" label="部门名称" width="260" /> <el-table-column prop="leader" label="负责人" width="120"> <template #default="scope"> diff --git a/src/views/system/dict/data.form.vue b/src/views/system/dict/data.form.vue index 9271e8a9..4eefc4db 100644 --- a/src/views/system/dict/data.form.vue +++ b/src/views/system/dict/data.form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -52,7 +52,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -63,8 +63,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -115,8 +115,8 @@ const colorTypeOptions = readonly([ /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -149,7 +149,7 @@ const submitForm = async () => { await DictDataApi.updateDictData(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue index 41bcbaa8..fbb651ff 100644 --- a/src/views/system/dict/data.vue +++ b/src/views/system/dict/data.vue @@ -180,7 +180,7 @@ const handleExport = async () => { await message.exportConfirm() // 发起导出 exportLoading.value = true - const data = await DictDataApi.exportDictDataApi(queryParams) + const data = await DictDataApi.exportDictData(queryParams) download.excel(data, '字典数据.xls') } catch { } finally { diff --git a/src/views/system/dict/form.vue b/src/views/system/dict/form.vue index 179656de..793186ad 100644 --- a/src/views/system/dict/form.vue +++ b/src/views/system/dict/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -34,7 +34,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -46,8 +46,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -65,8 +65,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -99,7 +99,7 @@ const submitForm = async () => { await DictTypeApi.updateDictType(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue index 4e304bf5..43e3a0bf 100644 --- a/src/views/system/dict/index.vue +++ b/src/views/system/dict/index.vue @@ -41,7 +41,6 @@ v-model="queryParams.createTime" value-format="yyyy-MM-dd HH:mm:ss" type="daterange" - range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" @@ -51,7 +50,12 @@ <el-form-item> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button type="primary" @click="openModal('create')" v-hasPermi="['system:dict:create']"> + <el-button + type="primary" + plain + @click="openModal('create')" + v-hasPermi="['system:dict:create']" + > <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> <el-button diff --git a/src/views/system/errorCode/form.vue b/src/views/system/errorCode/form.vue index f261ced1..3629168c 100644 --- a/src/views/system/errorCode/form.vue +++ b/src/views/system/errorCode/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -22,7 +22,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -32,8 +32,8 @@ import * as ErrorCodeApi from '@/api/system/errorCode' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 // 表单参数 @@ -54,8 +54,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -88,7 +88,7 @@ const submitForm = async () => { await ErrorCodeApi.updateErrorCodeApi(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/errorCode/index.vue b/src/views/system/errorCode/index.vue index c95d652c..f3105d3a 100644 --- a/src/views/system/errorCode/index.vue +++ b/src/views/system/errorCode/index.vue @@ -61,6 +61,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['system:error-code:create']" > @@ -121,8 +122,7 @@ </el-table-column> </el-table> <!-- 分页组件 --> - <pagination - v-show="total > 0" + <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" diff --git a/src/views/system/loginlog/LoginLogDetail.vue b/src/views/system/loginlog/LoginLogDetail.vue index f0890eca..3a8157ce 100644 --- a/src/views/system/loginlog/LoginLogDetail.vue +++ b/src/views/system/loginlog/LoginLogDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" width="800"> + <Dialog title="详情" v-model="dialogVisible" width="800"> <el-descriptions border :column="1"> <el-descriptions-item label="日志编号" min-width="120"> {{ detailData.id }} @@ -30,13 +30,13 @@ import { DICT_TYPE } from '@/utils/dict' import { formatDate } from '@/utils/formatTime' import * as LoginLogApi from '@/api/system/loginLog' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const open = async (data: LoginLogApi.LoginLogVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/mail/account/form.vue b/src/views/system/mail/account/form.vue index 78d7cf0d..3747d17f 100644 --- a/src/views/system/mail/account/form.vue +++ b/src/views/system/mail/account/form.vue @@ -1,9 +1,9 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <Form ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" /> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -14,16 +14,16 @@ import { rules, allSchemas } from './account.data' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type // 修改时,设置数据 if (id) { @@ -56,7 +56,7 @@ const submitForm = async () => { await MailAccountApi.updateMailAccount(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/mail/account/index.vue b/src/views/system/mail/account/index.vue index 1f80684f..24d06c5f 100644 --- a/src/views/system/mail/account/index.vue +++ b/src/views/system/mail/account/index.vue @@ -6,6 +6,7 @@ <template #actionMore> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['system:mail-account:create']" > diff --git a/src/views/system/mail/log/detail.vue b/src/views/system/mail/log/detail.vue index 1795f751..eaf8cf84 100644 --- a/src/views/system/mail/log/detail.vue +++ b/src/views/system/mail/log/detail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500"> <Descriptions :schema="allSchemas.detailSchema" :data="detailData"> <!-- 展示 HTML 内容 --> <template #templateContent="{ row }"> @@ -12,13 +12,13 @@ import * as MailLogApi from '@/api/system/mail/log' import { allSchemas } from './log.data' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const openModal = async (id: number) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/mail/template/form.vue b/src/views/system/mail/template/form.vue index 92f3c5c1..f1b8f9a2 100644 --- a/src/views/system/mail/template/form.vue +++ b/src/views/system/mail/template/form.vue @@ -1,9 +1,9 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" :scroll="true" :width="800" :max-height="500"> + <Dialog :title="dialogTitle" v-model="dialogVisible" :scroll="true" :width="800" :max-height="500"> <Form ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" /> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -14,16 +14,16 @@ import { rules, allSchemas } from './template.data' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type // 修改时,设置数据 if (id) { @@ -56,7 +56,7 @@ const submitForm = async () => { await MailTemplateApi.updateMailTemplate(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/mail/template/index.vue b/src/views/system/mail/template/index.vue index b9a5040b..51531c62 100644 --- a/src/views/system/mail/template/index.vue +++ b/src/views/system/mail/template/index.vue @@ -6,6 +6,7 @@ <template #actionMore> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['system:mail-account:create']" > diff --git a/src/views/system/mail/template/send.vue b/src/views/system/mail/template/send.vue index b4b411b9..19a1c23d 100644 --- a/src/views/system/mail/template/send.vue +++ b/src/views/system/mail/template/send.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="测试" v-model="modelVisible"> + <Dialog title="测试" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -27,7 +27,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -36,7 +36,7 @@ import * as MailTemplateApi from '@/api/system/mail/template' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ content: '', @@ -54,7 +54,7 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (id: number) => { - modelVisible.value = true + dialogVisible.value = true resetForm() // 设置数据 formLoading.value = true @@ -92,7 +92,7 @@ const submitForm = async () => { if (logId) { message.success('提交发送成功!发送结果,见发送日志编号:' + logId) } - modelVisible.value = false + dialogVisible.value = false } finally { formLoading.value = false } diff --git a/src/views/system/menu/MenuForm.vue b/src/views/system/menu/MenuForm.vue index 45bcedfb..1c6f2caf 100644 --- a/src/views/system/menu/MenuForm.vue +++ b/src/views/system/menu/MenuForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -108,7 +108,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -122,8 +122,8 @@ const { wsCache } = useCache() const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -152,8 +152,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const open = async (type: string, id?: number, parentId?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() if (parentId) { @@ -205,7 +205,7 @@ const submitForm = async () => { await MenuApi.updateMenu(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/notice/form.vue b/src/views/system/notice/form.vue index 87e75623..92f99dc9 100644 --- a/src/views/system/notice/form.vue +++ b/src/views/system/notice/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" width="800"> + <Dialog :title="dialogTitle" v-model="dialogVisible" width="800"> <el-form ref="formRef" :model="formData" @@ -39,7 +39,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -49,8 +49,8 @@ import * as NoticeApi from '@/api/system/notice' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -71,8 +71,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -105,7 +105,7 @@ const submitForm = async () => { await NoticeApi.updateNotice(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue index 65076f9f..d76bd520 100644 --- a/src/views/system/notice/index.vue +++ b/src/views/system/notice/index.vue @@ -37,6 +37,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['system:notice:create']" > @@ -114,11 +115,11 @@ const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 const list = ref([]) // 列表的数据 const queryParams = reactive({ + pageNo: 1, + pageSize: 10, title: '', type: undefined, - status: undefined, - pageNo: 1, - pageSize: 100 + status: undefined }) const queryFormRef = ref() // 搜索的表单 @@ -127,7 +128,6 @@ const getList = async () => { loading.value = true try { const data = await NoticeApi.getNoticePage(queryParams) - list.value = data.list total.value = data.total } finally { diff --git a/src/views/system/notify/message/NotifyMessageDetail.vue b/src/views/system/notify/message/NotifyMessageDetail.vue index 283575bb..0a29ebb1 100644 --- a/src/views/system/notify/message/NotifyMessageDetail.vue +++ b/src/views/system/notify/message/NotifyMessageDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500"> <el-descriptions border :column="1"> <el-descriptions-item label="编号" min-width="120"> {{ detailData.id }} @@ -45,13 +45,13 @@ import { DICT_TYPE } from '@/utils/dict' import { formatDate } from '@/utils/formatTime' import * as NotifyMessageApi from '@/api/system/notify/message' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const open = async (data: NotifyMessageApi.NotifyMessageVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/notify/my/MyNotifyMessageDetail.vue b/src/views/system/notify/my/MyNotifyMessageDetail.vue index 45af259d..bcad4711 100644 --- a/src/views/system/notify/my/MyNotifyMessageDetail.vue +++ b/src/views/system/notify/my/MyNotifyMessageDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="消息详情" v-model="modelVisible" :scroll="true" :max-height="500"> + <Dialog title="消息详情" v-model="dialogVisible" :scroll="true" :max-height="500"> <el-descriptions border :column="1"> <el-descriptions-item label="发送人"> {{ detailData.templateNickname }} @@ -27,13 +27,13 @@ import { DICT_TYPE } from '@/utils/dict' import { formatDate } from '@/utils/formatTime' import * as NotifyMessageApi from '@/api/system/notify/message' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const open = async (data: NotifyMessageApi.NotifyMessageVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/notify/template/index.vue b/src/views/system/notify/template/index.vue index c4113924..de14d5ca 100644 --- a/src/views/system/notify/template/index.vue +++ b/src/views/system/notify/template/index.vue @@ -46,7 +46,7 @@ </ContentWrap> <!-- 添加/修改的弹窗 --> - <XModal id="templateModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle"> + <XModal id="templateModel" :loading="modelLoading" v-model="dialogVisible" :title="dialogTitle"> <!-- 表单:添加/修改 --> <Form ref="formRef" @@ -70,7 +70,7 @@ @click="submitForm()" /> <!-- 按钮:关闭 --> - <XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" /> + <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" /> </template> </XModal> @@ -132,8 +132,8 @@ const [registerTable, { reload, deleteData }] = useXTable({ }) // 弹窗相关的变量 -const modelVisible = ref(false) // 是否显示弹出层 -const modelTitle = ref('edit') // 弹出层标题 +const dialogVisible = ref(false) // 是否显示弹出层 +const dialogTitle = ref('edit') // 弹出层标题 const modelLoading = ref(false) // 弹出层loading const actionType = ref('') // 操作按钮的类型 const actionLoading = ref(false) // 按钮 Loading @@ -143,9 +143,9 @@ const detailData = ref() // 详情 Ref // 设置标题 const setDialogTile = (type: string) => { modelLoading.value = true - modelTitle.value = t('action.' + type) + dialogTitle.value = t('action.' + type) actionType.value = type - modelVisible.value = true + dialogVisible.value = true } // 新增操作 @@ -188,7 +188,7 @@ const submitForm = async () => { await NotifyTemplateApi.updateNotifyTemplateApi(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false } finally { actionLoading.value = false // 刷新列表 diff --git a/src/views/system/oauth2/client/form.vue b/src/views/system/oauth2/client/ClientForm.vue similarity index 86% rename from src/views/system/oauth2/client/form.vue rename to src/views/system/oauth2/client/ClientForm.vue index 0822e59f..b5936c37 100644 --- a/src/views/system/oauth2/client/form.vue +++ b/src/views/system/oauth2/client/ClientForm.vue @@ -1,10 +1,10 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" width="800"> + <Dialog :title="dialogTitle" v-model="dialogVisible" scroll max-height="500px"> <el-form ref="formRef" :model="formData" :rules="formRules" - label-width="120px" + label-width="160px" v-loading="formLoading" > <el-form-item label="客户端编号" prop="secret"> @@ -25,11 +25,12 @@ <el-form-item label="状态" prop="status"> <el-radio-group v-model="formData.status"> <el-radio - v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" :key="dict.value" - :label="parseInt(dict.value)" - >{{ dict.label }}</el-radio + :label="dict.value" > + {{ dict.label }} + </el-radio> </el-radio-group> </el-form-item> <el-form-item label="访问令牌的有效期" prop="accessTokenValiditySeconds"> @@ -47,7 +48,7 @@ style="width: 500px" > <el-option - v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE)" + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" @@ -137,22 +138,20 @@ </el-form-item> </el-form> <template #footer> - <div class="dialog-footer"> - <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> - </div> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> <script setup lang="ts"> -import { DICT_TYPE, getDictOptions } from '@/utils/dict' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { CommonStatusEnum } from '@/utils/constants' import * as ClientApi from '@/api/system/oauth2/client' -import UploadImg from '@/components/UploadFile' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -162,7 +161,7 @@ const formData = ref({ name: undefined, logo: undefined, description: undefined, - status: DICT_TYPE.COMMON_STATUS, + status: CommonStatusEnum.ENABLE, accessTokenValiditySeconds: 30 * 60, refreshTokenValiditySeconds: 30 * 24 * 60, redirectUris: [], @@ -191,22 +190,22 @@ const formRules = reactive({ const formRef = ref() // 表单 Ref /** 打开弹窗 */ -const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 if (id) { formLoading.value = true try { - formData.value = await ClientApi.getOAuth2ClientApi(id) + formData.value = await ClientApi.getOAuth2Client(id) } finally { formLoading.value = false } } } -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 /** 提交表单 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 @@ -220,13 +219,13 @@ const submitForm = async () => { try { const data = formData.value as unknown as ClientApi.OAuth2ClientVO if (formType.value === 'create') { - await ClientApi.createOAuth2ClientApi(data) + await ClientApi.createOAuth2Client(data) message.success(t('common.createSuccess')) } else { - await ClientApi.updateOAuth2ClientApi(data) + await ClientApi.updateOAuth2Client(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { @@ -243,7 +242,7 @@ const resetForm = () => { name: undefined, logo: undefined, description: undefined, - status: DICT_TYPE.COMMON_STATUS, + status: CommonStatusEnum.ENABLE, accessTokenValiditySeconds: 30 * 60, refreshTokenValiditySeconds: 30 * 24 * 60, redirectUris: [], diff --git a/src/views/system/oauth2/client/index.vue b/src/views/system/oauth2/client/index.vue index c88af726..eab58726 100644 --- a/src/views/system/oauth2/client/index.vue +++ b/src/views/system/oauth2/client/index.vue @@ -1,206 +1,185 @@ <template> - <ContentWrap> - <!-- 列表 --> - <XTable @register="registerTable"> - <template #toolbar_buttons> - <!-- 操作:新增 --> - <XButton - type="primary" - preIcon="ep:zoom-in" - :title="t('action.add')" - v-hasPermi="['system:oauth2-client:create']" - @click="handleCreate()" - /> - </template> - <template #accessTokenValiditySeconds_default="{ row }"> - {{ row.accessTokenValiditySeconds + '秒' }} - </template> - <template #refreshTokenValiditySeconds_default="{ row }"> - {{ row.refreshTokenValiditySeconds + '秒' }} - </template> - <template #authorizedGrantTypes_default="{ row }"> - <el-tag - :disable-transitions="true" - :key="index" - v-for="(authorizedGrantType, index) in row.authorizedGrantTypes" - :index="index" - > - {{ authorizedGrantType }} - </el-tag> - </template> - <template #actionbtns_default="{ row }"> - <!-- 操作:修改 --> - <XTextButton - preIcon="ep:edit" - :title="t('action.edit')" - v-hasPermi="['system:oauth2-client:update']" - @click="handleUpdate(row.id)" - /> - <!-- 操作:详情 --> - <XTextButton - preIcon="ep:view" - :title="t('action.detail')" - v-hasPermi="['system:oauth2-client:query']" - @click="handleDetail(row.id)" - /> - <!-- 操作:删除 --> - <XTextButton - preIcon="ep:delete" - :title="t('action.del')" - v-hasPermi="['system:oauth2-client:delete']" - @click="deleteData(row.id)" - /> - </template> - </XTable> - </ContentWrap> - <!-- 弹窗 --> - <XModal id="postModel" v-model="dialogVisible" :title="dialogTitle"> - <!-- 表单:添加/修改 --> - <Form - ref="formRef" - v-if="['create', 'update'].includes(actionType)" - :schema="allSchemas.formSchema" - :rules="rules" - /> - <!-- 表单:详情 --> - <Descriptions - v-if="actionType === 'detail'" - :schema="allSchemas.detailSchema" - :data="detailData" + <!-- 搜索 --> + <content-wrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" > - <template #accessTokenValiditySeconds="{ row }"> - {{ row.accessTokenValiditySeconds + '秒' }} - </template> - <template #refreshTokenValiditySeconds="{ row }"> - {{ row.refreshTokenValiditySeconds + '秒' }} - </template> - <template #authorizedGrantTypes="{ row }"> - <el-tag - :disable-transitions="true" - :key="index" - v-for="(authorizedGrantType, index) in row.authorizedGrantTypes" - :index="index" + <el-form-item label="应用名" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入应用名" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + plain + type="primary" + @click="openForm('create')" + v-hasPermi="['system:oauth2-client:create']" > - {{ authorizedGrantType }} - </el-tag> - </template> - <template #scopes="{ row }"> - <el-tag - :disable-transitions="true" - :key="index" - v-for="(scopes, index) in row.scopes" - :index="index" - > - {{ scopes }} - </el-tag> - </template> - <template #autoApproveScopes="{ row }"> - <el-tag - :disable-transitions="true" - :key="index" - v-for="(autoApproveScopes, index) in row.autoApproveScopes" - :index="index" - > - {{ autoApproveScopes }} - </el-tag> - </template> - <template #redirectUris="{ row }"> - <el-tag - :disable-transitions="true" - :key="index" - v-for="(redirectUris, index) in row.redirectUris" - :index="index" - > - {{ redirectUris }} - </el-tag> - </template> - </Descriptions> - <template #footer> - <!-- 按钮:保存 --> - <XButton - v-if="['create', 'update'].includes(actionType)" - type="primary" - :title="t('action.save')" - :loading="actionLoading" - @click="submitForm()" + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </content-wrap> + + <!-- 列表 --> + <content-wrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="客户端编号" align="center" prop="clientId" /> + <el-table-column label="客户端密钥" align="center" prop="secret" /> + <el-table-column label="应用名" align="center" prop="name" /> + <el-table-column label="应用图标" align="center" prop="logo"> + <template #default="scope"> + <img width="40px" height="40px" :src="scope.row.logo" /> + </template> + </el-table-column> + <el-table-column label="状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="访问令牌的有效期" align="center" prop="accessTokenValiditySeconds"> + <template #default="scope">{{ scope.row.accessTokenValiditySeconds }} 秒</template> + </el-table-column> + <el-table-column label="刷新令牌的有效期" align="center" prop="refreshTokenValiditySeconds"> + <template #default="scope">{{ scope.row.refreshTokenValiditySeconds }} 秒</template> + </el-table-column> + <el-table-column label="授权类型" align="center" prop="authorizedGrantTypes"> + <template #default="scope"> + <el-tag + :disable-transitions="true" + :key="index" + v-for="(authorizedGrantType, index) in scope.row.authorizedGrantTypes" + :index="index" + > + {{ authorizedGrantType }} + </el-tag> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" /> - <!-- 按钮:关闭 --> - <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" /> - </template> - </XModal> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['system:oauth2-client:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['system:oauth2-client:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </content-wrap> + + <!-- 表单弹窗:添加/修改 --> + <ClientForm ref="formRef" @success="getList" /> </template> -<script setup lang="ts" name="Client"> -import type { FormExpose } from '@/components/Form' -// 业务相关的 import +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' import * as ClientApi from '@/api/system/oauth2/client' - -const { t } = useI18n() // 国际化 +import ClientForm from './ClientForm.vue' const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 -// 列表相关的变量 -const [registerTable, { reload, deleteData }] = useXTable({ - allSchemas: allSchemas, - getListApi: ClientApi.getOAuth2ClientPageApi, - deleteApi: ClientApi.deleteOAuth2ClientApi +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + status: null }) -// 弹窗相关的变量 -const dialogVisible = ref(false) // 是否显示弹出层 -const dialogTitle = ref('edit') // 弹出层标题 -const actionType = ref('') // 操作按钮的类型 -const actionLoading = ref(false) // 按钮 Loading -const formRef = ref<FormExpose>() // 表单 Ref -const detailData = ref() // 详情 Ref -// 设置标题 -const setDialogTile = (type: string) => { - dialogTitle.value = t('action.' + type) - actionType.value = type - dialogVisible.value = true +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ClientApi.getOAuth2ClientPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } } -// 新增操作 -const handleCreate = () => { - setDialogTile('create') +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() } -// 修改操作 -const handleUpdate = async (rowId: number) => { - setDialogTile('update') - // 设置数据 - const res = await ClientApi.getOAuth2ClientApi(rowId) - unref(formRef)?.setValues(res) +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() } -// 详情操作 -const handleDetail = async (rowId: number) => { - setDialogTile('detail') - const res = await ClientApi.getOAuth2ClientApi(rowId) - detailData.value = res +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) } -// 提交新增/修改的表单 -const submitForm = async () => { - const elForm = unref(formRef)?.getElFormRef() - if (!elForm) return - elForm.validate(async (valid) => { - if (valid) { - actionLoading.value = true - // 提交请求 - try { - const data = unref(formRef)?.formModel as ClientApi.OAuth2ClientVO - if (actionType.value === 'create') { - await ClientApi.createOAuth2ClientApi(data) - message.success(t('common.createSuccess')) - } else { - await ClientApi.updateOAuth2ClientApi(data) - message.success(t('common.updateSuccess')) - } - dialogVisible.value = false - } finally { - actionLoading.value = false - // 刷新列表 - await reload() - } - } - }) +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ClientApi.deleteOAuth2Client(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} } + +/** 初始化 **/ +onMounted(() => { + getList() +}) </script> diff --git a/src/views/system/operatelog/detail.vue b/src/views/system/operatelog/detail.vue index b3603e2e..cbf22dea 100644 --- a/src/views/system/operatelog/detail.vue +++ b/src/views/system/operatelog/detail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500" width="800"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500" width="800"> <el-descriptions border :column="1"> <el-descriptions-item label="日志主键" min-width="120"> {{ detailData.id }} @@ -61,13 +61,13 @@ import { formatDate } from '@/utils/formatTime' import * as OperateLogApi from '@/api/system/operatelog' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 /** 打开弹窗 */ const openModal = async (data: OperateLogApi.OperateLogVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/post/PostForm.vue b/src/views/system/post/PostForm.vue index a9dec8b0..70103906 100644 --- a/src/views/system/post/PostForm.vue +++ b/src/views/system/post/PostForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" width="800"> + <Dialog :title="dialogTitle" v-model="dialogVisible" width="800"> <el-form ref="formRef" :model="formData" @@ -29,7 +29,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -41,8 +41,8 @@ import * as PostApi from '@/api/system/post' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -63,8 +63,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const openModal = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -97,7 +97,7 @@ const submitForm = async () => { await PostApi.updatePost(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue index 03e491d0..4d1d306c 100644 --- a/src/views/system/post/index.vue +++ b/src/views/system/post/index.vue @@ -39,6 +39,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openModal('create')" v-hasPermi="['system:notice:create']" > diff --git a/src/views/system/role/MenuPermissionForm.vue b/src/views/system/role/MenuPermissionForm.vue deleted file mode 100644 index 70971781..00000000 --- a/src/views/system/role/MenuPermissionForm.vue +++ /dev/null @@ -1,202 +0,0 @@ -<template> - <Dialog :title="dialogScopeTitle" v-model="dialogScopeVisible" width="800"> - <el-form - ref="menuPermissionFormRef" - :model="dataScopeForm" - :inline="true" - label-width="80px" - v-loading="formLoading" - > - <el-form-item label="角色名称"> - <el-tag>{{ dataScopeForm.name }}</el-tag> - </el-form-item> - <el-form-item label="角色标识"> - <el-tag>{{ dataScopeForm.code }}</el-tag> - </el-form-item> - <!-- 分配角色的数据权限对话框 --> - <el-form-item label="权限范围" v-if="actionScopeType === 'data'"> - <el-select v-model="dataScopeForm.dataScope"> - <el-option - v-for="item in dataScopeDictDatas" - :key="item.value" - :label="item.label" - :value="item.value" - /> - </el-select> - </el-form-item> - <!-- 分配角色的菜单权限对话框 --> - <el-row> - <el-col :span="24"> - <el-form-item - label="权限范围" - v-if=" - actionScopeType === 'menu' || - dataScopeForm.dataScope === SystemDataScopeEnum.DEPT_CUSTOM - " - style="display: flex" - > - <el-card class="card" shadow="never"> - <template #header> - 父子联动(选中父节点,自动选择子节点): - <el-switch - v-model="checkStrictly" - inline-prompt - active-text="是" - inactive-text="否" - /> - 全选/全不选: - <el-switch - v-model="treeNodeAll" - inline-prompt - active-text="是" - inactive-text="否" - @change="handleCheckedTreeNodeAll()" - /> - </template> - <el-tree - ref="treeRef" - node-key="id" - show-checkbox - :check-strictly="!checkStrictly" - :props="defaultProps" - :data="treeOptions" - empty-text="加载中,请稍后" - /> - </el-card> - </el-form-item> </el-col - ></el-row> - </el-form> - <!-- 操作按钮 --> - <template #footer> - <div class="dialog-footer"> - <el-button - :title="t('action.save')" - :loading="actionLoading" - @click="submitScope()" - type="primary" - :disabled="formLoading" - > - 保存 - </el-button> - <el-button - :loading="actionLoading" - :title="t('dialog.close')" - @click="dialogScopeVisible = false" - >取 消</el-button - > - </div> - </template> - </Dialog> -</template> - -<script setup lang="ts"> -import * as RoleApi from '@/api/system/role' -import type { ElTree } from 'element-plus' -import type { FormExpose } from '@/components/Form' -import { handleTree, defaultProps } from '@/utils/tree' -import { SystemDataScopeEnum } from '@/utils/constants' -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' -import * as MenuApi from '@/api/system/menu' -import * as DeptApi from '@/api/system/dept' -import * as PermissionApi from '@/api/system/permission' -// ========== CRUD 相关 ========== -const actionLoading = ref(false) // 遮罩层 -const menuPermissionFormRef = ref<FormExpose>() // 表单 Ref -const { t } = useI18n() // 国际化 -const dialogScopeTitle = ref('菜单权限') -const dataScopeDictDatas = ref() -const message = useMessage() // 消息弹窗 -const actionScopeType = ref('') -// 选项 -const checkStrictly = ref(true) -const treeNodeAll = ref(false) -const dialogScopeVisible = ref(false) // 弹窗的是否展示 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const treeOptions = ref<any[]>([]) // 菜单树形结构 -const treeRef = ref<InstanceType<typeof ElTree>>() -// ========== 数据权限 ========== -const dataScopeForm = reactive({ - id: 0, - name: '', - code: '', - dataScope: 0, - checkList: [] -}) - -/** 打开弹窗 */ -const openModal = async (type: string, row: RoleApi.RoleVO) => { - dataScopeForm.id = row.id - dataScopeForm.name = row.name - dataScopeForm.code = row.code - actionScopeType.value = type - dialogScopeVisible.value = true - if (type === 'menu') { - const menuRes = await MenuApi.getSimpleMenusList() - treeOptions.value = handleTree(menuRes) - const role = await PermissionApi.listRoleMenusApi(row.id) - if (role) { - role?.forEach((item: any) => { - unref(treeRef)?.setChecked(item, true, false) - }) - } - } else if (type === 'data') { - const deptRes = await DeptApi.getSimpleDeptList() - treeOptions.value = handleTree(deptRes) - const role = await RoleApi.getRole(row.id) - dataScopeForm.dataScope = role.dataScope - if (role.dataScopeDeptIds) { - role.dataScopeDeptIds?.forEach((item: any) => { - unref(treeRef)?.setChecked(item, true, false) - }) - } - } -} - -// 保存权限 -const submitScope = async () => { - if ('data' === actionScopeType.value) { - const data = ref<PermissionApi.PermissionAssignRoleDataScopeReqVO>({ - roleId: dataScopeForm.id, - dataScope: dataScopeForm.dataScope, - dataScopeDeptIds: - dataScopeForm.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM - ? [] - : (treeRef.value!.getCheckedKeys(false) as unknown as Array<number>) - }) - await PermissionApi.assignRoleDataScopeApi(data.value) - } else if ('menu' === actionScopeType.value) { - const data = ref<PermissionApi.PermissionAssignRoleMenuReqVO>({ - roleId: dataScopeForm.id, - menuIds: [ - ...(treeRef.value!.getCheckedKeys(false) as unknown as Array<number>), - ...(treeRef.value!.getHalfCheckedKeys() as unknown as Array<number>) - ] - }) - await PermissionApi.assignRoleMenuApi(data.value) - } - message.success(t('common.updateSuccess')) - dialogScopeVisible.value = false -} - -// 全选/全不选 -const handleCheckedTreeNodeAll = () => { - treeRef.value!.setCheckedNodes(treeNodeAll.value ? treeOptions.value : []) -} - -const init = () => { - dataScopeDictDatas.value = getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE) -} - -defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗 -// ========== 初始化 ========== -onMounted(() => { - init() -}) -</script> -<style scoped> -.card { - width: 100%; - max-height: 400px; - overflow-y: scroll; -} -</style> diff --git a/src/views/system/role/RoleAssignMenuForm.vue b/src/views/system/role/RoleAssignMenuForm.vue new file mode 100644 index 00000000..cc8e2708 --- /dev/null +++ b/src/views/system/role/RoleAssignMenuForm.vue @@ -0,0 +1,154 @@ +<template> + <Dialog title="菜单权限" v-model="dialogVisible"> + <el-form ref="formRef" :model="formData" label-width="80px" v-loading="formLoading"> + <el-form-item label="角色名称"> + <el-tag>{{ formData.name }}</el-tag> + </el-form-item> + <el-form-item label="角色标识"> + <el-tag>{{ formData.code }}</el-tag> + </el-form-item> + <el-form-item label="菜单权限"> + <el-card class="cardHeight"> + <template #header> + 全选/全不选: + <el-switch + v-model="treeNodeAll" + inline-prompt + active-text="是" + inactive-text="否" + @change="handleCheckedTreeNodeAll" + /> + 全部展开/折叠: + <el-switch + v-model="menuExpand" + inline-prompt + active-text="展开" + inactive-text="折叠" + @change="handleCheckedTreeExpand" + /> + </template> + <el-tree + ref="treeRef" + node-key="id" + show-checkbox + :props="defaultProps" + :data="menuOptions" + empty-text="加载中,请稍候" + /> + </el-card> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { handleTree, defaultProps } from '@/utils/tree' +import * as RoleApi from '@/api/system/role' +import * as MenuApi from '@/api/system/menu' +import * as PermissionApi from '@/api/system/permission' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = reactive({ + id: 0, + name: '', + code: '', + menuIds: [] +}) +const formRef = ref() // 表单 Ref +const menuOptions = ref<any[]>([]) // 菜单树形结构 +const menuExpand = ref(false) // 展开/折叠 +const treeRef = ref() // 菜单树组件 Ref +const treeNodeAll = ref(false) // 全选/全不选 + +/** 打开弹窗 */ +const open = async (row: RoleApi.RoleVO) => { + dialogVisible.value = true + resetForm() + // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点 + menuOptions.value = handleTree(await MenuApi.getSimpleMenusList()) + // 设置数据 + formData.id = row.id + formData.name = row.name + formData.code = row.code + formLoading.value = true + try { + formData.value.menuIds = await PermissionApi.getRoleMenuList(row.id) + // 设置选中 + formData.value.menuIds.forEach((menuId: number) => { + treeRef.value.setChecked(menuId, true, false) + }) + } finally { + formLoading.value = false + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = { + roleId: formData.id, + menuIds: [ + ...(treeRef.value.getCheckedKeys(false) as unknown as Array<number>), // 获得当前选中节点 + ...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点 + ] + } + await PermissionApi.assignRoleMenu(data) + message.success(t('common.updateSuccess')) + dialogVisible.value = false + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + // 重置选项 + treeNodeAll.value = false + menuExpand.value = false + // 重置表单 + formData.value = { + id: 0, + name: '', + code: '', + menuIds: [] + } + treeRef.value?.setCheckedNodes([]) + formRef.value?.resetFields() +} + +/** 全选/全不选 */ +const handleCheckedTreeNodeAll = () => { + treeRef.value.setCheckedNodes(treeNodeAll.value ? menuOptions.value : []) +} + +/** 展开/折叠全部 */ +const handleCheckedTreeExpand = () => { + const nodes = treeRef.value?.store.nodesMap + for (let node in nodes) { + if (nodes[node].expanded === menuExpand.value) { + continue + } + nodes[node].expanded = menuExpand.value + } +} +</script> +<style lang="scss" scoped> +.cardHeight { + width: 100%; + max-height: 400px; + overflow-y: scroll; +} +</style> diff --git a/src/views/system/role/RoleDataPermissionForm.vue b/src/views/system/role/RoleDataPermissionForm.vue new file mode 100644 index 00000000..6b017278 --- /dev/null +++ b/src/views/system/role/RoleDataPermissionForm.vue @@ -0,0 +1,161 @@ +<template> + <Dialog title="菜单权限" v-model="dialogVisible" width="800"> + <el-form ref="formRef" :model="formData" label-width="80px" v-loading="formLoading"> + <el-form-item label="角色名称"> + <el-tag>{{ formData.name }}</el-tag> + </el-form-item> + <el-form-item label="角色标识"> + <el-tag>{{ formData.code }}</el-tag> + </el-form-item> + <el-form-item label="权限范围"> + <el-select v-model="formData.dataScope"> + <el-option + v-for="item in getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-form> + <el-form-item + label="权限范围" + v-if="formData.dataScope === SystemDataScopeEnum.DEPT_CUSTOM" + style="display: flex" + > + <el-card class="card" shadow="never"> + <template #header> + 全选/全不选: + <el-switch + v-model="treeNodeAll" + inline-prompt + active-text="是" + inactive-text="否" + @change="handleCheckedTreeNodeAll()" + /> + 全部展开/折叠: + <el-switch + v-model="deptExpand" + inline-prompt + active-text="展开" + inactive-text="折叠" + @change="handleCheckedTreeExpand" + /> + 父子联动(选中父节点,自动选择子节点): + <el-switch v-model="checkStrictly" inline-prompt active-text="是" inactive-text="否" /> + </template> + <el-tree + ref="treeRef" + node-key="id" + show-checkbox + :check-strictly="!checkStrictly" + :props="defaultProps" + :data="deptOptions" + empty-text="加载中,请稍后" + default-expand-all + /> + </el-card> + </el-form-item> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { handleTree, defaultProps } from '@/utils/tree' +import { SystemDataScopeEnum } from '@/utils/constants' +import * as RoleApi from '@/api/system/role' +import * as DeptApi from '@/api/system/dept' +import * as PermissionApi from '@/api/system/permission' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = reactive({ + id: 0, + name: '', + code: '', + dataScope: undefined, + dataScopeDeptIds: [] +}) +const formRef = ref() // 表单 Ref +const deptOptions = ref<any[]>([]) // 部门树形结构 +const deptExpand = ref(false) // 展开/折叠 +const treeRef = ref() // 菜单树组件 Ref +const treeNodeAll = ref(false) // 全选/全不选 +const checkStrictly = ref(true) // 是否严格模式,即父子不关联 + +/** 打开弹窗 */ +const open = async (row: RoleApi.RoleVO) => { + dialogVisible.value = true + resetForm() + // 加载 Dept 列表。注意,必须放在前面,不然下面 setChecked 没数据节点 + deptOptions.value = handleTree(await DeptApi.getSimpleDeptList()) + // 设置数据 + formData.id = row.id + formData.name = row.name + formData.code = row.code + formData.dataScope = row.dataScope + row.dataScopeDeptIds?.forEach((deptId: number) => { + treeRef.value.setChecked(deptId, true, false) + }) +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const submitForm = async () => { + formLoading.value = true + try { + const data = { + roleId: formData.id, + dataScope: formData.dataScope, + dataScopeDeptIds: + formData.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM + ? [] + : treeRef.value.getCheckedKeys(false) + } + await PermissionApi.assignRoleDataScope(data) + message.success(t('common.updateSuccess')) + dialogVisible.value = false + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + // 重置选项 + treeNodeAll.value = false + deptExpand.value = false + checkStrictly.value = true + // 重置表单 + formData.value = { + id: 0, + name: '', + code: '', + dataScope: undefined, + dataScopeDeptIds: [] + } + treeRef.value?.setCheckedNodes([]) + formRef.value?.resetFields() +} + +/** 全选/全不选 */ +const handleCheckedTreeNodeAll = () => { + treeRef.value.setCheckedNodes(treeNodeAll.value ? deptOptions.value : []) +} + +/** 展开/折叠全部 */ +const handleCheckedTreeExpand = () => { + const nodes = treeRef.value?.store.nodesMap + for (let node in nodes) { + if (nodes[node].expanded === deptExpand.value) { + continue + } + nodes[node].expanded = deptExpand.value + } +} +</script> diff --git a/src/views/system/role/RoleForm.vue b/src/views/system/role/RoleForm.vue index e6444a03..daae020e 100644 --- a/src/views/system/role/RoleForm.vue +++ b/src/views/system/role/RoleForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -32,7 +32,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -43,8 +43,8 @@ import * as RoleApi from '@/api/system/role' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -66,8 +66,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -113,7 +113,7 @@ const submitForm = async () => { await RoleApi.updateRole(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue index af8e0d3a..71f0c415 100644 --- a/src/views/system/role/index.vue +++ b/src/views/system/role/index.vue @@ -108,7 +108,7 @@ preIcon="ep:basketball" title="菜单权限" v-hasPermi="['system:permission:assign-role-menu']" - @click="handleScope('menu', scope.row)" + @click="openAssignMenuForm(scope.row)" > 菜单权限 </el-button> @@ -118,7 +118,7 @@ preIcon="ep:coin" title="数据权限" v-hasPermi="['system:permission:assign-role-data-scope']" - @click="handleScope('data', scope.row)" + @click="openDataPermissionForm(scope.row)" > 数据权限 </el-button> @@ -145,15 +145,18 @@ <!-- 表单弹窗:添加/修改 --> <RoleForm ref="formRef" @success="getList" /> <!-- 表单弹窗:菜单权限 --> - <MenuPermissionForm ref="menuPermissionFormRef" @success="getList" /> + <RoleAssignMenuForm ref="assignMenuFormRef" /> + <!-- 表单弹窗:数据权限 --> + <RoleDataPermissionForm ref="dataPermissionFormRef" /> </template> <script setup lang="tsx"> -import * as RoleApi from '@/api/system/role' -import RoleForm from './RoleForm.vue' -import MenuPermissionForm from './MenuPermissionForm.vue' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' +import * as RoleApi from '@/api/system/role' +import RoleForm from './RoleForm.vue' +import RoleAssignMenuForm from './RoleAssignMenuForm.vue' +import RoleDataPermissionForm from './RoleDataPermissionForm.vue' const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 @@ -202,9 +205,15 @@ const openForm = (type: string, id?: number) => { } /** 数据权限操作 */ -const menuPermissionFormRef = ref() -const handleScope = async (type: string, row: RoleApi.RoleVO) => { - menuPermissionFormRef.value.openModal(type, row) +const dataPermissionFormRef = ref() +const openDataPermissionForm = async (row: RoleApi.RoleVO) => { + dataPermissionFormRef.value.open(row) +} + +/** 菜单权限操作 */ +const assignMenuFormRef = ref() +const openAssignMenuForm = async (row: RoleApi.RoleVO) => { + assignMenuFormRef.value.open(row) } /** 删除按钮操作 */ diff --git a/src/views/system/sensitiveWord/SensitiveWordForm.vue b/src/views/system/sensitiveWord/SensitiveWordForm.vue index c069756b..aebaf6de 100644 --- a/src/views/system/sensitiveWord/SensitiveWordForm.vue +++ b/src/views/system/sensitiveWord/SensitiveWordForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -39,7 +39,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -50,8 +50,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -70,8 +70,8 @@ const tagList = ref([]) // 标签数组 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -106,7 +106,7 @@ const submitForm = async () => { await SensitiveWordApi.updateSensitiveWord(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/sensitiveWord/SensitiveWordTestForm.vue b/src/views/system/sensitiveWord/SensitiveWordTestForm.vue index 881309c8..80308033 100644 --- a/src/views/system/sensitiveWord/SensitiveWordTestForm.vue +++ b/src/views/system/sensitiveWord/SensitiveWordTestForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="检测敏感词" v-model="modelVisible"> + <Dialog title="检测敏感词" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -24,10 +24,8 @@ </el-form-item> </el-form> <template #footer> - <div class="dialog-footer"> - <el-button @click="submitForm" type="primary" :disabled="formLoading">检 测</el-button> - <el-button @click="modelVisible = false">取 消</el-button> - </div> + <el-button @click="submitForm" type="primary" :disabled="formLoading">检 测</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -35,7 +33,7 @@ import * as SensitiveWordApi from '@/api/system/sensitiveWord' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ text: '', @@ -50,7 +48,7 @@ const tagList = ref([]) // 标签数组 /** 打开弹窗 */ const open = async () => { - modelVisible.value = true + dialogVisible.value = true resetForm() // 获得 Tag 标签列表 tagList.value = await SensitiveWordApi.getSensitiveWordTagList() @@ -73,7 +71,7 @@ const submitForm = async () => { return } message.warning('包含敏感词:' + data.join(', ')) - modelVisible.value = false + dialogVisible.value = false } finally { formLoading.value = false } diff --git a/src/views/system/sms/channel/SmsChannelForm.vue b/src/views/system/sms/channel/SmsChannelForm.vue index 3145af91..cd491112 100644 --- a/src/views/system/sms/channel/SmsChannelForm.vue +++ b/src/views/system/sms/channel/SmsChannelForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -46,7 +46,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -57,8 +57,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -81,8 +81,8 @@ const formRef = ref() // 表单 Ref /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -116,7 +116,7 @@ const submitForm = async () => { await SmsChannelApi.updateSmsChannel(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/sms/channel/index.vue b/src/views/system/sms/channel/index.vue index 62cd7cc2..2eb48f02 100644 --- a/src/views/system/sms/channel/index.vue +++ b/src/views/system/sms/channel/index.vue @@ -40,6 +40,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openForm('create')" v-hasPermi="['system:sms-channel:create']" > diff --git a/src/views/system/sms/log/SmsLogDetail.vue b/src/views/system/sms/log/SmsLogDetail.vue index 736d0b8e..8a368b3e 100644 --- a/src/views/system/sms/log/SmsLogDetail.vue +++ b/src/views/system/sms/log/SmsLogDetail.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="详情" v-model="modelVisible" :scroll="true" :max-height="500" width="800"> + <Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500" width="800"> <el-descriptions border :column="1"> <el-descriptions-item label="日志主键" min-width="120"> {{ detailData.id }} @@ -65,14 +65,14 @@ import { formatDate } from '@/utils/formatTime' import * as SmsLogApi from '@/api/system/sms/smsLog' import * as SmsChannelApi from '@/api/system/sms/smsChannel' -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const detailLoading = ref(false) // 表单的加载中 const detailData = ref() // 详情数据 const channelList = ref([]) // 短信渠道列表 /** 打开弹窗 */ const open = async (data: SmsLogApi.SmsLogVO) => { - modelVisible.value = true + dialogVisible.value = true // 设置数据 detailLoading.value = true try { diff --git a/src/views/system/sms/template/SmsTemplateForm.vue b/src/views/system/sms/template/SmsTemplateForm.vue index 03684215..22de060a 100644 --- a/src/views/system/sms/template/SmsTemplateForm.vue +++ b/src/views/system/sms/template/SmsTemplateForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -59,7 +59,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -71,8 +71,8 @@ import { CommonStatusEnum } from '@/utils/constants' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型 const formData = ref<SmsTemplateApi.SmsTemplateVO>({ @@ -99,8 +99,8 @@ const formRef = ref() // 表单 Ref const channelList = ref<SmsChannelApi.SmsChannelVO[]>([]) // 短信渠道列表 const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -134,7 +134,7 @@ const submitForm = async () => { await SmsTemplateApi.updateSmsTemplate(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/sms/template/SmsTemplateSendForm.vue b/src/views/system/sms/template/SmsTemplateSendForm.vue index f2ecbe9f..2da0e3f6 100644 --- a/src/views/system/sms/template/SmsTemplateSendForm.vue +++ b/src/views/system/sms/template/SmsTemplateSendForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog title="测试" v-model="modelVisible"> + <Dialog title="测试" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -32,7 +32,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -40,7 +40,7 @@ import * as SmsTemplateApi from '@/api/system/sms/smsTemplate' const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 +const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 // 发送短信表单相关 @@ -59,7 +59,7 @@ const formRules = reactive({ const formRef = ref() // 表单 Ref const open = async (id: number) => { - modelVisible.value = true + dialogVisible.value = true resetForm() // 设置数据 formLoading.value = true @@ -97,7 +97,7 @@ const submitForm = async () => { if (logId) { message.success('提交发送成功!发送结果,见发送日志编号:' + logId) } - modelVisible.value = false + dialogVisible.value = false } finally { formLoading.value = false } diff --git a/src/views/system/tenant/form.vue b/src/views/system/tenant/form.vue index 4a6eaae4..ec19b487 100644 --- a/src/views/system/tenant/form.vue +++ b/src/views/system/tenant/form.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible" width="50%"> + <Dialog :title="dialogTitle" v-model="dialogVisible" width="50%"> <el-form ref="formRef" :model="formData" @@ -71,7 +71,7 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> @@ -83,8 +83,8 @@ import * as TenantPackageApi from '@/api/system/tenantPackage' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -112,8 +112,8 @@ const packageList = ref([]) // 租户套餐 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 修改时,设置数据 @@ -148,7 +148,7 @@ const submitForm = async () => { await TenantApi.updateTenant(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { diff --git a/src/views/system/tenant/index.vue b/src/views/system/tenant/index.vue index 1991fbea..ff65436b 100644 --- a/src/views/system/tenant/index.vue +++ b/src/views/system/tenant/index.vue @@ -71,7 +71,12 @@ <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button> - <el-button type="primary" @click="openForm('create')" v-hasPermi="['system:tenant:create']"> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['system:tenant:create']" + > <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> diff --git a/src/views/system/tenantPackage/TenantPackageForm.vue b/src/views/system/tenantPackage/TenantPackageForm.vue index 82fc351f..becbcaab 100644 --- a/src/views/system/tenantPackage/TenantPackageForm.vue +++ b/src/views/system/tenantPackage/TenantPackageForm.vue @@ -1,5 +1,5 @@ <template> - <Dialog :title="modelTitle" v-model="modelVisible"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> <el-form ref="formRef" :model="formData" @@ -57,24 +57,22 @@ </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="modelVisible = false">取 消</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> <script setup lang="ts" name="TenantPackageForm"> import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { CommonStatusEnum } from '@/utils/constants' -import { defaultProps } from '@/utils/tree' +import { defaultProps, handleTree } from '@/utils/tree' import * as TenantPackageApi from '@/api/system/tenantPackage' import * as MenuApi from '@/api/system/menu' import { ElTree } from 'element-plus' -import { handleTree } from '@/utils/tree' - const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const modelVisible = ref(false) // 弹窗的是否展示 -const modelTitle = ref('') // 弹窗的标题 +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ @@ -92,13 +90,13 @@ const formRules = reactive({ const formRef = ref() // 表单 Ref const menuOptions = ref<any[]>([]) // 树形结构数据 const menuExpand = ref(false) // 展开/折叠 -const treeRef = ref<InstanceType<typeof ElTree>>() // 树组件Ref +const treeRef = ref<InstanceType<typeof ElTree>>() // 树组件 Ref const treeNodeAll = ref(false) // 全选/全不选 /** 打开弹窗 */ const open = async (type: string, id?: number) => { - modelVisible.value = true - modelTitle.value = t('action.' + type) + dialogVisible.value = true + dialogTitle.value = t('action.' + type) formType.value = type resetForm() // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点 @@ -133,8 +131,8 @@ const submitForm = async () => { try { const data = formData.value as unknown as TenantPackageApi.TenantPackageVO data.menuIds = [ - ...(treeRef.value!.getCheckedKeys(false) as unknown as Array<number>), // 获得当前选中节点 - ...(treeRef.value!.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点 + ...(treeRef.value.getCheckedKeys(false) as unknown as Array<number>), // 获得当前选中节点 + ...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点 ] if (formType.value === 'create') { await TenantPackageApi.createTenantPackage(data) @@ -143,7 +141,7 @@ const submitForm = async () => { await TenantPackageApi.updateTenantPackage(data) message.success(t('common.updateSuccess')) } - modelVisible.value = false + dialogVisible.value = false // 发送操作成功的事件 emit('success') } finally { @@ -168,17 +166,19 @@ const resetForm = () => { formRef.value?.resetFields() } -// 全选/全不选 +/** 全选/全不选 */ const handleCheckedTreeNodeAll = () => { - treeRef.value!.setCheckedNodes(treeNodeAll.value ? menuOptions.value : []) + treeRef.value.setCheckedNodes(treeNodeAll.value ? menuOptions.value : []) } -// 全部(展开/折叠)TODO:for循环全部展开和折叠树组件数据 +/** 展开/折叠全部 */ const handleCheckedTreeExpand = () => { const nodes = treeRef.value?.store.nodesMap for (let node in nodes) { - if (nodes[node].expanded === menuExpand.value) continue - nodes[node].expanded = !nodes[node].expanded + if (nodes[node].expanded === menuExpand.value) { + continue + } + nodes[node].expanded = menuExpand.value } } </script> diff --git a/src/views/system/tenantPackage/index.vue b/src/views/system/tenantPackage/index.vue index ac24ea94..b20c432e 100644 --- a/src/views/system/tenantPackage/index.vue +++ b/src/views/system/tenantPackage/index.vue @@ -42,6 +42,7 @@ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button type="primary" + plain @click="openForm('create')" v-hasPermi="['system:tenant-package:create']" > @@ -63,11 +64,13 @@ </template> </el-table-column> <el-table-column label="备注" align="center" prop="remark" /> - <el-table-column label="创建时间" align="center" prop="createTime" width="180"> - <template #default="scope"> - <span>{{ formatDate(scope.row.createTime) }}</span> - </template> - </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + width="180" + :formatter="dateFormatter" + /> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template #default="scope"> <el-button @@ -103,7 +106,7 @@ </template> <script setup lang="ts" name="TenantPackage"> import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' -import { formatDate } from '@/utils/formatTime' +import { dateFormatter } from '@/utils/formatTime' import * as TenantPackageApi from '@/api/system/tenantPackage' import TenantPackageForm from './TenantPackageForm.vue' const message = useMessage() // 消息弹窗 @@ -112,7 +115,7 @@ const { t } = useI18n() // 国际化 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 const list = ref([]) // 列表的数据 -const queryParams: Record<string, any> = ref<Record<string, any>>({ +const queryParams = reactive({ pageNo: 1, pageSize: 10, name: null, diff --git a/src/views/system/user/components/UserDeptTree.vue b/src/views/system/user/DeptTree.vue similarity index 66% rename from src/views/system/user/components/UserDeptTree.vue rename to src/views/system/user/DeptTree.vue index 59004a92..87ffaf9a 100644 --- a/src/views/system/user/components/UserDeptTree.vue +++ b/src/views/system/user/DeptTree.vue @@ -1,6 +1,6 @@ <template> <div class="head-container"> - <el-input v-model="deptName" placeholder="请输入部门名称" clearable style="margin-bottom: 20px"> + <el-input v-model="deptName" placeholder="请输入部门名称" clearable class="mb-20px"> <template #prefix> <Icon icon="ep:search" /> </template> @@ -8,15 +8,15 @@ </div> <div class="head-container"> <el-tree - :data="deptOptions" + :data="deptList" :props="defaultProps" + node-key="id" :expand-on-click-node="false" :filter-node-method="filterNode" ref="treeRef" - node-key="id" default-expand-all highlight-current - @node-click="handleDeptNodeClick" + @node-click="handleNodeClick" /> </div> </template> @@ -26,25 +26,30 @@ import { ElTree } from 'element-plus' import * as DeptApi from '@/api/system/dept' import { defaultProps, handleTree } from '@/utils/tree' -const emits = defineEmits(['node-click']) const deptName = ref('') -const deptOptions = ref<Tree[]>([]) // 树形结构 +const deptList = ref<Tree[]>([]) // 树形结构 const treeRef = ref<InstanceType<typeof ElTree>>() + +/** 获得部门树 */ const getTree = async () => { const res = await DeptApi.getSimpleDeptList() - deptOptions.value = [] - deptOptions.value.push(...handleTree(res)) + deptList.value = [] + deptList.value.push(...handleTree(res)) } -const filterNode = (value: string, data: Tree) => { - if (!value) return true - return data.name.includes(value) +/** 基于名字过滤 */ +const filterNode = (name: string, data: Tree) => { + if (!name) return true + return data.name.includes(name) } -const handleDeptNodeClick = async (row: { [key: string]: any }) => { +/** 处理部门被点击 */ +const handleNodeClick = async (row: { [key: string]: any }) => { emits('node-click', row) } +const emits = defineEmits(['node-click']) +/** 初始化 */ onMounted(async () => { await getTree() }) diff --git a/src/views/system/user/UserAssignRoleForm.vue b/src/views/system/user/UserAssignRoleForm.vue new file mode 100644 index 00000000..e0259b7d --- /dev/null +++ b/src/views/system/user/UserAssignRoleForm.vue @@ -0,0 +1,93 @@ +<template> + <Dialog title="分配角色" v-model="dialogVisible"> + <el-form ref="formRef" :model="formData" label-width="80px" v-loading="formLoading"> + <el-form-item label="用户名称"> + <el-input v-model="formData.username" :disabled="true" /> + </el-form-item> + <el-form-item label="用户昵称"> + <el-input v-model="formData.nickname" :disabled="true" /> + </el-form-item> + <el-form-item label="角色"> + <el-select v-model="formData.roleIds" multiple placeholder="请选择角色"> + <el-option v-for="item in roleList" :key="item.id" :label="item.name" :value="item.id" /> + </el-select> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as PermissionApi from '@/api/system/permission' +import * as UserApi from '@/api/system/user' +import * as RoleApi from '@/api/system/role' +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = ref({ + id: undefined, + nickname: '', + username: '', + roleIds: [] +}) +const formRef = ref() // 表单 Ref +const roleList = ref([]) // 角色的列表 + +/** 打开弹窗 */ +const open = async (row: UserApi.UserVO) => { + dialogVisible.value = true + resetForm() + // 设置数据 + formData.value.id = row.id + formData.value.username = row.username + formData.value.nickname = row.nickname + // 获得角色拥有的菜单集合 + formLoading.value = true + try { + formData.value.roleIds = await PermissionApi.getUserRoleList(row.id) + } finally { + formLoading.value = false + } + // 获得角色列表 + roleList.value = await RoleApi.getSimpleRoleList() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + await PermissionApi.assignUserRole({ + userId: formData.value.id, + roleIds: formData.value.roleIds + }) + message.success(t('common.updateSuccess')) + dialogVisible.value = false + // 发送操作成功的事件 + emit('success', true) + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + nickname: '', + username: '', + roleIds: [] + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/system/user/components/UserForm.vue b/src/views/system/user/UserForm.vue similarity index 57% rename from src/views/system/user/components/UserForm.vue rename to src/views/system/user/UserForm.vue index 4ea21607..3afcbd1f 100644 --- a/src/views/system/user/components/UserForm.vue +++ b/src/views/system/user/UserForm.vue @@ -1,7 +1,12 @@ <template> - <!-- 添加或修改参数配置对话框 --> - <Dialog :title="title" :modelValue="showDialog" width="600px" append-to-body @close="closeDialog"> - <el-form ref="formRef" :model="formData" :rules="rules" label-width="80px"> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="80px" + v-loading="formLoading" + > <el-row> <el-col :span="12"> <el-form-item label="用户昵称" prop="nickname"> @@ -13,7 +18,7 @@ <el-tree-select node-key="id" v-model="formData.deptId" - :data="deptOptions" + :data="deptList" :props="defaultProps" check-strictly placeholder="请选择归属部门" @@ -56,7 +61,7 @@ <el-select v-model="formData.sex" placeholder="请选择"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" - :key="dict.value as number" + :key="dict.value" :label="dict.label" :value="dict.value" /> @@ -67,10 +72,10 @@ <el-form-item label="岗位"> <el-select v-model="formData.postIds" multiple placeholder="请选择"> <el-option - v-for="item in postOptions" + v-for="item in postList" :key="item.id" :label="item.name" - :value="item.id as number" + :value="item.id" /> </el-select> </el-form-item> @@ -85,53 +90,26 @@ </el-row> </el-form> <template #footer> - <div class="dialog-footer"> - <el-button type="primary" @click="submitForm">确 定</el-button> - <el-button @click="cancel">取 消</el-button> - </div> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> <script lang="ts" setup> -import { PostVO } from '@/api/system/post' -import * as PostApi from '@/api/system/post' -import { createUserApi, getUserApi, updateUserApi } from '@/api/system/user' -import * as DeptApi from '@/api/system/dept' - import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { CommonStatusEnum } from '@/utils/constants' import { defaultProps, handleTree } from '@/utils/tree' -import { ElForm, FormItemRule } from 'element-plus' -import { Arrayable } from 'element-plus/es/utils' -import { UserVO } from '@/api/login/types' - -type Form = InstanceType<typeof ElForm> - -const emits = defineEmits(['success']) - +import * as PostApi from '@/api/system/post' +import * as DeptApi from '@/api/system/dept' +import * as UserApi from '@/api/system/user' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 -const showDialog = ref(false) -// 弹出层标题 -const title = computed(() => { - return formData.value?.id ? '修改用户' : '添加用户' -}) - -const deptOptions = ref<Tree[]>([]) // 树形结构 -const getTree = async () => { - const res = await DeptApi.getSimpleDeptList() - deptOptions.value = [] - deptOptions.value.push(...handleTree(res)) -} -// 获取岗位列表 -const postOptions = ref<PostVO[]>([]) //岗位列表 -const getPostOptions = async () => { - const res = (await PostApi.getSimplePostList()) as PostVO[] - postOptions.value.push(...res) -} - -// 表单初始化参数 -const initParams = { +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ nickname: '', deptId: '', mobile: '', @@ -139,15 +117,13 @@ const initParams = { id: undefined, username: '', password: '', - sex: 1, + sex: undefined, postIds: [], remark: '', - status: '0', + status: CommonStatusEnum.ENABLE, roleIds: [] -} - -// 校验规则 -const rules = { +}) +const formRules = reactive({ username: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }], nickname: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }], password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }], @@ -165,73 +141,75 @@ const rules = { trigger: 'blur' } ] -} as Partial<Record<string, Arrayable<FormItemRule>>> -const formRef = ref<Form | null>() -const formData = ref<Recordable>({ ...initParams }) +}) +const formRef = ref() // 表单 Ref +const deptList = ref<Tree[]>([]) // 树形结构 +const postList = ref([]) // 岗位列表 -const resetForm = () => { - let form = formRef?.value - if (!form) return - formData.value = { ...initParams } - form && (form as Form).resetFields() -} -const closeDialog = () => { - showDialog.value = false -} -// 操作成功 -const operateOk = () => { - emits('success', true) - closeDialog() -} -const submitForm = () => { - let form = formRef.value as Form - form.validate(async (valid) => { - let data = formData.value - if (valid) { - try { - if (data?.id !== undefined) { - await updateUserApi(data) - message.success(t('common.updateSuccess')) - operateOk() - } else { - await createUserApi(data) - message.success(t('common.createSuccess')) - operateOk() - } - } catch (err) { - console.error(err) - } - } - }) -} -/* 取消 */ -const cancel = () => { - closeDialog() -} - -/* 打开弹框 */ -const openForm = (row: undefined | UserVO) => { +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type resetForm() - getTree() // 部门树 - if (row && row.id) { - const id = row.id - getUserApi(id).then((response) => { - formData.value = response - }) - } else { - formData.value = { ...initParams } + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await UserApi.getUser(id) + } finally { + formLoading.value = false + } } + // 加载部门树 + deptList.value = handleTree(await DeptApi.getSimpleDeptList()) + // 加载岗位列表 + postList.value = await PostApi.getSimplePostList() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 - showDialog.value = true +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + if (!formRef) return + const valid = await formRef.value.validate() + if (!valid) return + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as UserApi.UserVO + if (formType.value === 'create') { + await UserApi.createUser(data) + message.success(t('common.createSuccess')) + } else { + await UserApi.updateUser(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } } -// ========== 初始化 ========== -onMounted(async () => { - await getPostOptions() -}) - -defineExpose({ - resetForm, - openForm -}) +/** 重置表单 */ +const resetForm = () => { + formData.value = { + nickname: '', + deptId: '', + mobile: '', + email: '', + id: undefined, + username: '', + password: '', + sex: undefined, + postIds: [], + remark: '', + status: CommonStatusEnum.ENABLE, + roleIds: [] + } + formRef.value?.resetFields() +} </script> diff --git a/src/views/system/user/UserImportForm.vue b/src/views/system/user/UserImportForm.vue new file mode 100644 index 00000000..f03345e9 --- /dev/null +++ b/src/views/system/user/UserImportForm.vue @@ -0,0 +1,129 @@ +<template> + <Dialog title="用户导入" v-model="dialogVisible" width="400"> + <el-upload + ref="uploadRef" + :action="importUrl + '?updateSupport=' + updateSupport" + :headers="uploadHeaders" + v-model:file-list="fileList" + drag + accept=".xlsx, .xls" + :limit="1" + :on-success="submitFormSuccess" + :on-exceed="handleExceed" + :on-error="submitFormError" + :auto-upload="false" + :disabled="formLoading" + > + <Icon icon="ep:upload" /> + <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> + <template #tip> + <div class="el-upload__tip text-center"> + <div class="el-upload__tip"> + <el-checkbox v-model="updateSupport" /> 是否更新已经存在的用户数据 + </div> + <span>仅允许导入 xls、xlsx 格式文件。</span> + <el-link + type="primary" + :underline="false" + style="font-size: 12px; vertical-align: baseline" + @click="importTemplate" + > + 下载模板 + </el-link> + </div> + </template> + </el-upload> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script lang="ts" setup> +import * as UserApi from '@/api/system/user' +import { getAccessToken, getTenantId } from '@/utils/auth' +import download from '@/utils/download' +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中 +const uploadRef = ref() +const importUrl = + import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/import' +const uploadHeaders = ref() // 上传 Header 头 +const fileList = ref([]) // 文件列表 +const updateSupport = ref(0) // 是否更新已经存在的用户数据 + +/** 打开弹窗 */ +const open = () => { + dialogVisible.value = true + resetForm() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const submitForm = async () => { + if (fileList.value.length == 0) { + message.error('请上传文件') + return + } + // 提交请求 + uploadHeaders.value = { + Authorization: 'Bearer ' + getAccessToken(), + 'tenant-id': getTenantId() + } + formLoading.value = true + uploadRef.value!.submit() +} + +/** 文件上传成功 */ +const emits = defineEmits(['success']) +const submitFormSuccess = (response: any) => { + if (response.code !== 0) { + message.error(response.msg) + formLoading.value = false + return + } + // 拼接提示语 + const data = response.data + let text = '上传成功数量:' + data.createUsernames.length + ';' + for (let username of data.createUsernames) { + text += '< ' + username + ' >' + } + text += '更新成功数量:' + data.updateUsernames.length + ';' + for (const username of data.updateUsernames) { + text += '< ' + username + ' >' + } + text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';' + for (const username in data.failureUsernames) { + text += '< ' + username + ': ' + data.failureUsernames[username] + ' >' + } + message.alert(text) + // 发送操作成功的事件 + emits('success') +} + +/** 上传错误提示 */ +const submitFormError = (): void => { + message.error('上传失败,请您重新上传!') + formLoading.value = false +} + +/** 重置表单 */ +const resetForm = () => { + // 重置上传状态和文件 + formLoading.value = false + uploadRef.value?.clearFiles() +} + +/** 文件数超出提示 */ +const handleExceed = (): void => { + message.error('最多只能上传一个文件!') +} + +/** 下载模板操作 */ +const importTemplate = async () => { + const res = await UserApi.importUserTemplate() + download.excel(res, '用户导入模版.xls') +} +</script> diff --git a/src/views/system/user/components/UserAssignRoleForm.vue b/src/views/system/user/components/UserAssignRoleForm.vue deleted file mode 100644 index bbf37739..00000000 --- a/src/views/system/user/components/UserAssignRoleForm.vue +++ /dev/null @@ -1,108 +0,0 @@ -<template> - <Dialog - title="分配角色" - :modelValue="showDialog" - width="500px" - append-to-body - @close="closeDialog" - > - <el-form :model="formData" label-width="80px" ref="formRef"> - <el-form-item label="用户名称"> - <el-input v-model="formData.username" :disabled="true" /> - </el-form-item> - <el-form-item label="用户昵称"> - <el-input v-model="formData.nickname" :disabled="true" /> - </el-form-item> - <el-form-item label="角色"> - <el-select v-model="formData.roleIds" multiple placeholder="请选择角色"> - <el-option - v-for="item in roleOptions" - :key="item.id" - :label="item.name" - :value="item.id" - /> - </el-select> - </el-form-item> - </el-form> - <template #footer> - <div class="dialog-footer"> - <el-button type="primary" @click="submit">确 定</el-button> - <el-button @click="cancel">取 消</el-button> - </div> - </template> - </Dialog> -</template> - -<script setup lang="ts"> -import { - assignUserRoleApi, - listUserRolesApi, - PermissionAssignUserRoleReqVO -} from '@/api/system/permission' -import { UserVO } from '@/api/system/user' -import * as RoleApi from '@/api/system/role' - -const emits = defineEmits(['success']) - -const { t } = useI18n() // 国际化 -const message = useMessage() // 消息弹窗 - -// 表单初始化参数 -const initParams = { - nickname: '', - id: 0, - username: '', - roleIds: [] as number[] -} -const formData = ref<Recordable>({ ...initParams }) - -/* 弹框按钮操作 */ -// 点击取消 -const cancel = () => { - closeDialog() -} -// 关闭弹窗 -const closeDialog = () => { - showDialog.value = false -} -// 提交 -const submit = async () => { - const data = ref<PermissionAssignUserRoleReqVO>({ - userId: formData.value.id, - roleIds: formData.value.roleIds - }) - try { - await assignUserRoleApi(data.value) - message.success(t('common.updateSuccess')) - emits('success', true) - closeDialog() - } catch (error) { - console.error(error) - } -} - -const roleOptions = ref() -const userRole = reactive(initParams) -const showDialog = ref(false) -const formRef = ref() -const openForm = async (row: UserVO) => { - formRef.value?.resetFields() - userRole.id = row.id - userRole.username = row.username - userRole.nickname = row.nickname - - // 获得角色列表 - const roleOpt = await RoleApi.getSimpleRoleList() - roleOptions.value = [...roleOpt] - - // 获得角色拥有的菜单集合 - const roles = await listUserRolesApi(row.id) - userRole.roleIds = roles - formData.value = { ...userRole } - - showDialog.value = true -} -defineExpose({ - openForm -}) -</script> diff --git a/src/views/system/user/components/UserImportForm.vue b/src/views/system/user/components/UserImportForm.vue deleted file mode 100644 index f63936e2..00000000 --- a/src/views/system/user/components/UserImportForm.vue +++ /dev/null @@ -1,154 +0,0 @@ -<template> - <Dialog - :title="upload.title" - :modelValue="showDialog" - width="400px" - append-to-body - @close="closeDialog" - > - <el-upload - ref="uploadRef" - accept=".xlsx, .xls" - :limit="1" - :headers="upload.headers" - :action="upload.url + '?updateSupport=' + upload.updateSupport" - :disabled="upload.isUploading" - :on-progress="handleFileUploadProgress" - :on-success="handleFileSuccess" - :on-exceed="handleExceed" - :on-error="excelUploadError" - :auto-upload="false" - drag - > - <Icon icon="ep:upload" /> - <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> - <template #tip> - <div class="el-upload__tip text-center"> - <div class="el-upload__tip"> - <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据 - </div> - - <span>仅允许导入xls、xlsx格式文件。</span> - <el-link - type="primary" - :underline="false" - style="font-size: 12px; vertical-align: baseline" - @click="importTemplate" - >下载模板</el-link - > - </div> - </template> - </el-upload> - <template #footer> - <div class="dialog-footer"> - <el-button type="primary" @click="submitFileForm">确 定</el-button> - <el-button @click="cancel">取 消</el-button> - </div> - </template> - </Dialog> -</template> - -<script lang="ts" setup> -import { importUserTemplateApi } from '@/api/system/user' -import { getAccessToken, getTenantId } from '@/utils/auth' -import download from '@/utils/download' - -const emits = defineEmits(['success']) - -const message = useMessage() // 消息弹窗 - -const showDialog = ref(false) -const uploadRef = ref() - -// 用户导入参数 -const upload = reactive({ - // // 是否显示弹出层(用户导入) - // open: false, - // 弹出层标题(用户导入) - title: '用户导入', - // 是否禁用上传 - isUploading: false, - // 是否更新已经存在的用户数据 - updateSupport: 0, - // 设置上传的请求头部 - headers: { - Authorization: 'Bearer ' + getAccessToken(), - 'tenant-id': getTenantId() - }, - // 上传的地址 - url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/import' -}) - -// 文件上传中处理 -const handleFileUploadProgress = () => { - upload.isUploading = true -} -// 文件上传成功处理 -const handleFileSuccess = (response: any) => { - if (response.code !== 0) { - message.error(response.msg) - return - } - upload.isUploading = false - uploadRef.value?.clearFiles() - // 拼接提示语 - const data = response.data - let text = '上传成功数量:' + data.createUsernames.length + ';' - for (let username of data.createUsernames) { - text += '< ' + username + ' >' - } - text += '更新成功数量:' + data.updateUsernames.length + ';' - for (const username of data.updateUsernames) { - text += '< ' + username + ' >' - } - text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';' - for (const username in data.failureUsernames) { - text += '< ' + username + ': ' + data.failureUsernames[username] + ' >' - } - message.alert(text) - emits('success') - closeDialog() -} - -// 文件数超出提示 -const handleExceed = (): void => { - message.error('最多只能上传一个文件!') -} -// 上传错误提示 -const excelUploadError = (e): void => { - console.log(e) - message.error('导入数据失败,请您重新上传!') -} - -/** 下载模板操作 */ -const importTemplate = async () => { - try { - const res = await importUserTemplateApi() - download.excel(res, '用户导入模版.xls') - } catch (error) { - console.error(error) - } -} - -/* 弹框按钮操作 */ -// 点击取消 -const cancel = () => { - closeDialog() -} -// 关闭弹窗 -const closeDialog = () => { - showDialog.value = false -} -// 提交上传文件 -const submitFileForm = () => { - uploadRef.value?.submit() -} - -const openForm = () => { - uploadRef.value?.clearFiles() - showDialog.value = true -} -defineExpose({ - openForm -}) -</script> diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue index 1c36d376..c95c9fd7 100644 --- a/src/views/system/user/index.vue +++ b/src/views/system/user/index.vue @@ -1,256 +1,216 @@ <template> - <div class="app-container"> - <content-wrap> - <!-- 搜索工作栏 --> - <el-row :gutter="20"> - <!--部门数据--> - <el-col :span="4" :xs="24"> - <UserDeptTree @node-click="handleDeptNodeClick" /> - </el-col> - <!--用户数据--> - <el-col :span="20" :xs="24"> - <el-form - :model="queryParams" - ref="queryFormRef" - :inline="true" - v-show="showSearch" - label-width="68px" - > - <el-form-item label="用户名称" prop="username"> - <el-input - v-model="queryParams.username" - placeholder="请输入用户名称" - clearable - style="width: 240px" - @keyup.enter="handleQuery" - /> - </el-form-item> - <el-form-item label="手机号码" prop="mobile"> - <el-input - v-model="queryParams.mobile" - placeholder="请输入手机号码" - clearable - style="width: 240px" - @keyup.enter="handleQuery" - /> - </el-form-item> - <el-form-item label="状态" prop="status"> - <el-select - v-model="queryParams.status" - placeholder="用户状态" - clearable - style="width: 240px" - > - <el-option - v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" - :key="dict.value as number" - :label="dict.label" - :value="dict.value as number" - /> - </el-select> - </el-form-item> - <el-form-item label="创建时间" prop="createTime"> - <el-date-picker - v-model="queryParams.createTime" - style="width: 240px" - value-format="YYYY-MM-DD HH:mm:ss" - type="datetimerange" - range-separator="-" - start-placeholder="开始日期" - end-placeholder="结束日期" - /> - </el-form-item> - <el-form-item> - <el-button type="primary" @click="handleQuery" - ><Icon icon="ep:search" />搜索</el-button - > - <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button> - </el-form-item> - </el-form> - - <el-row :gutter="10" class="mb-8px"> - <el-col :span="1.5"> - <el-button - type="primary" - plain - size="small" - @click="handleAdd" - v-hasPermi="['system:user:create']" - ><Icon icon="ep:plus" />新增</el-button - > - </el-col> - <el-col :span="1.5"> - <el-button - type="info" - size="small" - @click="handleImport" - v-hasPermi="['system:user:import']" - ><Icon icon="ep:upload" />导入</el-button - > - </el-col> - <el-col :span="1.5"> - <el-button - type="warning" - size="small" - @click="handleExport" - :loading="exportLoading" - v-hasPermi="['system:user:export']" - ><Icon icon="ep:download" />导出</el-button - > - </el-col> - </el-row> - <el-table v-loading="loading" :data="userList"> - <el-table-column - label="用户编号" - align="center" - key="id" - prop="id" - v-if="columns[0].visible" + <el-row :gutter="20"> + <!-- 左侧部门树 --> + <el-col :span="4" :xs="24"> + <content-wrap class="h-1/1"> + <DeptTree @node-click="handleDeptNodeClick" /> + </content-wrap> + </el-col> + <el-col :span="20" :xs="24"> + <!-- 搜索 --> + <content-wrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户名称" prop="username"> + <el-input + v-model="queryParams.username" + placeholder="请输入用户名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" /> - <el-table-column - label="用户名称" - align="center" - key="username" - prop="username" - v-if="columns[1].visible" - :show-overflow-tooltip="true" + </el-form-item> + <el-form-item label="手机号码" prop="mobile"> + <el-input + v-model="queryParams.mobile" + placeholder="请输入手机号码" + clearable + @keyup.enter="handleQuery" + class="!w-240px" /> - <el-table-column - label="用户昵称" - align="center" - key="nickname" - prop="nickname" - v-if="columns[2].visible" - :show-overflow-tooltip="true" - /> - <el-table-column - label="部门" - align="center" - key="deptName" - prop="dept.name" - v-if="columns[3].visible" - :show-overflow-tooltip="true" - /> - <el-table-column - label="手机号码" - align="center" - key="mobile" - prop="mobile" - v-if="columns[4].visible" - width="120" - /> - <el-table-column label="状态" key="status" v-if="columns[5].visible" align="center"> - <template #default="scope"> - <el-switch - v-model="scope.row.status" - :active-value="0" - :inactive-value="1" - @change="handleStatusChange(scope.row)" - /> - </template> - </el-table-column> - <el-table-column - label="创建时间" - align="center" - prop="createTime" - v-if="columns[6].visible" - width="160" + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="用户状态" + clearable + class="!w-240px" > - <template #default="scope"> - <span>{{ parseTime(scope.row.createTime) }}</span> - </template> - </el-table-column> - <el-table-column - label="操作" - align="center" - width="160" - class-name="small-padding fixed-width" + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="datetimerange" + start-placeholder="开始日期" + end-placeholder="结束日期" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['system:user:create']" > - <template #default="scope"> - <div class="flex justify-center items-center"> - <el-button - type="primary" - link - @click="handleUpdate(scope.row)" - v-hasPermi="['system:user:update']" - ><Icon icon="ep:edit" />修改</el-button - > - <el-dropdown - @command="(command) => handleCommand(command, scope.$index, scope.row)" - v-hasPermi="[ - 'system:user:delete', - 'system:user:update-password', - 'system:permission:assign-user-role' - ]" - > - <el-button type="primary" link><Icon icon="ep:d-arrow-right" />更多</el-button> - <template #dropdown> - <el-dropdown-menu> - <!-- div包住避免控制台报错:Runtime directive used on component with non-element root node --> - <div v-if="scope.row.id !== 1" v-hasPermi="['system:user:delete']"> - <el-dropdown-item command="handleDelete" type="text" - ><Icon icon="ep:delete" />删除</el-dropdown-item - > - </div> - <div v-hasPermi="['system:user:update-password']"> - <el-dropdown-item command="handleResetPwd" type="text" - ><Icon icon="ep:key" />重置密码</el-dropdown-item - ></div - > - <div v-hasPermi="['system:permission:assign-user-role']"> - <el-dropdown-item command="handleRole" type="text" - ><Icon icon="ep:circle-check" />分配角色</el-dropdown-item - ></div - > - </el-dropdown-menu> - </template> - </el-dropdown> - </div> - </template> - </el-table-column> - </el-table> - <pagination - v-show="total > 0" - :total="total" - v-model:page="queryParams.pageNo" - v-model:limit="queryParams.pageSize" - @pagination="getList" + <Icon icon="ep:plus" /> 新增 + </el-button> + <el-button + type="warning" + plain + @click="handleImport" + v-hasPermi="['system:user:import']" + > + <Icon icon="ep:upload" /> 导入 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['system:user:export']" + > + <Icon icon="ep:download" />导出 + </el-button> + </el-form-item> + </el-form> + </content-wrap> + <content-wrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="用户编号" align="center" key="id" prop="id" /> + <el-table-column + label="用户名称" + align="center" + prop="username" + :show-overflow-tooltip="true" /> - </el-col> - </el-row> - </content-wrap> - <!-- 添加或修改用户对话框 --> - <UserForm ref="userFormRef" @success="getList" /> - <!-- 用户导入对话框 --> - <UserImportForm ref="userImportFormRef" @success="getList" /> - <!-- 分配角色 --> - <UserAssignRoleForm ref="userAssignRoleFormRef" @success="getList" /> - </div> + <el-table-column + label="用户昵称" + align="center" + prop="nickname" + :show-overflow-tooltip="true" + /> + <el-table-column + label="部门" + align="center" + key="deptName" + prop="dept.name" + :show-overflow-tooltip="true" + /> + <el-table-column label="手机号码" align="center" prop="mobile" width="120" /> + <el-table-column label="状态" key="status"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + @change="handleStatusChange(scope.row)" + /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180" + /> + <el-table-column label="操作" align="center" width="160"> + <template #default="scope"> + <div class="flex justify-center items-center"> + <el-button + type="primary" + link + @click="openForm('update', scope.row.id)" + v-hasPermi="['system:user:update']" + > + <Icon icon="ep:edit" />修改 + </el-button> + <el-dropdown + @command="(command) => handleCommand(command, scope.row)" + v-hasPermi="[ + 'system:user:delete', + 'system:user:update-password', + 'system:permission:assign-user-role' + ]" + > + <el-button type="primary" link><Icon icon="ep:d-arrow-right" /> 更多</el-button> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item + command="handleDelete" + v-if="checkPermi(['system:user:delete'])" + > + <Icon icon="ep:delete" />删除 + </el-dropdown-item> + <el-dropdown-item + command="handleResetPwd" + v-if="checkPermi(['system:user:update-password'])" + > + <Icon icon="ep:key" />重置密码 + </el-dropdown-item> + <el-dropdown-item + command="handleRole" + v-if="checkPermi(['system:permission:assign-user-role'])" + > + <Icon icon="ep:circle-check" />分配角色 + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> + </template> + </el-table-column> + </el-table> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </content-wrap> + </el-col> + </el-row> + + <!-- 添加或修改用户对话框 --> + <UserForm ref="formRef" @success="getList" /> + <!-- 用户导入对话框 --> + <UserImportForm ref="importFormRef" @success="getList" /> + <!-- 分配角色 --> + <UserAssignRoleForm ref="assignRoleFormRef" @success="getList" /> </template> - <script setup lang="ts" name="User"> -import download from '@/utils/download' -import { parseTime } from '@/utils/formatTime' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { checkPermi } from '@/utils/permission' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' import { CommonStatusEnum } from '@/utils/constants' - -import { - deleteUserApi, - exportUserApi, - resetUserPwdApi, - updateUserStatusApi, - getUserPageApi, - UserVO -} from '@/api/system/user' - -import UserForm from './components/UserForm.vue' -import UserImportForm from './components/UserImportForm.vue' -import UserAssignRoleForm from './components/UserAssignRoleForm.vue' -import UserDeptTree from './components/UserDeptTree.vue' - +import * as UserApi from '@/api/system/user' +import UserForm from './UserForm.vue' +import UserImportForm from './UserImportForm.vue' +import UserAssignRoleForm from './UserAssignRoleForm.vue' +import DeptTree from './DeptTree.vue' const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数 const queryParams = reactive({ pageNo: 1, pageSize: 10, @@ -260,35 +220,20 @@ const queryParams = reactive({ deptId: undefined, createTime: [] }) -const showSearch = ref(true) +const queryFormRef = ref() // 搜索的表单 -const handleDeptNodeClick = async (row: { [key: string]: any }) => { - queryParams.deptId = row.id - getList() -} - -// 用户列表 -const userList = ref<UserVO[]>([]) -const loading = ref(false) -const total = ref(0) -const columns = ref([ - { key: 0, label: `用户编号`, visible: true }, - { key: 1, label: `用户名称`, visible: true }, - { key: 2, label: `用户昵称`, visible: true }, - { key: 3, label: `部门`, visible: true }, - { key: 4, label: `手机号码`, visible: true }, - { key: 5, label: `状态`, visible: true }, - { key: 6, label: `创建时间`, visible: true } -]) -/* 查询列表 */ -const getList = () => { +/** 查询列表 */ +const getList = async () => { loading.value = true - getUserPageApi(queryParams).then((response) => { - userList.value = response.list - total.value = response.total + try { + const data = await UserApi.getUserPage(queryParams.value) + list.value = data.list + total.value = data.total + } finally { loading.value = false - }) + } } + /** 搜索按钮操作 */ const handleQuery = () => { queryParams.pageNo = 1 @@ -296,54 +241,67 @@ const handleQuery = () => { } /** 重置按钮操作 */ -const queryFormRef = ref() const resetQuery = () => { queryFormRef.value?.resetFields() handleQuery() } -// 添加或编辑 -const userFormRef = ref() -// 添加用户 -const handleAdd = () => { - userFormRef.value?.openForm() +/** 处理部门被点击 */ +const handleDeptNodeClick = async (row) => { + queryParams.deptId = row.id + await getList() } -// 用户导入 -const userImportFormRef = ref() +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 用户导入 */ +const importFormRef = ref() const handleImport = () => { - userImportFormRef.value?.openForm() + importFormRef.value.open() } -// 用户导出 +/** 修改用户状态 */ +const handleStatusChange = async (row: UserApi.UserVO) => { + try { + // 修改状态的二次确认 + const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用' + await message.confirm('确认要"' + text + '""' + row.username + '"用户吗?') + // 发起修改状态 + await UserApi.updateUserStatus(row.id, row.status) + // 刷新列表 + await getList() + } catch { + // 取消后,进行恢复按钮 + row.status = + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE + } +} + +/** 导出按钮操作 */ const exportLoading = ref(false) -const handleExport = () => { - message - .confirm('是否确认导出所有用户数据项?') - .then(async () => { - // 处理查询参数 - let params = { ...queryParams } - params.pageNo = 1 - params.pageSize = 99999 - exportLoading.value = true - const response = await exportUserApi(params) - download.excel(response, '用户数据.xls') - }) - .catch(() => {}) - .finally(() => { - exportLoading.value = false - }) +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await UserApi.exportUser(queryParams) + download.excel(data, '用户数据.xls') + } catch { + } finally { + exportLoading.value = false + } } -// 操作分发 -const handleCommand = (command: string, index: number, row: UserVO) => { - console.log(index) +/** 操作分发 */ +const handleCommand = (command: string, row: UserApi.UserVO) => { switch (command) { - case 'handleUpdate': - handleUpdate(row) //修改客户信息 - break case 'handleDelete': - handleDelete(row) //红号变更 + handleDelete(row.id) break case 'handleResetPwd': handleResetPwd(row) @@ -356,67 +314,42 @@ const handleCommand = (command: string, index: number, row: UserVO) => { } } -// 用户状态修改 -const handleStatusChange = (row: UserVO) => { - let text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用' - message - .confirm('确认要"' + text + '""' + row.username + '"用户吗?', t('common.reminder')) - .then(async () => { - row.status = - row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.ENABLE : CommonStatusEnum.DISABLE - await updateUserStatusApi(row.id, row.status) - message.success(text + '成功') - // 刷新列表 - getList() - }) - .catch(() => { - row.status = - row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE - }) +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await UserApi.deleteUser(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} } -// 具体数据单行操作 -/** 修改按钮操作 */ -const handleUpdate = (row: UserVO) => { - userFormRef.value?.openForm(row) +/** 重置密码 */ +const handleResetPwd = async (row: UserApi.UserVO) => { + try { + // 重置的二次确认 + const result = await message.prompt( + '请输入"' + row.username + '"的新密码', + t('common.reminder') + ) + const password = result.value + // 发起重置 + await UserApi.resetUserPwd(row.id, password) + message.success('修改成功,新密码是:' + password) + } catch {} } -// 删除用户 -const handleDelete = (row: UserVO) => { - const ids = row.id - message - .confirm('是否确认删除用户编号为"' + ids + '"的数据项?') - .then(async () => { - await deleteUserApi(ids) - message.success('删除成功') - getList() - }) - .catch((e) => { - console.error(e) - }) +/** 分配角色 */ +const assignRoleFormRef = ref() +const handleRole = (row: UserApi.UserVO) => { + assignRoleFormRef.value.open(row) } -// 重置密码 -const handleResetPwd = (row: UserVO) => { - message - .prompt('请输入"' + row.username + '"的新密码', t('common.reminder')) - .then(async ({ value }) => { - await resetUserPwdApi(row.id, value) - message.success('修改成功,新密码是:' + value) - }) - .catch((e) => { - console.error(e) - }) -} - -// 分配角色 -const userAssignRoleFormRef = ref() -const handleRole = (row: UserVO) => { - userAssignRoleFormRef.value?.openForm(row) -} - -// ========== 初始化 ========== -onMounted(async () => { +/** 初始化 */ +onMounted(() => { getList() }) </script>