diff --git a/backend/dvadmin/utils/viewset.py b/backend/dvadmin/utils/viewset.py index 8a7221d..0d6daa0 100644 --- a/backend/dvadmin/utils/viewset.py +++ b/backend/dvadmin/utils/viewset.py @@ -20,7 +20,8 @@ from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailRe from dvadmin.utils.permission import CustomPermission from django_restql.mixins import QueryArgumentsMixin -class CustomModelViewSet(ModelViewSet,ImportSerializerMixin,ExportSerializerMixin,QueryArgumentsMixin): + +class CustomModelViewSet(ModelViewSet, ImportSerializerMixin, ExportSerializerMixin, QueryArgumentsMixin): """ 自定义的ModelViewSet: 统一标准的返回格式;新增,查询,修改可使用不同序列化器 @@ -51,7 +52,6 @@ class CustomModelViewSet(ModelViewSet,ImportSerializerMixin,ExportSerializerMixi return self.values_queryset return super().get_queryset() - def get_serializer_class(self): action_serializer_name = f"{self.action}_serializer_class" action_serializer_class = getattr(self, action_serializer_name, None) @@ -80,7 +80,7 @@ class CustomModelViewSet(ModelViewSet,ImportSerializerMixin,ExportSerializerMixi page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True, request=request) - return self.get_paginated_response(serializer.data) + return SuccessResponse(serializer.data, msg="获取成功") serializer = self.get_serializer(queryset, many=True, request=request) return SuccessResponse(data=serializer.data, msg="获取成功") @@ -107,17 +107,17 @@ class CustomModelViewSet(ModelViewSet,ImportSerializerMixin,ExportSerializerMixi instance.delete() return DetailResponse(data=[], msg="删除成功") + keys = openapi.Schema(description='主键列表', type=openapi.TYPE_ARRAY, items=openapi.TYPE_STRING) - keys = openapi.Schema(description='主键列表',type=openapi.TYPE_ARRAY,items=openapi.TYPE_STRING) @swagger_auto_schema(request_body=openapi.Schema( type=openapi.TYPE_OBJECT, required=['keys'], properties={'keys': keys} ), operation_summary='批量删除') - @action(methods=['delete'],detail=False) - def multiple_delete(self,request,*args,**kwargs): + @action(methods=['delete'], detail=False) + def multiple_delete(self, request, *args, **kwargs): request_data = request.data - keys = request_data.get('keys',None) + keys = request_data.get('keys', None) if keys: self.get_queryset().filter(id__in=keys).delete() return SuccessResponse(data=[], msg="删除成功") diff --git a/web/package.json b/web/package.json index d675401..2ba351e 100644 --- a/web/package.json +++ b/web/package.json @@ -28,6 +28,8 @@ "mitt": "^3.0.0", "nprogress": "^0.2.0", "pinia": "^2.0.16", + "pinia-plugin-persist": "^1.0.0", + "pinia-plugin-persistedstate": "^3.0.2", "print-js": "^1.6.0", "qrcodejs2-fixes": "^0.0.2", "screenfull": "^6.0.2", diff --git a/web/src/i18n/lang/en.ts b/web/src/i18n/lang/en.ts index 11f9d54..d4213cc 100644 --- a/web/src/i18n/lang/en.ts +++ b/web/src/i18n/lang/en.ts @@ -14,65 +14,6 @@ export default { limitsFrontEndBtn: 'FrontEndBtn', limitsBackEnd: 'BackEnd', limitsBackEndEndPage: 'BackEndEndPage', - menu: 'menu', - menu1: 'menu1', - menu11: 'menu11', - menu12: 'menu12', - menu121: 'menu121', - menu122: 'menu122', - menu13: 'menu13', - menu2: 'menu2', - funIndex: 'function', - funTagsView: 'funTagsView', - funCountup: 'countup', - funWangEditor: 'wangEditor', - funCropper: 'cropper', - funQrcode: 'qrcode', - funEchartsMap: 'EchartsMap', - funPrintJs: 'PrintJs', - funClipboard: 'Copy cut', - funGridLayout: 'Drag layout', - funSplitpanes: 'Pane splitter', - funDragVerify: 'Validator', - pagesIndex: 'pages', - pagesFiltering: 'Filtering', - pagesFilteringDetails: 'FilteringDetails', - pagesFilteringDetails1: 'FilteringDetails1', - pagesIocnfont: 'iconfont icon', - pagesElement: 'element icon', - pagesAwesome: 'awesome icon', - pagesFormAdapt: 'FormAdapt', - pagesTableRules: 'pagesTableRules', - pagesFormI18n: 'FormI18n', - pagesFormRules: 'Multi form validation', - pagesDynamicForm: 'Dynamic complex form', - pagesWorkflow: 'Workflow', - pagesListAdapt: 'ListAdapt', - pagesWaterfall: 'Waterfall', - pagesSteps: 'Steps', - pagesPreview: 'Large preview', - pagesWaves: 'Wave effect', - pagesTree: 'tree alter table', - pagesDrag: 'Drag command', - pagesLazyImg: 'Image lazy loading', - makeIndex: 'makeIndex', - makeSelector: 'Icon selector', - makeNoticeBar: 'notification bar', - makeSvgDemo: 'Svgicon demo', - paramsIndex: 'Routing parameters', - paramsCommon: 'General routing', - paramsDynamic: 'Dynamic routing', - paramsCommonDetails: 'General routing details', - paramsDynamicDetails: 'Dynamic routing details', - chartIndex: 'chartIndex', - visualizingIndex: 'visualizingIndex', - visualizingLinkDemo1: 'visualizingLinkDemo1', - visualizingLinkDemo2: 'visualizingLinkDemo2', - personal: 'personal', - tools: 'tools', - layoutLinkView: 'LinkView', - layoutIfameView: 'IfameView', - demo1:'demo1' }, staticRoutes: { signIn: 'signIn', diff --git a/web/src/i18n/lang/zh-cn.ts b/web/src/i18n/lang/zh-cn.ts index bec73ad..96ec7a5 100644 --- a/web/src/i18n/lang/zh-cn.ts +++ b/web/src/i18n/lang/zh-cn.ts @@ -2,12 +2,22 @@ export default { router: { home: '首页', - system: '系统设置', + system: '系统管理', + config: '常规配置', + log: '日志管理', + /* 常规配置 */ + configSystem: '系统配置', + configDict: '字典管理', + configArea: '地区管理', + configFile: '附件管理', + /* 系统管理 */ systemMenu: '菜单管理', systemRole: '角色管理', systemUser: '用户管理', systemDept: '部门管理', - systemDic: '字典管理', + /* 日志管理 */ + loginLog: '登录日志', + operationLog: '操作日志', systemApiWhiteList: '接口白名单', limits: '权限管理', limitsFrontEnd: '前端控制', @@ -15,65 +25,6 @@ export default { limitsFrontEndBtn: '按钮权限', limitsBackEnd: '后端控制', limitsBackEndEndPage: '页面权限', - menu: '菜单嵌套', - menu1: '菜单1', - menu11: '菜单11', - menu12: '菜单12', - menu121: '菜单121', - menu122: '菜单122', - menu13: '菜单13', - menu2: '菜单2', - funIndex: '功能', - funTagsView: 'tagsView 操作', - funCountup: '数字滚动', - funWangEditor: 'Editor 编辑器', - funCropper: '图片裁剪', - funQrcode: '二维码生成', - funEchartsMap: '地理坐标/地图', - funPrintJs: '页面打印', - funClipboard: '复制剪切', - funGridLayout: '拖拽布局', - funSplitpanes: '窗格拆分器', - funDragVerify: '验证器', - pagesIndex: '页面', - pagesFiltering: '过滤筛选组件', - pagesFilteringDetails: '过滤筛选组件详情', - pagesFilteringDetails1: '过滤筛选组件详情111', - pagesIocnfont: 'ali 字体图标', - pagesElement: 'ele 字体图标', - pagesAwesome: 'awe 字体图标', - pagesFormAdapt: '表单自适应', - pagesTableRules: '表单表格验证', - pagesFormI18n: '表单国际化', - pagesFormRules: '多表单验证', - pagesDynamicForm: '动态复杂表单', - pagesWorkflow: '工作流', - pagesListAdapt: '列表自适应', - pagesWaterfall: '瀑布屏', - pagesSteps: '步骤条', - pagesPreview: '大图预览', - pagesWaves: '波浪效果', - pagesTree: '树形改表格', - pagesDrag: '拖动指令', - pagesLazyImg: '图片懒加载', - makeIndex: '组件封装', - makeSelector: '图标选择器', - makeNoticeBar: '滚动通知栏', - makeSvgDemo: 'svgIcon 演示', - paramsIndex: '路由参数', - paramsCommon: '普通路由', - paramsDynamic: '动态路由', - paramsCommonDetails: '普通路由详情', - paramsDynamicDetails: '动态路由详情', - chartIndex: '大数据图表', - visualizingIndex: '数据可视化', - visualizingLinkDemo1: '数据可视化演示1', - visualizingLinkDemo2: '数据可视化演示2', - personal: '个人中心', - tools: '工具类集合', - layoutLinkView: '外链', - layoutIfameView: '内嵌 iframe', - demo1: 'demo1', }, staticRoutes: { signIn: '登录', diff --git a/web/src/main.ts b/web/src/main.ts index 199936a..02c2cd6 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,20 +1,21 @@ import { createApp } from 'vue'; -import pinia from '/@/stores/index'; import App from './App.vue'; import router from './router'; import { directive } from '/@/utils/directive'; import { i18n } from '/@/i18n/index'; import other from '/@/utils/other'; - import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; import '/@/theme/index.scss'; import mitt from 'mitt'; import VueGridLayout from 'vue-grid-layout'; - +import piniaPersist from 'pinia-plugin-persist' +// @ts-ignore import fastCrud from './settings.ts' +import pinia from './stores'; const app = createApp(App); +pinia.use(piniaPersist) directive(app); other.elSvg(app); diff --git a/web/src/router/route.ts b/web/src/router/route.ts index c9cf0be..748f3db 100644 --- a/web/src/router/route.ts +++ b/web/src/router/route.ts @@ -47,7 +47,7 @@ export const dynamicRoutes: Array = [ }, }, { - path: '/system', + path: '/config', name: 'system', component: () => import('/@/layout/routerView/parent.vue'), redirect: '/system/menu', @@ -122,21 +122,6 @@ export const dynamicRoutes: Array = [ icon: 'ele-OfficeBuilding', }, }, - { - path: '/system/dic', - name: 'systemDic', - component: () => import('/@/views/system/dic/index.vue'), - meta: { - title: 'message.router.systemDic', - isLink: '', - isHide: false, - isKeepAlive: true, - isAffix: false, - isIframe: false, - roles: ['admin'], - icon: 'ele-SetUp', - }, - }, { path: '/system/apiWhiteList', name: 'apiWhiteList', @@ -154,6 +139,132 @@ export const dynamicRoutes: Array = [ }, ], }, + { + path: '/config', + name: 'config', + component: () => import('/@/layout/routerView/parent.vue'), + redirect: '/config/menu', + meta: { + title: 'message.router.config', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-configure', + }, + children: [ + { + path: '/system/configSystem', + name: 'configSystem', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.configSystem', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-system', + }, + }, + { + path: '/system/dict', + name: 'configDict', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.configDict', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-dict', + }, + }, + { + path: '/system/area', + name: 'configArea', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.configArea', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-Area', + }, + }, + { + path: '/system/file', + name: 'configFile', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.configFile', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-file', + }, + }, + ], + }, + { + path: '/log', + name: 'log', + component: () => import('/@/layout/routerView/parent.vue'), + redirect: '/log/loginLog', + meta: { + title: 'message.router.log', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-rizhi', + }, + children: [ + { + path: '/log/loginLog', + name: 'loginLog', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.loginLog', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-guanlidenglurizhi', + }, + }, + { + path: '/log/operationLog', + name: 'operationLog', + component: () => import('/@/views/system/dic/index.vue'), + meta: { + title: 'message.router.operationLog', + isLink: '', + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ['admin'], + icon: 'iconfont icon-caozuorizhi', + }, + }, + ], + }, ], }, ]; diff --git a/web/src/settings.ts b/web/src/settings.ts index fe877ef..81d2dc8 100644 --- a/web/src/settings.ts +++ b/web/src/settings.ts @@ -1,21 +1,21 @@ // 引入fast-crud -import {FastCrud} from "@fast-crud/fast-crud"; +import { FastCrud } from "@fast-crud/fast-crud"; import "@fast-crud/fast-crud/dist/style.css"; - +import { setLogger } from '@fast-crud/fast-crud' // element import ui from "@fast-crud/ui-element"; -import {request} from "/@/utils/service.ts"; +import { request } from "/@/utils/service.ts"; export default { async install(app: any, options: any) { // 先安装ui app.use(ui); -// 然后安装FastCrud + // 然后安装FastCrud app.use(FastCrud, { //i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件 // 此处配置公共的dictRequest(字典请求) - async dictRequest({dict}:any) { - return await request({url: dict.url}); //根据dict的url,异步返回一个字典数组 + async dictRequest({ dict }: any) { + return await request({ url: dict.url }); //根据dict的url,异步返回一个字典数组 }, //公共crud配置 commonOptions() { @@ -24,19 +24,20 @@ export default { //接口请求配置 //你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项 //请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html - transformQuery: ({page, form, sort}:any) => { + transformQuery: ({ page, form, sort }: any) => { //转换为你pageRequest所需要的请求参数结构 - return {page, form, sort}; + return { page, form, sort }; }, - transformRes: ({res}:any) => { + transformRes: ({ res }: any) => { //将pageRequest的返回数据,转换为fast-crud所需要的格式 //return {records,currentPage,pageSize,total}; - return {...res} + return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total } } }, }; }, }); + setLogger({ level: 'error' }) } } diff --git a/web/src/stores/dictionary.ts b/web/src/stores/dictionary.ts index db1d418..30d5013 100644 --- a/web/src/stores/dictionary.ts +++ b/web/src/stores/dictionary.ts @@ -49,4 +49,7 @@ export const DictionaryStore = defineStore('Dictionary', { }) }, }, + persist: { + enabled: true, + }, }); diff --git a/web/src/stores/interface/index.ts b/web/src/stores/interface/index.ts index 5a4d40b..ee606cb 100644 --- a/web/src/stores/interface/index.ts +++ b/web/src/stores/interface/index.ts @@ -88,3 +88,7 @@ export interface ThemeConfigState { export interface ThemeConfigStates { themeConfig: ThemeConfigState; } + +export interface DictionaryStates { + data: any; +} \ No newline at end of file diff --git a/web/src/stores/themeConfig.ts b/web/src/stores/themeConfig.ts index f2663b5..02b0269 100644 --- a/web/src/stores/themeConfig.ts +++ b/web/src/stores/themeConfig.ts @@ -135,7 +135,7 @@ export const useThemeConfig = defineStore('themeConfig', { // 默认初始语言,可选值"",默认 zh-cn globalI18n: 'zh-cn', // 默认全局组件大小,可选值"",默认 'large' - globalComponentSize: 'large', + globalComponentSize: 'default', }, }), actions: { diff --git a/web/src/utils/service.ts b/web/src/utils/service.ts index 002fe37..04b936e 100644 --- a/web/src/utils/service.ts +++ b/web/src/utils/service.ts @@ -1,9 +1,10 @@ import axios from "axios"; import { get } from "lodash-es"; +// @ts-ignore import { errorLog, errorCreate } from "./tools.ts"; // import { env } from "/src/utils/util.env"; // import { useUserStore } from "../store/modules/user"; -import { Session } from '/@/utils/storage'; +import { Local, Session } from '/@/utils/storage'; /** * @description 创建请求实例 */ @@ -37,6 +38,13 @@ function createService() { } else { // 有 code 代表这是一个后端接口 可以进行进一步的判断 switch (code) { + case 401: + Local.clear(); + Session.clear(); + window.location.reload(); + dataAxios.msg = "登录授权过期,请重新登录"; + errorCreate(`${dataAxios.msg}: ${response.config.url}`); + break case 2000: // @ts-ignore if (response.config.unpack === false) { diff --git a/web/src/utils/setIconfont.ts b/web/src/utils/setIconfont.ts index a6acf68..c005a8b 100644 --- a/web/src/utils/setIconfont.ts +++ b/web/src/utils/setIconfont.ts @@ -1,6 +1,7 @@ // 字体图标 url const cssCdnUrlList: Array = [ '//at.alicdn.com/t/font_2298093_y6u00apwst.css', + '//at.alicdn.com/t/c/font_3882322_8vb7gh5lw4t.css', //dvadmin3项目用icon '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', ]; // 第三方 js url diff --git a/web/src/views/system/dept/api.ts b/web/src/views/system/dept/api.ts new file mode 100644 index 0000000..199acee --- /dev/null +++ b/web/src/views/system/dept/api.ts @@ -0,0 +1,41 @@ +import { request } from '/@/utils/service'; +import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud'; + +export const apiPrefix = '/api/system/dept/'; +export function GetList(query: PageQuery) { + return request({ + url: apiPrefix, + method: 'get', + data: query, + }); +} +export function GetObj(id: InfoReq) { + return request({ + url: apiPrefix + id, + method: 'get', + }); +} + +export function AddObj(obj: AddReq) { + return request({ + url: apiPrefix, + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: EditReq) { + return request({ + url: apiPrefix + obj.id + '/', + method: 'put', + data: obj, + }); +} + +export function DelObj(id: DelReq) { + return request({ + url: apiPrefix + id + '/', + method: 'delete', + data: { id }, + }); +} diff --git a/web/src/views/system/dept/component/addDept.vue b/web/src/views/system/dept/component/addDept.vue deleted file mode 100644 index e4063e4..0000000 --- a/web/src/views/system/dept/component/addDept.vue +++ /dev/null @@ -1,173 +0,0 @@ - - - diff --git a/web/src/views/system/dept/component/editDept.vue b/web/src/views/system/dept/component/editDept.vue deleted file mode 100644 index 7b16f44..0000000 --- a/web/src/views/system/dept/component/editDept.vue +++ /dev/null @@ -1,179 +0,0 @@ - - - diff --git a/web/src/views/system/dept/crud.tsx b/web/src/views/system/dept/crud.tsx new file mode 100644 index 0000000..f83e768 --- /dev/null +++ b/web/src/views/system/dept/crud.tsx @@ -0,0 +1,223 @@ +import * as api from "./api"; +import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, } from "@fast-crud/fast-crud"; +import { request } from "/@/utils/service"; +import { dictionary } from "/@/utils/dictionary"; +interface CreateCrudOptionsTypes { + crudOptions: CrudOptions; +} + +export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes { + const pageRequest = async (query: PageQuery) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + _index: { + title: '序号', + form: { show: false }, + column: { + //type: 'index', + align: 'center', + width: '70px', + columnSetDisabled: true, //禁止在列设置中选择 + formatter: (context) => { + //计算序号,你可以自定义计算规则,此处为翻页累加 + let index = context.index ?? 1; + let pagination = crudExpose.crudBinding.value.pagination; + return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1; + }, + }, + }, + search: { + title: '关键词', + column: { + show: false + }, + search: { + show: true, + component: { + props: { + clearable: true + }, + placeholder: '请输入关键词' + } + }, + form: { + show: false, + component: { + props: { + clearable: true + } + } + }, + }, + parent: { + column: { + show: false + }, + title: '上级部门', + type: 'dict-tree', + dict: dict({ + url: '/api/system/dept/all_dept/', + value: 'id', + label: 'name', + isTree: true, + getData: async ({ url }: { url: string }) => { + return request({ + url: url, + }).then((ret: any) => { + return ret.data + }) + } + }), + form: { + helper: '默认留空为创建者的部门', + component: { + span: 12, + props: { + props: { + value: "id", + label: "name", + } + } + } + } + }, + name: { + title: '部门名称', + sortable: true, + treeNode: true, // 设置为树形列 + search: { + disabled: false, + component: { + props: { + clearable: true + } + } + }, + width: 180, + type: 'input', + form: { + rules: [ + // 表单校验规则 + { required: true, message: '部门名称必填项' } + ], + component: { + span: 12, + props: { + clearable: true + }, + placeholder: '请输入部门名称' + }, + } + }, + key: { + title: '部门标识', + sortable: true, + form: { + component: { + props: { + clearable: true + }, + placeholder: '请输入标识字符' + }, + } + }, + owner: { + title: '负责人', + sortable: true, + form: { + component: { + span: 12, + props: { + clearable: true + }, + placeholder: '请输入负责人' + } + } + }, + phone: { + title: '联系电话', + sortable: true, + form: { + component: { + span: 12, + props: { + clearable: true + }, + placeholder: '请输入联系电话' + } + } + }, + email: { + title: '邮箱', + sortable: true, + form: { + component: { + span: 12, + props: { + clearable: true + }, + placeholder: '请输入邮箱' + }, + rules: [ + { + type: 'email', + message: '请输入正确的邮箱地址', + trigger: ['blur', 'change'] + } + ] + } + }, + sort: { + title: '排序', + sortable: true, + width: 80, + type: 'number', + form: { + value: 1, + component: { + span: 12, + placeholder: '请选择序号' + } + } + }, + status: { + title: '状态', + sortable: true, + search: { + disabled: false + }, + type: 'dict-radio', + dict: dict({ + data: dictionary('button_status_bool') + }), + form: { + value: true, + component: { + span: 12, + placeholder: '请选择状态' + } + } + } + } + }, + } +} diff --git a/web/src/views/system/dept/index.vue b/web/src/views/system/dept/index.vue index 97dae42..1fa8634 100644 --- a/web/src/views/system/dept/index.vue +++ b/web/src/views/system/dept/index.vue @@ -1,163 +1,26 @@ - diff --git a/web/src/views/system/login/component/account.vue b/web/src/views/system/login/component/account.vue index 012c6b6..a953220 100644 --- a/web/src/views/system/login/component/account.vue +++ b/web/src/views/system/login/component/account.vue @@ -73,6 +73,7 @@ import { formatAxis } from '/@/utils/formatTime'; import { NextLoading } from '/@/utils/loading'; import * as loginApi from '/@/views/system/login/api'; import { useUserInfo } from '/@/stores/userInfo'; +import { DictionaryStore } from '/@/stores/dictionary'; import { Md5 } from 'ts-md5'; export default defineComponent({ @@ -136,8 +137,9 @@ export default defineComponent({ }; // 登录成功后的跳转 const loginSuccess = () => { - //登录成功获取用户信息 + //登录成功获取用户信息,获取系统字典数据 getUserInfo(); + DictionaryStore().getSystemDictionarys(); // 初始化登录成功时间问候语 let currentTimeInfo = currentTime.value; diff --git a/web/src/views/system/menu/index.vue b/web/src/views/system/menu/index.vue index a4af8ee..1fa8634 100644 --- a/web/src/views/system/menu/index.vue +++ b/web/src/views/system/menu/index.vue @@ -1,111 +1,26 @@ - diff --git a/web/src/views/system/role/index.vue b/web/src/views/system/role/index.vue index 5cae97c..d667228 100644 --- a/web/src/views/system/role/index.vue +++ b/web/src/views/system/role/index.vue @@ -5,7 +5,7 @@ {{ scope.row.url }} - + @@ -13,9 +13,9 @@ import { ref, onMounted } from 'vue'; import { useExpose, useCrud } from '@fast-crud/fast-crud'; import { createCrudOptions } from './curd'; -import RolePermission from '/@/views/system/rolePermission/index.vue' +import RolePermission from '/@/views/system/rolePermission/index.vue'; const rolePermission = ref(); -defineExpose(rolePermission) +defineExpose(rolePermission); // crud组件的ref const crudRef = ref(); // crud 配置的ref @@ -23,7 +23,7 @@ const crudBinding = ref(); // 暴露的方法 const { crudExpose } = useExpose({ crudRef, crudBinding }); // 你的crud配置 -const { crudOptions } = createCrudOptions({ crudExpose ,rolePermission }); +const { crudOptions } = createCrudOptions({ crudExpose, rolePermission }); // 初始化crud配置 const { resetCrudOptions } = useCrud({ crudExpose, crudOptions }); // 你可以调用此方法,重新初始化crud配置 @@ -33,7 +33,4 @@ const { resetCrudOptions } = useCrud({ crudExpose, crudOptions }); onMounted(() => { crudExpose.doRefresh(); }); - - - diff --git a/web/src/views/system/user/api.ts b/web/src/views/system/user/api.ts new file mode 100644 index 0000000..dfdb765 --- /dev/null +++ b/web/src/views/system/user/api.ts @@ -0,0 +1,41 @@ +import { request } from '/@/utils/service'; +import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud'; + +export const apiPrefix = '/api/system/user/'; +export function GetList(query: PageQuery) { + return request({ + url: apiPrefix, + method: 'get', + data: query, + }); +} +export function GetObj(id: InfoReq) { + return request({ + url: apiPrefix + id, + method: 'get', + }); +} + +export function AddObj(obj: AddReq) { + return request({ + url: apiPrefix, + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: EditReq) { + return request({ + url: apiPrefix + obj.id + '/', + method: 'put', + data: obj, + }); +} + +export function DelObj(id: DelReq) { + return request({ + url: apiPrefix + id + '/', + method: 'delete', + data: { id }, + }); +} diff --git a/web/src/views/system/user/component/addUser.vue b/web/src/views/system/user/component/addUser.vue deleted file mode 100644 index 4a78153..0000000 --- a/web/src/views/system/user/component/addUser.vue +++ /dev/null @@ -1,200 +0,0 @@ - - - diff --git a/web/src/views/system/user/component/editUser.vue b/web/src/views/system/user/component/editUser.vue deleted file mode 100644 index b7a0793..0000000 --- a/web/src/views/system/user/component/editUser.vue +++ /dev/null @@ -1,202 +0,0 @@ - - - diff --git a/web/src/views/system/user/crud.tsx b/web/src/views/system/user/crud.tsx new file mode 100644 index 0000000..ec1819e --- /dev/null +++ b/web/src/views/system/user/crud.tsx @@ -0,0 +1,323 @@ +import * as api from "./api"; +import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, } from "@fast-crud/fast-crud"; +import { request } from "/@/utils/service"; +import { dictionary } from "/@/utils/dictionary"; +interface CreateCrudOptionsTypes { + crudOptions: CrudOptions; +} + +export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes { + const pageRequest = async (query: PageQuery) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: 400, + buttons: { + orderExample: { + text: "重置密码", + click: () => { + console.log("reset password") + } + } + }, + + }, + columns: { + _index: { + title: '序号', + form: { show: false }, + column: { + //type: 'index', + align: 'center', + width: '70px', + columnSetDisabled: true, //禁止在列设置中选择 + formatter: (context) => { + //计算序号,你可以自定义计算规则,此处为翻页累加 + let index = context.index ?? 1; + let pagination = crudExpose.crudBinding.value.pagination; + return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1; + }, + }, + }, + search: { + title: '关键词', + column: { + show: false + }, + search: { + show: true, + component: { + props: { + clearable: true + }, + placeholder: '请输入关键词' + } + }, + form: { + show: false, + component: { + props: { + clearable: true + } + } + }, + }, + username: { + title: '账号', + search: { + show: true, + }, + minWidth: 100, + type: 'input', + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '账号必填项' + } + ], + component: { + placeholder: '请输入账号' + }, + } + }, + password: { + title: '密码', + type: 'input', + column: { + show: false + }, + editForm: { + show: false + }, + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '密码必填项' + } + ], + component: { + span: 12, + showPassword: true, + placeholder: '请输入密码' + }, + // value: vm.systemConfig('base.default_password'), + }, + /* valueResolve(row, key) { + if (row.password) { + row.password = vm.$md5(row.password) + } + } */ + }, + name: { + title: '姓名', + search: { + show: true, + }, + type: 'input', + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '姓名必填项' + } + ], + component: { + span: 12, + placeholder: '请输入姓名' + }, + } + }, + dept: { + title: '部门', + search: { + disabled: true + }, + type: 'dict-tree', + dict: dict({ + isTree: true, + url: '/api/system/dept/all_dept/', + value: 'id', + label: 'name', + getData: async ({ url }: { url: string }) => { + return request({ + url: url, + }).then((ret: any) => { + return ret.data + }) + } + }), + form: { + component: { + filterable: true, + placeholder: '请选择角色', + props: { + props: { + value: "id", + label: "name", + } + } + }, + }, + }, + role: { + title: '角色', + search: { + disabled: true + }, + type: 'dict-tree', + dict: dict({ + url: '/api/system/role/', + value: 'id', + label: 'name', + isTree: true, + getData: async ({ url }: { url: string }) => { + return request({ + url: url, + params: { + page: 1, + limit: 10 + } + }).then((ret: any) => { + return ret.data + }) + } + }), + form: { + component: { + filterable: true, + placeholder: '请选择角色', + props: { + props: { + value: "id", + label: "name", + } + } + }, + } + }, + mobile: { + title: '手机号码', + search: { + show: true, + }, + type: 'input', + form: { + rules: [ + { + max: 20, + message: '请输入正确的手机号码', + trigger: 'blur' + }, + { + pattern: /^1[3-9]\d{9}$/, + message: '请输入正确的手机号码' + } + ], + component: { + placeholder: '请输入手机号码' + } + } + }, + email: { + title: '邮箱', + form: { + rules: [ + { + type: 'email', + message: '请输入正确的邮箱地址', + trigger: ['blur', 'change'] + } + ], + component: { + placeholder: '请输入邮箱' + } + } + }, + gender: { + title: '性别', + type: 'dict-radio', + dict: dict({ + data: dictionary('gender') + }), + form: { + value: 1, + component: { + span: 12 + } + }, + component: { props: { color: 'auto' } } // 自动染色 + }, + user_type: { + title: '用户类型', + search: { + show: true, + }, + type: 'dict-select', + dict: dict({ + data: dictionary('user_type') + }), + form: { + show: false, + value: 0, + component: { + span: 12 + } + } + }, + is_active: { + title: '状态', + search: { + show: true, + }, + type: 'dict-radio', + dict: dict({ + data: dictionary('button_status_bool') + }), + form: { + value: true, + component: { + span: 12 + } + } + }, + avatar: { + title: '头像', + type: 'avatar-cropper', + form: { + component: { + props: { + elProps: { // 与el-uploader 配置一致 + multiple: false, + limit: 1 // 限制5个文件 + }, + sizeLimit: 500 * 1024 // 不能超过限制 + }, + span: 24 + }, + helper: '限制文件大小不能超过500k' + } + } + } + } + }; +} diff --git a/web/src/views/system/user/index.vue b/web/src/views/system/user/index.vue index a120c59..1fa8634 100644 --- a/web/src/views/system/user/index.vue +++ b/web/src/views/system/user/index.vue @@ -1,177 +1,26 @@ - diff --git a/web/yarn.lock b/web/yarn.lock index 0fa3b89..6ba8c88 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -2254,6 +2254,18 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pinia-plugin-persist@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz#fc696f225527f30bd5955109fafadd43c725e888" + integrity sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw== + dependencies: + vue-demi "^0.12.1" + +pinia-plugin-persistedstate@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.0.2.tgz#c604591442d23ba3a86eaf8156f019812d59038e" + integrity sha512-84vPyUhPA/8Pr+1mT1ioNb2d8z4tvdgYRqMQf8xyauOVBKjo0ZcRBwPQBV7ZAJG43Kwar43nXG2jU+ZMvAFFRQ== + pinia@^2.0.16: version "2.0.16" resolved "https://registry.npmjs.org/pinia/-/pinia-2.0.16.tgz" @@ -2656,6 +2668,11 @@ vue-demi@*: resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.5.tgz" integrity sha512-tO3K2bML3AwiHmVHeKCq6HLef2st4zBXIV5aEkoJl6HZ+gJWxWv2O8wLH8qrA3SX3lDoTDHNghLX1xZg83MXvw== +vue-demi@^0.12.1: + version "0.12.5" + resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz#8eeed566a7d86eb090209a11723f887d28aeb2d1" + integrity sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q== + vue-eslint-parser@^9.0.1, vue-eslint-parser@^9.0.3: version "9.0.3" resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz"