diff --git a/admin/package.json b/admin/package.json index 80ef212..37cc730 100644 --- a/admin/package.json +++ b/admin/package.json @@ -1,7 +1,8 @@ { - "name": "vue-project", - "version": "0.0.0", + "name": "x_admin", + "version": "0.0.1", "license": "MIT", + "type": "module", "scripts": { "dev": "vite", "prod": "vite build", @@ -13,54 +14,55 @@ "dependencies": { "@element-plus/icons-vue": "^2.3.1", "@highlightjs/vue-plugin": "^2.1.0", - "@logicflow/core": "^1.2.18", - "@logicflow/extension": "^1.2.19", - "@vue/shared": "^3.4.3", - "@vueuse/core": "^10.7.1", + "@logicflow/core": "^1.2.22", + "@logicflow/extension": "^1.2.22", + "@vue/shared": "^3.4.19", + "@vueuse/core": "^10.7.2", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.12", - "axios": "^1.6.3", + "axios": "^1.6.7", "consola": "^3.2.3", "crypto-js": "^4.2.0", "css-color-function": "^1.3.3", - "echarts": "^5.4.3", - "element-plus": "^2.4.4", + "echarts": "^5.5.0", + "element-plus": "^2.5.6", "highlight.js": "^11.9.0", "lodash-es": "^4.17.21", "nprogress": "^0.2.0", "pinia": "^2.1.7", + "query-string": "^8.2.0", "vform3-builds": "^3.0.10", - "vue": "^3.4.3", + "vue": "^3.4.19", "vue-clipboard3": "^2.0.0", - "vue-echarts": "^6.6.8", + "vue-echarts": "^6.6.9", "vue-router": "^4.2.5", "vue3-video-play": "^1.3.2", "vuedraggable": "^4.1.0" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.6.1", + "@rushstack/eslint-patch": "^1.7.2", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.10.6", + "@types/node": "^20.11.19", "@types/nprogress": "^0.2.3", - "@vitejs/plugin-vue": "^5.0.2", + "@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue-jsx": "^3.1.0", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^12.0.0", "@vue/tsconfig": "^0.5.1", - "autoprefixer": "^10.4.16", + "autoprefixer": "^10.4.17", "eslint": "^8.56.0", - "eslint-plugin-vue": "^9.19.2", + "eslint-plugin-vue": "^9.21.1", "execa": "^8.0.1", "fs-extra": "^11.2.0", - "postcss": "^8.4.32", - "prettier": "^3.1.1", + "postcss": "^8.4.35", + "prettier": "^3.2.5", "rollup-plugin-visualizer": "^5.12.0", - "sass": "^1.69.6", - "tailwindcss": "^3.4.0", + "sass": "^1.71.0", + "tailwindcss": "^3.4.1", "typescript": "~5.3.3", - "unplugin-auto-import": "^0.17.3", + "unplugin-auto-import": "^0.17.5", "unplugin-vue-components": "^0.26.0", - "vite": "^4.5.0", + "vite": "^5.1.3", "vite-plugin-compression": "^0.5.1", "vite-plugin-style-import": "^2.0.0", "vite-plugin-svg-icons": "^2.0.1", diff --git a/admin/postcss.config.js b/admin/postcss.config.cjs similarity index 100% rename from admin/postcss.config.js rename to admin/postcss.config.cjs diff --git a/admin/src/api/perms/admin.ts b/admin/src/api/perms/admin.ts index 18de0a2..566e56e 100644 --- a/admin/src/api/perms/admin.ts +++ b/admin/src/api/perms/admin.ts @@ -1,5 +1,6 @@ import request from '@/utils/request' - +import queryString from 'query-string' +import { getToken } from '@/utils/auth' // 管理员列表 export function adminLists(params: any) { return request.get({ url: '/system/admin/list', params }) @@ -34,3 +35,10 @@ export function adminStatus(params: any) { export function adminListByDeptId(params: any) { return request.get({ url: '/system/admin/ListByDeptId', params }) } + +// 导出 +export function adminExportFile(params: any) { + // return request.get({ url: '/system/admin/ExportFile', params }) + return (window.location.href = + `/api/admin/system/admin/ExportFile?token=${getToken()}&` + queryString.stringify(params)) +} diff --git a/admin/src/components/daterange-picker/index.vue b/admin/src/components/daterange-picker/index.vue index 9f97531..1c5fe2a 100644 --- a/admin/src/components/daterange-picker/index.vue +++ b/admin/src/components/daterange-picker/index.vue @@ -8,37 +8,49 @@ start-placeholder="开始时间" end-placeholder="结束时间" clearable + @change="changeDate" > diff --git a/admin/src/components/flow/Approver.vue b/admin/src/components/flow/Approver.vue index 15a7c43..65eb0dd 100644 --- a/admin/src/components/flow/Approver.vue +++ b/admin/src/components/flow/Approver.vue @@ -2,6 +2,7 @@ diff --git a/admin/tailwind.config.js b/admin/tailwind.config.cjs similarity index 100% rename from admin/tailwind.config.js rename to admin/tailwind.config.cjs diff --git a/admin/tsconfig.json b/admin/tsconfig.json index 36ebd44..29fe369 100644 --- a/admin/tsconfig.json +++ b/admin/tsconfig.json @@ -7,14 +7,17 @@ "auto-imports.d.ts", "typings/**/*.d.ts" ], + "exclude": ["dist"], "compilerOptions": { "module": "esnext", "moduleResolution": "node", "allowJs": true, + "outDir": "./dist", "isolatedModules": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] - } + }, + "types": ["element-plus/global"] } } diff --git a/admin/vite.config.ts b/admin/vite.config.ts index 3830703..d8e446d 100644 --- a/admin/vite.config.ts +++ b/admin/vite.config.ts @@ -41,8 +41,8 @@ export default ({ mode }) => { } }), Components({ - directoryAsNamespace: true - // resolvers: [ElementPlusResolver()] + directoryAsNamespace: true, + resolvers: [ElementPlusResolver()] }), // createStyleImportPlugin({ // resolves: [ElementPlusResolve()] diff --git a/server/admin/system/admin/admin.go b/server/admin/system/admin/admin.go index 058616d..7c09eb9 100644 --- a/server/admin/system/admin/admin.go +++ b/server/admin/system/admin/admin.go @@ -5,6 +5,7 @@ import ( "x_admin/config" "x_admin/core/request" "x_admin/core/response" + "x_admin/util/excel" "x_admin/util" @@ -44,6 +45,28 @@ func (ah AdminHandler) Self(c *gin.Context) { response.CheckAndRespWithData(c, res, err) } +func (ah AdminHandler) ExportFile(c *gin.Context) { + var listReq SystemAuthAdminListReq + if response.IsFailWithResp(c, util.VerifyUtil.VerifyQuery(c, &listReq)) { + return + } + res, err := Service.ExportFile(listReq) + if err != nil { + response.FailWithMsg(c, response.SystemError, "查询导出失败") + return + } + f, err := excel.NormalDynamicExport(res, "Sheet1", "用户信息", "", true, false, nil) + if err != nil { + response.FailWithMsg(c, response.SystemError, "导出失败") + return + } + excel.DownLoadExcel("用户信息", c.Writer, f) + // c.Header("Content-Type", "application/octet-stream") + // c.Header("Content-Disposition", "attachment; filename="+"用户信息.xlsx") + // c.Header("Content-Transfer-Encoding", "binary") + // f.Write(c.Writer) +} + // list 管理员列表 func (ah AdminHandler) List(c *gin.Context) { var page request.PageReq @@ -114,12 +137,12 @@ func (ah AdminHandler) Disable(c *gin.Context) { response.CheckAndResp(c, Service.Disable(c, disableReq.ID)) } -// @Summary 获取部门的用户 -// @Description 获取部门的用户 -// @Tags 管理员 -// @Param deptId path int true "部门id" -// @Success 200 {object} response.Response "{"code": 200, "data": []}" -// @Router /system/admin/ListByDeptId/{deptId} [get] +// @Summary 获取部门的用户 +// @Description 获取部门的用户 +// @Tags 管理员 +// @Param deptId path int true "部门id" +// @Success 200 {object} response.Response "{"code": 200, "data": []}" +// @Router /system/admin/ListByDeptId/{deptId} [get] func (ah AdminHandler) ListByDeptId(c *gin.Context) { deptIdStr, bool := c.GetQuery("deptId") if bool == false { diff --git a/server/admin/system/admin/schema.go b/server/admin/system/admin/schema.go index 87e5c7d..b4bf346 100644 --- a/server/admin/system/admin/schema.go +++ b/server/admin/system/admin/schema.go @@ -63,20 +63,20 @@ type SystemAuthAdminDisableReq struct { //SystemAuthAdminResp 管理员返回信息 type SystemAuthAdminResp struct { - ID uint `json:"id" structs:"id"` // 主键 - Username string `json:"username" structs:"username"` // 账号 - Nickname string `json:"nickname" structs:"nickname"` // 昵称 - Avatar string `json:"avatar" structs:"avatar"` // 头像 - Role string `json:"role" structs:"role"` // 角色 - DeptId uint `json:"deptId" structs:"deptId"` // 部门ID - PostId uint `json:"postId" structs:"postId"` // 岗位ID - Dept string `json:"dept" structs:"dept"` // 部门 - IsMultipoint uint8 `json:"isMultipoint" structs:"isMultipoint"` // 多端登录: [0=否, 1=是] - IsDisable uint8 `json:"isDisable" structs:"isDisable"` // 是否禁用: [0=否, 1=是] - LastLoginIp string `json:"lastLoginIp" structs:"lastLoginIp"` // 最后登录IP - LastLoginTime core.TsTime `json:"lastLoginTime" structs:"lastLoginTime"` // 最后登录时间 - CreateTime core.TsTime `json:"createTime" structs:"createTime"` // 创建时间 - UpdateTime core.TsTime `json:"updateTime" structs:"updateTime"` // 更新时间 + ID uint `json:"id" structs:"id"` // 主键 + Username string `json:"username" structs:"username" excel:"name:账号;"` // 账号 + Nickname string `json:"nickname" structs:"nickname" excel:"name:昵称;"` // 昵称 + Avatar string `json:"avatar" structs:"avatar" excel:"name:头像;"` // 头像 + Role string `json:"role" structs:"role" excel:"name:角色;"` // 角色 + DeptId uint `json:"deptId" structs:"deptId" excel:"name:部门ID;"` // 部门ID + PostId uint `json:"postId" structs:"postId" excel:"name:岗位ID;"` // 岗位ID + Dept string `json:"dept" structs:"dept" excel:"name:部门;"` // 部门 + IsMultipoint uint8 `json:"isMultipoint" structs:"isMultipoint" excel:"name:多端登录;"` // 多端登录: [0=否, 1=是] + IsDisable uint8 `json:"isDisable" structs:"isDisable" excel:"name:是否禁用;"` // 是否禁用: [0=否, 1=是] + LastLoginIp string `json:"lastLoginIp" structs:"lastLoginIp" excel:"name:最后登录IP;"` // 最后登录IP + LastLoginTime core.TsTime `json:"lastLoginTime" structs:"lastLoginTime" excel:"name:最后登录时间;"` // 最后登录时间 + CreateTime core.TsTime `json:"createTime" structs:"createTime" excel:"name:创建时间;"` // 创建时间 + UpdateTime core.TsTime `json:"updateTime" structs:"updateTime" excel:"name:更新时间;"` // 更新时间 } //SystemAuthAdminSelfOneResp 当前管理员返回部分信息 diff --git a/server/admin/system/admin/service.go b/server/admin/system/admin/service.go index ebe26da..62b745c 100644 --- a/server/admin/system/admin/service.go +++ b/server/admin/system/admin/service.go @@ -122,6 +122,39 @@ func (adminSrv systemAuthAdminService) ListByUserIdOrDeptIdPostId(userId, deptId } return adminResp, nil } +func (adminSrv systemAuthAdminService) ExportFile(listReq SystemAuthAdminListReq) (res []SystemAuthAdminResp, e error) { + // 查询 + adminTbName := core.DBTableName(&system_model.SystemAuthAdmin{}) + roleTbName := core.DBTableName(&system_model.SystemAuthRole{}) + deptTbName := core.DBTableName(&system_model.SystemAuthDept{}) + adminModel := adminSrv.db.Table(adminTbName+" AS admin").Where("admin.is_delete = ?", 0).Joins( + fmt.Sprintf("LEFT JOIN %s ON admin.role = %s.id", roleTbName, roleTbName)).Joins( + fmt.Sprintf("LEFT JOIN %s ON admin.dept_id = %s.id", deptTbName, deptTbName)).Select( + fmt.Sprintf("admin.*, %s.name as dept, %s.name as role", deptTbName, roleTbName)) + // 条件 + if listReq.Username != "" { + adminModel = adminModel.Where("username like ?", "%"+listReq.Username+"%") + } + if listReq.Nickname != "" { + adminModel = adminModel.Where("nickname like ?", "%"+listReq.Nickname+"%") + } + if listReq.Role >= 0 { + adminModel = adminModel.Where("role = ?", listReq.Role) + } + // 数据 + var adminResp []SystemAuthAdminResp + err := adminModel.Order("id desc, sort desc").Find(&adminResp).Error + if e = response.CheckErr(err, "List Find err"); e != nil { + return + } + for i := 0; i < len(adminResp); i++ { + adminResp[i].Avatar = util.UrlUtil.ToAbsoluteUrl(adminResp[i].Avatar) + if adminResp[i].ID == 1 { + adminResp[i].Role = "系统管理员" + } + } + return adminResp, nil +} // List 管理员列表 func (adminSrv systemAuthAdminService) List(page request.PageReq, listReq SystemAuthAdminListReq) (res response.PageResp, e error) { diff --git a/server/admin/system/enter.go b/server/admin/system/enter.go index 7a21032..f92187d 100644 --- a/server/admin/system/enter.go +++ b/server/admin/system/enter.go @@ -30,6 +30,8 @@ func AdminRoute(rg *gin.RouterGroup) { rg.POST("/admin/del", middleware.RecordLog("管理员删除"), handle.Del) rg.POST("/admin/disable", middleware.RecordLog("管理员状态切换"), handle.Disable) + rg.GET("/admin/ExportFile", middleware.RecordLog("管理员导出"), handle.ExportFile) + } func RoleRoute(rg *gin.RouterGroup) { // db := core.GetDB() diff --git a/server/util/excel/excel_export.go b/server/util/excel/excel_export.go index f6c33fc..e62c4e5 100644 --- a/server/util/excel/excel_export.go +++ b/server/util/excel/excel_export.go @@ -16,13 +16,25 @@ import ( func GetExcelColumnName(columnNumber int) string { columnName := "" for columnNumber > 0 { - columnNumber-- - columnName = fmt.Sprint('A'+columnNumber%26) + columnName - columnNumber /= 26 + remainder := (columnNumber - 1) % 26 + columnName = string('A'+remainder) + columnName + columnNumber = (columnNumber - 1) / 26 } return columnName } +// NormalDynamicExport 导出excel +// ** 需要在传入的结构体中的字段加上tag:excelize:"title:列头名称;index:列下标(从0开始);" +// list 需要导出的对象数组、sheet sheet名称、title 标题、isGhbj 是否设置隔行背景色 +func NormalDynamicExport(list interface{}, sheet, title, fields string, isGhbj, isIgnore bool, changeHead map[string]string) (file *excelize.File, err error) { + e := ExcelInit() + err = ExportExcel(sheet, title, fields, isGhbj, isIgnore, list, changeHead, e) + if err != nil { + return + } + return e.F, err +} + // ExportExcel excel导出 func ExportExcel(sheet, title, fields string, isGhbj, isIgnore bool, list interface{}, changeHead map[string]string, e *Excel) (err error) { index, _ := e.F.GetSheetIndex(sheet) @@ -47,44 +59,6 @@ func ExportExcel(sheet, title, fields string, isGhbj, isIgnore bool, list interf return } -// ================================= 普通导出 ================================= - -// NormalDownLoad 导出excel并下载(单个sheet) -func NormalDownLoad(fileName, sheet, title string, isGhbj bool, list interface{}, res http.ResponseWriter) error { - f, err := NormalDynamicExport(list, sheet, title, "", isGhbj, false, nil) - if err != nil { - return err - } - DownLoadExcel(fileName, res, f) - return nil -} - -// NormalDynamicDownLoad 动态导出excel并下载(单个sheet) -// isIgnore 是否忽略指定字段(true 要忽略的字段 false 要导出的字段) -// fields 选择的字段,多个字段用逗号隔开,最后一个字段后面也要加逗号,如:字段1,字段2,字段3, -// changeHead 要改变表头的字段,格式是{"字段1":"更改的表头1","字段2":"更改的表头2"} -func NormalDynamicDownLoad(fileName, sheet, title, fields string, isGhbj, isIgnore bool, - list interface{}, changeHead map[string]string, res http.ResponseWriter) error { - f, err := NormalDynamicExport(list, sheet, title, fields, isGhbj, isIgnore, changeHead) - if err != nil { - return err - } - DownLoadExcel(fileName, res, f) - return nil -} - -// NormalDynamicExport 导出excel -// ** 需要在传入的结构体中的字段加上tag:excelize:"title:列头名称;index:列下标(从0开始);" -// list 需要导出的对象数组、sheet sheet名称、title 标题、isGhbj 是否设置隔行背景色 -func NormalDynamicExport(list interface{}, sheet, title, fields string, isGhbj, isIgnore bool, changeHead map[string]string) (file *excelize.File, err error) { - e := ExcelInit() - err = ExportExcel(sheet, title, fields, isGhbj, isIgnore, list, changeHead, e) - if err != nil { - return - } - return e.F, err -} - // 构造表头(endColName 最后一列的列名 dataRow 数据行开始的行号) func normalBuildTitle(e *Excel, sheet, title, fields string, isIgnore bool, changeHead map[string]string, dataValue reflect.Value) (endColName string, dataRow int, err error) { dataType := dataValue.Type().Elem() // 获取导入目标对象的类型信息 diff --git a/server/util/excel/excel_test.go b/server/util/excel/excel_test.go index f6b4f9b..bb1dc4d 100644 --- a/server/util/excel/excel_test.go +++ b/server/util/excel/excel_test.go @@ -66,3 +66,11 @@ func TestImports(t *testing.T) { fmt.Println(t) } } + +func TestGetExcelColumnName(t *testing.T) { + for i := 0; i < 100; i++ { + var col = GetExcelColumnName(i) + fmt.Println("col:", col) + } + +} diff --git a/server/util/excel/测试.xlsx b/server/util/excel/测试.xlsx index f206a33..65ff8d7 100644 Binary files a/server/util/excel/测试.xlsx and b/server/util/excel/测试.xlsx differ