├── .DS_Store
├── .gitee
├── ISSUE_TEMPLATE.zh-CN.md
└── PULL_REQUEST_TEMPLATE.zh-CN.md
├── .gitignore
├── LICENSE
├── README.md
├── deploy
└── docker-compose
│ └── docker-compose-dev.yaml
├── service
├── .DS_Store
├── .gitignore
├── Dockerfile
├── app
│ ├── bootstrap.go
│ ├── controllers
│ │ ├── genExample
│ │ │ └── newsController.go
│ │ └── system
│ │ │ ├── commonController.go
│ │ │ ├── genController.go
│ │ │ ├── menuController.go
│ │ │ ├── operationLogController.go
│ │ │ ├── roleController.go
│ │ │ ├── testController.go
│ │ │ └── userController.go
│ ├── event
│ │ ├── loginEvent.go
│ │ └── testEvent.go
│ ├── listener
│ │ └── testListener.go
│ ├── middleware
│ │ ├── event.go
│ │ ├── evnCheck.go
│ │ ├── jwt.go
│ │ ├── limiter.go
│ │ ├── operationLog.go
│ │ └── permission.go
│ ├── models
│ │ ├── adminUser.go
│ │ ├── article.go
│ │ ├── jwt.go
│ │ ├── menu.go
│ │ ├── menuApiList.go
│ │ ├── news.go
│ │ ├── operationLog.go
│ │ └── role.go
│ ├── repositorys
│ │ ├── baseRepository.go
│ │ ├── genRepository.go
│ │ ├── newsRepository.go
│ │ ├── operationLogRepository.go
│ │ ├── permissionRepository.go
│ │ ├── roleRepository.go
│ │ ├── systemMenuRepository.go
│ │ └── userRepository.go
│ ├── requests
│ │ ├── genRequest.go
│ │ ├── login.go
│ │ ├── menuPost.go
│ │ ├── newsRequest.go
│ │ ├── role.go
│ │ ├── roleList.go
│ │ ├── user.go
│ │ └── userAdd.go
│ └── validators
│ │ ├── cacheCodeValidator.go
│ │ └── demo.go
├── config.example
├── config
│ └── common.go
├── databases
│ ├── gc_admin_menu.sql
│ ├── gc_admin_user.sql
│ ├── gc_menu_api_list.sql
│ ├── gc_role.sql
│ ├── gc_role_menu.sql
│ └── gc_user_role.sql
├── global
│ ├── common.go
│ ├── model.go
│ ├── request.go
│ ├── response.go
│ └── response
│ │ └── response.go
├── go.mod
├── go.sum
├── gsadmin20240909
├── initialize
│ ├── configInit.go
│ ├── dbInit.go
│ ├── eventInit.go
│ ├── validatorInit.go
│ └── zapInit.go
├── main.go
├── pkg
│ └── event
│ │ └── eventDispatcher.go
├── router
│ ├── controllers.go
│ ├── router.go
│ └── systemApi.go
├── test
│ ├── demo_test.go
│ ├── orm_test.go
│ └── repository_test.go
└── web
│ ├── js
│ └── model
│ │ └── news.js
│ └── view
│ └── news
│ ├── form.vue
│ └── index.vue
└── web
└── scui
├── .editorconfig
├── .env.development
├── .env.production
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── jsconfig.json
├── package.json
├── public
├── code
│ └── list
│ │ ├── index.vue
│ │ └── save.vue
├── config.js
├── favicon.ico
├── img
│ ├── 404.png
│ ├── auth_banner.jpg
│ ├── avatar.jpg
│ ├── avatar2.gif
│ ├── avatar3.gif
│ ├── gslogo.png
│ ├── loginbg.svg
│ ├── logo-r.bak.png
│ ├── logo-r.png
│ ├── logo.bak.png
│ ├── logo.png
│ ├── no-widgets.svg
│ ├── tasks-example.png
│ └── ver.svg
├── index.html
└── tinymce
│ ├── langs
│ └── zh_CN.js
│ └── skins
│ ├── content
│ ├── dark
│ │ ├── content.css
│ │ └── content.min.css
│ ├── default
│ │ ├── content.css
│ │ └── content.min.css
│ ├── document
│ │ ├── content.css
│ │ └── content.min.css
│ ├── tinymce-5-dark
│ │ ├── content.css
│ │ └── content.min.css
│ ├── tinymce-5
│ │ ├── content.css
│ │ └── content.min.css
│ └── writer
│ │ ├── content.css
│ │ └── content.min.css
│ └── ui
│ ├── oxide-dark
│ ├── content.css
│ ├── content.inline.css
│ ├── content.inline.min.css
│ ├── content.min.css
│ ├── skin.css
│ ├── skin.min.css
│ ├── skin.shadowdom.css
│ └── skin.shadowdom.min.css
│ ├── oxide
│ ├── content.css
│ ├── content.inline.css
│ ├── content.inline.min.css
│ ├── content.min.css
│ ├── skin.css
│ ├── skin.min.css
│ ├── skin.shadowdom.css
│ └── skin.shadowdom.min.css
│ ├── tinymce-5-dark
│ ├── content.css
│ ├── content.inline.css
│ ├── content.inline.min.css
│ ├── content.min.css
│ ├── skin.css
│ ├── skin.min.css
│ ├── skin.shadowdom.css
│ └── skin.shadowdom.min.css
│ └── tinymce-5
│ ├── content.css
│ ├── content.inline.css
│ ├── content.inline.min.css
│ ├── content.min.css
│ ├── skin.css
│ ├── skin.min.css
│ ├── skin.shadowdom.css
│ └── skin.shadowdom.min.css
├── src
├── App.vue
├── api
│ ├── index.js
│ └── model
│ │ ├── auth.js
│ │ ├── common.js
│ │ ├── demo.js
│ │ ├── demo2.js
│ │ ├── gen.js
│ │ ├── news.js
│ │ ├── system.js
│ │ ├── user.js
│ │ ├── userMember.js
│ │ └── userMember.js.bak
├── assets
│ └── icons
│ │ ├── BugFill.vue
│ │ ├── BugLine.vue
│ │ ├── Code.vue
│ │ ├── Download.vue
│ │ ├── FileExcel.vue
│ │ ├── FilePpt.vue
│ │ ├── FileWord.vue
│ │ ├── Organization.vue
│ │ ├── Upload.vue
│ │ ├── Vue.vue
│ │ ├── Wechat.vue
│ │ └── index.js
├── components
│ ├── HelloWorld.vue
│ ├── scCodeEditor
│ │ └── index.vue
│ ├── scContextmenu
│ │ ├── index.vue
│ │ └── item.vue
│ ├── scCron
│ │ └── index.vue
│ ├── scCropper
│ │ └── index.vue
│ ├── scDialog
│ │ └── index.vue
│ ├── scEcharts
│ │ ├── echarts-theme-T.js
│ │ └── index.vue
│ ├── scEditor
│ │ └── index.vue
│ ├── scFileExport
│ │ ├── column.vue
│ │ └── index.vue
│ ├── scFileImport
│ │ └── index.vue
│ ├── scFileSelect
│ │ └── index.vue
│ ├── scFilterBar
│ │ ├── index.vue
│ │ ├── my.vue
│ │ ├── pinyin.js
│ │ └── pySelect.vue
│ ├── scForm
│ │ ├── index.vue
│ │ └── items
│ │ │ └── tableselect.vue
│ ├── scFormTable
│ │ └── index.vue
│ ├── scIconSelect
│ │ └── index.vue
│ ├── scMini
│ │ ├── scStatusIndicator.vue
│ │ └── scTrend.vue
│ ├── scPageHeader
│ │ └── index.vue
│ ├── scPasswordStrength
│ │ └── index.vue
│ ├── scQrCode
│ │ └── index.vue
│ ├── scSelect
│ │ └── index.vue
│ ├── scSelectFilter
│ │ └── index.vue
│ ├── scStatistic
│ │ └── index.vue
│ ├── scTable
│ │ ├── column.js
│ │ ├── columnSetting.vue
│ │ └── index.vue
│ ├── scTableSelect
│ │ └── index.vue
│ ├── scTitle
│ │ └── index.vue
│ ├── scUpload
│ │ ├── file.vue
│ │ ├── index.vue
│ │ └── multiple.vue
│ ├── scVideo
│ │ └── index.vue
│ ├── scWaterMark
│ │ └── index.vue
│ └── scWorkflow
│ │ ├── index.vue
│ │ ├── nodeWrap.vue
│ │ ├── nodes
│ │ ├── addNode.vue
│ │ ├── approver.vue
│ │ ├── branch.vue
│ │ ├── promoter.vue
│ │ └── send.vue
│ │ └── select.vue
├── config
│ ├── fileSelect.js
│ ├── filterBar.js
│ ├── iconSelect.js
│ ├── index.js
│ ├── myConfig.js
│ ├── route.js
│ ├── select.js
│ ├── table.js
│ ├── tableSelect.js
│ ├── upload.js
│ └── workflow.js
├── directives
│ ├── auth.js
│ ├── copy.js
│ ├── role.js
│ └── time.js
├── layout
│ ├── components
│ │ ├── NavMenu.vue
│ │ ├── iframeView.vue
│ │ ├── search.vue
│ │ ├── setting.vue
│ │ ├── sideM.vue
│ │ ├── tags.vue
│ │ ├── tasks.vue
│ │ ├── topbar.vue
│ │ └── userbar.vue
│ ├── index.vue
│ └── other
│ │ ├── 404.vue
│ │ └── empty.vue
├── locales
│ ├── index.js
│ └── lang
│ │ ├── en.js
│ │ └── zh-cn.js
├── main.js
├── router
│ ├── index.js
│ ├── scrollBehavior.js
│ └── systemRouter.js
├── scui.js
├── store
│ ├── index.js
│ └── modules
│ │ ├── global.js
│ │ ├── iframe.js
│ │ ├── keepAlive.js
│ │ └── viewTags.js
├── style
│ ├── app.scss
│ ├── dark.scss
│ ├── fix.scss
│ ├── media.scss
│ ├── pages.scss
│ └── style.scss
├── utils
│ ├── color.js
│ ├── db.js
│ ├── errorHandler.js
│ ├── load.js
│ ├── permission.js
│ ├── print.js
│ ├── request.js
│ ├── template.js
│ ├── tool.js
│ ├── useTabs.js
│ └── verificate.js
└── views
│ ├── demo2
│ ├── form.vue
│ └── index.vue
│ ├── gen
│ └── index.vue
│ ├── home
│ ├── index.vue
│ ├── widgets
│ │ ├── components
│ │ │ ├── about.vue
│ │ │ ├── echarts.vue
│ │ │ ├── index.js
│ │ │ ├── progress.vue
│ │ │ ├── time.vue
│ │ │ ├── ver.vue
│ │ │ └── welcome.vue
│ │ └── index.vue
│ └── work
│ │ ├── components
│ │ └── myapp.vue
│ │ └── index.vue
│ ├── login
│ ├── components
│ │ ├── commonPage.vue
│ │ ├── passwordForm.vue
│ │ └── phoneForm.vue
│ ├── index.vue
│ ├── resetPassword.vue
│ └── userRegister.vue
│ ├── news
│ ├── form.vue
│ └── index.vue
│ ├── other
│ ├── about.vue
│ ├── directive.vue
│ ├── fullpage.vue
│ ├── loadJS.vue
│ ├── verificate.vue
│ └── viewTags.vue
│ ├── setting
│ ├── client
│ │ ├── index.vue
│ │ └── save.vue
│ ├── dept
│ │ ├── index.vue
│ │ └── save.vue
│ ├── dic
│ │ ├── dic.vue
│ │ ├── index.vue
│ │ └── list.vue
│ ├── log
│ │ ├── index.vue
│ │ └── info.vue
│ ├── menu
│ │ ├── index.vue
│ │ └── save.vue
│ ├── role
│ │ ├── index.vue
│ │ ├── permission.vue
│ │ └── save.vue
│ ├── system
│ │ └── index.vue
│ ├── systemLog
│ │ └── index.vue
│ ├── table
│ │ ├── index.vue
│ │ └── save.vue
│ ├── task
│ │ ├── index.vue
│ │ ├── logs.vue
│ │ └── save.vue
│ └── user
│ │ ├── index.vue
│ │ └── save.vue
│ ├── template
│ ├── layout
│ │ ├── blank.vue
│ │ ├── layoutLCR.vue
│ │ └── layoutTCB.vue
│ ├── list
│ │ ├── crud
│ │ │ ├── detail.vue
│ │ │ ├── index.vue
│ │ │ ├── info.vue
│ │ │ └── save.vue
│ │ ├── son.vue
│ │ ├── tab.vue
│ │ ├── tree.vue
│ │ └── width.vue
│ └── other
│ │ └── stepform.vue
│ ├── userCenter
│ ├── index.vue
│ └── user
│ │ ├── account.vue
│ │ ├── logs.vue
│ │ ├── password.vue
│ │ ├── pushSettings.vue
│ │ ├── seting.vue
│ │ ├── space.vue
│ │ └── upToEnterprise.vue
│ ├── userMember.bak
│ ├── form.vue
│ └── index.vue
│ ├── userMember
│ ├── form.vue
│ └── index.vue
│ └── vab
│ ├── chart.vue
│ ├── codeeditor.vue
│ ├── contextmenu.vue
│ ├── cron.vue
│ ├── cropper.vue
│ ├── dialog
│ ├── dialog1.vue
│ ├── dialog2.vue
│ └── index.vue
│ ├── drag.vue
│ ├── editor.vue
│ ├── fileselect.vue
│ ├── filterBar.vue
│ ├── form.vue
│ ├── formtable.vue
│ ├── iconfont.vue
│ ├── iconselect.vue
│ ├── importexport.vue
│ ├── mini.vue
│ ├── print.vue
│ ├── qrcode.vue
│ ├── select.vue
│ ├── selectFilter.vue
│ ├── statistic.vue
│ ├── table
│ ├── base.vue
│ ├── column.vue
│ ├── remote.vue
│ └── thead.vue
│ ├── tableselect.vue
│ ├── upload.vue
│ ├── video.vue
│ ├── watermark.vue
│ └── workflow.vue
└── vue.config.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/.DS_Store
--------------------------------------------------------------------------------
/.gitee/ISSUE_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | #### 请提交Issue时填写以下信息,以便我们更快更准确的定位和复现问题,谢谢。
2 |
3 | 1 GSAdmin版本号
4 | 2、问题的描述
5 | 3、报错信息
6 | 4、重现步骤
7 | 5、期待的结果
8 | 6、最小化可复现问题的源码
--------------------------------------------------------------------------------
/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | #### 请在提交PR前阅读以下说明,谢谢。
2 |
3 | - 提交 PR 前请rebase,确保commit记录的整洁。
4 | - 确保 PR 是提交到develop分支,而不是master分支。
5 | - 在下方填写本次 PR 的目的。
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /.idea/*
3 | /service/config.yaml
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 梦工厂
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/service/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/service/.DS_Store
--------------------------------------------------------------------------------
/service/.gitignore:
--------------------------------------------------------------------------------
1 | /upload/
2 | /upload/*
3 | /.idea/
4 | /.idea/*
5 | /logs/
6 |
--------------------------------------------------------------------------------
/service/Dockerfile:
--------------------------------------------------------------------------------
1 | # 第一阶段:构建 Go 应用
2 | FROM golang:1.23-alpine AS builder
3 |
4 | # 设置 Go 的工作目录
5 | WORKDIR /app
6 | # 复制源代码
7 | COPY . .
8 |
9 | # 编译 Go 应用
10 |
11 | RUN go env -w GO111MODULE=on \
12 | && go env -w GOPROXY=https://goproxy.cn,direct \
13 | && go env -w CGO_ENABLED=0 \
14 | && go env \
15 | && go mod tidy \
16 | && go build -o server .
17 |
18 | # 第二阶段:构建精简的运行时镜像
19 | FROM alpine:latest
20 |
21 | # 安装一些基础工具(可选)
22 | # RUN apk --no-cache add ca-certificates
23 | ENV TZ=Asia/Shanghai
24 | RUN apk update && apk add --no-cache tzdata openntpd \
25 | && ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
26 |
27 | # 设置工作目录
28 | WORKDIR /root/
29 |
30 | # 从构建阶段复制二进制文件到容器中
31 | COPY --from=builder /app/server .
32 | COPY --from=builder /app/config.yaml .
33 |
34 | # 暴露应用端口
35 | EXPOSE 8080
36 |
37 | # 启动应用
38 | CMD ["./server"]
39 |
--------------------------------------------------------------------------------
/service/app/bootstrap.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | "github.com/sonhineboy/gsadmin/service/initialize"
7 | "github.com/sonhineboy/gsadmin/service/router"
8 | "golang.org/x/time/rate"
9 | "os"
10 | )
11 |
12 | func Start() {
13 | global.SuperAdmin = "administrator"
14 | global.GsE = gin.Default()
15 | global.Config = initialize.ConfigInit(global.GsAppPath)
16 | loadObject()
17 | router.RouteInit(global.GsE)
18 | }
19 |
20 | func TestLoad() {
21 | dir, err := os.Getwd()
22 | if err != nil {
23 | }
24 | global.GsAppPath = dir + "/../"
25 | global.Config = initialize.ConfigInit(global.GsAppPath)
26 | loadObject()
27 | }
28 |
29 | func loadObject() {
30 | global.Db = initialize.DbInit(global.Config)
31 | initialize.AutoMigrate(global.Db)
32 | global.EventDispatcher = initialize.EventInit()
33 | global.Limiter = rate.NewLimiter(global.Config.Rate.Limit, global.Config.Rate.Burst)
34 | global.Logger = initialize.ZapInit(global.Config)
35 | global.ValidatorManager = initialize.InitValidator()
36 | }
37 |
38 | func DiyDefer() {
39 | initialize.DbClose(global.Db)
40 | initialize.ZapSync(global.Logger)
41 | }
42 |
--------------------------------------------------------------------------------
/service/app/controllers/system/operationLogController.go:
--------------------------------------------------------------------------------
1 | package system
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/app/repositorys"
6 | "github.com/sonhineboy/gsadmin/service/global"
7 | "github.com/sonhineboy/gsadmin/service/global/response"
8 | )
9 |
10 | type OperationLogController struct{}
11 |
12 | func (o *OperationLogController) List(c *gin.Context) {
13 |
14 | var (
15 | params global.List
16 | operationLog repositorys.OperationLogRepository
17 | )
18 | _ = c.ShouldBind(¶ms)
19 | operationLog.Where = params.Where
20 |
21 | response.Success(c, "ok", operationLog.List(params.Page, params.PageSize, "created_at"))
22 | }
23 |
--------------------------------------------------------------------------------
/service/app/event/loginEvent.go:
--------------------------------------------------------------------------------
1 | package event
2 |
3 | import "github.com/sonhineboy/gsadmin/service/app/models"
4 |
5 | type LoginEvent struct {
6 | Name string
7 | User models.AdminUser
8 | }
9 |
10 | func NewLoginEvent(name string, user models.AdminUser) *LoginEvent {
11 | return &LoginEvent{
12 | Name: name,
13 | User: user,
14 | }
15 | }
16 |
17 | func (t LoginEvent) GetEventName() string {
18 | return "loginEvent"
19 | }
20 |
--------------------------------------------------------------------------------
/service/app/event/testEvent.go:
--------------------------------------------------------------------------------
1 | package event
2 |
3 | type TestEvent struct {
4 | Name string
5 | }
6 |
7 | func NewTestEvent(name string) *TestEvent {
8 | return &TestEvent{Name: name}
9 | }
10 |
11 | func (t TestEvent) GetEventName() string {
12 | return "testEvent"
13 | }
14 |
--------------------------------------------------------------------------------
/service/app/listener/testListener.go:
--------------------------------------------------------------------------------
1 | package listener
2 |
3 | import (
4 | "fmt"
5 | event2 "github.com/sonhineboy/gsadmin/service/app/event"
6 | "github.com/sonhineboy/gsadmin/service/pkg/event"
7 | )
8 |
9 | type TestListener struct {
10 | }
11 |
12 | func NewTestListener() *TestListener {
13 | return &TestListener{}
14 | }
15 |
16 | func (t *TestListener) Process(event event.Event) {
17 | switch ev := event.(type) {
18 | case *event2.LoginEvent:
19 | fmt.Println(ev.Name)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/service/app/middleware/event.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | )
7 |
8 | func Event() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 | c.Set("e", global.EventDispatcher)
11 | c.Next()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/service/app/middleware/evnCheck.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | "github.com/sonhineboy/gsadmin/service/global/response"
7 | )
8 |
9 | func EnvCheck() gin.HandlerFunc {
10 |
11 | return func(context *gin.Context) {
12 |
13 | if global.Config.Env == "dev" {
14 | context.Next()
15 | } else {
16 | response.Failed(context, "生产环境当前操作不允许!")
17 | context.Abort()
18 | return
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/service/app/middleware/jwt.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/app/models"
6 | "github.com/sonhineboy/gsadmin/service/app/repositorys"
7 | "github.com/sonhineboy/gsadmin/service/global"
8 | "net/http"
9 | "strings"
10 | )
11 |
12 | // JWTAuth 中间件,检查token
13 | func JWTAuth() gin.HandlerFunc {
14 | return func(ctx *gin.Context) {
15 | authHeader := ctx.Request.Header.Get("Authorization")
16 | if authHeader == "" {
17 | ctx.JSON(http.StatusOK, gin.H{
18 | "code": -1,
19 | "msg": "无权限访问,请求未携带token",
20 | })
21 | ctx.Abort() //结束后续操作
22 | return
23 | }
24 | //按空格拆分
25 | parts := strings.SplitN(authHeader, " ", 2)
26 | if !(len(parts) == 2 && parts[0] == "Bearer") {
27 | global.Response{}.Failed(ctx, "请求头中auth格式有误")
28 | ctx.Abort()
29 | return
30 | }
31 |
32 | claims, err := models.ParseToken(parts[1], global.Config.MyJwt.Secret)
33 | if err != nil {
34 | ctx.JSON(http.StatusUnauthorized, gin.H{
35 | "code": -1,
36 | "msg": "无权限访问,token无效" + err.Error(),
37 | })
38 | ctx.Abort()
39 | return
40 | }
41 | // 将当前请求的claims信息保存到请求的上下文c上
42 | ctx.Set("claims", claims)
43 | ctx.Set("permission", repositorys.NewPermissionRepository(claims))
44 | ctx.Next() // 后续的处理函数可以用过ctx.Get("claims")来获取当前请求的用户信息
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/service/app/middleware/limiter.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | )
7 |
8 | func Limiter() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 | if global.Limiter.Allow() == false {
11 | global.Response{}.Failed(c, "当前请求过快,请稍后再试!")
12 | c.Abort()
13 | return
14 | }
15 | c.Next()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/service/app/middleware/operationLog.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/gin-gonic/gin"
7 | "github.com/sonhineboy/gsadmin/service/app/models"
8 | "github.com/sonhineboy/gsadmin/service/app/repositorys"
9 | "github.com/sonhineboy/gsadmin/service/global"
10 | "strings"
11 | )
12 |
13 | func OperationLog() gin.HandlerFunc {
14 |
15 | return func(c *gin.Context) {
16 |
17 | cCp := c.Copy()
18 | go func() {
19 | contentType := cCp.GetHeader("Content-Type")
20 | var (
21 | doData []byte
22 | log models.OperationLog
23 | )
24 | method := c.Request.Method
25 | //参数
26 | if method == "GET" {
27 | doData, _ = json.Marshal(cCp.Request.URL.Query())
28 | }
29 | if method == "POST" {
30 |
31 | if strings.Contains(contentType, "multipart/form-data") {
32 | doData = []byte("图片上传")
33 | } else {
34 | doData, _ = cCp.GetRawData()
35 | }
36 | }
37 |
38 | claims, ok := repositorys.GetCustomClaims(c)
39 | if ok == true {
40 | log.UserId = claims.Id
41 | log.UserName = claims.Name
42 | } else {
43 | log.UserId = 0
44 | }
45 |
46 | var where = make(map[string]interface{})
47 | var d models.MenuApiList
48 | db := global.Db.Model(&models.MenuApiList{})
49 | where["url"] = cCp.Request.URL.Path
50 | tx := db.Preload("Menu").Where(where).First(&d)
51 | if tx.Error != nil {
52 | fmt.Println("日志记录错误:", tx.Error.Error())
53 | log.PathName = "未知请求"
54 | } else {
55 | title, ok := d.Menu.Meta["title"]
56 |
57 | if ok {
58 | log.PathName = title.(string)
59 | }
60 | }
61 | log.Method = cCp.Request.Method
62 | log.DoData = string(doData)
63 | log.Ip = cCp.ClientIP()
64 |
65 | log.UrlPath = cCp.Request.URL.Path
66 |
67 | global.Db.Create(&log)
68 |
69 | }()
70 |
71 | c.Next()
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/service/app/middleware/permission.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/app/models"
6 | "github.com/sonhineboy/gsadmin/service/app/repositorys"
7 | "github.com/sonhineboy/gsadmin/service/global"
8 | "net/http"
9 | )
10 |
11 | func Permission() gin.HandlerFunc {
12 |
13 | return func(c *gin.Context) {
14 |
15 | var (
16 | apiList []models.MenuApiList
17 | systemMenuRepository repositorys.SystemMenuRepository
18 | )
19 |
20 | err := systemMenuRepository.GetApiList(c, &apiList)
21 | if err != nil {
22 | c.JSON(http.StatusForbidden, gin.H{
23 | "code": -1,
24 | "msg": "无权限访问!",
25 | })
26 | c.Abort()
27 | return
28 | }
29 |
30 | isAllow := false
31 | for _, api := range apiList {
32 | if api.Url == c.Request.URL.Path {
33 | isAllow = true
34 | }
35 | }
36 | Claims, _ := systemMenuRepository.GetCustomClaims(c)
37 |
38 | if !isAllow && !global.IsSuperAdmin(Claims.Roles, global.SuperAdmin) {
39 | c.JSON(http.StatusForbidden, gin.H{
40 | "code": -1,
41 | "msg": "无权限访问!",
42 | })
43 | c.Abort()
44 | return
45 | }
46 | c.Next()
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/service/app/models/adminUser.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "github.com/sonhineboy/gsadmin/service/global"
4 |
5 | type AdminUser struct {
6 | global.GsModel
7 | Nickname string `gorm:"column:nickname;type:varchar(255);comment:昵称" json:"nickname"`
8 | RealName string `gorm:"column:real_name;type:varchar(255);comment:真实名称" json:"real_name"`
9 | Password string `gorm:"column:password;type:varchar(255)" json:"password"`
10 | Email string `gorm:"column:email;type:varchar(255)" json:"email"`
11 | Name string `gorm:"uniqueIndex;type:varchar(100);default:" json:"name"`
12 | Avatar string `gorm:"column:avatar;type:varchar(255);default:''" json:"avatar"`
13 | Roles []Role `json:"group" gorm:"many2many:user_role"`
14 | }
15 |
--------------------------------------------------------------------------------
/service/app/models/article.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | )
7 |
8 | type Article struct {
9 | Title string `gorm:"column:title;type:varchar(100);NOT NULL" json:"title"`
10 | Cid uint64 `gorm:"column:cid;type:bigint(20) unsigned;NOT NULL" json:"cid"`
11 | Desc string `gorm:"column:desc;type:varchar(200)" json:"desc"`
12 | Content string `gorm:"column:content;type:longtext" json:"content"`
13 | Img string `gorm:"column:img;type:varchar(100)" json:"img"`
14 | CommentCount int64 `gorm:"column:comment_count;type:bigint(20);default:0;NOT NULL" json:"comment_count"`
15 | ReadCount int64 `gorm:"column:read_count;type:bigint(20);default:0;NOT NULL" json:"read_count"`
16 | global.GsModel
17 | }
18 |
19 | func (m *Article) TableName() string {
20 | return fmt.Sprint(global.Config.Db.TablePrefix, "article")
21 | }
22 |
--------------------------------------------------------------------------------
/service/app/models/menu.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | "gorm.io/datatypes"
7 | )
8 |
9 | type AdminMenu struct {
10 | global.GsModel
11 | Meta datatypes.JSONMap `gorm:"type:varchar(500);column:meta;comment:元数据" json:"meta"`
12 | Component string `gorm:"type:varchar(100);column:component;comment:组件" json:"component"`
13 | Name string `gorm:"type:varchar(80);column:name;comment:别名" json:"name"`
14 | ParentId uint `gorm:"type:int(11);column:parent_id;comment:上级id" json:"parent_id"`
15 | Sort int `gorm:"type:int(11);column:sort;comment:排序;default:0" json:"sort"`
16 | Path string `gorm:"type:varchar(100);column:path;comment:路径" json:"path"`
17 | Redirect string `gorm:"type:varchar(200);column:redirect;comment:重定向uri" json:"redirect"`
18 | ApiList []MenuApiList `gorm:"foreignKey:MenuId;references:ID" json:"apiList"`
19 | }
20 |
21 | type TreeMenu struct {
22 | AdminMenu
23 | Children []*TreeMenu `json:"children"`
24 | }
25 |
26 | func (a AdminMenu) Test() {
27 |
28 | fmt.Println("test-------")
29 | }
30 |
--------------------------------------------------------------------------------
/service/app/models/menuApiList.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/global"
5 | )
6 |
7 | type MenuApiList struct {
8 | global.GsModel
9 | Code string `gorm:"column:code;type:varchar(100);comment:关键字" json:"code"`
10 | Url string `gorm:"column:url;type:varchar(100);comment:地址" json:"url"`
11 | MenuId uint `gorm:"column:menu_id;type:int;" json:"menu_id"`
12 | Menu AdminMenu `gorm:"foreignKey:id;references:menu_id"`
13 | }
14 |
--------------------------------------------------------------------------------
/service/app/models/news.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | )
7 |
8 | type News struct {
9 | global.GsModel
10 | Title string `gorm:"column:title;type:varchar(255);not null;comment:标题;" json:"title"`
11 | Author string `gorm:"column:author;type:varchar(255);not null;comment:作者;" json:"author"`
12 | Content string `gorm:"column:content;type:text;not null;comment:内容;" json:"content"`
13 | Image string `gorm:"column:image;comment:缩略图;" json:"image"`
14 | }
15 |
16 | func (m *News) TableName() string {
17 | return fmt.Sprint(global.Config.Db.TablePrefix, "news")
18 | }
19 |
--------------------------------------------------------------------------------
/service/app/models/operationLog.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "github.com/sonhineboy/gsadmin/service/global"
4 |
5 | type OperationLog struct {
6 | global.GsModel
7 | UserId uint `gorm:"column:user_id;type:int(11);comment:用户ID" json:"user_id"`
8 | UrlPath string `gorm:"column:user_path;type:varchar(100);comment:访问路径" json:"url_path"`
9 | Ip string `gorm:"column:ip;type:varchar(50);comment:IP" json:"ip"`
10 | Method string `gorm:"column:method;type:varchar(50);comment:请求方式" json:"method"`
11 | PathName string `gorm:"column:path_name;type:varchar(100);comment:请求名称" json:"path_name"`
12 | DoData string `gorm:"column:do_data;type:text;comment:处理数据;default:null" json:"do_data"`
13 | UserName string `gorm:"column:user_name;type:varchar(40);comment:用户名;default:未知;NOT NULL" json:"user_name"`
14 | }
15 |
--------------------------------------------------------------------------------
/service/app/models/role.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "github.com/sonhineboy/gsadmin/service/global"
4 |
5 | type Role struct {
6 | global.GsModel
7 | Alias string `gorm:"type:varchar(50);column:alias;" json:"alias"`
8 | Label string `gorm:"type:varchar(100);column:label;" json:"label"`
9 | Remark string `gorm:"type:varchar(255);column:remark" json:"remark"`
10 | Sort int `gorm:"type:int(3);column:sort" json:"sort"`
11 | Status *int `json:"status" gorm:"type:int(3);column:status"`
12 | Menus []AdminMenu `json:"menus" gorm:"many2many:role_menu"`
13 | }
14 |
--------------------------------------------------------------------------------
/service/app/repositorys/baseRepository.go:
--------------------------------------------------------------------------------
1 | package repositorys
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/global"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type BaseRepository struct {
9 | db *gorm.DB
10 | }
11 |
12 | //
13 | // SetDb
14 | // @Description: 设置Db
15 | // @receiver r *BaseRepository
16 | // @param db *gorm.DB
17 | // @return *BaseRepository
18 | //
19 | func (r *BaseRepository) SetDb(db *gorm.DB) {
20 | r.db = db
21 | }
22 |
23 | //
24 | // getDb
25 | // @Description: 私有方法 主要应对事务问题
26 | // @receiver r *BaseRepository
27 | // @return *gorm.DB
28 | //
29 | func (r *BaseRepository) getDb() *gorm.DB {
30 | if r.db == nil {
31 | return global.Db
32 | } else {
33 | return r.db
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/service/app/repositorys/operationLogRepository.go:
--------------------------------------------------------------------------------
1 | package repositorys
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/app/models"
5 | "github.com/sonhineboy/gsadmin/service/global"
6 | )
7 |
8 | type OperationLogRepository struct {
9 | Model models.OperationLog
10 | Where map[string]interface{}
11 | }
12 |
13 | func (o *OperationLogRepository) List(page int, pageSize int, sortField string) map[string]interface{} {
14 | var (
15 | total int64
16 | data []models.OperationLog
17 | offSet int
18 | )
19 | db := global.Db.Model(&o.Model)
20 |
21 | if o.Where != nil && len(o.Where) > 0 {
22 | createdAt, ok := o.Where["created_at"]
23 | if ok {
24 | delete(o.Where, "created_at")
25 | createdAtMap, ok := createdAt.(map[string]interface{})
26 | if ok {
27 | start, startOk := createdAtMap["begin"]
28 | end, endOk := createdAtMap["end"]
29 | if startOk && endOk {
30 | db.Where("created_at BETWEEN ? and ?", start, end)
31 | }
32 | }
33 |
34 | }
35 |
36 | db.Where(o.Where)
37 | }
38 | db.Count(&total)
39 |
40 | offSet = (page - 1) * pageSize
41 | db.Preload("Menus").Limit(pageSize).Order(sortField + " desc" + ",id desc").Offset(offSet)
42 | db.Find(&data)
43 | return global.Pages(page, pageSize, int(total), data)
44 | }
45 |
--------------------------------------------------------------------------------
/service/app/repositorys/permissionRepository.go:
--------------------------------------------------------------------------------
1 | package repositorys
2 |
3 | import (
4 | "errors"
5 | "github.com/gin-gonic/gin"
6 | "github.com/sonhineboy/gsadmin/service/app/models"
7 | )
8 |
9 | type PermissionRepository struct {
10 | CustomClaims *models.CustomClaims
11 | }
12 |
13 | func NewPermissionRepository(customClaims *models.CustomClaims) *PermissionRepository {
14 | return &PermissionRepository{
15 | CustomClaims: customClaims,
16 | }
17 | }
18 |
19 | func NewDefaultPermissionRepository(c *gin.Context) (*PermissionRepository, error) {
20 | custom, ok := GetCustomClaims(c)
21 | if ok {
22 | return &PermissionRepository{
23 | CustomClaims: custom,
24 | }, nil
25 | } else {
26 | return nil, errors.New("初始化失败")
27 | }
28 | }
29 |
30 | func GetCustomClaims(c *gin.Context) (*models.CustomClaims, bool) {
31 | v, ok := c.Get("claims")
32 | claims, err := v.(*models.CustomClaims)
33 | if ok && err {
34 | return claims, true
35 | } else {
36 | return &models.CustomClaims{}, false
37 | }
38 | }
39 |
40 | func GetPermission(c *gin.Context) *PermissionRepository {
41 | v, ok := c.Get("permission")
42 | permission, err := v.(*PermissionRepository)
43 | if ok && err {
44 | return permission
45 | } else {
46 | return nil
47 | }
48 | }
49 |
50 | // IsRole 判断是否某个用户组
51 | func (p *PermissionRepository) IsRole(role string) bool {
52 | for _, v := range p.CustomClaims.Roles {
53 | if v == role {
54 | return true
55 | }
56 | }
57 | return false
58 | }
59 |
60 | //判断是否纯在否个权限
61 | func (p *PermissionRepository) isHasPermission(permission string) bool {
62 | for _, v := range p.CustomClaims.Permission {
63 | if v == permission {
64 | return true
65 | }
66 | }
67 | return false
68 | }
69 |
70 | //判断是有api权限
71 | func (p *PermissionRepository) isApi(url string, code string) bool {
72 |
73 | v, ok := p.CustomClaims.ApiList[url]
74 |
75 | if ok == false {
76 | return false
77 | }
78 |
79 | if v != code {
80 | return false
81 | }
82 | return true
83 | }
84 |
--------------------------------------------------------------------------------
/service/app/requests/genRequest.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/sonhineboy/gsadminGen/pkg"
5 | )
6 |
7 | type GenFields struct {
8 | TableName string `form:"table_name" binding:"required" required_msg:"当前字段必填"`
9 | }
10 |
11 | type GenCode struct {
12 | Fields []pkg.Field `json:"fields" binding:"required"`
13 | Checkbox []string `json:"checkbox" binding:"required"`
14 | ControllerPackage string `json:"controllerPackage" binding:"required"`
15 | TableDiyName string `json:"tableDiyName" binding:"required"`
16 | MenuName string `json:"menuName"`
17 | }
18 |
--------------------------------------------------------------------------------
/service/app/requests/login.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type Login struct {
4 | Name string `form:"username" binding:"required" msg:"用户名不能为空" json:"username"`
5 | PassWord string `form:"password" binding:"required" msg:"密码不能为空" json:"password"`
6 |
7 | CaptchaId string `json:"captchaId" binding:"required"`
8 | CaptchaValue string `json:"captchaValue" binding:"required,cacheCode=CaptchaId"`
9 | }
10 |
--------------------------------------------------------------------------------
/service/app/requests/menuPost.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type MenuPost struct {
4 | Meta map[string]interface{} `json:"meta"`
5 | Component string `json:"component"`
6 | Name string `json:"name"`
7 | ParentId uint `json:"parentId"`
8 | Path string `json:"path"`
9 | Redirect string `json:"redirect"`
10 | Id string `json:"id"`
11 | ApiList []map[string]string `json:"apiList"`
12 | Sort int `json:"sort"`
13 | }
14 |
15 | type MenuDel struct {
16 | Ids []uint `json:"ids"`
17 | }
18 |
--------------------------------------------------------------------------------
/service/app/requests/newsRequest.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type DeleteNewsRequest struct {
4 | Ids []int `binding:"required"`
5 | }
6 |
7 | type NewsRequest struct {
8 | Title string `json:"title" binding:"required"`
9 | Author string `json:"author" binding:"required"`
10 | Content string `json:"content" binding:"required"`
11 | Image string `json:"image"`
12 |
13 | }
--------------------------------------------------------------------------------
/service/app/requests/role.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type Role struct {
4 | Alias string `json:"alias"`
5 | Label string `json:"label"`
6 | Remark string `json:"remark"`
7 | Sort int `json:"sort"`
8 | Status int `json:"status"`
9 | Id uint `json:"id"`
10 | }
11 |
12 | type RoleDel struct {
13 | Ids []uint `json:"id"`
14 | }
15 |
16 | type RoleUpMenus struct {
17 | Id uint `json:"id" binding:"required"`
18 | Menus []uint `json:"menus" binding:"required"`
19 | }
20 |
--------------------------------------------------------------------------------
/service/app/requests/roleList.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type RoleList struct {
4 | Page int `json:"page" form:"page"`
5 | PageSize int `json:"pageSize" form:"pageSize"`
6 | Where map[string]interface{} `json:"where" form:"where"`
7 | }
8 |
--------------------------------------------------------------------------------
/service/app/requests/user.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import "github.com/sonhineboy/gsadmin/service/global"
4 |
5 | type UserList struct {
6 | global.List
7 | }
8 |
9 | type UserUpdate struct {
10 | Id uint `json:"id"`
11 | Name string `json:"name" binding:"required" msg:"用户名不能为空"`
12 | PassWord string `json:"password"`
13 | CPassWord string `json:"password2"`
14 | RealName string `json:"real_name" binding:"required,min=2" min_msg:"长度最小大于2" msg:"真实姓名不能为空"`
15 | Avatar string `json:"avatar" binding:"required,min=3" min_msg:"长度最小大于3" msg:"通向不能为空"`
16 | Roles []uint `json:"group"`
17 | }
18 |
--------------------------------------------------------------------------------
/service/app/requests/userAdd.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | type UserAdd struct {
4 | Name string `json:"name" binding:"required" msg:"用户名不能为空"`
5 | PassWord string `json:"password" binding:"required,min=3,eqfield=CPassWord" min_msg:"长度最小大于3" eqfield_msg:"两次输入密码不一致" msg:"密码不能为空"`
6 | CPassWord string `json:"password2" binding:"required,min=3" min_msg:"长度最小大于3" msg:"密码不能为空"`
7 | RealName string `json:"real_name" binding:"required,min=2" min_msg:"长度最小大于2" msg:"真实姓名不能为空"`
8 | Avatar string `json:"avatar" binding:"required,min=3" min_msg:"长度最小大于3" msg:"通向不能为空"`
9 | Roles []uint `json:"group"`
10 | }
11 |
--------------------------------------------------------------------------------
/service/app/validators/cacheCodeValidator.go:
--------------------------------------------------------------------------------
1 | package validators
2 |
3 | import (
4 | "github.com/dchest/captcha"
5 | "github.com/go-playground/validator/v10"
6 | "github.com/sonhineboy/gsadminValidator/ginValidator"
7 | )
8 |
9 | type CacheCodeValidator struct {
10 | ginValidator.BaseValidator
11 | }
12 |
13 | func (c *CacheCodeValidator) TagName() string {
14 | return "cacheCode"
15 | }
16 |
17 | // Messages 规则错误提示信息
18 | func (c *CacheCodeValidator) Messages() string {
19 | return "{0} 验证码错误"
20 | }
21 |
22 | // Validator 规则验证逻辑
23 | func (c *CacheCodeValidator) Validator(fl validator.FieldLevel) bool {
24 | keyId := fl.Param()
25 | return captcha.VerifyString(fl.Parent().FieldByName(keyId).String(), fl.Field().String())
26 | }
27 |
--------------------------------------------------------------------------------
/service/app/validators/demo.go:
--------------------------------------------------------------------------------
1 | package validators
2 |
3 | import (
4 | "github.com/go-playground/validator/v10"
5 | "github.com/sonhineboy/gsadminValidator/ginValidator"
6 | )
7 |
8 | type DemoValidator struct {
9 | ginValidator.BaseValidator
10 | }
11 |
12 | func (d *DemoValidator) TagName() string {
13 | return "Demo"
14 | }
15 |
16 | func (d *DemoValidator) Messages() string {
17 | //This is error message
18 | return ""
19 | }
20 |
21 | func (d *DemoValidator) Validator(fl validator.FieldLevel) bool {
22 | //To Do .....
23 | return true
24 | }
25 |
--------------------------------------------------------------------------------
/service/config.example:
--------------------------------------------------------------------------------
1 | env: 'dev' #dev 开发环境会启用代码生成工具
2 | db:
3 | type: 'mysql'
4 | host: '127.0.0.1' #这个地方填写msyql服务的IP
5 | port: '3306'
6 | max-idle-conns: 10
7 | max-open-conns: 100
8 | name: 'root'
9 | password: ''
10 | database: 'gin_scuiadmin'
11 | table_prefix: 'gc_'
12 |
13 | myjwt:
14 | secret: 'BbT4nlt4YCVAolTKf9ImxbYs7u1BGusKPtWuWLQ3ZtuOyk3F57' #这里是你的jwt秘钥
15 | expires_at: 36000 #过期时间
16 | app:
17 | host: "http://localhost:8080" #这里localhost 要替换成你的宿主机IP
18 | port: ":8080"
19 | uploadFile: "/upload" #文件上传地址
20 | rate: #限流配置
21 | limit: 15
22 | burst: 15
23 | logger:
24 | drive: "zap" # 日志记录驱动
25 | path: "logs" # 日志存放路径
26 | size: 10 # 日志大小
27 | maxAge: 3 #日志保留天数
28 | stdOut: true #控制台输出
--------------------------------------------------------------------------------
/service/config/common.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "golang.org/x/time/rate"
5 | )
6 |
7 | type Config struct {
8 | Env string `yaml:"env"`
9 | Db struct {
10 | Type string `yaml:"type"`
11 | MaxIdleConns int `yaml:"max-idle-conns"`
12 | MaxOpenConns int `yaml:"max-open-conns"`
13 | Port string `yaml:"port"`
14 | Host string `yaml:"host"`
15 | TablePrefix string `yaml:"table_prefix"`
16 | Database string `yaml:"database"`
17 | User string `yaml:"name"`
18 | PassWord string `yaml:"password"`
19 | }
20 | MyJwt struct {
21 | Secret string `yaml:"secret"`
22 | ExpiresAt int64 `yaml:"expires_at"`
23 | }
24 | App struct {
25 | Host string `yaml:"host"`
26 | Port string `yaml:"port"`
27 | UploadFile string `yaml:"uploadFile"`
28 | }
29 | Rate struct {
30 | Limit rate.Limit `yaml:"limit"`
31 | Burst int `yaml:"burst"`
32 | }
33 | Logger struct {
34 | Drive string `yaml:"drive"`
35 | Path string `yaml:"path"`
36 | Size int `yaml:"size"`
37 | MaxAge int `yaml:"maxAge"`
38 | StdOut bool `yaml:"stdOut"`
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/service/databases/gc_role_menu.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 44);
2 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 44);
3 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 45);
4 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 45);
5 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (1, 46);
6 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 46);
7 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 46);
8 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (1, 47);
9 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 47);
10 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 47);
11 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 48);
12 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 48);
13 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 100);
14 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 100);
15 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 101);
16 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 102);
17 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (14, 103);
18 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 110);
19 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 111);
20 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 112);
21 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 113);
22 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 114);
23 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 115);
24 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 116);
25 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 117);
26 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 118);
27 | INSERT INTO `gc_role_menu`(`role_id`, `admin_menu_id`) VALUES (15, 119);
28 |
--------------------------------------------------------------------------------
/service/databases/gc_user_role.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (57, 14);
2 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (74, 14);
3 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (75, 14);
4 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (77, 14);
5 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (54, 15);
6 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (75, 15);
7 | INSERT INTO `gc_user_role`(`admin_user_id`, `role_id`) VALUES (76, 15);
8 |
--------------------------------------------------------------------------------
/service/global/model.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "database/sql/driver"
5 | "fmt"
6 | "gorm.io/gorm"
7 | "time"
8 | )
9 |
10 | type GsModel struct {
11 | ID uint `gorm:"primarykey;autoIncrement" json:"id"` // 主键ID
12 | CreatedAt *LocalTime `json:"created_at" gorm:"type:datetime(3)"` // 创建时间
13 | UpdatedAt *LocalTime `json:"updated_at" gorm:"type:datetime(3)"` // 更新时间
14 | DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
15 | }
16 |
17 | type LocalTime time.Time
18 |
19 | func (t *LocalTime) MarshalJSON() ([]byte, error) {
20 | tTime := time.Time(*t)
21 | return []byte(fmt.Sprintf("\"%v\"", tTime.Format("2006-01-02 15:04:05"))), nil
22 | }
23 |
24 | // Scan @ignore waring
25 | func (t *LocalTime) Scan(v interface{}) error {
26 | if value, ok := v.(time.Time); ok {
27 | *t = LocalTime(value)
28 | return nil
29 | }
30 | return fmt.Errorf("can not convert %v to timestamp", v)
31 | }
32 |
33 | func (t LocalTime) Value() (driver.Value, error) {
34 | var zeroTime time.Time
35 | tlt := time.Time(t)
36 | //判断给定时间是否和默认零时间的时间戳相同
37 | if tlt.UnixNano() == zeroTime.UnixNano() {
38 | return nil, nil
39 | }
40 | return tlt, nil
41 | }
42 |
--------------------------------------------------------------------------------
/service/global/request.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | type List struct {
4 | Page int `json:"page" form:"page"`
5 | PageSize int `json:"pageSize" form:"pageSize"`
6 | Where map[string]interface{} `json:"where" form:"where"`
7 | }
8 |
9 | type Del struct {
10 | Ids []uint `json:"id"`
11 | }
12 |
--------------------------------------------------------------------------------
/service/global/response.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/global/response"
6 | )
7 |
8 | type Response struct {
9 | Code int `json:"code"`
10 | Msg string `json:"msg"`
11 | Message string `json:"message"`
12 | Data interface{} `json:"data"`
13 | }
14 |
15 | // Success disabled
16 | func (r Response) Success(c *gin.Context, msg string, data interface{}) {
17 |
18 | response.Success(c, msg, data)
19 | }
20 |
21 | // Failed Deprecated
22 | func (r Response) Failed(c *gin.Context, err string) {
23 | response.Failed(c, err)
24 | }
25 |
--------------------------------------------------------------------------------
/service/global/response/response.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | type Response struct {
9 | Code int `json:"code"`
10 | Msg string `json:"msg"`
11 | Message string `json:"message"`
12 | Data interface{} `json:"data"`
13 | }
14 |
15 | func Success(c *gin.Context, msg string, data interface{}) {
16 | r := Response{
17 | Code: 200,
18 | Msg: msg,
19 | Message: msg,
20 | Data: data,
21 | }
22 | c.JSON(http.StatusOK, r)
23 | }
24 |
25 | // Failed Deprecated
26 | func Failed(c *gin.Context, err string) {
27 | r := Response{
28 | Code: 422,
29 | Msg: err,
30 | Message: err,
31 | Data: []string{},
32 | }
33 | c.JSON(http.StatusOK, r)
34 | }
35 |
--------------------------------------------------------------------------------
/service/gsadmin20240909:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/service/gsadmin20240909
--------------------------------------------------------------------------------
/service/initialize/configInit.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/config"
5 | "gopkg.in/yaml.v3"
6 | "io/ioutil"
7 | )
8 |
9 | func ConfigInit(path string) *config.Config {
10 | var c config.Config
11 | configFile, err := ioutil.ReadFile(path + "config.yaml")
12 | if err != nil {
13 | panic(err)
14 | }
15 | err2 := yaml.Unmarshal(configFile, &c)
16 | if err2 != nil {
17 | panic(err)
18 | }
19 | return &c
20 | }
21 |
--------------------------------------------------------------------------------
/service/initialize/dbInit.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "fmt"
5 | "github.com/sonhineboy/gsadmin/service/app/models"
6 | "github.com/sonhineboy/gsadmin/service/config"
7 | "gorm.io/driver/mysql"
8 | "gorm.io/gorm"
9 | "gorm.io/gorm/schema"
10 | "time"
11 | )
12 |
13 | func DbInit(c *config.Config) *gorm.DB {
14 | dsn := fmt.Sprint(c.Db.User, ":", c.Db.PassWord, "@tcp(", c.Db.Host, ":", c.Db.Port, ")/", c.Db.Database, "?charset=utf8mb4&parseTime=True&loc=Local")
15 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
16 | NamingStrategy: schema.NamingStrategy{
17 | TablePrefix: c.Db.TablePrefix,
18 | SingularTable: true,
19 | },
20 | DisableForeignKeyConstraintWhenMigrating: true,
21 | })
22 |
23 | if err != nil {
24 | panic(err.Error())
25 | }
26 | sqlDb, _ := db.DB()
27 | sqlDb.SetMaxOpenConns(c.Db.MaxOpenConns)
28 | sqlDb.SetMaxIdleConns(c.Db.MaxIdleConns)
29 | sqlDb.SetConnMaxIdleTime(time.Hour)
30 | return db
31 | }
32 |
33 | func AutoMigrate(db *gorm.DB) {
34 |
35 | err := db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(
36 | //slot start not delete
37 | &models.AdminUser{},
38 | &models.AdminMenu{},
39 | &models.MenuApiList{},
40 | &models.Role{},
41 | &models.OperationLog{},
42 | &models.News{},
43 | //slot end not delete
44 | )
45 |
46 | if err != nil {
47 | panic(err)
48 | }
49 | }
50 |
51 | func DbClose(db *gorm.DB) func() {
52 | return func() {
53 | db, _ := db.DB()
54 | _ = db.Close()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/service/initialize/eventInit.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/app/event"
5 | "github.com/sonhineboy/gsadmin/service/app/listener"
6 | event2 "github.com/sonhineboy/gsadmin/service/pkg/event"
7 | )
8 |
9 | func EventInit() event2.DispatcherEvent {
10 |
11 | EventDispatcher := event2.NewDispatcher()
12 | EventDispatcher.Register(event.TestEvent{}.GetEventName(), listener.NewTestListener())
13 | EventDispatcher.Register(event.LoginEvent{}.GetEventName(), listener.NewTestListener())
14 | return EventDispatcher
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/service/initialize/validatorInit.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/app/validators"
5 | "github.com/sonhineboy/gsadminValidator/ginValidator"
6 | )
7 |
8 | func InitValidator() *ginValidator.CustomValidatorManager {
9 | return initCustomValidator(initTrans())
10 | }
11 |
12 | func initTrans() *ginValidator.Trans {
13 | tran := ginValidator.NewDefaultTrans()
14 | err := tran.SetUp()
15 | if err != nil {
16 | panic(err)
17 | }
18 | return tran
19 | }
20 |
21 | func initCustomValidator(tran *ginValidator.Trans) *ginValidator.CustomValidatorManager {
22 | customValidator := ginValidator.NewCustomValidatorManager(make(map[string]ginValidator.CustomValidator), tran.GetValidate(), tran.GetTrans())
23 | customValidator.Adds(
24 | new(validators.CacheCodeValidator),
25 | )
26 | customValidator.RegisterToValidate()
27 | return customValidator
28 | }
29 |
--------------------------------------------------------------------------------
/service/initialize/zapInit.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "fmt"
5 | "github.com/sonhineboy/gsadmin/service/config"
6 | "go.uber.org/zap"
7 | "go.uber.org/zap/zapcore"
8 | "gopkg.in/natefinch/lumberjack.v2"
9 | "os"
10 | "time"
11 | )
12 |
13 | func ZapInit(c *config.Config) *zap.SugaredLogger {
14 | core := zapcore.NewCore(enc(), ws(c), zap.NewAtomicLevel())
15 | logger := zap.New(core, zap.AddStacktrace(zap.ErrorLevel))
16 | return logger.Sugar()
17 | }
18 |
19 | func ZapSync(zap *zap.SugaredLogger) {
20 | err := zap.Sync()
21 | if err != nil {
22 | return
23 | }
24 | }
25 |
26 | func enc() zapcore.Encoder {
27 | cfg := zap.NewProductionEncoderConfig()
28 | cfg.TimeKey = "time"
29 | cfg.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
30 | encoder.AppendString(t.Format("2006-01-02 15:04:05"))
31 | }
32 | return zapcore.NewJSONEncoder(cfg)
33 | }
34 |
35 | func ws(c *config.Config) zapcore.WriteSyncer {
36 | logFileName := fmt.Sprintf("./%s/web-%v.log", c.Logger.Path, time.Now().Format("2006-01-02"))
37 | lumberjackLogger := &lumberjack.Logger{
38 | Filename: logFileName,
39 | MaxSize: c.Logger.Size,
40 | //MaxBackups: c.Logger.MaxAge,
41 | MaxAge: c.Logger.MaxAge,
42 | Compress: false,
43 | }
44 |
45 | if c.Logger.StdOut {
46 | return zapcore.NewMultiWriteSyncer(zapcore.AddSync(lumberjackLogger), zapcore.AddSync(os.Stdout))
47 | } else {
48 | return zapcore.AddSync(lumberjackLogger)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "github.com/sonhineboy/gsadmin/service/app"
7 | "github.com/sonhineboy/gsadmin/service/global"
8 | _ "github.com/sonhineboy/gsadmin/service/router"
9 | "net/http"
10 | "os"
11 | "os/signal"
12 | "syscall"
13 | "time"
14 | )
15 |
16 | func main() {
17 | dir, err := os.Getwd()
18 | if err != nil {
19 | panic(fmt.Sprintf("GetWd err:%v", err))
20 | }
21 | global.GsAppPath = dir + string(os.PathSeparator)
22 | app.Start()
23 | defer func() {
24 | app.DiyDefer()
25 | }()
26 |
27 | run()
28 | }
29 |
30 | // run 开始监听并启动web服务
31 | func run() {
32 | svr := &http.Server{
33 | Addr: global.Config.App.Port,
34 | Handler: global.GsE,
35 | }
36 |
37 | go func() {
38 | if err := svr.ListenAndServe(); err != nil {
39 | global.Logger.Errorf("listen: %s\n", err)
40 | }
41 | }()
42 |
43 | sigs := make(chan os.Signal, 1)
44 | signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
45 | <-sigs
46 | global.Logger.Infof("Shutting down server...")
47 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
48 | defer cancel()
49 | if err := svr.Shutdown(ctx); err != nil {
50 | global.Logger.Errorf("stutdown err %v", err)
51 | }
52 | global.Logger.Infof("shutdown-->ok")
53 | }
54 |
--------------------------------------------------------------------------------
/service/pkg/event/eventDispatcher.go:
--------------------------------------------------------------------------------
1 | package event
2 |
3 | import "errors"
4 |
5 | type Event interface {
6 | GetEventName() string
7 | }
8 |
9 | type Listener interface {
10 | Process(event Event)
11 | }
12 |
13 | type DispatcherEvent struct {
14 | Event map[string][]Listener
15 | }
16 |
17 | func NewDispatcher() DispatcherEvent {
18 | return DispatcherEvent{
19 | Event: make(map[string][]Listener),
20 | }
21 | }
22 |
23 | func (e *DispatcherEvent) Register(eventName string, listener Listener) {
24 | e.Event[eventName] = append(e.Event[eventName], listener)
25 | }
26 |
27 | func (e *DispatcherEvent) Dispatch(eventName Event) error {
28 | listener, ok := e.Event[eventName.GetEventName()]
29 | if ok {
30 | for _, v := range listener {
31 | v.Process(eventName)
32 | }
33 | return nil
34 | }
35 | return errors.New("未知事件")
36 | }
37 |
--------------------------------------------------------------------------------
/service/router/controllers.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/sonhineboy/gsadmin/service/app/controllers/system"
5 | )
6 |
7 | type Controllers struct {
8 | system.UserController
9 | system.CommonController
10 | system.MenuController
11 | system.RoleController
12 | system.OperationLogController
13 | }
14 |
15 | var ApiControllers = new(Controllers)
16 |
--------------------------------------------------------------------------------
/service/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sonhineboy/gsadmin/service/app/controllers/system"
6 | "github.com/sonhineboy/gsadmin/service/app/middleware"
7 | )
8 |
9 | func RouteInit(e *gin.Engine) {
10 | e.Use(middleware.Limiter(), middleware.Event())
11 | e.GET("/api/common/captcha/img/:id/:w/:h", ApiControllers.CommonController.CaptchaImage)
12 | e.GET("/api/common/captcha/info", ApiControllers.CommonController.CaptchaInfo)
13 | e.GET("/api/common/version", ApiControllers.CommonController.GetVersion)
14 | e.POST("/api/user/login", ApiControllers.UserController.Login)
15 | e.Static("/api/system/common/file", ApiControllers.CommonController.GetFileBasePath())
16 | e.GET("/api/system/dept/list", system.DeptList)
17 | e.GET("/api/demo/page", system.DemoUser)
18 | e.POST("/api/demo/order", system.OrderDemo)
19 |
20 | r := e.Group("api")
21 | {
22 | SystemApiInit(r)
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/service/test/orm_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "fmt"
5 | "gorm.io/driver/mysql"
6 | "gorm.io/gorm"
7 | "testing"
8 | )
9 |
10 | func TestTableInfo(t *testing.T) {
11 | dsn := "root:@tcp(127.0.0.1:3306)/gin_scuiadmin?charset=utf8mb4&parseTime=True&loc=Local"
12 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
13 |
14 | if err != nil {
15 | t.Error(err)
16 | }
17 |
18 | //types, err := db.Migrator().ColumnTypes("member")
19 | //if err != nil {
20 | // return
21 | //}
22 | //
23 | //for _, columnType := range types {
24 | //
25 | // v, _ := columnType.DefaultValue()
26 | //
27 | // fmt.Println("default", v, "len", len(v))
28 | //}
29 |
30 | var indexes []map[string]interface{}
31 | db.Raw("show Index from member").Scan(&indexes)
32 |
33 | indexMap := make(map[string]map[string]interface{}, 20)
34 |
35 | for _, v := range indexes {
36 | columnName, _ := v["Column_name"]
37 | if columnNameString, ok := columnName.(string); ok {
38 | indexMap[columnNameString] = map[string]interface{}{
39 | "Non_unique": v["Non_unique"],
40 | "Index_type": v["Index_type"],
41 | }
42 | } else {
43 | continue
44 | }
45 | }
46 |
47 | fmt.Println(indexMap)
48 | }
49 |
50 | func GetIndexType(field string) {
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/service/test/repository_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestSelect(t *testing.T) {
8 |
9 | //add
10 | //var data models.Article
11 | //data.Title = "标题"
12 | //data.Cid = 2
13 | //data.CommentCount = 10
14 | //data.Content = "这里是内容"
15 | //data.Desc = "这里是描述"
16 | //data.Img = "xxx"
17 | //data.ReadCount = 10
18 | //
19 | //err :=testRe.Add(&data)
20 | //if err !=nil {
21 | // t.Error(err)
22 | //}
23 |
24 | //updata
25 | //db :=testRe.UpdateById(4,map[string]interface{}{"read_count":40})
26 | //fmt.Println(db.RowsAffected)
27 |
28 | //updataByWhere
29 | //db := testRe.UpdateByWhere(map[string]interface{}{"read_count": 50}, map[string]interface{}{"read_count": 40})
30 | //fmt.Println(db.RowsAffected)
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/service/web/js/model/news.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | index: {
6 | url: `${config.API_URL}/news/index`,
7 | name: "列表",
8 | get: async function (params) {
9 | return await http.get(this.url, params)
10 | }
11 | },
12 | get: {
13 | url: `${config.API_URL}/news/`,
14 | name: "单条信息",
15 | get: async function (id) {
16 | return await http.get(this.url + id)
17 | }
18 | },
19 | save: {
20 | url: `${config.API_URL}/news/save`,
21 | name: "添加信息",
22 | post: async function (params) {
23 | return http.post(this.url, params)
24 | },
25 | },
26 | edit: {
27 | url: `${config.API_URL}/news/edit/`,
28 | name: "编辑信息",
29 | post: async function (id, params) {
30 | return http.post(this.url + id, params)
31 | },
32 | },
33 | delete: {
34 | url: `${config.API_URL}/news/delete`,
35 | name: "删除信息",
36 | post: async function (params) {
37 | return http.post(this.url, params)
38 | },
39 | }
40 | }
--------------------------------------------------------------------------------
/web/scui/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = tab
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/web/scui/.env.development:
--------------------------------------------------------------------------------
1 | # 本地环境
2 | NODE_ENV = development
3 |
4 | # 标题
5 | VUE_APP_TITLE = GS Admin
6 |
7 | # 接口地址
8 | # VUE_APP_API_BASEURL = https://www.fastmock.site/mock/5039c4361c39a7e3252c5b55971f1bd3/api
9 |
10 | VUE_APP_API_BASEURL = http://localhost:8080/api
11 | # 本地端口
12 | VUE_APP_PORT = 2000
13 |
14 | # 是否开启代理
15 | VUE_APP_PROXY = true
16 |
--------------------------------------------------------------------------------
/web/scui/.env.production:
--------------------------------------------------------------------------------
1 | # 生产环境
2 | NODE_ENV = production
3 |
4 | # 标题
5 | VUE_APP_TITLE = GS Admin
6 |
7 | # 接口地址
8 | VUE_APP_API_BASEURL = /api
9 |
--------------------------------------------------------------------------------
/web/scui/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | /package-lock.json
25 |
--------------------------------------------------------------------------------
/web/scui/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 sakuya
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/web/scui/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
SCUI Admin
19 |
20 |
21 |
22 | ## 介绍
23 | SCUI 是一个中后台前端解决方案,基于VUE3和elementPlus实现。
24 | 使用最新的前端技术栈,提供各类实用的组件方便在业务开发时的调用,并且持续性的提供丰富的业务模板帮助你快速搭建企业级中后台前端任务。
25 |
26 | SCUI的宗旨是 让一切复杂的东西傻瓜化。
27 |
28 | 
29 |
30 | ## 演示和文档
31 |
32 | | 类型 | 链接 |
33 | | -------- | -------- |
34 | | 文档地址 | https://lolicode.gitee.io/scui-doc/ |
35 | | 演示地址 | https://lolicode.gitee.io/scui-doc/demo/#/login |
36 |
37 |
38 |
39 | ## 特点
40 |
41 | - **组件** 多个独家组件、业务模板
42 | - **权限** 完整的鉴权体系和高精度权限控制
43 | - **布局** 提供多套布局模式,满足各种视觉需求
44 | - **API** 完善的API管理,使用真实网络MOCK
45 | - **配置** 统一的全局配置和组件配置,支持build后配置热更新
46 | - **性能** 在减少带宽请求和前端算力上多次优化,并且持续着
47 | - **其他** 多功能视图标签、动态权限菜单、控制台组态化、统一异常处理等等
48 |
49 |
50 | ## 部分截图
51 |
52 | 
53 |
54 | ## 安装教程
55 | ``` sh
56 | # 克隆项目
57 | git clone https://gitee.com/lolicode/scui.git
58 |
59 | # 进入项目目录
60 | cd scui
61 |
62 | # 安装依赖
63 | npm i
64 |
65 | # 启动项目(开发模式)
66 | npm run serve
67 | ```
68 | 启动完成后浏览器访问 http://localhost:2800
69 |
70 | ## 鸣谢
71 |
72 |
73 |
74 | ## 支持
75 | 如果觉得本项目还不错或在工作中有所启发,请在Gitee(码云)帮开发者点亮星星,这是对开发者最大的支持和鼓励!
76 |
--------------------------------------------------------------------------------
/web/scui/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/web/scui/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": ["src/*"]
9 | },
10 | "lib": [
11 | "esnext",
12 | "dom",
13 | "dom.iterable",
14 | "scripthost"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/web/scui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scui",
3 | "version": "1.6.6",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build --report",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@element-plus/icons-vue": "2.0.6",
12 | "@tinymce/tinymce-vue": "5.0.0",
13 | "axios": "0.27.2",
14 | "codemirror": "5.65.5",
15 | "core-js": "3.24.1",
16 | "cropperjs": "1.5.12",
17 | "crypto-js": "4.1.1",
18 | "echarts": "5.3.3",
19 | "element-plus": "2.2.12",
20 | "nprogress": "0.2.0",
21 | "qrcodejs2": "0.0.2",
22 | "sortablejs": "^1.15.0",
23 | "tinymce": "6.1.2",
24 | "vue": "3.2.37",
25 | "vue-i18n": "9.2.2",
26 | "vue-router": "4.1.3",
27 | "vuedraggable": "4.0.3",
28 | "vuex": "4.0.2",
29 | "xgplayer": "2.31.7",
30 | "xgplayer-hls": "2.5.2"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "7.18.9",
34 | "@babel/eslint-parser": "7.18.9",
35 | "@vue/cli-plugin-babel": "5.0.8",
36 | "@vue/cli-plugin-eslint": "5.0.8",
37 | "@vue/cli-service": "5.0.8",
38 | "eslint": "8.21.0",
39 | "eslint-plugin-vue": "9.3.0",
40 | "sass": "1.37.5",
41 | "sass-loader": "10.1.1"
42 | },
43 | "eslintConfig": {
44 | "root": true,
45 | "env": {
46 | "node": true
47 | },
48 | "globals": {
49 | "APP_CONFIG": true
50 | },
51 | "extends": [
52 | "plugin:vue/vue3-essential",
53 | "eslint:recommended"
54 | ],
55 | "parserOptions": {
56 | "parser": "@babel/eslint-parser"
57 | },
58 | "rules": {
59 | "indent": 0,
60 | "no-tabs": 0,
61 | "no-mixed-spaces-and-tabs": 0,
62 | "vue/no-unused-components": 0,
63 | "vue/multi-word-component-names": 0
64 | }
65 | },
66 | "browserslist": [
67 | "> 1%",
68 | "last 2 versions",
69 | "not dead",
70 | "not ie 11"
71 | ]
72 | }
73 |
--------------------------------------------------------------------------------
/web/scui/public/code/list/save.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | <% column.forEach(function(item, index){ %>
13 |
14 |
15 |
16 | <% })%>
17 |
18 |
19 |
20 |
70 |
71 |
73 |
--------------------------------------------------------------------------------
/web/scui/public/config.js:
--------------------------------------------------------------------------------
1 |
2 | // 此文件非必要,在生产环境下此文件配置可覆盖运行配置,开发环境下不起效
3 | // 详情见 src/config/index.js
4 |
5 | const APP_CONFIG = {
6 | //标题
7 | //APP_NAME: "SCUI",
8 |
9 | //接口地址,如遇跨域需使用nginx代理
10 | //API_URL: "https://www.fastmock.site/mock/5039c4361c39a7e3252c5b55971f1bd3/api"
11 | }
12 |
--------------------------------------------------------------------------------
/web/scui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/favicon.ico
--------------------------------------------------------------------------------
/web/scui/public/img/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/404.png
--------------------------------------------------------------------------------
/web/scui/public/img/auth_banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/auth_banner.jpg
--------------------------------------------------------------------------------
/web/scui/public/img/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/avatar.jpg
--------------------------------------------------------------------------------
/web/scui/public/img/avatar2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/avatar2.gif
--------------------------------------------------------------------------------
/web/scui/public/img/avatar3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/avatar3.gif
--------------------------------------------------------------------------------
/web/scui/public/img/gslogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/gslogo.png
--------------------------------------------------------------------------------
/web/scui/public/img/logo-r.bak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/logo-r.bak.png
--------------------------------------------------------------------------------
/web/scui/public/img/logo-r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/logo-r.png
--------------------------------------------------------------------------------
/web/scui/public/img/logo.bak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/logo.bak.png
--------------------------------------------------------------------------------
/web/scui/public/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/logo.png
--------------------------------------------------------------------------------
/web/scui/public/img/tasks-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonhineboy/gsadmin/b411c9a29e8001ca2ed020a62fea044083584401/web/scui/public/img/tasks-example.png
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/dark/content.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #222f3e;
3 | color: #fff;
4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
5 | line-height: 1.4;
6 | margin: 1rem;
7 | }
8 | a {
9 | color: #4099ff;
10 | }
11 | table {
12 | border-collapse: collapse;
13 | }
14 | /* Apply a default padding if legacy cellpadding attribute is missing */
15 | table:not([cellpadding]) th,
16 | table:not([cellpadding]) td {
17 | padding: 0.4rem;
18 | }
19 | /* Set default table styles if a table has a positive border attribute
20 | and no inline css */
21 | table[border]:not([border="0"]):not([style*="border-width"]) th,
22 | table[border]:not([border="0"]):not([style*="border-width"]) td {
23 | border-width: 1px;
24 | }
25 | /* Set default table styles if a table has a positive border attribute
26 | and no inline css */
27 | table[border]:not([border="0"]):not([style*="border-style"]) th,
28 | table[border]:not([border="0"]):not([style*="border-style"]) td {
29 | border-style: solid;
30 | }
31 | /* Set default table styles if a table has a positive border attribute
32 | and no inline css */
33 | table[border]:not([border="0"]):not([style*="border-color"]) th,
34 | table[border]:not([border="0"]):not([style*="border-color"]) td {
35 | border-color: #6d737b;
36 | }
37 | figure {
38 | display: table;
39 | margin: 1rem auto;
40 | }
41 | figure figcaption {
42 | color: #8a8f97;
43 | display: block;
44 | margin-top: 0.25rem;
45 | text-align: center;
46 | }
47 | hr {
48 | border-color: #6d737b;
49 | border-style: solid;
50 | border-width: 1px 0 0 0;
51 | }
52 | code {
53 | background-color: #6d737b;
54 | border-radius: 3px;
55 | padding: 0.1rem 0.2rem;
56 | }
57 | .mce-content-body:not([dir=rtl]) blockquote {
58 | border-left: 2px solid #6d737b;
59 | margin-left: 1.5rem;
60 | padding-left: 1rem;
61 | }
62 | .mce-content-body[dir=rtl] blockquote {
63 | border-right: 2px solid #6d737b;
64 | margin-right: 1.5rem;
65 | padding-right: 1rem;
66 | }
67 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/dark/content.min.css:
--------------------------------------------------------------------------------
1 | body{background-color:#222f3e;color:#fff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/default/content.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
3 | line-height: 1.4;
4 | margin: 1rem;
5 | }
6 | table {
7 | border-collapse: collapse;
8 | }
9 | /* Apply a default padding if legacy cellpadding attribute is missing */
10 | table:not([cellpadding]) th,
11 | table:not([cellpadding]) td {
12 | padding: 0.4rem;
13 | }
14 | /* Set default table styles if a table has a positive border attribute
15 | and no inline css */
16 | table[border]:not([border="0"]):not([style*="border-width"]) th,
17 | table[border]:not([border="0"]):not([style*="border-width"]) td {
18 | border-width: 1px;
19 | }
20 | /* Set default table styles if a table has a positive border attribute
21 | and no inline css */
22 | table[border]:not([border="0"]):not([style*="border-style"]) th,
23 | table[border]:not([border="0"]):not([style*="border-style"]) td {
24 | border-style: solid;
25 | }
26 | /* Set default table styles if a table has a positive border attribute
27 | and no inline css */
28 | table[border]:not([border="0"]):not([style*="border-color"]) th,
29 | table[border]:not([border="0"]):not([style*="border-color"]) td {
30 | border-color: #ccc;
31 | }
32 | figure {
33 | display: table;
34 | margin: 1rem auto;
35 | }
36 | figure figcaption {
37 | color: #999;
38 | display: block;
39 | margin-top: 0.25rem;
40 | text-align: center;
41 | }
42 | hr {
43 | border-color: #ccc;
44 | border-style: solid;
45 | border-width: 1px 0 0 0;
46 | }
47 | code {
48 | background-color: #e8e8e8;
49 | border-radius: 3px;
50 | padding: 0.1rem 0.2rem;
51 | }
52 | .mce-content-body:not([dir=rtl]) blockquote {
53 | border-left: 2px solid #ccc;
54 | margin-left: 1.5rem;
55 | padding-left: 1rem;
56 | }
57 | .mce-content-body[dir=rtl] blockquote {
58 | border-right: 2px solid #ccc;
59 | margin-right: 1.5rem;
60 | padding-right: 1rem;
61 | }
62 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/default/content.min.css:
--------------------------------------------------------------------------------
1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/document/content.min.css:
--------------------------------------------------------------------------------
1 | @media screen{html{background:#f4f4f4;min-height:100%}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(100vh - 1rem);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/tinymce-5-dark/content.min.css:
--------------------------------------------------------------------------------
1 | body{background-color:#2f3742;color:#dfe0e4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/tinymce-5/content.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
3 | line-height: 1.4;
4 | margin: 1rem;
5 | }
6 | table {
7 | border-collapse: collapse;
8 | }
9 | /* Apply a default padding if legacy cellpadding attribute is missing */
10 | table:not([cellpadding]) th,
11 | table:not([cellpadding]) td {
12 | padding: 0.4rem;
13 | }
14 | /* Set default table styles if a table has a positive border attribute
15 | and no inline css */
16 | table[border]:not([border="0"]):not([style*="border-width"]) th,
17 | table[border]:not([border="0"]):not([style*="border-width"]) td {
18 | border-width: 1px;
19 | }
20 | /* Set default table styles if a table has a positive border attribute
21 | and no inline css */
22 | table[border]:not([border="0"]):not([style*="border-style"]) th,
23 | table[border]:not([border="0"]):not([style*="border-style"]) td {
24 | border-style: solid;
25 | }
26 | /* Set default table styles if a table has a positive border attribute
27 | and no inline css */
28 | table[border]:not([border="0"]):not([style*="border-color"]) th,
29 | table[border]:not([border="0"]):not([style*="border-color"]) td {
30 | border-color: #ccc;
31 | }
32 | figure {
33 | display: table;
34 | margin: 1rem auto;
35 | }
36 | figure figcaption {
37 | color: #999;
38 | display: block;
39 | margin-top: 0.25rem;
40 | text-align: center;
41 | }
42 | hr {
43 | border-color: #ccc;
44 | border-style: solid;
45 | border-width: 1px 0 0 0;
46 | }
47 | code {
48 | background-color: #e8e8e8;
49 | border-radius: 3px;
50 | padding: 0.1rem 0.2rem;
51 | }
52 | .mce-content-body:not([dir=rtl]) blockquote {
53 | border-left: 2px solid #ccc;
54 | margin-left: 1.5rem;
55 | padding-left: 1rem;
56 | }
57 | .mce-content-body[dir=rtl] blockquote {
58 | border-right: 2px solid #ccc;
59 | margin-right: 1.5rem;
60 | padding-right: 1rem;
61 | }
62 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/tinymce-5/content.min.css:
--------------------------------------------------------------------------------
1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/writer/content.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
3 | line-height: 1.4;
4 | margin: 1rem auto;
5 | max-width: 900px;
6 | }
7 | table {
8 | border-collapse: collapse;
9 | }
10 | /* Apply a default padding if legacy cellpadding attribute is missing */
11 | table:not([cellpadding]) th,
12 | table:not([cellpadding]) td {
13 | padding: 0.4rem;
14 | }
15 | /* Set default table styles if a table has a positive border attribute
16 | and no inline css */
17 | table[border]:not([border="0"]):not([style*="border-width"]) th,
18 | table[border]:not([border="0"]):not([style*="border-width"]) td {
19 | border-width: 1px;
20 | }
21 | /* Set default table styles if a table has a positive border attribute
22 | and no inline css */
23 | table[border]:not([border="0"]):not([style*="border-style"]) th,
24 | table[border]:not([border="0"]):not([style*="border-style"]) td {
25 | border-style: solid;
26 | }
27 | /* Set default table styles if a table has a positive border attribute
28 | and no inline css */
29 | table[border]:not([border="0"]):not([style*="border-color"]) th,
30 | table[border]:not([border="0"]):not([style*="border-color"]) td {
31 | border-color: #ccc;
32 | }
33 | figure {
34 | display: table;
35 | margin: 1rem auto;
36 | }
37 | figure figcaption {
38 | color: #999;
39 | display: block;
40 | margin-top: 0.25rem;
41 | text-align: center;
42 | }
43 | hr {
44 | border-color: #ccc;
45 | border-style: solid;
46 | border-width: 1px 0 0 0;
47 | }
48 | code {
49 | background-color: #e8e8e8;
50 | border-radius: 3px;
51 | padding: 0.1rem 0.2rem;
52 | }
53 | .mce-content-body:not([dir=rtl]) blockquote {
54 | border-left: 2px solid #ccc;
55 | margin-left: 1.5rem;
56 | padding-left: 1rem;
57 | }
58 | .mce-content-body[dir=rtl] blockquote {
59 | border-right: 2px solid #ccc;
60 | margin-right: 1.5rem;
61 | padding-right: 1rem;
62 | }
63 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/content/writer/content.min.css:
--------------------------------------------------------------------------------
1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/oxide-dark/skin.shadowdom.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll {
2 | overflow: hidden;
3 | }
4 | .tox-fullscreen {
5 | border: 0;
6 | height: 100%;
7 | margin: 0;
8 | overflow: hidden;
9 | overscroll-behavior: none;
10 | padding: 0;
11 | touch-action: pinch-zoom;
12 | width: 100%;
13 | }
14 | .tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
15 | display: none;
16 | }
17 | .tox.tox-tinymce.tox-fullscreen,
18 | .tox-shadowhost.tox-fullscreen {
19 | left: 0;
20 | position: fixed;
21 | top: 0;
22 | z-index: 1200;
23 | }
24 | .tox.tox-tinymce.tox-fullscreen {
25 | background-color: transparent;
26 | }
27 | .tox-fullscreen .tox.tox-tinymce-aux,
28 | .tox-fullscreen ~ .tox.tox-tinymce-aux {
29 | z-index: 1201;
30 | }
31 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/oxide/skin.shadowdom.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll {
2 | overflow: hidden;
3 | }
4 | .tox-fullscreen {
5 | border: 0;
6 | height: 100%;
7 | margin: 0;
8 | overflow: hidden;
9 | overscroll-behavior: none;
10 | padding: 0;
11 | touch-action: pinch-zoom;
12 | width: 100%;
13 | }
14 | .tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
15 | display: none;
16 | }
17 | .tox.tox-tinymce.tox-fullscreen,
18 | .tox-shadowhost.tox-fullscreen {
19 | left: 0;
20 | position: fixed;
21 | top: 0;
22 | z-index: 1200;
23 | }
24 | .tox.tox-tinymce.tox-fullscreen {
25 | background-color: transparent;
26 | }
27 | .tox-fullscreen .tox.tox-tinymce-aux,
28 | .tox-fullscreen ~ .tox.tox-tinymce-aux {
29 | z-index: 1201;
30 | }
31 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/oxide/skin.shadowdom.min.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll {
2 | overflow: hidden;
3 | }
4 | .tox-fullscreen {
5 | border: 0;
6 | height: 100%;
7 | margin: 0;
8 | overflow: hidden;
9 | overscroll-behavior: none;
10 | padding: 0;
11 | touch-action: pinch-zoom;
12 | width: 100%;
13 | }
14 | .tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
15 | display: none;
16 | }
17 | .tox.tox-tinymce.tox-fullscreen,
18 | .tox-shadowhost.tox-fullscreen {
19 | left: 0;
20 | position: fixed;
21 | top: 0;
22 | z-index: 1200;
23 | }
24 | .tox.tox-tinymce.tox-fullscreen {
25 | background-color: transparent;
26 | }
27 | .tox-fullscreen .tox.tox-tinymce-aux,
28 | .tox-fullscreen ~ .tox.tox-tinymce-aux {
29 | z-index: 1201;
30 | }
31 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
2 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/tinymce-5/skin.shadowdom.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll {
2 | overflow: hidden;
3 | }
4 | .tox-fullscreen {
5 | border: 0;
6 | height: 100%;
7 | margin: 0;
8 | overflow: hidden;
9 | overscroll-behavior: none;
10 | padding: 0;
11 | touch-action: pinch-zoom;
12 | width: 100%;
13 | }
14 | .tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
15 | display: none;
16 | }
17 | .tox.tox-tinymce.tox-fullscreen,
18 | .tox-shadowhost.tox-fullscreen {
19 | left: 0;
20 | position: fixed;
21 | top: 0;
22 | z-index: 1200;
23 | }
24 | .tox.tox-tinymce.tox-fullscreen {
25 | background-color: transparent;
26 | }
27 | .tox-fullscreen .tox.tox-tinymce-aux,
28 | .tox-fullscreen ~ .tox.tox-tinymce-aux {
29 | z-index: 1201;
30 | }
31 |
--------------------------------------------------------------------------------
/web/scui/public/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.css:
--------------------------------------------------------------------------------
1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
2 |
--------------------------------------------------------------------------------
/web/scui/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
43 |
44 |
47 |
--------------------------------------------------------------------------------
/web/scui/src/api/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 自动import导入所有 api 模块
3 | */
4 |
5 | const files = require.context('./model', false, /\.js$/)
6 | const modules = {}
7 | files.keys().forEach((key) => {
8 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
9 | })
10 |
11 | export default modules
12 |
--------------------------------------------------------------------------------
/web/scui/src/api/model/auth.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | token: {
6 | url: `${config.API_URL}/user/login`,
7 | name: "登录获取TOKEN",
8 | post: async function(data={}){
9 | return await http.post(this.url, data);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/web/scui/src/api/model/common.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 |
6 | captchaInfo:{
7 | url: `${config.API_URL}/common/captcha/info`,
8 | name: "文件上传",
9 | get: async function(){
10 | return await http.get(this.url);
11 | }
12 | },
13 | upload: {
14 | url: `${config.API_URL}/system/common/upload`,
15 | name: "文件上传",
16 | post: async function(data, config={}){
17 | return await http.post(this.url, data, config);
18 | }
19 | },
20 | uploadFile: {
21 | url: `${config.API_URL}/uploadFile`,
22 | name: "附件上传",
23 | post: async function(data, config={}){
24 | return await http.post(this.url, data, config);
25 | }
26 | },
27 | exportFile: {
28 | url: `${config.API_URL}/fileExport`,
29 | name: "导出附件",
30 | get: async function(data, config={}){
31 | return await http.get(this.url, data, config);
32 | }
33 | },
34 | importFile: {
35 | url: `${config.API_URL}/fileImport`,
36 | name: "导入附件",
37 | post: async function(data, config={}){
38 | return await http.post(this.url, data, config);
39 | }
40 | },
41 | file: {
42 | menu: {
43 | url: `${config.API_URL}/file/menu`,
44 | name: "获取文件分类",
45 | get: async function(){
46 | return await http.get(this.url);
47 | }
48 | },
49 | list: {
50 | url: `${config.API_URL}/file/list`,
51 | name: "获取文件列表",
52 | get: async function(params){
53 | return await http.get(this.url, params);
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/web/scui/src/api/model/demo.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | ver: {
6 | url: `${config.API_URL}/common/version`,
7 | name: "获取最新版本号",
8 | get: async function(params){
9 | return await http.get(this.url, params);
10 | }
11 | },
12 | post: {
13 | url: `${config.API_URL}/demo/post`,
14 | name: "分页列表",
15 | post: async function(data){
16 | return await http.post(this.url, data, {
17 | headers: {
18 | //'response-status': 401
19 | }
20 | });
21 | }
22 | },
23 | page: {
24 | url: `${config.API_URL}/demo/page`,
25 | name: "分页列表",
26 | get: async function(params){
27 | return await http.get(this.url, params);
28 | }
29 | },
30 | list: {
31 | url: `${config.API_URL}/demo/list`,
32 | name: "数据列表",
33 | get: async function(params){
34 | return await http.get(this.url, params);
35 | }
36 | },
37 | menu: {
38 | url: `${config.API_URL}/demo/menu`,
39 | name: "普通用户菜单",
40 | get: async function(){
41 | return await http.get(this.url);
42 | }
43 | },
44 | status: {
45 | url: `${config.API_URL}/demo/status`,
46 | name: "模拟无权限",
47 | get: async function(code){
48 | return await http.get(this.url, {}, {
49 | headers: {
50 | "response-status": code
51 | }
52 | });
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/web/scui/src/api/model/demo2.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | index: {
6 | url: `${config.API_URL}/demo2/index`,
7 | name: "列表",
8 | get: async function (params) {
9 | return await http.get(this.url, params)
10 | }
11 | },
12 | get: {
13 | url: `${config.API_URL}/demo2/`,
14 | name: "单条信息",
15 | get: async function (id) {
16 | return await http.get(this.url + id)
17 | }
18 | },
19 | save: {
20 | url: `${config.API_URL}/demo2/save`,
21 | name: "添加信息",
22 | post: async function (params) {
23 | return http.post(this.url, params)
24 | },
25 | },
26 | edit: {
27 | url: `${config.API_URL}/demo2/edit/`,
28 | name: "编辑信息",
29 | post: async function (id, params) {
30 | return http.post(this.url + id, params)
31 | },
32 | },
33 | delete: {
34 | url: `${config.API_URL}/demo2/delete`,
35 | name: "删除信息",
36 | post: async function (params) {
37 | return http.post(this.url, params)
38 | },
39 | }
40 | }
--------------------------------------------------------------------------------
/web/scui/src/api/model/gen.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 |
6 | tables: {
7 | url: `${config.API_URL}/gen/tables`,
8 | name: "获取表名",
9 | get: async function (params) {
10 | return await http.get(this.url, params);
11 | }
12 | },
13 | genField: {
14 | url: `${config.API_URL}/gen/fields`,
15 | name: "生成字段",
16 | get: async function (params) {
17 | return await http.get(this.url, params)
18 | }
19 | },
20 | genCode: {
21 | url: `${config.API_URL}/gen/genCode`,
22 | name: "生成代码",
23 | post: async function (params) {
24 | return await http.post(this.url, params)
25 | }
26 | }
27 |
28 |
29 | }
--------------------------------------------------------------------------------
/web/scui/src/api/model/news.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | index: {
6 | url: `${config.API_URL}/news/index`,
7 | name: "列表",
8 | get: async function (params) {
9 | return await http.get(this.url, params)
10 | }
11 | },
12 | get: {
13 | url: `${config.API_URL}/news/`,
14 | name: "单条信息",
15 | get: async function (id) {
16 | return await http.get(this.url + id)
17 | }
18 | },
19 | save: {
20 | url: `${config.API_URL}/news/save`,
21 | name: "添加信息",
22 | post: async function (params) {
23 | return http.post(this.url, params)
24 | },
25 | },
26 | edit: {
27 | url: `${config.API_URL}/news/edit/`,
28 | name: "编辑信息",
29 | post: async function (id, params) {
30 | return http.post(this.url + id, params)
31 | },
32 | },
33 | delete: {
34 | url: `${config.API_URL}/news/delete`,
35 | name: "删除信息",
36 | post: async function (params) {
37 | return http.post(this.url, params)
38 | },
39 | }
40 | }
--------------------------------------------------------------------------------
/web/scui/src/api/model/user.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | user: {
6 | add: {
7 | url: `${config.API_URL}/user/add`,
8 | name: "添加用户",
9 | post: async function(data){
10 | return await http.post(this.url,data);
11 | }
12 | },
13 | up:{
14 | url: `${config.API_URL}/user/update`,
15 | name: "更新信息",
16 | post: async function(data){
17 | return await http.post(this.url,data);
18 | }
19 | },
20 | del:{
21 | url: `${config.API_URL}/user/del`,
22 | name: "删除用户",
23 | post: async function(data){
24 | return await http.post(this.url,data);
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/web/scui/src/api/model/userMember.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | index: {
6 | url: `${config.API_URL}/userMember/index`,
7 | name: "列表",
8 | get: async function (params) {
9 | return await http.get(this.url, params)
10 | }
11 | },
12 | get: {
13 | url: `${config.API_URL}/userMember/`,
14 | name: "单条信息",
15 | get: async function (id) {
16 | return await http.get(this.url + id)
17 | }
18 | },
19 | save: {
20 | url: `${config.API_URL}/userMember/save`,
21 | name: "添加信息",
22 | post: async function (params) {
23 | return http.post(this.url, params)
24 | },
25 | },
26 | edit: {
27 | url: `${config.API_URL}/userMember/edit/`,
28 | name: "编辑信息",
29 | post: async function (id, params) {
30 | return http.post(this.url + id, params)
31 | },
32 | },
33 | delete: {
34 | url: `${config.API_URL}/userMember/delete`,
35 | name: "删除信息",
36 | post: async function (params) {
37 | return http.post(this.url, params)
38 | },
39 | }
40 | }
--------------------------------------------------------------------------------
/web/scui/src/api/model/userMember.js.bak:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 | import http from "@/utils/request"
3 |
4 | export default {
5 | index: {
6 | url: `${config.API_URL}/userMember/index`,
7 | name: "列表",
8 | get: async function (params) {
9 | return await http.get(this.url, params)
10 | }
11 | },
12 | get: {
13 | url: `${config.API_URL}/userMember/`,
14 | name: "单条信息",
15 | get: async function (id) {
16 | return await http.get(this.url + id)
17 | }
18 | },
19 | save: {
20 | url: `${config.API_URL}/userMember/save`,
21 | name: "添加信息",
22 | post: async function (params) {
23 | return http.post(this.url, params)
24 | },
25 | },
26 | edit: {
27 | url: `${config.API_URL}/userMember/edit/`,
28 | name: "编辑信息",
29 | post: async function (id, params) {
30 | return http.post(this.url + id, params)
31 | },
32 | },
33 | delete: {
34 | url: `${config.API_URL}/userMember/delete`,
35 | name: "删除信息",
36 | post: async function (params) {
37 | return http.post(this.url, params)
38 | },
39 | }
40 | }
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/BugFill.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/BugLine.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/Code.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/Download.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/FileExcel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/FilePpt.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/FileWord.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/Organization.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/Upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/Vue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/assets/icons/index.js:
--------------------------------------------------------------------------------
1 | export { default as Vue } from './Vue.vue'
2 | export { default as Code } from './Code.vue'
3 | export { default as Wechat } from './Wechat.vue'
4 | export { default as BugFill } from './BugFill.vue'
5 | export { default as BugLine } from './BugLine.vue'
6 | export { default as FileWord } from './FileWord.vue'
7 | export { default as FileExcel } from './FileExcel.vue'
8 | export { default as FilePpt } from './FilePpt.vue'
9 | export { default as Organization } from './Organization.vue'
10 | export { default as Upload } from './Upload.vue'
11 | export { default as Download } from './Download.vue'
--------------------------------------------------------------------------------
/web/scui/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
5 |
6 |
7 |
15 |
16 |
17 |
33 |
--------------------------------------------------------------------------------
/web/scui/src/components/scEcharts/echarts-theme-T.js:
--------------------------------------------------------------------------------
1 | const T = {
2 | "color": [
3 | "#409EFF",
4 | "#36CE9E",
5 | "#f56e6a",
6 | "#626c91",
7 | "#edb00d",
8 | "#909399"
9 | ],
10 | 'grid': {
11 | 'left': '3%',
12 | 'right': '3%',
13 | 'bottom': '10',
14 | 'top': '40',
15 | 'containLabel': true
16 | },
17 | "legend": {
18 | "textStyle": {
19 | "color": "#999"
20 | },
21 | "inactiveColor": "rgba(128,128,128,0.4)"
22 | },
23 | "categoryAxis": {
24 | "axisLine": {
25 | "show": true,
26 | "lineStyle": {
27 | "color": "rgba(128,128,128,0.2)",
28 | "width": 1
29 | }
30 | },
31 | "axisTick": {
32 | "show": false,
33 | "lineStyle": {
34 | "color": "#333"
35 | }
36 | },
37 | "axisLabel": {
38 | "color": "#999"
39 | },
40 | "splitLine": {
41 | "show": false,
42 | "lineStyle": {
43 | "color": [
44 | "#eee"
45 | ]
46 | }
47 | },
48 | "splitArea": {
49 | "show": false,
50 | "areaStyle": {
51 | "color": [
52 | "rgba(255,255,255,0.01)",
53 | "rgba(0,0,0,0.01)"
54 | ]
55 | }
56 | }
57 | },
58 | "valueAxis": {
59 | "axisLine": {
60 | "show": false,
61 | "lineStyle": {
62 | "color": "#999"
63 | }
64 | },
65 | "splitLine": {
66 | "show": true,
67 | "lineStyle": {
68 | "color": "rgba(128,128,128,0.2)"
69 | }
70 | }
71 | }
72 | }
73 |
74 | export default T
75 |
--------------------------------------------------------------------------------
/web/scui/src/components/scEcharts/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
65 |
--------------------------------------------------------------------------------
/web/scui/src/components/scFileExport/column.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ scope.row.label }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
54 |
--------------------------------------------------------------------------------
/web/scui/src/components/scFilterBar/pySelect.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/web/scui/src/components/scForm/items/tableselect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
35 |
36 |
38 |
--------------------------------------------------------------------------------
/web/scui/src/components/scMini/scStatusIndicator.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
48 |
--------------------------------------------------------------------------------
/web/scui/src/components/scMini/scTrend.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{prefix}}
16 | {{modelValue}}
17 | {{suffix}}
18 |
19 |
20 |
21 |
56 |
57 |
67 |
--------------------------------------------------------------------------------
/web/scui/src/components/scPageHeader/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
29 |
30 |
31 |
40 |
41 |
53 |
--------------------------------------------------------------------------------
/web/scui/src/components/scTable/column.js:
--------------------------------------------------------------------------------
1 | import { h, resolveComponent } from 'vue'
2 |
3 | export default {
4 | render() {
5 | return h (
6 | resolveComponent("el-table-column"),
7 | {
8 | index: this.index,
9 | ...this.$attrs
10 | },
11 | this.$slots
12 | )
13 | },
14 | methods: {
15 | index(index){
16 | if(this.$attrs.type=="index"){
17 | let page = this.$parent.$parent.currentPage
18 | let pageSize = this.$parent.$parent.pageSize
19 | return (page - 1) * pageSize + index + 1
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/web/scui/src/components/scTitle/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
7 |
22 |
23 |
26 |
--------------------------------------------------------------------------------
/web/scui/src/components/scWaterMark/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
63 |
64 |
67 |
--------------------------------------------------------------------------------
/web/scui/src/components/scWorkflow/nodeWrap.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
55 |
56 |
58 |
--------------------------------------------------------------------------------
/web/scui/src/config/fileSelect.js:
--------------------------------------------------------------------------------
1 | import API from "@/api";
2 |
3 | //文件选择器配置
4 |
5 | export default {
6 | apiObj: API.common.upload,
7 | menuApiObj: API.common.file.menu,
8 | listApiObj: API.common.file.list,
9 | successCode: 200,
10 | maxSize: 30,
11 | max: 99,
12 | uploadParseData: function (res) {
13 | return {
14 | id: res.data.id,
15 | fileName: res.data.fileName,
16 | url: res.data.src
17 | }
18 | },
19 | listParseData: function (res) {
20 | return {
21 | rows: res.data.rows,
22 | total: res.data.total,
23 | msg: res.message,
24 | code: res.code
25 | }
26 | },
27 | request: {
28 | page: 'page',
29 | pageSize: 'pageSize',
30 | keyword: 'keyword',
31 | menuKey: 'groupId'
32 | },
33 | menuProps: {
34 | key: 'id',
35 | label: 'label',
36 | children: 'children'
37 | },
38 | fileProps: {
39 | key: 'id',
40 | fileName: 'fileName',
41 | url: 'url'
42 | },
43 | files: {
44 | doc: {
45 | icon: 'sc-icon-file-word-2-fill',
46 | color: '#409eff'
47 | },
48 | docx: {
49 | icon: 'sc-icon-file-word-2-fill',
50 | color: '#409eff'
51 | },
52 | xls: {
53 | icon: 'sc-icon-file-excel-2-fill',
54 | color: '#67C23A'
55 | },
56 | xlsx: {
57 | icon: 'sc-icon-file-excel-2-fill',
58 | color: '#67C23A'
59 | },
60 | ppt: {
61 | icon: 'sc-icon-file-ppt-2-fill',
62 | color: '#F56C6C'
63 | },
64 | pptx: {
65 | icon: 'sc-icon-file-ppt-2-fill',
66 | color: '#F56C6C'
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/web/scui/src/config/filterBar.js:
--------------------------------------------------------------------------------
1 | export default {
2 | //运算符
3 | operator: [
4 | {
5 | label: '等于',
6 | value: '=',
7 | },
8 | {
9 | label: '不等于',
10 | value: '!=',
11 | },
12 | {
13 | label: '大于',
14 | value: '>',
15 | },
16 | {
17 | label: '大于等于',
18 | value: '>=',
19 | },
20 | {
21 | label: '小于',
22 | value: '<',
23 | },
24 | {
25 | label: '小于等于',
26 | value: '<=',
27 | },
28 | {
29 | label: '包含',
30 | value: 'include',
31 | },
32 | {
33 | label: '不包含',
34 | value: 'notinclude',
35 | }
36 | ],
37 | //过滤结果运算符的分隔符
38 | separator: '|',
39 | //获取我的常用
40 | getMy: function (name) {
41 | return new Promise((resolve) => {
42 | console.log(`这里可以根据${name}参数请求接口`)
43 | var list = []
44 | setTimeout(()=>{
45 | resolve(list)
46 | },500)
47 | })
48 | },
49 | /**
50 | * 常用保存处理 返回resolve后继续操作
51 | * @name scFilterBar组件的props->filterName
52 | * @obj 过滤项整理好的对象
53 | */
54 | saveMy: function (name, obj) {
55 | return new Promise((resolve) => {
56 | console.log(name, obj)
57 | setTimeout(()=>{
58 | resolve(true)
59 | },500)
60 | })
61 | },
62 | /**
63 | * 常用删除处理 返回resolve后继续操作
64 | * @name scFilterBar组件的props->filterName
65 | */
66 | delMy: function (name) {
67 | return new Promise((resolve) => {
68 | console.log(name)
69 | setTimeout(()=>{
70 | resolve(true)
71 | },500)
72 | })
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/web/scui/src/config/index.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_CONFIG = {
2 | //标题
3 | APP_NAME: process.env.VUE_APP_TITLE,
4 |
5 | //首页地址
6 | DASHBOARD_URL: "/dashboard",
7 |
8 | //版本号
9 | APP_VER: "1.5.2.1",
10 |
11 | //内核版本号
12 | CORE_VER: "1.5.2.1",
13 |
14 | //接口地址
15 | API_URL: process.env.NODE_ENV === 'development' && process.env.VUE_APP_PROXY === 'true' ? "/api" : process.env.VUE_APP_API_BASEURL,
16 |
17 | //请求超时
18 | TIMEOUT: 10000,
19 |
20 | //TokenName
21 | TOKEN_NAME: "Authorization",
22 |
23 | //Token前缀,注意最后有个空格,如不需要需设置空字符串
24 | TOKEN_PREFIX: "Bearer ",
25 |
26 | //追加其他头
27 | HEADERS: {},
28 |
29 | //请求是否开启缓存
30 | REQUEST_CACHE: false,
31 |
32 | //布局 默认:default | 通栏:header | 经典:menu | 功能坞:dock
33 | //dock将关闭标签和面包屑栏
34 | LAYOUT: 'default',
35 |
36 | //菜单是否折叠
37 | MENU_IS_COLLAPSE: false,
38 |
39 | //菜单是否启用手风琴效果
40 | MENU_UNIQUE_OPENED: false,
41 |
42 | //是否开启多标签
43 | LAYOUT_TAGS: true,
44 |
45 | //语言
46 | LANG: 'zh-cn',
47 |
48 | //主题颜色
49 | COLOR: '',
50 |
51 | //是否加密localStorage, 为空不加密,可填写AES(模式ECB,移位Pkcs7)加密
52 | LS_ENCRYPTION: '',
53 |
54 | //localStorageAES加密秘钥,位数建议填写8的倍数
55 | LS_ENCRYPTION_key: '2XNN4K8LC0ELVWN4',
56 |
57 | //控制台首页默认布局
58 | DEFAULT_GRID: {
59 | //默认分栏数量和宽度 例如 [24] [18,6] [8,8,8] [6,12,6]
60 | layout: [12, 6, 6],
61 | //小组件分布,com取值:views/home/components 文件名
62 | copmsList: [
63 | ['welcome'],
64 | ['about', 'ver'],
65 | ['time', 'progress']
66 | ]
67 | }
68 | }
69 |
70 | //合并业务配置
71 | import MY_CONFIG from "./myConfig"
72 | Object.assign(DEFAULT_CONFIG, MY_CONFIG)
73 |
74 | // 如果生产模式,就合并动态的APP_CONFIG
75 | // public/config.js
76 | if (process.env.NODE_ENV === 'production') {
77 | Object.assign(DEFAULT_CONFIG, APP_CONFIG)
78 | }
79 |
80 | export default DEFAULT_CONFIG
81 |
--------------------------------------------------------------------------------
/web/scui/src/config/myConfig.js:
--------------------------------------------------------------------------------
1 | //业务配置
2 | //会合并至this.$CONFIG
3 | //生产模式 public/config.js 同名key会覆盖这里的配置从而实现打包后的热更新
4 | //为避免和SCUI框架配置混淆建议添加前缀 MY_
5 | //全局可使用 this.$CONFIG.MY_KEY 访问
6 |
7 | export default {
8 | //是否显示第三方授权登录
9 | MY_SHOW_LOGIN_OAUTH: false
10 | }
11 |
--------------------------------------------------------------------------------
/web/scui/src/config/route.js:
--------------------------------------------------------------------------------
1 | // 静态路由配置
2 | // 书写格式与动态路由格式一致,全部经由框架统一转换
3 | // 比较动态路由在meta中多加入了role角色权限,为数组类型。一个菜单是否有权限显示,取决于它以及后代菜单是否有权限。
4 | // routes 显示在左侧菜单中的路由(显示顺序在动态路由之前)
5 | // 示例如下
6 |
7 | // const routes = [
8 | // {
9 | // name: "demo",
10 | // path: "/demo",
11 | // meta: {
12 | // icon: "el-icon-eleme-filled",
13 | // title: "演示",
14 | // role: ["SA"]
15 | // },
16 | // children: [{
17 | // name: "demopage",
18 | // path: "/demopage",
19 | // component: "test/autocode/index",
20 | // meta: {
21 | // icon: "el-icon-menu",
22 | // title: "演示页面",
23 | // role: ["SA"]
24 | // }
25 | // }]
26 | // }
27 | // ]
28 |
29 | const routes = []
30 |
31 | export default routes;
32 |
--------------------------------------------------------------------------------
/web/scui/src/config/select.js:
--------------------------------------------------------------------------------
1 | import API from "@/api";
2 |
3 | //字典选择器配置
4 |
5 | export default {
6 | dicApiObj: API.system.dic.get, //获取字典接口对象
7 | parseData: function (res) {
8 | return {
9 | data: res.data, //分析行数据字段结构
10 | msg: res.message, //分析描述字段结构
11 | code: res.code //分析状态字段结构
12 | }
13 | },
14 | request: {
15 | name: 'name' //规定搜索字段
16 | },
17 | props: {
18 | label: 'label', //映射label显示字段
19 | value: 'value', //映射value值字段
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/web/scui/src/config/table.js:
--------------------------------------------------------------------------------
1 | //数据表格配置
2 |
3 | import tool from '@/utils/tool'
4 |
5 | export default {
6 | successCode: 200, //请求完成代码
7 | pageSize: 20, //表格每一页条数
8 | pageSizes: [10, 20, 30, 40, 50], //表格可设置的一页条数
9 | paginationLayout: "total, sizes, prev, pager, next, jumper", //表格分页布局,可设置"total, sizes, prev, pager, next, jumper"
10 | parseData: function (res) { //数据分析
11 | return {
12 | data: res.data, //分析无分页的数据字段结构
13 | rows: res.data.rows, //分析行数据字段结构
14 | total: res.data.total, //分析总数字段结构
15 | summary: res.data.summary, //分析合计行字段结构
16 | msg: res.message, //分析描述字段结构
17 | code: res.code //分析状态字段结构
18 | }
19 | },
20 | request: { //请求规定字段
21 | page: 'page', //规定当前分页字段
22 | pageSize: 'pageSize', //规定一页条数字段
23 | prop: 'prop', //规定排序字段名字段
24 | order: 'order' //规定排序规格字段
25 | },
26 | /**
27 | * 自定义列保存处理
28 | * @tableName scTable组件的props->tableName
29 | * @column 用户配置好的列
30 | */
31 | columnSettingSave: function (tableName, column) {
32 | return new Promise((resolve) => {
33 | setTimeout(()=>{
34 | //这里为了演示使用了session和setTimeout演示,开发时应用数据请求
35 | tool.session.set(tableName, column)
36 | resolve(true)
37 | },1000)
38 | })
39 | },
40 | /**
41 | * 获取自定义列
42 | * @tableName scTable组件的props->tableName
43 | * @column 组件接受到的props->column
44 | */
45 | columnSettingGet: function (tableName, column) {
46 | return new Promise((resolve) => {
47 | //这里为了演示使用了session和setTimeout演示,开发时应用数据请求
48 | const userColumn = tool.session.get(tableName)
49 | if(userColumn){
50 | resolve(userColumn)
51 | }else{
52 | resolve(column)
53 | }
54 | })
55 | },
56 | /**
57 | * 重置自定义列
58 | * @tableName scTable组件的props->tableName
59 | * @column 组件接受到的props->column
60 | */
61 | columnSettingReset: function (tableName, column) {
62 | return new Promise((resolve) => {
63 | //这里为了演示使用了session和setTimeout演示,开发时应用数据请求
64 | setTimeout(()=>{
65 | tool.session.remove(tableName)
66 | resolve(column)
67 | },1000)
68 | })
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/web/scui/src/config/tableSelect.js:
--------------------------------------------------------------------------------
1 | //表格选择器配置
2 |
3 | export default {
4 | pageSize: 20, //表格每一页条数
5 | parseData: function (res) {
6 | return {
7 | data: res.data,
8 | rows: res.data.rows, //分析行数据字段结构
9 | total: res.data.total, //分析总数字段结构
10 | msg: res.message, //分析描述字段结构
11 | code: res.code //分析状态字段结构
12 | }
13 | },
14 | request: {
15 | page: 'page', //规定当前分页字段
16 | pageSize: 'pageSize', //规定一页条数字段
17 | keyword: 'keyword' //规定搜索字段
18 | },
19 | props: {
20 | label: 'label', //映射label显示字段
21 | value: 'value', //映射value值字段
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/web/scui/src/config/upload.js:
--------------------------------------------------------------------------------
1 | import API from "@/api";
2 |
3 | //上传配置
4 |
5 | export default {
6 | apiObj: API.common.upload, //上传请求API对象
7 | filename: "file", //form请求时文件的key
8 | successCode: 200, //请求完成代码
9 | maxSize: 10, //最大文件大小 默认10MB
10 | parseData: function (res) {
11 | return {
12 | code: res.code, //分析状态字段结构
13 | fileName: res.data.fileName,//分析文件名称
14 | src: res.data.src, //分析图片远程地址结构
15 | msg: res.message //分析描述字段结构
16 | }
17 | },
18 | apiObjFile: API.common.uploadFile, //附件上传请求API对象
19 | maxSizeFile: 10 //最大文件大小 默认10MB
20 | }
21 |
--------------------------------------------------------------------------------
/web/scui/src/config/workflow.js:
--------------------------------------------------------------------------------
1 | import API from "@/api";
2 |
3 | //审批工作流人员/组织选择器配置
4 |
5 | export default {
6 | //配置接口正常返回代码
7 | successCode: 200,
8 | //配置组织
9 | group: {
10 | //请求接口对象
11 | apiObj: API.system.dept.list,
12 | //接受数据字段映射
13 | parseData: function (res) {
14 | return {
15 | rows: res.data,
16 | msg: res.message,
17 | code: res.code
18 | }
19 | },
20 | //显示数据字段映射
21 | props: {
22 | key: 'id',
23 | label: 'label',
24 | children: 'children'
25 | }
26 | },
27 | //配置用户
28 | user: {
29 | apiObj: API.demo.page,
30 | pageSize: 20,
31 | parseData: function (res) {
32 | return {
33 | rows: res.data.rows,
34 | total: res.data.total,
35 | msg: res.message,
36 | code: res.code
37 | }
38 | },
39 | props: {
40 | key: 'id',
41 | label: 'user',
42 | },
43 | request: {
44 | page: 'page',
45 | pageSize: 'pageSize',
46 | groupId: 'groupId',
47 | keyword: 'keyword'
48 | }
49 | },
50 | //配置角色
51 | role: {
52 | //请求接口对象
53 | apiObj: API.system.dept.list,
54 | //接受数据字段映射
55 | parseData: function (res) {
56 | return {
57 | rows: res.data,
58 | msg: res.message,
59 | code: res.code
60 | }
61 | },
62 | //显示数据字段映射
63 | props: {
64 | key: 'id',
65 | label: 'label',
66 | children: 'children'
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/web/scui/src/directives/auth.js:
--------------------------------------------------------------------------------
1 | import { permission } from '@/utils/permission'
2 |
3 | export default {
4 | mounted(el, binding) {
5 | const { value } = binding
6 | if(Array.isArray(value)){
7 | let ishas = false;
8 | value.forEach(item => {
9 | if(permission(item)){
10 | ishas = true;
11 | }
12 | })
13 | if (!ishas){
14 | el.parentNode.removeChild(el)
15 | }
16 | }else{
17 | if(!permission(value)){
18 | el.parentNode.removeChild(el);
19 | }
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/web/scui/src/directives/copy.js:
--------------------------------------------------------------------------------
1 | import { ElMessage } from 'element-plus'
2 |
3 | export default {
4 | mounted(el, binding) {
5 | el.$value = binding.value
6 | el.handler = () => {
7 | const textarea = document.createElement('textarea')
8 | textarea.readOnly = 'readonly'
9 | textarea.style.position = 'absolute'
10 | textarea.style.left = '-9999px'
11 | textarea.value = el.$value
12 | document.body.appendChild(textarea)
13 | textarea.select()
14 | textarea.setSelectionRange(0, textarea.value.length)
15 | const result = document.execCommand('Copy')
16 | if (result) {
17 | ElMessage.success("复制成功")
18 | }
19 | document.body.removeChild(textarea)
20 | }
21 | el.addEventListener('click', el.handler)
22 | },
23 | updated(el, binding){
24 | el.$value = binding.value
25 | },
26 | unmounted(el){
27 | el.removeEventListener('click', el.handler)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/web/scui/src/directives/role.js:
--------------------------------------------------------------------------------
1 | import { rolePermission } from '@/utils/permission'
2 |
3 | export default {
4 | mounted(el, binding) {
5 | const { value } = binding
6 | if(Array.isArray(value)){
7 | let ishas = false;
8 | value.forEach(item => {
9 | if(rolePermission(item)){
10 | ishas = true;
11 | }
12 | })
13 | if (!ishas){
14 | el.parentNode.removeChild(el)
15 | }
16 | }else{
17 | if(!rolePermission(value)){
18 | el.parentNode.removeChild(el);
19 | }
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/web/scui/src/layout/components/NavMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {}'>
8 |
9 |
10 | {{navMenu.meta.title}}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{navMenu.meta.title}}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
39 |
--------------------------------------------------------------------------------
/web/scui/src/layout/components/iframeView.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
62 |
63 |
67 |
--------------------------------------------------------------------------------
/web/scui/src/layout/components/topbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{item.meta.title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
42 |
43 |
50 |
--------------------------------------------------------------------------------
/web/scui/src/layout/other/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
无权限或找不到页面
8 |
当前页面无权限访问或者打开了一个不存在的链接,请检查当前账户权限和链接的可访问性。
9 |
返回首页
10 |
重新登录
11 |
返回上一页
12 |
13 |
14 |
15 |
16 |
31 |
32 |
45 |
--------------------------------------------------------------------------------
/web/scui/src/layout/other/empty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/scui/src/locales/index.js:
--------------------------------------------------------------------------------
1 | import sysConfig from "@/config"
2 | import tool from '@/utils/tool'
3 | import { createI18n } from 'vue-i18n'
4 | import el_zh_cn from 'element-plus/lib/locale/lang/zh-cn'
5 | import el_en from 'element-plus/lib/locale/lang/en'
6 |
7 | import zh_cn from './lang/zh-cn.js'
8 | import en from './lang/en.js'
9 |
10 | const messages = {
11 | 'zh-cn': {
12 | el: el_zh_cn,
13 | ...zh_cn
14 | },
15 | 'en': {
16 | el: el_en,
17 | ...en
18 | }
19 | }
20 |
21 | const i18n = createI18n({
22 | locale: tool.data.get("APP_LANG") || sysConfig.LANG,
23 | fallbackLocale: 'zh-cn',
24 | globalInjection: true,
25 | messages,
26 | })
27 |
28 | export default i18n;
29 |
--------------------------------------------------------------------------------
/web/scui/src/locales/lang/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | login: {
3 | slogan: 'High performance / delicate / grace',
4 | describe: 'Vue3 + element plus based front-end solutions in the background.',
5 | signInTitle: 'Sign in',
6 | accountLogin: 'Account sign in',
7 | mobileLogin: 'Mobile sign in',
8 | rememberMe: 'Remember me',
9 | forgetPassword: 'Forget password',
10 | signIn: 'Sign in',
11 | signInOther: 'Sign in with',
12 | userPlaceholder: 'user / phone / email',
13 | userError: 'Please input a user name',
14 | PWPlaceholder: 'Please input a password',
15 | PWError: 'Please input a password',
16 | admin: 'Administrator',
17 | user: 'User',
18 | mobilePlaceholder: 'Mobile',
19 | mobileError: 'Please input mobile',
20 | smsPlaceholder: 'SMS Code',
21 | smsError: 'Please input sms code',
22 | smsGet: 'Get SMS Code',
23 | smsSent: 'SMS sent to mobile number',
24 | noAccount: 'No account?',
25 | createAccount: 'Create a new account',
26 | wechatLoginTitle: 'QR code sign in',
27 | wechatLoginMsg: 'Please use wechat to scan and log in | Auto scan after 3 seconds of simulation',
28 | wechatLoginResult: 'Scanned | Please click authorize login in the device'
29 | },
30 | user: {
31 | dynamic: 'Dynamic',
32 | info: 'User Info',
33 | settings: 'Settings',
34 | nightmode: 'night mode',
35 | nightmode_msg: 'Suitable for low light environment,The current night mode is beta',
36 | language: 'language',
37 | language_msg: 'Translation in progress,Temporarily translated the text of this view',
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/web/scui/src/locales/lang/zh-cn.js:
--------------------------------------------------------------------------------
1 | export default {
2 | login: {
3 | slogan: '高性能 / 精致 / 优雅',
4 | describe: '基于GIN + Vue3 + Element-Plus 的中后台前端解决方案。',
5 | signInTitle: '用户登录',
6 | accountLogin: '账号登录',
7 | mobileLogin: '手机号登录',
8 | rememberMe: '24小时免登录',
9 | forgetPassword: '忘记密码',
10 | signIn: '登录',
11 | signInOther: '其他登录方式',
12 | userPlaceholder: '用户名 / 手机 / 邮箱',
13 | userError: '请输入用户名',
14 | PWPlaceholder: '请输入密码',
15 | PWError: '请输入密码',
16 | admin: '管理员',
17 | user: '用户',
18 | mobilePlaceholder: '手机号码',
19 | mobileError: '请输入手机号码',
20 | smsPlaceholder: '短信验证码',
21 | smsError: '请输入短信验证码',
22 | smsGet: '获取验证码',
23 | smsSent: '已发送短信至手机号码',
24 | noAccount: '还没有账号?',
25 | createAccount: '创建新账号',
26 | wechatLoginTitle: '二维码登录',
27 | wechatLoginMsg: '请使用微信扫一扫登录 | 模拟3秒后自动扫描',
28 | wechatLoginResult: '已扫描 | 请在设备中点击授权登录'
29 | },
30 | user: {
31 | dynamic: '近期动态',
32 | info: '个人信息',
33 | settings: '设置',
34 | nightmode: '黑夜模式',
35 | nightmode_msg: '适合光线较弱的环境,当前黑暗模式为beta版本',
36 | language: '语言',
37 | language_msg: '翻译进行中,暂翻译了本视图的文本',
38 | },
39 | validate: {
40 | required: ":attribute 字段必须填写!"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/web/scui/src/main.js:
--------------------------------------------------------------------------------
1 | import ElementPlus from 'element-plus'
2 | import 'element-plus/dist/index.css'
3 | import 'element-plus/theme-chalk/display.css'
4 | import scui from './scui'
5 | import i18n from './locales'
6 | import router from './router'
7 | import store from './store'
8 | import { createApp } from 'vue'
9 | import App from './App.vue'
10 |
11 |
12 | const app = createApp(App);
13 |
14 | app.use(store);
15 | app.use(router);
16 | app.use(ElementPlus);
17 | app.use(i18n);
18 | app.use(scui);
19 |
20 | //挂载app
21 | app.mount('#app');
22 |
23 | const debounce = (fn, delay) => {
24 | let timer = null;
25 | return function () {
26 | let context = this;
27 | let args = arguments;
28 | clearTimeout(timer);
29 | timer = setTimeout(function () {
30 | fn.apply(context, args);
31 | }, delay);
32 | }
33 | }
34 |
35 | const _ResizeObserver = window.ResizeObserver;
36 | window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
37 | constructor(callback) {
38 | callback = debounce(callback, 16);
39 | super(callback);
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/web/scui/src/router/scrollBehavior.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 | import { nextTick } from 'vue'
3 |
4 | export function beforeEach(to, from){
5 | var adminMain = document.querySelector('#adminui-main')
6 | if(!adminMain){return false}
7 | store.commit("updateViewTags", {
8 | fullPath: from.fullPath,
9 | scrollTop: adminMain.scrollTop
10 | })
11 | }
12 |
13 | export function afterEach(to){
14 | var adminMain = document.querySelector('#adminui-main')
15 | if(!adminMain){return false}
16 | nextTick(()=>{
17 | var beforeRoute = store.state.viewTags.viewTags.filter(v => v.fullPath == to.fullPath)[0]
18 | if(beforeRoute){
19 | adminMain.scrollTop = beforeRoute.scrollTop || 0
20 | }
21 | })
22 | }
--------------------------------------------------------------------------------
/web/scui/src/router/systemRouter.js:
--------------------------------------------------------------------------------
1 | import config from "@/config"
2 |
3 | //系统路由
4 | const routes = [
5 | {
6 | name: "layout",
7 | path: "/",
8 | component: () => import(/* webpackChunkName: "layout" */ '@/layout'),
9 | redirect: config.DASHBOARD_URL || '/dashboard',
10 | children: []
11 | },
12 | {
13 | path: "/login",
14 | component: () => import(/* webpackChunkName: "login" */ '@/views/login'),
15 | meta: {
16 | title: "登录"
17 | }
18 | },
19 | {
20 | path: "/user_register",
21 | component: () => import(/* webpackChunkName: "userRegister" */ '@/views/login/userRegister'),
22 | meta: {
23 | title: "注册"
24 | }
25 | },
26 | {
27 | path: "/reset_password",
28 | component: () => import(/* webpackChunkName: "resetPassword" */ '@/views/login/resetPassword'),
29 | meta: {
30 | title: "重置密码"
31 | }
32 | }
33 | ]
34 |
35 | export default routes;
36 |
--------------------------------------------------------------------------------
/web/scui/src/store/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 自动import导入所有 vuex 模块
3 | */
4 |
5 | import { createStore } from 'vuex';
6 |
7 | const files = require.context('./modules', false, /\.js$/);
8 | const modules = {}
9 | files.keys().forEach((key) => {
10 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
11 | })
12 |
13 | export default createStore({
14 | modules
15 | });
16 |
--------------------------------------------------------------------------------
/web/scui/src/store/modules/global.js:
--------------------------------------------------------------------------------
1 | import config from "@/config";
2 |
3 | export default {
4 | state: {
5 | //移动端布局
6 | ismobile: false,
7 | //布局
8 | layout: config.LAYOUT,
9 | //菜单是否折叠 toggle
10 | menuIsCollapse: config.MENU_IS_COLLAPSE,
11 | //多标签栏
12 | layoutTags: config.LAYOUT_TAGS,
13 | //主题
14 | theme: config.THEME,
15 | },
16 | mutations: {
17 | SET_ismobile(state, key){
18 | state.ismobile = key
19 | },
20 | SET_layout(state, key){
21 | state.layout = key
22 | },
23 | SET_theme(state, key){
24 | state.theme = key
25 | },
26 | TOGGLE_menuIsCollapse(state){
27 | state.menuIsCollapse = !state.menuIsCollapse
28 | },
29 | TOGGLE_layoutTags(state){
30 | state.layoutTags = !state.layoutTags
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/web/scui/src/store/modules/iframe.js:
--------------------------------------------------------------------------------
1 | export default {
2 | state: {
3 | iframeList: []
4 | },
5 | mutations: {
6 | setIframeList(state, route){
7 | state.iframeList = []
8 | state.iframeList.push(route)
9 | },
10 | pushIframeList(state, route){
11 | let target = state.iframeList.find((item) => item.path === route.path)
12 | if(!target){
13 | state.iframeList.push(route)
14 | }
15 | },
16 | removeIframeList(state, route){
17 | state.iframeList.forEach((item, index) => {
18 | if (item.path === route.path){
19 | state.iframeList.splice(index, 1)
20 | }
21 | })
22 | },
23 | refreshIframe(state, route){
24 | state.iframeList.forEach((item) => {
25 | if (item.path == route.path){
26 | var url = route.meta.url;
27 | item.meta.url = '';
28 | setTimeout(function() {
29 | item.meta.url = url
30 | }, 200);
31 | }
32 | })
33 | },
34 | clearIframeList(state){
35 | state.iframeList = []
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/web/scui/src/store/modules/keepAlive.js:
--------------------------------------------------------------------------------
1 | export default {
2 | state: {
3 | keepLiveRoute: [],
4 | routeKey: null,
5 | routeShow: true
6 | },
7 | mutations: {
8 | pushKeepLive(state, component){
9 | if(!state.keepLiveRoute.includes(component)){
10 | state.keepLiveRoute.push(component)
11 | }
12 | },
13 | removeKeepLive(state, component){
14 | var index = state.keepLiveRoute.indexOf(component);
15 | if(index !== -1){
16 | state.keepLiveRoute.splice(index, 1);
17 | }
18 | },
19 | clearKeepLive(state){
20 | state.keepLiveRoute = []
21 | },
22 | setRouteKey(state, key){
23 | state.routeKey = key
24 | },
25 | setRouteShow(state, key){
26 | state.routeShow = key
27 | }
28 | },
29 | actions: {
30 | setRouteKey({ commit }, key) {
31 | commit('setRouteKey', key);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/web/scui/src/store/modules/viewTags.js:
--------------------------------------------------------------------------------
1 | import router from '@/router'
2 |
3 | export default {
4 | state: {
5 | viewTags: []
6 | },
7 | mutations: {
8 | pushViewTags(state, route){
9 | let backPathIndex = state.viewTags.findIndex(item => item.fullPath == router.options.history.state.back)
10 | let target = state.viewTags.find((item) => item.fullPath === route.fullPath)
11 | let isName = route.name
12 | if(!target && isName){
13 | if(backPathIndex == -1){
14 | state.viewTags.push(route)
15 | }else{
16 | state.viewTags.splice(backPathIndex+1, 0, route)
17 | }
18 | }
19 | },
20 | removeViewTags(state, route){
21 | state.viewTags.forEach((item, index) => {
22 | if (item.fullPath === route.fullPath){
23 | state.viewTags.splice(index, 1)
24 | }
25 | })
26 | },
27 | updateViewTags(state, route){
28 | state.viewTags.forEach((item) => {
29 | if (item.fullPath == route.fullPath){
30 | item = Object.assign(item, route)
31 | }
32 | })
33 | },
34 | updateViewTagsTitle(state, title=''){
35 | const nowFullPath = location.hash.substring(1)
36 | state.viewTags.forEach((item) => {
37 | if (item.fullPath == nowFullPath){
38 | item.meta.title = title
39 | }
40 | })
41 | },
42 | clearViewTags(state){
43 | state.viewTags = []
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/web/scui/src/style/dark.scss:
--------------------------------------------------------------------------------
1 | @import '~element-plus/theme-chalk/src/dark/css-vars.scss';
2 |
3 | html.dark {
4 | //变量
5 | --el-text-color-primary: #d0d0d0;
6 | --el-color-primary-dark-2: var(--el-color-primary-light-2) !important;
7 | --el-color-primary-light-9: var(--el-color-primary-dark-8) !important;
8 | --el-color-primary-light-8: var(--el-color-primary-dark-7) !important;
9 | --el-color-primary-light-7: var(--el-color-primary-dark-6) !important;
10 | --el-color-primary-light-5: var(--el-color-primary-dark-4) !important;
11 | --el-color-primary-light-3: var(--el-color-primary-dark-3) !important;
12 |
13 | //背景
14 | #app {background: var(--el-bg-color);}
15 |
16 | //登录背景
17 | .login_bg {background: var(--el-bg-color);}
18 |
19 | //框架
20 | .adminui-header {background: var(--el-bg-color-overlay);border-bottom: 1px solid var(--el-border-color-light);height:59px;}
21 | .aminui-side-split {background: var(--el-bg-color);}
22 | .aminui-side-split li {color: var(--el-text-color-primary);}
23 | .aminui-side {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
24 | .adminui-side-top, .adminui-side-bottom {border-color: var(--el-border-color-light);}
25 | .adminui-side-top h2 {color: var(--el-text-color-primary);}
26 | .adminui-topbar, .adminui-tags {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
27 | .adminui-main {background: var(--el-bg-color);}
28 | .drawerBG {background: var(--el-bg-color);}
29 | .adminui-header-menu .el-menu {--el-menu-bg-color:var(--el-bg-color-overlay) !important;--el-menu-hover-bg-color: #171819 !important;}
30 |
31 | //组件
32 | .el-header, .el-main.nopadding, .el-footer {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
33 | .el-main {background: var(--el-bg-color);}
34 | .el-aside {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);}
35 | .el-table .el-table__body-wrapper {background: var(--el-bg-color);}
36 | .el-table th.is-sortable:hover {background: #111;}
37 | }
38 |
--------------------------------------------------------------------------------
/web/scui/src/style/style.scss:
--------------------------------------------------------------------------------
1 | @import 'app.scss';
2 | @import 'fix.scss';
3 | @import 'pages.scss';
4 | @import 'media.scss';
5 | @import 'dark.scss';
6 |
--------------------------------------------------------------------------------
/web/scui/src/utils/color.js:
--------------------------------------------------------------------------------
1 | export default {
2 | //hex颜色转rgb颜色
3 | HexToRgb(str) {
4 | if (!str) {
5 | str = "#409EFF"
6 | }
7 | str = str.replace("#", "")
8 | var hxs = str.match(/../g)
9 | for (var i = 0; i < 3; i++) hxs[i] = parseInt(hxs[i], 16)
10 | return hxs
11 | },
12 | //rgb颜色转hex颜色
13 | RgbToHex(a, b, c) {
14 | var hexs = [a.toString(16), b.toString(16), c.toString(16)]
15 | for (var i = 0; i < 3; i++) {
16 | if (hexs[i].length == 1) hexs[i] = "0" + hexs[i]
17 | }
18 | return "#" + hexs.join("");
19 | },
20 | //加深
21 | darken(color, level) {
22 | var rgbc = this.HexToRgb(color)
23 | for (var i = 0; i < 3; i++) rgbc[i] = Math.floor(rgbc[i] * (1 - level))
24 | return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2])
25 | },
26 | //变淡
27 | lighten(color, level) {
28 | var rgbc = this.HexToRgb(color)
29 | for (var i = 0; i < 3; i++) rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i])
30 | return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2])
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/scui/src/utils/errorHandler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局代码错误捕捉
3 | * 比如 null.length 就会被捕捉到
4 | */
5 |
6 | export default (error, vm)=>{
7 | //过滤HTTP请求错误
8 | if(error.status || error.status==0){
9 | return false
10 | }
11 |
12 | var errorMap = {
13 | InternalError: "Javascript引擎内部错误",
14 | ReferenceError: "未找到对象",
15 | TypeError: "使用了错误的类型或对象",
16 | RangeError: "使用内置对象时,参数超范围",
17 | SyntaxError: "语法错误",
18 | EvalError: "错误的使用了Eval",
19 | URIError: "URI错误"
20 | }
21 | var errorName = errorMap[error.name] || "未知错误"
22 |
23 | console.warn(`[SCUI error]: ${error}`);
24 | console.error(error);
25 | //throw error;
26 |
27 | vm.$nextTick(() => {
28 | vm.$notify.error({
29 | title: errorName,
30 | message: error
31 | });
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/web/scui/src/utils/load.js:
--------------------------------------------------------------------------------
1 | /**
2 | * loadJS 异步加载远程JS
3 | * @constructor
4 | * @param {string} src - 必填,需要加载的URL路径
5 | * @param {string} keyName - 必填,唯一key和JS返回的全局的对象名
6 | * @param {string} callbackName - 非必填,如果远程JS有callback,则可更有效的判断是否完成加载
7 | */
8 | export function loadJS (src, keyName, callbackName) {
9 | return new Promise((resolve, reject) => {
10 | let has = document.head.querySelector("script[loadKey="+keyName+"]")
11 | if(has){
12 | return resolve(window[keyName])
13 | }
14 | let script = document.createElement("script")
15 | script.type = "text/javascript"
16 | script.src = src
17 | script.setAttribute("loadKey", keyName)
18 | document.head.appendChild(script)
19 | script.onload = () => {
20 | if(callbackName){
21 | window[callbackName] = () => {
22 | return resolve(window[keyName])
23 | }
24 | }else{
25 | setTimeout(()=>{
26 | return resolve(window[keyName])
27 | },50)
28 | }
29 | }
30 | script.onerror = (err) => {
31 | return reject(err)
32 | }
33 | })
34 | }
35 |
36 | /**
37 | * loadCSS 异步加载远程css
38 | * @constructor
39 | * @param {string} src - 必填,需要加载的URL路径
40 | * @param {string} keyName - 必填,唯一key
41 | */
42 | export function loadCSS (src, keyName) {
43 | return new Promise((resolve, reject) => {
44 | let has = document.head.querySelector("link[loadKey="+keyName+"]")
45 | if(has){
46 | return resolve()
47 | }
48 | let link = document.createElement('link')
49 | link.rel = "stylesheet"
50 | link.href = src
51 | link.setAttribute("loadKey", keyName)
52 | document.head.appendChild(link)
53 | link.onload = () => {
54 | return resolve()
55 | }
56 | link.onerror = (err) => {
57 | return reject(err)
58 | }
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/web/scui/src/utils/permission.js:
--------------------------------------------------------------------------------
1 | import tool from '@/utils/tool';
2 |
3 | export function permission(data) {
4 | let permissions = tool.data.get("PERMISSIONS");
5 | if(!permissions){
6 | return false;
7 | }
8 | let isHave = permissions.includes(data);
9 | return isHave;
10 | }
11 |
12 | export function rolePermission(data) {
13 | let userInfo = tool.data.get("USER_INFO");
14 | if(!userInfo){
15 | return false;
16 | }
17 | let role = userInfo.role;
18 | if(!role){
19 | return false;
20 | }
21 | let isHave = role.includes(data);
22 | return isHave;
23 | }
24 |
--------------------------------------------------------------------------------
/web/scui/src/utils/useTabs.js:
--------------------------------------------------------------------------------
1 | import { nextTick } from 'vue'
2 | import NProgress from 'nprogress'
3 | import 'nprogress/nprogress.css'
4 | import router from '@/router'
5 | import store from '@/store'
6 |
7 | export default {
8 | //刷新标签
9 | refresh() {
10 | NProgress.start()
11 | const route = router.currentRoute.value
12 | store.commit("removeKeepLive", route.name)
13 | store.commit("setRouteShow", false)
14 | nextTick(() => {
15 | store.commit("pushKeepLive", route.name)
16 | store.commit("setRouteShow", true)
17 | NProgress.done()
18 | })
19 | },
20 | //关闭标签
21 | close(tag) {
22 | const route = tag || router.currentRoute.value
23 | store.commit("removeViewTags", route)
24 | store.commit("removeIframeList", route)
25 | store.commit("removeKeepLive", route.name)
26 | const tagList = store.state.viewTags.viewTags
27 | const latestView = tagList.slice(-1)[0]
28 | if (latestView) {
29 | router.push(latestView)
30 | } else {
31 | router.push('/')
32 | }
33 | },
34 | //关闭标签后处理
35 | closeNext(next) {
36 | const route = router.currentRoute.value
37 | store.commit("removeViewTags", route)
38 | store.commit("removeIframeList", route)
39 | store.commit("removeKeepLive", route.name)
40 | if(next){
41 | const tagList = store.state.viewTags.viewTags
42 | next(tagList)
43 | }
44 | },
45 | //关闭其他
46 | closeOther() {
47 | const route = router.currentRoute.value
48 | const tagList = [...store.state.viewTags.viewTags]
49 | tagList.forEach(tag => {
50 | if(tag.meta&&tag.meta.affix || route.fullPath==tag.fullPath){
51 | return true
52 | }else{
53 | this.close(tag)
54 | }
55 | })
56 | },
57 | //设置标题
58 | setTitle(title){
59 | store.commit("updateViewTagsTitle", title)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/web/scui/src/utils/verificate.js:
--------------------------------------------------------------------------------
1 |
2 | //验证手机号
3 | export function verifyPhone(rule, value, callback) {
4 | let reg = /^[1][3, 4, 5, 6, 7, 8, 9][0-9]{9}$/
5 | if(!reg.test(value)){
6 | return callback(new Error('请输入正确的手机号码'))
7 | }
8 | callback()
9 | }
10 |
11 | //车牌号码
12 | export function verifyCars(rule, value, callback) {
13 | let reg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/
14 | if(!reg.test(value)){
15 | return callback(new Error('请输入正确的车牌号码'))
16 | }
17 | callback()
18 | }
19 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
55 |
56 |
58 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/widgets/components/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | GS Admin=gin+scui 它是golang 开发的一个企业级后台。遵循MIT开源协议。前端框架是scui,SCUI基于 Vue3、elementPlus 持续性的提供独家组件和丰富的业务模板帮助你快速搭建企业级中后台前端任务。后端框架是gin,Gin是一个golang的微框架,封装比较优雅,具有快速灵活,容错方便等特点。
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
31 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/widgets/components/index.js:
--------------------------------------------------------------------------------
1 | import {markRaw} from 'vue';
2 | const resultComps = {}
3 | let requireComponent = require.context(
4 | './', // 在当前目录下查找
5 | false, // 不遍历子文件夹
6 | /\.vue$/ // 正则匹配 以 .vue结尾的文件
7 | )
8 | requireComponent.keys().forEach(fileName => {
9 | let comp = requireComponent(fileName)
10 | resultComps[fileName.replace(/^\.\/(.*)\.\w+$/, '$1')] = comp.default
11 | })
12 | export default markRaw(resultComps)
13 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/widgets/components/progress.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ percentage }}%
7 | 当前进度
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
33 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/widgets/components/time.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ time }}
5 |
{{ day }}
6 |
7 |
8 |
9 |
10 |
35 |
36 |
41 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/widgets/components/ver.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
GS Admin {{$CONFIG.CORE_VER}}
6 |
最新版本 {{ver}}
7 |
8 |
9 | 日志
10 | gitee
11 | github
12 |
13 |
14 |
15 |
16 |
46 |
--------------------------------------------------------------------------------
/web/scui/src/views/home/work/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
34 |
35 |
38 |
--------------------------------------------------------------------------------
/web/scui/src/views/login/components/commonPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
14 |
15 |
16 |
17 |
{{title}}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
36 |
--------------------------------------------------------------------------------
/web/scui/src/views/other/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
{{data.name}}
9 |
{{data.version}}
10 |
11 |
12 |
13 |
14 | {{value}}
15 |
16 |
17 |
18 |
19 | {{value}}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
45 |
46 |
53 |
--------------------------------------------------------------------------------
/web/scui/src/views/other/fullpage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
30 |
31 |
33 |
--------------------------------------------------------------------------------
/web/scui/src/views/other/loadJS.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
演示了使用 @/utils/load 加载百度地图的JSAPI和它的GL库BMapGLLib
7 |
当然也可以像传统网页一样加载任何JS和CSS,甚至可以是JQ。
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
58 |
59 |
61 |
--------------------------------------------------------------------------------
/web/scui/src/views/setting/log/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{data.url}}
6 | {{data.type}}
7 | {{data.code}}
8 | {{data.name}}
9 | {{data.time}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Request: {
18 | User-Agent: "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
19 | },
20 | Response: {
21 | Content-Type: "application/json; charset=utf-8",
22 | Date: "Fri, 25 Jun 2021 03:02:14 GMT",
23 | Server: "nginx/1.17.8"
24 | }
25 |
26 |
27 |
28 |
29 |
30 |
31 |
51 |
52 |
55 |
--------------------------------------------------------------------------------
/web/scui/src/views/template/layout/blank.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/web/scui/src/views/template/layout/layoutLCR.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Left Header
6 | Left Main
7 | Left Footer
8 |
9 |
10 |
11 | Header
12 |
13 |
14 |
15 | Footer
16 |
17 |
18 |
19 | Right Header
20 | Right Main
21 | Right Footer
22 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
40 |
--------------------------------------------------------------------------------
/web/scui/src/views/template/layout/layoutTCB.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Header
4 |
5 |
6 |
7 | Footer
8 |
9 |
10 |
11 |
21 |
22 |
24 |
--------------------------------------------------------------------------------
/web/scui/src/views/template/list/crud/detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 保存
13 |
14 |
15 |
16 |
17 |
18 |
19 |
40 |
41 |
43 |
--------------------------------------------------------------------------------
/web/scui/src/views/template/list/crud/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{val}}
6 |
7 |
8 |
9 |
10 |
11 |
31 |
32 |
34 |
--------------------------------------------------------------------------------
/web/scui/src/views/userCenter/user/account.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 账号信息用于登录,系统不允许修改
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 保存
25 |
26 |
27 |
28 |
29 |
30 |
44 |
45 |
47 |
--------------------------------------------------------------------------------
/web/scui/src/views/userCenter/user/logs.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 成功
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
38 |
39 |
41 |
--------------------------------------------------------------------------------
/web/scui/src/views/userCenter/user/pushSettings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 短信推送
6 | 微信推送
7 |
8 |
9 | 短信推送
10 | 微信推送
11 |
12 |
13 |
14 |
15 |
16 |
28 |
29 |
31 |
--------------------------------------------------------------------------------
/web/scui/src/views/userCenter/user/space.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
53 |
54 |
56 |
--------------------------------------------------------------------------------
/web/scui/src/views/userCenter/user/upToEnterprise.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 立即升级为企业账号
4 |
5 |
6 |
7 |
16 |
17 |
19 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/cron.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/dialog/dialog1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 内容1
4 |
5 | 取 消
6 | 确 定
7 |
8 |
9 |
10 |
11 |
26 |
27 |
29 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/dialog/dialog2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 内容2
4 |
5 | 取 消
6 | 确 定
7 |
8 |
9 |
10 |
11 |
26 |
27 |
29 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 绑定的内容
9 | 占位符
10 | 编辑器高度,默认300
11 | 禁用编辑器 Boolean
12 | 插入自定义模板 Array
13 | 自定义工具栏,使用"|"竖杠分割,使用"\"斜杠分组,默认:'undo redo | forecolor backcolor bold italic underline strikethrough link | blocks fontfamily fontsize | \
14 | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | pagebreak | \
15 | image media table template preview | code selectall'
16 |
17 |
18 |
19 |
20 |
21 |
49 |
50 |
52 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/fileselect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 自定义插槽
10 |
11 |
12 |
13 |
14 |
15 |
16 | 切换multiple
17 | 切换hideUpload
18 |
19 |
20 |
21 |
22 |
23 |
24 |
52 |
53 |
56 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/iconfont.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
32 |
33 |
43 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/iconselect.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 保存
20 | 重置
21 | {{disabled?'移除禁用':'设为禁用'}}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
69 |
70 |
72 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/mini.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 设置reverse可反转颜色。
25 |
26 |
27 |
28 |
29 |
30 |
持续更新中...
31 |
非常欢迎提交Issue/PR完善和补充更多好玩的原子组件
32 |
原子组件库位置:@/components/scMini/*
33 |
提交想法
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
61 |
62 |
68 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/print.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 普通打印
8 |
9 |
10 |
11 |
12 |
打印内容2
13 |
14 |
15 |
16 |
17 | 动态打印
18 |
19 |
20 |
21 |
22 |
23 |
24 |
45 |
46 |
50 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/qrcode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
45 |
46 |
48 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/select.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ data.label }}
35 | {{ data.value }}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
63 |
64 |
66 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/selectFilter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ filterData }}
8 |
9 |
10 |
11 |
12 |
90 |
91 |
93 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/statistic.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
59 |
60 |
65 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/table/base.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{scope.row.sex}}
16 | {{scope.row.sex}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
54 |
55 |
57 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/table/column.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
72 |
73 |
75 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/table/remote.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
45 |
46 |
48 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/table/thead.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
36 |
37 |
39 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/video.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
63 |
64 |
66 |
--------------------------------------------------------------------------------
/web/scui/src/views/vab/watermark.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 创建水印
14 | 移除水印
15 |
16 |
17 |
18 |
19 |
61 |
62 |
64 |
--------------------------------------------------------------------------------
/web/scui/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 |
3 | module.exports = defineConfig({
4 | //设置为空打包后不分更目录还是多级目录
5 | publicPath:'',
6 | //build编译后存放静态文件的目录
7 | //assetsDir: "static",
8 |
9 | // build编译后不生成资源MAP文件
10 | productionSourceMap: false,
11 |
12 | //开发服务,build后的生产模式还需nginx代理
13 | devServer: {
14 | open: false, //运行后自动打开浏览器
15 | port: process.env.VUE_APP_PORT, //挂载端口
16 | proxy: {
17 | '/api': {
18 | target: process.env.VUE_APP_API_BASEURL,
19 | ws: true,
20 | pathRewrite: {
21 | '^/api': '/'
22 | }
23 | }
24 | },
25 | client: {
26 | overlay: false,
27 | },
28 | },
29 |
30 | chainWebpack: config => {
31 | // 移除 prefetch 插件
32 | config.plugins.delete('preload');
33 | config.plugins.delete('prefetch');
34 | config.resolve.alias.set('vue-i18n', 'vue-i18n/dist/vue-i18n.cjs.js');
35 | },
36 |
37 | configureWebpack: {
38 | //性能提示
39 | performance: {
40 | hints: false
41 | },
42 | optimization: {
43 | splitChunks: {
44 | chunks: "all",
45 | automaticNameDelimiter: '~',
46 | name: "scuiChunks",
47 | cacheGroups: {
48 | //第三方库抽离
49 | vendor: {
50 | name: "modules",
51 | test: /[\\/]node_modules[\\/]/,
52 | priority: -10
53 | },
54 | elicons: {
55 | name: "elicons",
56 | test: /[\\/]node_modules[\\/]@element-plus[\\/]icons-vue[\\/]/
57 | },
58 | tinymce: {
59 | name: "tinymce",
60 | test: /[\\/]node_modules[\\/]tinymce[\\/]/
61 | },
62 | echarts: {
63 | name: "echarts",
64 | test: /[\\/]node_modules[\\/]echarts[\\/]/
65 | },
66 | xgplayer: {
67 | name: "xgplayer",
68 | test: /[\\/]node_modules[\\/]xgplayer.*[\\/]/
69 | },
70 | codemirror: {
71 | name: "codemirror",
72 | test: /[\\/]node_modules[\\/]codemirror[\\/]/
73 | }
74 | }
75 | }
76 | }
77 | }
78 | })
79 |
--------------------------------------------------------------------------------