This commit is contained in:
2025-06-20 17:46:57 +08:00
parent fd0345a034
commit c6d9db8136
294 changed files with 20 additions and 32097 deletions

BIN
blazing.exe Normal file

Binary file not shown.

View File

@@ -1,72 +0,0 @@
.PHONY: help
help: ## 查看帮助
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: pack.template-simple
pack.template-simple:
@rm -fr temp
@mkdir temp || exit 0
@cd temp && git clone --depth=1 -b simple https://github.com/cool-team-official/cool-admin-go.git cool-admin-go-simple
@rm -fr temp/cool-admin-go-simple/.git
@cd temp && gf pack cool-admin-go-simple ../internal/packed/cool-admin-go-simple.go -n=packed -y
@rm -fr temp/cool-admin-go-simple
.PHONY: pack.template-simple.ssh
pack.template-simple.ssh:
@rm -fr temp
@mkdir temp || exit 0
@cd temp && git clone --depth=1 -b simple git@github.com:cool-team-official/cool-admin-go.git cool-admin-go-simple
@rm -fr temp/cool-admin-go-simple/.git
@cd temp && gf pack cool-admin-go-simple ../internal/packed/cool-admin-go-simple.go -n=packed -y
@rm -fr temp
.PHONY: pack.docs
pack.docs:
@rm -fr temp
@mkdir temp || exit 0
@cd temp && git clone --depth=1 -b gh-pages https://github.com/cool-team-official/cool-admin-go.git docs/cool-admin-go
@rm -fr temp/docs/cool-admin-go/.git
@cd temp && gf pack docs ../internal/packed/docs.go -n=packed -y
@rm -fr temp/docs
.PHONY: pack.docs.ssh
pack.docs.ssh:
@rm -fr temp
@mkdir temp || exit 0
@cd temp && git clone --depth=1 -b gh-pages git@github.com:cool-team-official/cool-admin-go.git docs/cool-admin-go
@rm -fr temp/docs/cool-admin-go/.git
@cd temp && gf pack docs ../internal/packed/docs.go -n=packed -y
@rm -fr temp/docs
# Install/Update to the latest CLI tool.
.PHONY: cli
cli:
@set -e; \
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \
chmod +x gf && \
./gf install && \
rm ./gf
# Install/Update to the latest cool-tools.
.PHONY: tools
tools:
@set -e; \
curl -L https://download.fastgit.org/cool-team-official/cool-admin-go/releases/latest/download/cool-tools_$(shell go env GOOS)_$(shell go env GOARCH) -o ./cool-tools && \
chmod +x cool-tools && \
./cool-tools install && \
rm ./cool-tools
# Check and install cool-tools.
.PHONY: tools.install
tools.install:
@set -e; \
echo "Checking cool-tools..."; \
cool-tools -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \
echo "cool-tools is not installed, start proceeding auto installation..."; \
make tools; \
fi;\
echo "cool-tools is installed.";

View File

@@ -1,19 +0,0 @@
# CoolTools
[![Go Reference](https://pkg.go.dev/badge/github.com/cool-team-official/cool-admin-go/cool-tools.svg)](https://pkg.go.dev/github.com/cool-team-official/cool-admin-go/cool-tools)
cool-tools is a collection of tools for cool people.
## 安装
```bash
go install github.com/cool-team-official/cool-admin-go/cool-tools@latest
```
## 使用
初始化一个新的 cool-admin-go 项目
```bash
cool-tools init <project-name>
```

View File

@@ -1,32 +0,0 @@
module github.com/cool-team-official/cool-admin-go/cool-tools
go 1.18
require github.com/gogf/gf/v2 v2.6.3
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -1,72 +0,0 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gogf/gf/v2 v2.6.3 h1:DoqeuwU98wotpFoDSQEx8RZbmJdK8KdGiJtzJeqpyIo=
github.com/gogf/gf/v2 v2.6.3/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,12 +0,0 @@
# CLI tool, only in development environment.
# https://goframe.org/pages/viewpage.action?pageId=3673173
gfcli:
build:
name: "cool-tools"
arch: "all"
system: "linux,darwin,windows"

View File

@@ -1,14 +0,0 @@
package cmd
import (
"github.com/gogf/gf/v2/os/gcmd"
)
var (
Main = gcmd.Command{
Name: "cool-tools",
Usage: "cool-tools [command] [args...]",
Brief: "cool-tools is a collection of tools for cool people.",
Description: `cool-tools is a collection of tools for cool people.`,
}
)

View File

@@ -1,328 +0,0 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
"os"
"regexp"
"runtime"
"strings"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gtag"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
)
var (
Build = cBuild{
nodeNameInConfigFile: "gfcli.build",
packedGoFileName: "internal/packed/build_pack_data.go",
}
)
type cBuild struct {
g.Meta `name:"build" brief:"{cBuildBrief}" dc:"{cBuildDc}" eg:"{cBuildEg}" ad:"{cBuildAd}"`
nodeNameInConfigFile string // nodeNameInConfigFile is the node name for compiler configurations in configuration file.
packedGoFileName string // packedGoFileName specifies the file name for packing common folders into one single go file.
}
const (
cBuildBrief = `cross-building go project for lots of platforms`
cBuildEg = `
cool-tools build main.go
cool-tools build main.go --pack public,template
cool-tools build main.go --cgo
cool-tools build main.go -m none
cool-tools build main.go -n my-app -a all -s all
cool-tools build main.go -n my-app -a amd64,386 -s linux -p .
cool-tools build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
`
cBuildDc = `
The "build" command is most commonly used command, which is designed as a powerful wrapper for
"go build" command for convenience cross-compiling usage.
It provides much more features for building binary:
1. Cross-Compiling for many platforms and architectures.
2. Configuration file support for compiling.
3. Build-In Variables.
`
cBuildAd = `
PLATFORMS
darwin amd64,arm64
freebsd 386,amd64,arm
linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le
netbsd 386,amd64,arm
openbsd 386,amd64,arm
windows 386,amd64
`
// https://golang.google.cn/doc/install/source
cBuildPlatforms = `
darwin amd64
darwin arm64
ios amd64
ios arm64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux mips
linux mipsle
linux mips64
linux mips64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
windows 386
windows amd64
android arm
dragonfly amd64
plan9 386
plan9 amd64
solaris amd64
`
)
func init() {
gtag.Sets(g.MapStrStr{
`cBuildBrief`: cBuildBrief,
`cBuildDc`: cBuildDc,
`cBuildEg`: cBuildEg,
`cBuildAd`: cBuildAd,
})
Main.AddObject(Build)
}
type cBuildInput struct {
g.Meta `name:"build" config:"gfcli.build"`
File string `name:"FILE" arg:"true" brief:"building file path"`
Name string `short:"n" name:"name" brief:"output binary name"`
Version string `short:"v" name:"version" brief:"output binary version"`
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"`
}
type cBuildOutput struct{}
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
mlog.SetHeaderPrint(true)
mlog.Debugf(`build input: %+v`, in)
// Necessary check.
if gproc.SearchBinary("go") == "" {
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
}
var (
parser = gcmd.ParserFromCtx(ctx)
file = parser.GetArg(2).String()
)
if len(file) < 1 {
// Check and use the main.go file.
if gfile.Exists("main.go") {
file = "main.go"
} else {
mlog.Fatal("build file path cannot be empty")
}
}
if in.Name == "" {
in.Name = gfile.Name(file)
}
if len(in.Name) < 1 || in.Name == "*" {
mlog.Fatal("name cannot be empty")
}
if in.Mod != "" && in.Mod != "none" {
mlog.Debugf(`mod is %s`, in.Mod)
if in.Extra == "" {
in.Extra = fmt.Sprintf(`-mod=%s`, in.Mod)
} else {
in.Extra = fmt.Sprintf(`-mod=%s %s`, in.Mod, in.Extra)
}
}
if in.Extra != "" {
in.Extra += " "
}
var (
customSystems = gstr.SplitAndTrim(in.System, ",")
customArches = gstr.SplitAndTrim(in.Arch, ",")
)
if len(in.Version) > 0 {
in.Path += "/" + in.Version
}
// System and arch checks.
var (
spaceRegex = regexp.MustCompile(`\s+`)
platformMap = make(map[string]map[string]bool)
)
for _, line := range strings.Split(strings.TrimSpace(cBuildPlatforms), "\n") {
line = gstr.Trim(line)
line = spaceRegex.ReplaceAllString(line, " ")
var (
array = strings.Split(line, " ")
system = strings.TrimSpace(array[0])
arch = strings.TrimSpace(array[1])
)
if platformMap[system] == nil {
platformMap[system] = make(map[string]bool)
}
platformMap[system][arch] = true
}
// Auto packing.
if in.PackSrc != "" {
if in.PackDst == "" {
mlog.Fatal(`parameter "packDst" should not be empty when "packSrc" is used`)
}
if gfile.Exists(in.PackDst) && !gfile.IsFile(in.PackDst) {
mlog.Fatalf(`parameter "packDst" path "%s" should be type of file not directory`, in.PackDst)
}
if !gfile.Exists(in.PackDst) {
// Remove the go file that is automatically packed resource.
defer func() {
_ = gfile.Remove(in.PackDst)
mlog.Printf(`remove the automatically generated resource go file: %s`, in.PackDst)
}()
}
// remove black space in separator.
in.PackSrc, _ = gregex.ReplaceString(`,\s+`, `,`, in.PackSrc)
packCmd := fmt.Sprintf(`gf pack %s %s --keepPath=true`, in.PackSrc, in.PackDst)
mlog.Print(packCmd)
gproc.MustShellRun(ctx, packCmd)
}
// Injected information by building flags.
ldFlags := fmt.Sprintf(
`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`,
c.getBuildInVarStr(ctx, in),
)
// start building
mlog.Print("start building...")
if in.Cgo {
genv.MustSet("CGO_ENABLED", "1")
} else {
genv.MustSet("CGO_ENABLED", "0")
}
var (
cmd = ""
ext = ""
)
for system, item := range platformMap {
cmd = ""
ext = ""
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
continue
}
for arch := range item {
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
continue
}
if len(customSystems) == 0 && len(customArches) == 0 {
if runtime.GOOS == "windows" {
ext = ".exe"
}
// Single binary building, output the binary to current working folder.
output := ""
if len(in.Output) > 0 {
output = "-o " + in.Output + ext
} else {
output = "-o " + in.Name + ext
}
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
} else {
// Cross-building, output the compiled binary to specified path.
if system == "windows" {
ext = ".exe"
}
genv.MustSet("GOOS", system)
genv.MustSet("GOARCH", arch)
cmd = fmt.Sprintf(
`go build -o %s/%s/%s%s -ldflags "%s" %s%s`,
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
)
}
mlog.Debug(cmd)
// It's not necessary printing the complete command string.
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
mlog.Print(cmdShow)
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
mlog.Printf(
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
system, arch, gstr.Trim(result),
`you may use command option "--debug" to enable debug info and check the details`,
)
if in.ExitWhenError {
os.Exit(1)
}
} else {
mlog.Debug(gstr.Trim(result))
}
// single binary building.
if len(customSystems) == 0 && len(customArches) == 0 {
goto buildDone
}
}
}
buildDone:
mlog.Print("done!")
return
}
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
// file as json.
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
buildInVarMap := in.VarMap
if buildInVarMap == nil {
buildInVarMap = make(g.Map)
}
buildInVarMap["builtGit"] = c.getGitCommit(ctx)
buildInVarMap["builtTime"] = gtime.Now().String()
b, err := json.Marshal(buildInVarMap)
if err != nil {
mlog.Fatal(err)
}
return gbase64.EncodeToString(b)
}
// getGitCommit retrieves and returns the latest git commit hash string if present.
func (c cBuild) getGitCommit(ctx context.Context) string {
if gproc.SearchBinary("git") == "" {
return ""
}
var (
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
s, _ = gproc.ShellExec(ctx, cmd)
)
mlog.Debug(cmd)
if s != "" {
if !gstr.Contains(s, "fatal") {
return gstr.Trim(s)
}
}
return ""
}

View File

@@ -1,99 +0,0 @@
package cmd
import (
"context"
"strings"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/allyes"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/util/gtag"
)
var (
Pack = cPack{}
)
type cPack struct {
g.Meta `name:"pack" usage:"{cPackUsage}" brief:"{cPackBrief}" eg:"{cPackEg}"`
}
const (
cPackUsage = `cool-tools pack SRC DST`
cPackBrief = `packing any file/directory to a resource file, or a go file`
cPackEg = `
cool-tools pack public data.bin
cool-tools pack public,template data.bin
cool-tools pack public,template packed/data.go
cool-tools pack public,template,config packed/data.go
cool-tools pack public,template,config packed/data.go -n=packed -p=/var/www/my-app
cool-tools pack /var/www/public packed/data.go -n=packed
`
cPackSrcBrief = `source path for packing, which can be multiple source paths.`
cPackDstBrief = `
destination file path for packed file. if extension of the filename is ".go" and "-n" option is given,
it enables packing SRC to go file, or else it packs SRC into a binary file.
`
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
cPackPrefixBrief = `prefix for each file packed into the resource file`
cPackKeepPathBrief = `keep the source path from system to resource file, usually for relative path`
)
func init() {
gtag.Sets(g.MapStrStr{
`cPackUsage`: cPackUsage,
`cPackBrief`: cPackBrief,
`cPackEg`: cPackEg,
`cPackSrcBrief`: cPackSrcBrief,
`cPackDstBrief`: cPackDstBrief,
`cPackNameBrief`: cPackNameBrief,
`cPackPrefixBrief`: cPackPrefixBrief,
`cPackKeepPathBrief`: cPackKeepPathBrief,
})
Main.AddObject(Pack)
}
type cPackInput struct {
g.Meta `name:"pack"`
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
KeepPath bool `name:"keepPath" short:"k" brief:"{cPackKeepPathBrief}" orphan:"true"`
}
type cPackOutput struct{}
func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err error) {
if gfile.Exists(in.Dst) && gfile.IsDir(in.Dst) {
mlog.Fatalf("DST path '%s' cannot be a directory", in.Dst)
}
if !gfile.IsEmpty(in.Dst) && !allyes.Check() {
s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", in.Dst)
if strings.EqualFold(s, "n") {
return
}
}
if in.Name == "" && gfile.ExtName(in.Dst) == "go" {
in.Name = gfile.Basename(gfile.Dir(in.Dst))
}
var option = gres.Option{
Prefix: in.Prefix,
KeepPath: in.KeepPath,
}
if in.Name != "" {
if err = gres.PackToGoFileWithOption(in.Src, in.Dst, in.Name, option); err != nil {
mlog.Fatalf("pack failed: %v", err)
}
} else {
if err = gres.PackToFileWithOption(in.Src, in.Dst, option); err != nil {
mlog.Fatalf("pack failed: %v", err)
}
}
mlog.Print("done!")
return
}

View File

@@ -1,170 +0,0 @@
package cmd
// 同步自 gf v2.2.2 https://github.com/gogf/gf/blob/60d828397149ed281111a7530074ce20f997224a/cmd/gf/internal/cmd/cmd_build.go
import (
"context"
"fmt"
"runtime"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gfsnotify"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/os/gtimer"
"github.com/gogf/gf/v2/util/gtag"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
)
var (
Run = cRun{}
)
type cRun struct {
g.Meta `name:"run" usage:"{cRunUsage}" brief:"{cRunBrief}" eg:"{cRunEg}" dc:"{cRunDc}"`
}
type cRunApp struct {
File string // Go run file name.
Path string // Directory storing built binary.
Options string // Extra "go run" options.
Args string // Custom arguments.
}
const (
cRunUsage = `cool-tools run FILE [OPTION]`
cRunBrief = `running go codes with hot-compiled-like feature`
cRunEg = `
cool-tools run main.go
cool-tools run main.go --args "server -p 8080"
cool-tools run main.go -mod=vendor
`
cRunDc = `
The "run" command is used for running go codes with hot-compiled-like feature,
which compiles and runs the go codes asynchronously when codes change.
`
cRunFileBrief = `building file path.`
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
cRunArgsBrief = `custom arguments for your process`
)
var (
process *gproc.Process
)
func init() {
gtag.Sets(g.MapStrStr{
`cRunUsage`: cRunUsage,
`cRunBrief`: cRunBrief,
`cRunEg`: cRunEg,
`cRunDc`: cRunDc,
`cRunFileBrief`: cRunFileBrief,
`cRunPathBrief`: cRunPathBrief,
`cRunExtraBrief`: cRunExtraBrief,
`cRunArgsBrief`: cRunArgsBrief,
})
// 注册命令
Main.AddObject(Run)
}
type (
cRunInput struct {
g.Meta `name:"run"`
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"`
}
cRunOutput struct{}
)
func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err error) {
// Necessary check.
if gproc.SearchBinary("go") == "" {
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
}
app := &cRunApp{
File: in.File,
Path: in.Path,
Options: in.Extra,
Args: in.Args,
}
dirty := gtype.NewBool()
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
if gfile.ExtName(event.Path) != "go" {
return
}
// Variable `dirty` is used for running the changes only one in one second.
if !dirty.Cas(false, true) {
return
}
// With some delay in case of multiple code changes in very short interval.
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
defer dirty.Set(false)
mlog.Printf(`go file changes: %s`, event.String())
app.Run(ctx)
})
})
if err != nil {
mlog.Fatal(err)
}
go app.Run(ctx)
select {}
}
func (app *cRunApp) Run(ctx context.Context) {
// Rebuild and run the codes.
renamePath := ""
mlog.Printf("build: %s", app.File)
outputPath := gfile.Join(app.Path, gfile.Name(app.File))
if runtime.GOOS == "windows" {
outputPath += ".exe"
if gfile.Exists(outputPath) {
renamePath = outputPath + "~"
if err := gfile.Rename(outputPath, renamePath); err != nil {
mlog.Print(err)
}
}
}
// In case of `pipe: too many open files` error.
// Build the app.
buildCommand := fmt.Sprintf(
`go build -o %s %s %s`,
outputPath,
app.Options,
app.File,
)
mlog.Print(buildCommand)
result, err := gproc.ShellExec(ctx, buildCommand)
if err != nil {
mlog.Printf("build error: \n%s%s", result, err.Error())
return
}
// Kill the old process if build successfully.
if process != nil {
if err := process.Kill(); err != nil {
mlog.Debugf("kill process error: %s", err.Error())
//return
}
}
// Run the binary file.
runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args)
mlog.Print(runCommand)
if runtime.GOOS == "windows" {
// Special handling for windows platform.
// DO NOT USE "cmd /c" command.
process = gproc.NewProcess(runCommand, nil)
} else {
process = gproc.NewProcessCmd(runCommand, nil)
}
if pid, err := process.Start(ctx); err != nil {
mlog.Printf("build running error: %s", err.Error())
} else {
mlog.Printf("build running pid: %d", pid)
}
}

View File

@@ -1,80 +0,0 @@
package cmd
import (
"context"
"net"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/util/gconv"
)
var (
Docs = gcmd.Command{
Name: "docs",
Usage: "cool-tools docs",
Brief: "查看帮助文档",
Description: "查看帮助文档",
Func: func(ctx context.Context, parser *gcmd.Parser) error {
s := g.Server("docs")
// 获取本机未占用的端口
port, err := getfreeport()
if err != nil {
mlog.Fatal(err)
return err
}
// 获取本机ip
// ip, err := getlocalip()
// if err != nil {
// mlog.Fatal(err)
// return err
// }
s.SetServerRoot("docs")
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.RedirectTo("/cool-admin-go/")
})
// 设置端口
s.SetPort(gconv.Int(port))
mlog.Printf("CoolAdminGo docs server is running at %s", "http://"+"127.0.0.1"+":"+gconv.String(port)+"/cool-admin-go/")
s.Run()
return nil
},
}
)
func init() {
Main.AddCommand(&Docs)
}
// getfreeport 获取本机未占用的端口
func getfreeport() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}
// getlocalip 获取本机ip
// func getlocalip() (string, error) {
// addrs, err := net.InterfaceAddrs()
// if err != nil {
// return "", err
// }
// for _, address := range addrs {
// // 检查ip地址判断是否回环地址
// if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
// if ipnet.IP.To4() != nil {
// return ipnet.IP.String(), nil
// }
// }
// }
// return "", nil
// }

View File

@@ -1,27 +0,0 @@
package cmd
import (
"context"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gres"
)
var (
Dump = &gcmd.Command{
Name: "dump",
Usage: "cool-tools dump",
Brief: "查看打包的资源文件",
Description: "查看打包的资源文件",
Arguments: []gcmd.Argument{},
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
gres.Dump()
return nil
},
}
)
// init
func init() {
Main.AddCommand(Dump)
}

View File

@@ -1,18 +0,0 @@
package cmd
import (
"github.com/gogf/gf/v2/frame/g"
)
var (
Gen = cGen{}
)
type cGen struct {
g.Meta `name:"gen" brief:"生成代码" description:"生成代码, 例如: cool-tools gen model"`
cGenModel
}
func init() {
Main.AddObject(Gen)
}

View File

@@ -1,46 +0,0 @@
package cmd
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/gogf/gf/v2/frame/g"
"gorm.io/gen"
)
type cGenModel struct {
g.Meta `name:"model" brief:"生成模型代码" description:"生成模型代码, 例如: cool-tools gen model"`
}
var (
GenModel = cGenModel{}
)
type cGenModelInput struct {
g.Meta `name:"model" brief:"生成模型代码" description:"生成模型代码, 例如: cool-tools gen model"`
Database string `v:"required#请输入数据库名称" arg:"true" name:"database" brief:"数据库名称" description:"数据库名称"`
}
type cGenModelOutput struct{}
func (c *cGenModel) Index(ctx g.Ctx, in cGenModelInput) (out cGenModelOutput, err error) {
g.Log().Print(ctx, in.Database)
// 获取数据库
db, err := cool.InitDB(in.Database)
if err != nil {
panic(err.Error())
}
generator := gen.NewGenerator(gen.Config{
OutPath: "temp/gen/model",
OutFile: "",
ModelPkgPath: "",
WithUnitTest: false,
FieldNullable: true,
FieldCoverable: true,
FieldSignable: true,
FieldWithIndexTag: true,
FieldWithTypeTag: true,
Mode: 0,
})
generator.UseDB(db)
generator.GenerateAllTable()
generator.Execute()
return
}

View File

@@ -1,68 +0,0 @@
package cmd
import (
"context"
"strings"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
)
var (
Init = gcmd.Command{
Name: "init",
Usage: "cool-tools init [dst]",
Brief: "创建一个新的cool-admin-go项目",
Arguments: []gcmd.Argument{
{
Name: "dst",
// Short: "m",
Brief: "the destination path",
IsArg: true,
},
},
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
dst := parser.GetArg(2).String()
if dst == "" {
dst = "."
}
// 如果目标路径不存在,则创建目标路径
if gfile.IsEmpty(dst) {
if err = gfile.Mkdir(dst); err != nil {
return
}
} else {
if !gfile.IsDir(dst) {
g.Log().Panicf(ctx, "%s is not a directory", dst)
} else {
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, dst)
if strings.EqualFold(s, "n") {
return
}
}
}
err = gres.Export("cool-admin-go-simple", dst, gres.ExportOption{
RemovePrefix: "cool-admin-go-simple",
})
if err != nil {
return
}
err = gfile.ReplaceDir("cool-admin-go-simple", gfile.Basename(gfile.RealPath(dst)), dst, "*", true)
if err != nil {
return
}
g.Log().Infof(ctx, "init success")
return nil
},
}
)
// init 初始化模块
func init() {
Main.AddCommand(&Init)
}

View File

@@ -1,25 +0,0 @@
package cmd
import (
"context"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/service"
"github.com/gogf/gf/v2/os/gcmd"
)
var (
Install = gcmd.Command{
Name: "install",
Usage: "cool-tools install",
Brief: "Install cool-tools to the system.",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
err = service.Install.Run(ctx)
return
},
}
)
// init
func init() {
Main.AddCommand(&Install)
}

View File

@@ -1,61 +0,0 @@
package cmd
import (
"context"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/service"
"github.com/gogf/gf/v2/os/gcmd"
)
var (
Module = &gcmd.Command{
Name: "module",
Usage: "cool-tools module moduleName",
Brief: "在modules目录下创建模块",
Description: "在modules目录下创建模块, 并且创建相应的目录结构,注意: 如果模块已经存在, 则会覆盖原有的模块,本命令需在项目根目录下执行.",
Arguments: []gcmd.Argument{
{
Name: "moduleName",
IsArg: true,
Orphan: false,
},
},
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
moduleName := parser.GetArg(2).String()
if moduleName == "" {
println("moduleName is empty")
return nil
}
err = service.CreatModule(ctx, moduleName)
return
},
}
M = &gcmd.Command{
Name: "m",
Usage: "cool-tools module moduleName",
Brief: "在modules目录下创建模块,为module的简写模式",
Description: "在modules目录下创建模块, 并且创建相应的目录结构,注意: 如果模块已经存在, 则会覆盖原有的模块,本命令需在项目根目录下执行.",
Arguments: []gcmd.Argument{
{
Name: "moduleName",
IsArg: true,
Orphan: false,
},
},
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
moduleName := parser.GetArg(2).String()
if moduleName == "" {
println("moduleName is empty")
return nil
}
err = service.CreatModule(ctx, moduleName)
return
},
}
)
func init() {
Main.AddCommand(Module)
Main.AddCommand(M)
}

View File

@@ -1,61 +0,0 @@
package cmd
import (
"context"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
)
var (
SnippetsMaker = gcmd.Command{
Name: "snippetsmaker",
Usage: "snippetsmaker",
Brief: "代码片段生成器",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Debug(ctx, "snippetsmaker,生成工具^.^")
files := garray.New(true)
files.Append("modules/demo/model/demo_sample.go")
files.Append("modules/demo/service/demo_sample.go")
files.Append("modules/demo/controller/admin/demo_sample.go")
// 遍历files
for _, file := range files.Slice() {
sArray := garray.NewStrArray()
gfile.ReadLines(file.(string), func(line string) error {
// g.Log().Debug(ctx, line)
// println(line)
// search := `Sample`
// replace := `${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}`
// replaceArray := []string{"Sample", "${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}", "sample", "${TM_FILENAME_BASE/(.*)/${1:/downcase}/}", "demo", "${2:模块名称}", "app", "${TM_DIRECTORY/^.+[\\/\\\\]+(.*)$/$1/}"}
replaceArray := []string{"DemoSample", "${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}", "demo_sample", "${TM_FILENAME_BASE/(.*)/${1:/downcase}/}"}
result := gstr.ReplaceByArray(line, replaceArray)
sArray.Append(gstr.AddSlashes(result))
return nil
})
// g.Dump(sArray)
println(file.(string))
println("--------------------------------------code start------------------------------------------")
println(`"body":[`)
sArray.Iterator(
func(index int, value string) bool {
println("\"" + value + "\",")
return true
},
)
println("]")
println("--------------------------------------code end------------------------------------------")
}
return nil
},
}
)
func init() {
Main.AddCommand(&SnippetsMaker)
}

View File

@@ -1,53 +0,0 @@
package cmd
import (
"context"
"github.com/gogf/gf/v2/os/gbuild"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/util/gutil"
)
type sVersion struct {
Name string //程序名称
Homepage string //程序主页
Version string //程序版本
GoFrame string //goframe version
Golang string //golang version
Git string //git commit id
Time string //build datetime
InstallPath string //安装路径
}
var (
Version = gcmd.Command{
Name: "version",
Usage: "cool-tools version",
Brief: "查看版本信息",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
info := gbuild.Info()
binVersion := "v1.5.10"
// 生成sVersion结构体
res := sVersion{
Name: "cool-tools",
Homepage: "https://cool-js.com",
Version: binVersion,
GoFrame: info.GoFrame,
Golang: info.Golang,
Git: info.Git,
Time: info.Time,
InstallPath: gfile.SelfDir(),
}
// mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
gutil.Dump(res)
return nil
},
}
)
// init 初始化模块
func init() {
Main.AddCommand(&Version)
}

View File

@@ -1 +0,0 @@
package consts

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
package packed

View File

@@ -1,235 +0,0 @@
package service
import (
"context"
"runtime"
"strings"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/allyes"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
var (
Install = serviceInstall{}
)
type serviceInstall struct{}
type serviceInstallAvailablePath struct {
dirPath string
filePath string
writable bool
installed bool
}
func (s serviceInstall) Run(ctx context.Context) (err error) {
// Ask where to install.
paths := s.getAvailablePaths()
if len(paths) <= 0 {
mlog.Printf("no path detected, you can manually install cool-tools by copying the binary to path folder.")
return
}
mlog.Printf("I found some installable paths for you(from $PATH): ")
mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path")
// Print all paths status and determine the default selectedID value.
var (
selectedID = -1
newPaths []serviceInstallAvailablePath
pathSet = gset.NewStrSet() // Used for repeated items filtering.
)
for _, path := range paths {
if !pathSet.AddIfNotExist(path.dirPath) {
continue
}
newPaths = append(newPaths, path)
}
paths = newPaths
for id, path := range paths {
mlog.Printf(" %2d | %8t | %9t | %s", id, path.writable, path.installed, path.dirPath)
if selectedID == -1 {
// Use the previously installed path as the most priority choice.
if path.installed {
selectedID = id
}
}
}
// If there's no previously installed path, use the first writable path.
if selectedID == -1 {
// Order by choosing priority.
commonPaths := garray.NewStrArrayFrom(g.SliceStr{
s.getGoPathBin(),
`/usr/local/bin`,
`/usr/bin`,
`/usr/sbin`,
`C:\Windows`,
`C:\Windows\system32`,
`C:\Go\bin`,
`C:\Program Files`,
`C:\Program Files (x86)`,
})
// Check the common installation directories.
commonPaths.Iterator(func(k int, v string) bool {
for id, aPath := range paths {
if strings.EqualFold(aPath.dirPath, v) {
selectedID = id
return false
}
}
return true
})
if selectedID == -1 {
selectedID = 0
}
}
if allyes.Check() {
// Use the default selectedID.
mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID)
} else {
for {
// Get input and update selectedID.
var (
inputID int
input = gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID)
)
if input != "" {
inputID = gconv.Int(input)
}
// Check if out of range.
if inputID >= len(paths) || inputID < 0 {
mlog.Printf("invalid install destination Id: %d", inputID)
continue
}
selectedID = inputID
break
}
}
// Get selected destination path.
dstPath := paths[selectedID]
// Install the new binary.
err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
if err != nil {
mlog.Printf("install cool-tools binary to '%s' failed: %v", dstPath.dirPath, err)
mlog.Printf("you can manually install cool-tools by copying the binary to folder: %s", dstPath.dirPath)
} else {
mlog.Printf("cool-tools binary is successfully installed to: %s", dstPath.dirPath)
}
// Uninstall the old binary.
for _, path := range paths {
// Do not delete myself.
if path.filePath != "" && path.filePath != dstPath.filePath && gfile.SelfPath() != path.filePath {
_ = gfile.Remove(path.filePath)
}
}
return
}
// IsInstalled checks and returns whether the binary is installed.
func (s serviceInstall) IsInstalled() bool {
paths := s.getAvailablePaths()
for _, aPath := range paths {
if aPath.installed {
return true
}
}
return false
}
// getGoPathBinFilePath retrieves ad returns the GOPATH/bin path for binary.
func (s serviceInstall) getGoPathBin() string {
if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
return gfile.Join(goPath, "bin")
}
return ""
}
// getAvailablePaths returns the installation paths data for the binary.
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
var (
folderPaths []serviceInstallAvailablePath
binaryFileName = "cool-tools" + gfile.Ext(gfile.SelfPath())
)
// $GOPATH/bin
if goPathBin := s.getGoPathBin(); goPathBin != "" {
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, goPathBin, binaryFileName,
)
}
switch runtime.GOOS {
case "darwin":
darwinInstallationCheckPaths := []string{"/usr/local/bin"}
for _, v := range darwinInstallationCheckPaths {
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, v, binaryFileName,
)
}
fallthrough
default:
// Search and find the writable directory path.
envPath := genv.Get("PATH", genv.Get("Path").String()).String()
if gstr.Contains(envPath, ";") {
// windows.
for _, v := range gstr.SplitAndTrim(envPath, ";") {
if v == "." {
continue
}
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, v, binaryFileName,
)
}
} else if gstr.Contains(envPath, ":") {
// *nix.
for _, v := range gstr.SplitAndTrim(envPath, ":") {
if v == "." {
continue
}
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, v, binaryFileName,
)
}
} else if envPath != "" {
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, envPath, binaryFileName,
)
} else {
folderPaths = s.checkAndAppendToAvailablePath(
folderPaths, "/usr/local/bin", binaryFileName,
)
}
}
return folderPaths
}
// checkAndAppendToAvailablePath checks if `path` is writable and already installed.
// It adds the `path` to `folderPaths` if it is writable or already installed, or else it ignores the `path`.
func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInstallAvailablePath, dirPath string, binaryFileName string) []serviceInstallAvailablePath {
var (
filePath = gfile.Join(dirPath, binaryFileName)
writable = gfile.IsWritable(dirPath)
installed = gfile.Exists(filePath)
)
if !writable && !installed {
return folderPaths
}
return append(
folderPaths,
serviceInstallAvailablePath{
dirPath: dirPath,
writable: writable,
filePath: filePath,
installed: installed,
})
}

View File

@@ -1,67 +0,0 @@
package service
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/text/gstr"
)
func CreatModule(ctx context.Context, moduleName string) (err error) {
// 检测当前目录是否存在go.mod文件
if !gfile.Exists("go.mod") {
err = gerror.New("当前目录不存在go.mod文件,请在项目根目录下执行")
return
}
module := ""
// 读取go.mod文件第一行文本
gfile.ReadLines("go.mod", func(text string) error {
if gstr.Contains(text, "module") && module == "" {
// println("module:", text)
module = gstr.StrEx(text, "module")
// println("module:", module)
module = gstr.TrimAll(module)
// println("module:", module)
return nil
}
return nil
})
if module == "" {
err = gerror.New("go.mod文件中不存在module行")
return
}
// println(module)
// 创建模块目录
moduleDir := gfile.Join(gfile.Pwd(), "modules", moduleName)
if gfile.Exists(moduleDir) {
err = gerror.New("模块已经存在,请先删除原有模块")
return
}
err = gfile.Mkdir(moduleDir)
if err != nil {
return
}
// 创建模块目录结构
err = gres.Export("cool-admin-go-simple/modules/demo", moduleDir, gres.ExportOption{
RemovePrefix: "cool-admin-go-simple/modules/demo",
})
if err != nil {
return
}
// 替换import路径
err = gfile.ReplaceDir("cool-admin-go-simple/modules/demo", module+"/modules/"+moduleName, moduleDir, "*", true)
if err != nil {
return
}
// 重命名demo.go 为 moduleName.go
err = gfile.Rename(gfile.Join(moduleDir, "demo.go"), gfile.Join(moduleDir, moduleName+".go"))
if err != nil {
return
}
println("创建模块成功:", moduleDir)
return nil
}

View File

@@ -1,22 +0,0 @@
package allyes
import (
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/genv"
)
const (
EnvName = "GF_CLI_ALL_YES"
)
// Init initializes the package manually.
func Init() {
if gcmd.GetOpt("y") != nil {
genv.MustSet(EnvName, "1")
}
}
// Check checks whether option allow all yes for command.
func Check() bool {
return genv.Get(EnvName).String() == "1"
}

View File

@@ -1,66 +0,0 @@
package mlog
import (
"context"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/glog"
)
const (
headerPrintEnvName = "GF_CLI_MLOG_HEADER"
)
var (
ctx = context.TODO()
logger = glog.New()
)
func init() {
logger.SetStack(false)
if genv.Get(headerPrintEnvName).String() == "1" {
logger.SetHeaderPrint(true)
} else {
logger.SetHeaderPrint(false)
}
if gcmd.GetOpt("debug") != nil || gcmd.GetOpt("gf.debug") != nil {
logger.SetDebug(true)
} else {
logger.SetDebug(false)
}
}
// SetHeaderPrint enables/disables header printing to stdout.
func SetHeaderPrint(enabled bool) {
logger.SetHeaderPrint(enabled)
if enabled {
_ = genv.Set(headerPrintEnvName, "1")
} else {
_ = genv.Set(headerPrintEnvName, "0")
}
}
func Print(v ...interface{}) {
logger.Print(ctx, v...)
}
func Printf(format string, v ...interface{}) {
logger.Printf(ctx, format, v...)
}
func Fatal(v ...interface{}) {
logger.Fatal(ctx, v...)
}
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(ctx, format, v...)
}
func Debug(v ...interface{}) {
logger.Debug(ctx, v...)
}
func Debugf(format string, v ...interface{}) {
logger.Debugf(ctx, format, v...)
}

View File

@@ -1,35 +0,0 @@
package main
import (
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/cmd"
_ "github.com/cool-team-official/cool-admin-go/cool-tools/internal/packed"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/allyes"
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
)
const (
cliFolderName = `hack`
)
func main() {
// gres.Dump()
// CLI configuration.
if path, _ := gfile.Search(cliFolderName); path != "" {
if adapter, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile); ok {
if err := adapter.SetPath(path); err != nil {
mlog.Fatal(err)
}
}
}
// -y option checks.
allyes.Init()
err := cmd.Main.RunWithError(gctx.New())
if err != nil {
println(err.Error())
}
}

View File

@@ -1,38 +0,0 @@
server:
address: ":8000"
openapiPath: "/api.json"
swaggerPath: "/swagger"
logger:
level : "all"
stdout: true
database:
default:
- type: "mysql"
link: "root:123456@tcp(127.0.0.1:3306)/cooltest?charset=utf8mb4&parseTime=True&loc=Local"
role: "master"
debug: true
createdAt: "createTime"
updatedAt: "updateTime"
- type: "mysql"
link: "root:123456@tcp(127.0.0.1:3306)/cooltest?charset=utf8mb4&parseTime=True&loc=Local"
role: "slave"
debug: true
createdAt: "createTime"
updatedAt: "updateTime"
test:
type: "sqlite"
link: "./temp/db.sqlite"
logLevel: "all"
createdAt: "createTime"
updatedAt: "updateTime"
bill:
type: "mssql"
# link: "sqlserver://sa:fjTGgpaFlp3LLi3tsB@localhost:1433?database=bill"
link: "server=localhost;user id=sa;password=fjTGgpaFlp3LLi3tsB;port=1433;database=bill;"
logLevel: "all"
createdAt: "createTime"
updatedAt: "updateTime"

View File

@@ -1,21 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: template-single
labels:
app: template-single
spec:
replicas: 1
selector:
matchLabels:
app: template-single
template:
metadata:
labels:
app: template-single
spec:
containers:
- name : main
image: template-single
imagePullPolicy: Always

View File

@@ -1,8 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml

View File

@@ -1,12 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: template-single
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8000
selector:
app: template-single

View File

@@ -1,14 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: template-single-configmap
data:
config.yaml: |
server:
address: ":8000"
openapiPath: "/api.json"
swaggerPath: "/swagger"
logger:
level : "all"
stdout: true

View File

@@ -1,10 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: template-single
spec:
template:
spec:
containers:
- name : main
image: template-single:develop

View File

@@ -1,14 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- configmap.yaml
patchesStrategicMerge:
- deployment.yaml
namespace: default

View File

@@ -1,16 +0,0 @@
FROM loads/alpine:3.8
###############################################################################
# INSTALLATION
###############################################################################
ENV WORKDIR /app
ADD resource $WORKDIR/
ADD ./temp/linux_amd64/main $WORKDIR/main
RUN chmod +x $WORKDIR/main
###############################################################################
# START
###############################################################################
WORKDIR $WORKDIR
CMD ./main

View File

@@ -1,8 +0,0 @@
#!/bin/bash
# This shell is executed before docker build.

Binary file not shown.

View File

@@ -1,37 +0,0 @@
module.exports = {
title: 'CoolAdminGo文档',
description: 'CoolAdminGo文档',
base: '/cool-admin-go/',
locales: {
'/': {
lang: 'zh-CN',
title: 'CoolAdminGo文档',
description: 'CoolAdminGo文档',
},
},
themeConfig: {
logo: '/logo-admin-new.png',
repo: 'https://github.com/cool-team-official/cool-admin-go',
docsDir: 'docs',
editLinks: true,
editLinkText: ' GitHub 上编辑此页',
nav: [
{ text: '首页', link: '/' },
{ text: 'CoolAdmin官网', link: 'https://cool-js.com' },
{ text: 'GoFrame官网', link: 'https://goframe.org' },
],
lastUpdated: '上次更新',
sidebar: [
"/",
"/introduction",
"/feedback",
"/development",
"/cli",
"/quick_start",
"/config",
"/changelog",
"/known_issues",
],
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -1,89 +0,0 @@
#!/bin/bash
###############################################################################
# Install Golang 安装Golang,仅限linux系统. #
# Author: LiDong #
# Email: cnlidong@live.cn #
# Date: 2022-08-15 #
###############################################################################
# 出错退出
set -e
# 进入脚本所在目录
# 获取第一个参数设为版本号
VERSION=$1
# 判断版本号参数是否为空
if [ -z "$VERSION" ]; then
echo "Usage: $0 VERSION [mirror]"
echo "Example: $0 1.19.2 https://mirrors.aliyun.com/golang"
echo "You can visit https://go.dev/dl/ to find version"
exit 1
fi
echo "version: $VERSION"
# 获取第二个参数为镜像地址前缀
PREFIX=$2
# 判断镜像地址前缀参数是否为空
if [ -z "$PREFIX" ]; then
PREFIX="https://go.dev/dl"
fi
echo "prefix: $PREFIX"
# 判断当前用户是否为root不是则退出
if [ "$(id -u)" != "0" ]; then
echo "Error: This script must be run as root."
echo "You can use 'sudo su' command switch to root "
exit 1
fi
# 安装Golang
echo "Install Golang..."
# 获取当前操作系统
OS=$(uname)
# 判断是否是Linux系统,如果不是则退出
if [ $OS != "Linux" ]; then
echo "Not Linux system, exit..."
exit
else
OS="linux"
fi
# 获取CPU类型
ARCH=$(uname -m)
# 转换CPU类型为go env arch格式
if [ $ARCH = "x86_64" ]; then
ARCH="amd64"
elif [ $ARCH = "i686" ]; then
ARCH="386"
elif [ $ARCH = "armv6l" ]; then
ARCH="armv6l"
elif [ $ARCH = "aarch64" ]; then
ARCH="arm64"
else
echo "Not support CPU, exit..."
exit
fi
# 安装Golang
echo "Download Golang..."
echo $PREFIX/go${VERSION}.$OS-$ARCH.tar.gz
curl -L $PREFIX/go${VERSION}.$OS-$ARCH.tar.gz -o /tmp/go${VERSION}.$OS-$ARCH.tar.gz
tar -C /usr/local -xzf /tmp/go${VERSION}.$OS-$ARCH.tar.gz
# 删除临时文件
rm -f /tmp/go${VERSION}.$OS-$ARCH.tar.gz
# 配置环境变量
echo 'export PATH=$PATH:/usr/local/node/bin' >>/etc/profile
echo 'export PATH=$PATH:/usr/local/go/bin' >>/etc/profile
source /etc/profile
echo 'export PATH=$PATH:$(go env GOPATH)/bin' >>/etc/profile
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
source /etc/profile
# 安装成功
echo "Install Golang success!"
# 查看Golang版本
go version
# 提示重启终端
echo "Please restart the terminal to take effect!"
echo "安装成功,请重启终端使其生效!"
echo "You can use 'source /etc/profile' command to make the PATH changes effective immediately."
echo "你可以使用 'source /etc/profile' 命令使其立即生效."

View File

@@ -1,75 +0,0 @@
#!/bin/bash
# Install Node.js on linux x86_64
# 出错时停止
set -e
# 判断当前操作系统是否为Linux,如果不是则退出
if [ "$(uname)" != "Linux" ]; then
echo "Error: This script only supports Linux."
exit 1
fi
# 获取当前CPU架构
ARCH=$(uname -m)
# 判断当前CPU架构是否为x86_64,如果不是则退出
if [ "$ARCH" != "x86_64" ]; then
echo "Error: This script only supports x86_64."
exit 1
fi
# 判断当前是否为root,如果不是则退出
if [ "$(id -u)" != "0" ]; then
echo "Error: This script must be run as root."
echo "You can use 'sudo su' command switch to root "
exit 1
fi
# 获取第一个参数为版本号
VERSION=$1
# 校验版本号
if [ -z "$VERSION" ]; then
echo "Usage: $0 VERSION"
echo "Example: $0 18.12.0"
echo "You can visit https://nodejs.org/en/download/ to find version"
exit 1
fi
# 校验版本号格式
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "Invalid version: $VERSION, should be like 1.2.3"
exit 1
fi
# 下载安装包
wget https://nodejs.org/dist/v$VERSION/node-v$VERSION-linux-x64.tar.xz
# 解压
tar -xvf node-v$VERSION-linux-x64.tar.xz
# 移动到 /usr/local
mv node-v$VERSION-linux-x64 /usr/local/node
# 添加到环境变量
echo 'export PATH=$PATH:/usr/local/node/bin' >>/etc/profile
# 使环境变量生效
source /etc/profile
# 删除安装包
rm node-v$VERSION-linux-x64.tar.xz
# 查看版本
node -v
npm -v
# 激活yarn
corepack enable
# 配置淘宝镜像
npm config set registry https://registry.npmmirror.com
# 查看配置
npm config list
# 提示安装成功
echo "Node.js $VERSION installed successfully."
# 提示用户重启终端
echo "Please restart your terminal to make the PATH changes effective."
echo "You can use 'source /etc/profile' command to make the PATH changes effective immediately."

View File

@@ -1,25 +0,0 @@
# CoolAdminGo 文档
本文档使用`vuepress`编写可使用`cool-tools docs`进行本地预览版本随`CoolAdminGo`版本更新
## 文档目录
- [介绍](introduction.md)
- [问题反馈](feedback.md)
- [开发环境](development.md)
- [开发工具](cli.md)
- [快速开始](quick_start.md)
- [配置](config.md)
- [CRUD](crud.md)
- [数据库](database.md)
- [文件上传](file_upload.md)
- [表单验证](form_validate.md)
- [系统模块](system_module.md)
- [分布式函数](distributed_function.md)
- [定时任务](cron.md)
- [部署](deploy.md)
- [日志](log.md)
- [常见问题](faq.md)
- [更新日志](changelog.md)
- [已知问题](known_issues.md)
- [贡献代码](contributing.md)

View File

@@ -1,92 +0,0 @@
# 更新日志
## 1.5.0
- 更新 gf v2.4.0
## 1.0.19
- 修复base/parma模块的bug 感谢 @vera-byte
## 1.0.18
- 增加app接口下的EPS以匹配cool-uni
- 更新依赖
## 1.0.17
- list page 接口增加 ModifyAfter 钩子,用于对数据进行修改
- 更新 gf v2.3.2
## 1.0.16
- 更新依赖
- cool-tools创建的项目增加容器开发环境
## 1.0.15
- 更新 gf v2.3.1
## 1.0.14
- 更新 gf v2.3.0
- 调整 base 模块中的事务对象为接口定义以匹配 gf 的变更
- 引入 redis (gf v2.3.0 版本 redis 拆分为单独的库)
- 增加用户时对密码进行 md5 加密
## 1.0.13
- 增加 pgsql 支持
- 调整部分表字段类型以兼容 pgsql
## 1.0.12
- 更新 gf 版本至 v2.2.6(sql 统计中 total int64 修改为 int)
## 1.0.11
- 修复 dict 模块的 bug
## 1.0.10
- 修复 base/menu/add 不支持数组菜单的问题
## 1.0.9
- GetCfgWithDefault 函数支持环境变量,优先级 配置文件>环境变量>默认值
- 更新 gf 版本至 v2.2.5 修复软删除 bug
## 1.0.8
- 更新依赖包版本
- 调整 cool-tools version 命令输出格式
- 修复 go install 模式安装的 cool-tools docs 命令无法使用的问题
## 1.0.7
- 更新 gf 依赖至 v2.2.4
- cool-tools 增加 -y 支持
- 集成 gf pack
## 1.0.6
- docs 独立为单独 mod,减小主库体积
- 清理部分无用文件,减小主库体积
- 更新依赖
- 主库移除内置的前端
- 增加 make frontend 命令,用于构建前端
- 权限中间件移除部分 Debug 日志
- 清理 cool-tools 模块部分无用文件
- 引入 gf run 命令至 cool-tools 模块
- 引入 gf build 命令至 cool-tools 模块
- 调整 cool-tools 使用 hack 目录下的配置文件
## 1.0.5
- 本次更新主要针对主库开发环境
- Added changelog.md
- 更新主框架开发用前端打包脚本 使用 make public 更新
- 更新主框架支持 remote container 开发, 基于`cool-admin-codespace`镜像
- 调整优化文档发布更新了一些依赖

View File

@@ -1,88 +0,0 @@
# 开发工具
[返回目录](README.md)
::: warning 注意
以下部分命令需您已经安装了 Go 语言环境如果没有安装请自行安装如果已经安装请自行配置环境变量
:::
## cool-tools
cool-tools 是一个用于快速生成`CoolAdminGo`项目的脚手架工具可用于快速生成项目模块页面接口等
### `cool-tools`安装
Linux, Mac 可使用以下命令安装
github 下载
```bash
wget -O cool-tools \
https://github.com/cool-team-official/cool-admin-go/releases/latest/download/cool-tools_$(go env GOOS)_$(go env GOARCH) \
&& chmod +x cool-tools \
&& ./cool-tools install \
&& rm ./cool-tools
```
从镜像下载
```bash
wget -O cool-tools \
https://gh.hjmcloud.cn/github.com/cool-team-official/cool-admin-go/releases/latest/download/cool-tools_$(go env GOOS)_$(go env GOARCH) \
&& chmod +x cool-tools \
&& ./cool-tools install \
&& rm ./cool-tools
```
验证
```bash
cool-tools version
```
Windows 可以直接下载编译后的可执行文件下载地址[releases](https://github.com/cool-team-official/cool-admin-go/releases),选择对应的版本下载。下载后复制到`PATH`环境变量中的目录下即可。
::: tip 提示
在正确地将 GOPATH/bin 目录添加到 PATH 环境变量中后可以直接使用`go install`命令安装,因为该安装方式为本地编译安装,可享受`goproxy`的加速服务,安装速度更快,适用于所有平台
:::
```bash
go install github.com/cool-team-official/cool-admin-go/cool-tools@latest
```
## gf
`GoFrame`框架提供了功能强大的`gf`命令行开发辅助工具是框架发展的一个重要组成部分
### `gf`安装
Linux, Mac 可使用以下命令安装
github 下载
```bash
wget -O gf \
https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) \
&& chmod +x gf \
&& ./gf install \
&& rm ./gf
```
使用镜像下载
```
wget -O gf \
https://gh.hjmcloud.cn/github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) \
&& chmod +x gf \
&& ./gf install \
&& rm ./gf
```
验证
```bash
gf version
```
更多`gf`工具的安装使用说明,可以访问 [https://goframe.org/pages/viewpage.action?pageId=1114260](https://goframe.org/pages/viewpage.action?pageId=1114260)

View File

@@ -1,154 +0,0 @@
# 配置
[返回目录](README.md)
## 配置文件
`CoolAdminGo`配置文件默认位于`mainfest/config/config.yaml`.继承自`GoFrame`的配置文件支持多种格式包括`yaml``toml``json``ini``xml`可访问[GoFrame 配置文件](https://goframe.org/pages/viewpage.action?pageId=1114668)查看更多配置文件格式。
## 数据库配置
`CoolAdminGo`支持多种数据库可通过引入不同的依赖包进行切换同时您也可以开发自己的数据库驱动
### SQLite 配置
使用`sqllite`数据库时需在`main.go`中引入`_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/sqlite"`然后在`config.yaml`中配置`sqlite`数据库
::: warning 注意
`sqlite`引入应早于使用数据库的包 为防止编辑器自动排序可在数据包引入下方加一个空行
:::
```go
// main.go
import (
// 引入sqlite驱动
_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/sqlite"
// 引入其他包
"github.com/cool-team-official/cool-admin-go/pkg/dao"
)
```
配置文件中相关配置如下
```yaml
database:
default: # 数据源名称,当不指定数据源时 default 为默认数据源
type: "sqlite" # 数据库类型
link: "cool.sqlite" # 数据库文件名称,可以带路径,如:/tmp/cool.sqlite
extra: busy_timeout=5000 # 数据库连接扩展参数
createdAt: "createTime" # 创建时间字段
updatedAt: "updateTime" # 更新时间字段
debug: true # 是否开启调试模式开启后会打印SQL日志
```
### MySQL 配置
使用`mysql`数据库时需在`main.go`中引入`_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/mysql"`然后在`config.yaml`中配置`mysql`数据库
::: warning 注意
`mysql`引入应早于使用数据库的包 为防止编辑器自动排序可在数据包引入下方加一个空行
:::
```go
// main.go
import (
// 引入mysql驱动
_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/mysql"
// 引入其他包
"github.com/cool-team-official/cool-admin-go/pkg/dao"
)
```
配置文件中相关配置如下
```yaml
database:
default: # 数据源名称,当不指定数据源时 default 为默认数据源
type: "mysql" # 数据库类型
host: "127.0.0.1" # 数据库地址
port: "3306" # 数据库端口
user: "root" # 数据库用户名
pass: "123456" # 数据库密码
name: "cooltest" # 数据库名称
charset: "utf8mb4" # 数据库编码
timezone: "Asia/Shanghai" # 数据库时区
debug: true # 是否开启调试模式开启后会打印SQL日志
createdAt: "createTime" # 创建时间字段
updatedAt: "updateTime" # 更新时间字段
```
可以使用`docker-compose`快速启动一个`mysql`数据库配置如下
```yaml
# docker-compose.yaml
version: "3"
services:
# mysql8 数据库
mysql8:
image: mysql:8
container_name: mysql8
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
# restart: always # 重启策略
environment:
TZ: Asia/Shanghai # 指定时区
MYSQL_ROOT_PASSWORD: "123456" # 配置root用户密码
MYSQL_DATABASE: "cooltest" # 业务库名
MYSQL_USER: "cooltest" # 业务库用户名
MTSQL_PASSWORD: "123123" # 业务库密码
ports:
- 3306:3306
volumes:
- ./data/mysql/:/var/lib/mysql/
```
启动`mysql`数据库
```bash
docker compose -f "docker-compose.yml" up -d --build mysql8
```
关闭`mysql`数据库
```bash
docker compose -f "docker-compose.yml" down mysql8
```
### PostgreSQL 配置
使用`postgresql`数据库时需在`main.go`中引入`_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/pgsql"`然后在`config.yaml`中配置`postgresql`数据库
::: warning 注意
`postgresql`引入应早于使用数据库的包 为防止编辑器自动排序可在数据包引入下方加一个空行
:::
```go
// main.go
import (
// 引入postgresql驱动
_ "github.com/cool-team-official/cool-admin-go/contrib/drivers/pgsql"
// 引入其他包
"github.com/cool-team-official/cool-admin-go/pkg/dao"
)
```
配置文件中相关配置如下
```yaml
database:
default:
type: "pgsql" # 数据库类型
host: "127.0.0.1" # 数据库地址
port: "5432" # 数据库端口
user: "cooltest" # 数据库用户名
pass: "123456" # 数据库密码
name: "cooltest" # 数据库名称
debug: true # 是否开启调试模式开启后会打印SQL日志
createdAt: "createTime" # 创建时间字段
updatedAt: "updateTime" # 更新时间字段
```

View File

@@ -1,118 +0,0 @@
# 开发环境
[返回目录](README.md)
::: warning 注意
推荐使用 Linux MacOS 进行开发Windows 下可使用 WSL2
Linux WSL2 下推荐使用 root 用户进行开发.
:::
## Node.js 环境
官网下载地址[https://nodejs.org/en/download/](https://nodejs.org/en/download/)
一般选择 LTS 版本即可
MacOS 下可使用 Homebrew 进行安装 nvm
```bash
brew install nvm
```
nvm node 版本管理工具可以通过`nvm install <version>` 安装指定版本使用 `nvm use <version>` 切换版本
或者直接下载 pkg 安装包进行安装
Linux 下可使用以下脚本进行安装
```bash
wget -O nodejs-install.sh https://cool-team-official.github.io/cool-admin-go/scripts/nodejs-install.sh \
&& chmod +x nodejs-install.sh \
&& ./nodejs-install.sh 18.12.0
```
脚本文件内容如下:
<<< @/docs/.vuepress/public/scripts/nodejs-install.sh
::: tip
安装完成后可使用`node -v`查看版本号使用`npm -v`查看 npm 版本号
为提高依赖包下载速度可使用`npm config set registry https://registry.npmmirror.com`切换到淘宝镜像
新版本的 node 已经集成了 yarn,需激活`corepack`,可使用 `corepack enable`命令激活激活后可使用`yarn -v`查看版本号
Linux 安装脚本已完成镜像切换及 corepack 激活
:::
## Go 环境
官网下载地址[https://go.dev/dl/](https://go.dev/dl/)
一般选择最新版本即可
MacOS 下可使用 Homebrew 进行安装
```bash
brew install go
```
或者直接下载 pkg 安装包进行安装
Linux 下可使用以下脚本进行安装
```bash
wget -O golang-install.sh https://cool-team-official.github.io/cool-admin-go/scripts/golang-install.sh \
&& chmod +x golang-install.sh \
&& ./golang-install.sh 1.19.3
```
脚本文件内容如下:
<<< @/docs/.vuepress/public/scripts/golang-install.sh
::: tip
安装完成后可使用`go version`查看版本号
为提高依赖下载速度推荐配置`goproxy`可使用`go env -w GOPROXY=https://goproxy.cn,direct`切换到 goproxy.cn 镜像
:::
## VSCode
官网下载地址[https://code.visualstudio.com/](https://code.visualstudio.com/)
一般选择最新版本即可
推荐安装以下插件
- [Go](https://marketplace.visualstudio.com/items?itemName=golang.go)
- [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)
- [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
## Docker
云原生时代Docker 已经成为开发者必备的工具之一
开发过程中我们将使用 Docker 进行数据库管理以及打包测试
官网下载地址[https://www.docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop)
一般选择最新版本即可
配置 Docker 镜像加速器
```bash
# Linux
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
```
MacOS Windows 下可在 Docker Desktop 的设置中配置

View File

@@ -1,48 +0,0 @@
#!/usr/bin/env sh
# This script deploys the documentation to the gh-pages branch.
# 发布文档到 https://cooladmingo.github.io
# 确保脚本抛出遇到的错误
set -e
# 检测是否存在 package.json,如果不存在说明运行目录不对
if [ ! -f "package.json" ]; then
echo "package.json not found, please run this script in the root directory of the project"
exit 1
fi
# 生成静态文件
npm run docs:build
# 进入生成的文件夹
cd docs/.vuepress/dist
# 如果是发布到自定义域名
# echo 'www.example.com' > CNAME
# 获取当前时间
now=$(date "+%Y.%m.%d-%H.%M.%S")
echo "${now}" > version.txt
git init
git add -A
git commit -m 'deploy'
# 如果当前运行在 github codespace , 则使用 https 方式提交.否则使用 ssh 方式提交
if [ -n "$CODESPACES" ]; then
echo "github codespace detected, using https to push"
git push -f https://github.com/cool-team-official/cool-admin-go.git main:gh-pages
else
echo "github codespace not detected, use ssh"
git push -f git@github.com:cool-team-official/cool-admin-go.git master:gh-pages
fi
# 如果发布到 https://<USERNAME>.github.io
# git push -f git@github.com:cooladmingo/cooladmingo.github.io.git master:gh-pages
# git push -f https://github.com/cool-team-official/cool-admin-go.git master:gh-pages
# 如果发布到 https://<USERNAME>.github.io/<REPO>
# git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages
cd -

View File

@@ -1 +0,0 @@
package docs

View File

@@ -1,13 +0,0 @@
# 问题反馈
[返回目录](README.md)
## 方式一
在代码仓库中提交[issue](https://github.com/cool-team-official/cool-admin-go/issues)
## 方式二
技术交流群
![企业微信](https://cool-js.com/wechat.png)

View File

@@ -1,3 +0,0 @@
module github.com/cool-team-official/cool-admin-go/docs
go 1.18

View File

@@ -1,30 +0,0 @@
# 项目介绍
[返回目录](README.md)
`CoolAdminGo` 基于 [goframe](https://goframe.org) 开发的后端项目,提供了基础的 CURD 结构,以及基础的用户管理模块,可以快速搭建后端项目。做为`CoolAdmin`系列的`go`版本后端,提供了与`node`版本后端相同的接口,可以快速切换前端项目。
`GoFrame`是一款模块化高性能企业级的 Go 基础开发框架`GoFrame`是一款通用性的基础开发框架 Golang 标准库的一个增强扩展级包含通用核心的基础开发组件优点是实战化模块化文档全面模块丰富易用性高通用性强面向团队如果您想使用 Golang 开发一个业务型项目无论是小型还是中大型项目`GoFrame`是您的不二之选如果您想开发一个 Golang 组件库`GoFrame`提供开箱即用丰富强大的基础组件库也能助您的工作事半功倍如果您是团队 Leader`GoFrame`丰富的资料文档详尽的代码注释活跃的社区成员将会极大降低您的指导成本支持团队快速接入语言转型与能力提升
## 代码仓库
CoolAdminGo 是开源免费的遵循`MIT`开源协议意味着您无需支付任何费用也无需授权即可将它应用到您的产品中
::: warning 注意
开源免费并不意味着您可以将 cool-admin 应用到非法的领域比如涉及赌博暴力等方面如因此产生纠纷等法律问题cool-admin 不承担任何责任
:::
CoolAdminGo 代码仓库地址[https://github.com/cool-team-official/cool-admin-go](https://github.com/cool-team-official/cool-admin-go)
::: tip 提示
如果您觉得 CoolAdminGo 帮助到了您欢迎给我们点个 star您的支持是我们最大的动力
通常情况下您并不需要直接使用 CoolAdminGo 代码仓库而是使用我们提供的脚手架工具快速搭建项目
:::
## 技术选型
- [CoolAdmin](https://cool-js.com)`CoolAdmin` 项目官网。
- [GoFrame](https://goframe.org)`GoFrame` 项目官网, `CoolAdminGo` 基于 `GoFrame` 开发。
- [Gorm](https://gorm.io)`Gorm` 数据库操作库, `CoolAdminGo` 基于 `Gorm` 实现数据表创建功能。
- [Goproxy](https://goproxy.cn)`Goproxy` 代理。

View File

@@ -1,4 +0,0 @@
# 已知问题
[返回目录](README.md)

View File

@@ -1,49 +0,0 @@
# 快速开始
[返回目录](README.md)
## 工程目录
一个合理的工程目录可以让开发更加高效`CoolAdminGo`项目推荐的工程目录如下
```bash
# 假定项目名称为 cool-study
cool-study/
├── backend # 后端代码
├── cool-study # 主库,存放生产部署相关脚本等
├── frontend # 前端代码
└── mobile # 移动端代码
```
## 创建后端项目
```bash
# 创建工程目录
mkdir cool-study
# 进入工程目录
cd cool-study
# 创建后端代码目录
cool-tools init backend
# 进入后端代码目录
cd backend
# 安装依赖
go mod tidy
# 开发模式运行后端项目
gf run main.go
```
## 创建前端项目
```bash
# 进入工程目录
cd cool-study
# 拉取前端代码
git clone https://github.com/cool-team-official/cool-admin-vue frontend
# 如果网络不好,可以使用国内镜像
git clone https://gitee.com/cool-team-official/cool-admin-vue frontend
# 进入前端代码目录
cd frontend
# 安装依赖
yarn
```

View File

@@ -1,5 +0,0 @@
node_modules
.DS_Store
dist
dist-ssr
*.local

View File

@@ -1,11 +0,0 @@
# 🎨 editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true

View File

@@ -1 +0,0 @@
vite.config.ts

View File

@@ -1,66 +0,0 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
ecmaFeatures: {
jsx: true,
tsx: true
}
},
extends: [
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended"
],
rules: {
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-function": "off",
"vue/component-name-in-template-casing": ["error", "kebab-case"],
"vue/component-definition-name-casing": ["error", "kebab-case"],
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^h$",
varsIgnorePattern: "^h$"
}
],
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^h$",
varsIgnorePattern: "^h$"
}
],
"space-before-function-paren": "off",
"vue/attributes-order": "off",
"vue/one-component-per-file": "off",
"vue/html-closing-bracket-newline": "off",
"vue/max-attributes-per-line": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/multi-word-component-names": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/attribute-hyphenation": "off",
"vue/html-self-closing": "off",
"vue/require-default-prop": "off",
"vue/v-on-event-hyphenation": "off"
}
};

View File

@@ -1,4 +0,0 @@
*.js text eol=lf
*.json text eol=lf
*.ts text eol=lf
*.vue text eol=lf

5
frontend/.gitignore vendored
View File

@@ -1,5 +0,0 @@
node_modules
.DS_Store
dist
dist-ssr
*.local

View File

@@ -1,8 +0,0 @@
{
"tabWidth": 4,
"useTabs": true,
"semi": true,
"singleQuote": false,
"printWidth": 100,
"trailingComma": "none"
}

View File

@@ -1,15 +0,0 @@
{
"module-config": {
"prefix": "module-config",
"scope": "typescript",
"body": [
"import { ModuleConfig } from \"/@/cool\";",
"",
"export default (): ModuleConfig => {",
" return {};",
"};",
""
],
"description": "module config snippets"
}
}

View File

@@ -1,66 +0,0 @@
{
"cl-crud": {
"prefix": "cl-crud",
"scope": "vue",
"body": [
"<template>",
" <cl-crud ref=\"Crud\">",
" <cl-row>",
" <!-- 刷新按钮 -->",
" <cl-refresh-btn />",
" <!-- 新增按钮 -->",
" <cl-add-btn />",
" <!-- 删除按钮 -->",
" <cl-multi-delete-btn />",
" <cl-flex1 />",
" <!-- 关键字搜索 -->",
" <cl-search-key />",
" </cl-row>",
"",
" <cl-row>",
" <!-- 数据表格 -->",
" <cl-table ref=\"Table\" />",
" </cl-row>",
"",
" <cl-row>",
" <cl-flex1 />",
" <!-- 分页控件 -->",
" <cl-pagination />",
" </cl-row>",
"",
" <!-- 新增、编辑 -->",
" <cl-upsert ref=\"Upsert\" />",
" </cl-crud>",
"</template>",
"",
"<script lang=\"ts\" name=\"菜单名称\" setup>",
"import { useCrud, useTable, useUpsert } from \"@cool-vue/crud\";",
"import { useCool } from \"/@/cool\";",
"",
"const { service } = useCool();",
"",
"// cl-upsert 配置",
"const Upsert = useUpsert({",
" items: []",
"});",
"",
"// cl-table 配置",
"const Table = useTable({",
" columns: []",
"});",
"",
"// cl-crud 配置",
"const Crud = useCrud(",
" {",
" service: service.demo.goods",
" },",
" (app) => {",
" app.refresh();",
" }",
");",
"</script>",
""
],
"description": "cl-crud snippets"
}
}

View File

@@ -1,4 +0,0 @@
{
"editor.cursorSmoothCaretAnimation": true,
"editor.formatOnSave": true,
}

View File

@@ -1,16 +0,0 @@
FROM node:lts-alpine
WORKDIR /build
# 设置Node-Sass的镜像地址
RUN npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
# 设置npm镜像
RUN npm config set registry https://registry.npm.taobao.org
COPY package.json /build/package.json
RUN yarn
COPY ./ /build
RUN npm run build
FROM nginx
RUN mkdir /app
COPY --from=0 /build/dist /app
COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 cool-team-official
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,65 +0,0 @@
# cool-admin [vue3 - ts - vite]
<p align="center">
<a href="https://show.cool-admin.com/" target="blank"><img src="https://admin.cool-js.com/logo.png" width="200" alt="cool-admin Logo" /></a>
</p>
<p align="center">cool-admin 一个很酷的后台权限管理系统开源免费模块化插件化极速开发 CRUD方便快速构建迭代后台管理系统 <a href="https://cool-js.com" target="_blank">文档</a> 进一步了解</p>
<p align="center">
<a href="https://github.com/cool-team-official/cool-admin-vue/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="GitHub license" />
<a href=""><img src="https://img.shields.io/github/package-json/v/cool-team-official/cool-admin-vue?style=flat-square" alt="GitHub tag"></a>
<img src="https://img.shields.io/github/last-commit/cool-team-official/cool-admin-vue?style=flat-square" alt="GitHub tag"></a>
</p>
## 地址
- [ vue2.x + element-ui](https://github.com/cool-team-official/cool-admin-vue)
- [ vue3.x + element-plus + ts + webpack](https://github.com/cool-team-official/cool-admin-vue/tree/vue3-ts-webpack)
- [📌 vue3.x + element-plus + ts + vite](https://github.com/cool-team-official/cool-admin-vue/tree/vue3-ts-vite)
- [🌐 码云仓库地址](https://gitee.com/cool-team-official/cool-admin-vue)
## 演示
[https://show.cool-admin.com](https://show.cool-admin.com)
账户admin密码123456
<img src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/home-mini.png" alt="Admin Home" ></a>
## 项目后端
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)
## 微信群
<img width="260" src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/wechat.jpeg" alt="Admin Wechat"></a>
## 安装项目依赖
推荐使用 `yarn`
```shell
yarn
```
解决 `node-sass` 网络慢的方法
```shell
yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass
```
## 运行应用程序
安装过程完成后运行以下命令启动服务您可以在浏览器中预览网站 [http://localhost:9000](http://localhost:9000)
```shell
yarn dev
```
### 低价服务器
[阿里云腾讯云华为云低价云服务器不限新老](https://cool-js.com/ad/server.html)

View File

@@ -1,69 +0,0 @@
import { Plugin } from "vite";
import { parseJson } from "./utils";
import { createEps, createMenu, createSvg, createTag, getEps, getModules } from "./lib";
export function cool(): Plugin {
return {
name: "vite-cool",
enforce: "pre",
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
function done(data: any) {
res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
res.end(JSON.stringify(data));
}
if (req.url?.includes("__cool")) {
const body = await parseJson(req);
let next: any;
switch (req.url) {
// 快速创建菜单
case "/__cool_createMenu":
next = createMenu(body);
break;
// 获取模块列表
case "/__cool_modules":
next = getModules();
break;
// 创建描述文件
case "/__cool_eps":
next = createEps(body);
break;
}
if (next) {
next.then((data: any) => {
done({
code: 1000,
data
});
}).catch((err: Error) => {
done({
code: 1001,
message: err.message
});
});
}
} else {
next();
}
});
},
transform(code, id) {
return createTag(code, id);
},
transformIndexHtml(html) {
return createSvg(html);
},
config() {
return {
define: {
__EPS__: getEps()
}
};
}
};
}

View File

@@ -1,35 +0,0 @@
export default {
entity: {
mapping: [
{
// 自定义匹配
custom: ({ entityName, propertyName, type }) => {
// status 原本是tinyint如果是1的话== true 是可以的,但是不能 === true请谨慎使用
if (propertyName === "status" && type == "tinyint") return "boolean";
// 如果没有返回null或者不返回则继续遍历其他匹配规则
return null;
}
},
{
type: "string",
test: ["varchar", "text","simple-json"]
},
{
type: "string[]",
test: ["simple-array"]
},
{
type: "Date",
test: ["datetime", "date"]
},
{
type: "number",
test: ["tinyint", "int", "decimal"]
},
{
type: "BigInt",
test: ["bigint"]
}
]
}
};

View File

@@ -1,311 +0,0 @@
import prettier from "prettier";
import { isEmpty, last } from "lodash";
import { createDir, firstUpperCase, readFile, toCamel } from "../../utils";
import { createWriteStream } from "fs";
import { join } from "path";
import config from "./config";
interface Options {
list: {
prefix: string;
name: string;
columns: any[];
api: {
name: string;
method: string;
path: string;
summary: string;
dts: {
parameters: {
description: string;
schema: {
type: string;
};
name: string;
required: boolean;
}[];
};
}[];
}[];
service: {
[key: string]: any;
};
}
// 临时目录路径
const tempPath = join(__dirname, "../../temp");
// 获取类型
function getType({ entityName, propertyName, type }) {
for (const map of config.entity.mapping) {
if (map.custom) {
const resType = map.custom({ entityName, propertyName, type });
if (resType) return resType;
}
if (map.test) {
if (map.test.includes(type)) return map.type;
}
}
return type;
}
// 创建 Entity
function createEntity({ list }: Options) {
const t0: any[] = [];
for (const item of list) {
if (!item.name) continue;
const t = [`interface ${item.name} {`];
for (const col of item.columns || []) {
// 描述
t.push("\n");
t.push("/**\n");
t.push(` * ${col.comment}\n`);
t.push(" */\n");
t.push(
`${col.propertyName}?: ${getType({
entityName: item.name,
propertyName: col.propertyName,
type: col.type
})};`
);
}
t.push("\n");
t.push("/**\n");
t.push(` * 任意键值\n`);
t.push(" */\n");
t.push(`[key: string]: any;`);
t.push("}");
t0.push(t);
}
return t0.map((e) => e.join("")).join("\n\n");
}
// 创建 Service
function createService({ list, service }: Options) {
const t0: any[] = [];
const t1 = [
`type Service = {
request(options: {
url: string;
method?: 'POST' | 'GET' | string;
data?: any;
params?: any;
proxy?: boolean;
[key: string]: any;
}): Promise<any>;
`
];
// 处理数据
function deep(d: any, k?: string) {
if (!k) k = "";
for (const i in d) {
const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, "")));
if (d[i].namespace) {
// 查找配置
const item = list.find((e) => (e.prefix || "").includes(d[i].namespace));
if (item) {
const t = [`interface ${name} {`];
t1.push(`${i}: ${name};`);
// 插入方法
if (item.api) {
// 权限列表
const permission: string[] = [];
item.api.forEach((a) => {
// 方法名
const n = toCamel(a.name || last(a.path.split("/")) || "").replace(
/[:\/-]/g,
""
);
if (n) {
// 参数类型
let q: string[] = [];
// 参数列表
const { parameters = [] } = a.dts || {};
parameters.forEach((p) => {
if (p.description) {
q.push(`\n/** ${p.description} */\n`);
}
if (p.name.includes(":")) {
return false;
}
const a = `${p.name}${p.required ? "" : "?"}`;
const b = `${p.schema.type || "string"}`;
q.push(`${a}: ${b},`);
});
if (isEmpty(q)) {
q = ["any"];
} else {
q.unshift("{");
q.push("}");
}
// 返回类型
let res = "";
// 实体名
const en = item.name || "any";
switch (a.path) {
case "/page":
res = `
{
pagination: { size: number; page: number; total: number };
list: ${en} [];
[key: string]: any;
}
`;
break;
case "/list":
res = `${en} []`;
break;
case "/info":
res = en;
break;
default:
res = "any";
break;
}
// 描述
t.push("\n");
t.push("/**\n");
t.push(` * ${a.summary || n}\n`);
t.push(" */\n");
t.push(
`${n}(data${q.length == 1 ? "?" : ""}: ${q.join(
""
)}): Promise<${res}>;`
);
}
permission.push(n);
});
// 权限标识
t.push("\n");
t.push("/**\n");
t.push(" * 权限标识\n");
t.push(" */\n");
t.push(
`permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };`
);
// 权限状态
t.push("\n");
t.push("/**\n");
t.push(" * 权限状态\n");
t.push(" */\n");
t.push(
`_permission: { ${permission
.map((e) => `${e}: boolean;`)
.join("\n")} };`
);
// 请求
t.push("\n");
t.push("/**\n");
t.push(" * 请求\n");
t.push(" */\n");
t.push(`request: Service['request']`);
}
t.push("}");
t0.push(t);
}
} else {
t1.push(`${i}: {`);
deep(d[i], name);
t1.push(`},`);
}
}
}
// 深度
deep(service);
// 结束
t1.push("}");
// 追加
t0.push(t1);
return t0.map((e) => e.join("")).join("\n\n");
}
// 创建描述文件
export async function createEps(options: Options) {
// 文件内容
const text = `
declare namespace Eps {
${createEntity(options)}
${createService(options)}
}
`;
// 文本内容
const content = prettier.format(text, {
parser: "typescript",
useTabs: true,
tabWidth: 4,
endOfLine: "lf",
semi: true,
singleQuote: false,
printWidth: 100,
trailingComma: "none"
});
// 创建 temp 目录
createDir(tempPath);
// 创建 eps 描述文件
createWriteStream(join(tempPath, "eps.d.ts"), {
flags: "w"
}).write(content);
// 创建 eps 数据文件
createWriteStream(join(tempPath, "eps.json"), {
flags: "w"
}).write(
JSON.stringify(
(options.list || []).map((e) => {
const req = e.api.map((a) => {
const arr = [a.name ? `/${a.name}` : a.path];
if (a.method) {
arr.push(a.method);
}
return arr;
});
return [e.prefix, e.name || "", req];
})
)
);
}
// 获取描述
export function getEps() {
return JSON.stringify(readFile(join(tempPath, "eps.json")));
}

View File

@@ -1,5 +0,0 @@
export * from "./eps";
export * from "./menu";
export * from "./module";
export * from "./svg";
export * from "./tag";

View File

@@ -1,364 +0,0 @@
import { createWriteStream } from "fs";
import prettier from "prettier";
import { join } from "path";
import { mkdirs } from "../../utils";
import rules from "./rules";
import { isFunction, isRegExp, isString } from "lodash";
// 格式化
function format(data: any) {
return {
label: data.label,
prop: data.prop,
...data,
component: data.component
};
}
// 颜色
const colors = [
"#409EFF",
"#67C23A",
"#E6A23C",
"#F56C6C",
"#909399",
"#B0CFEB",
"#FF9B91",
"#E6A23C",
"#BFAD6F",
"#FB78F2"
];
// 组件处理器
const handler = {
// 单选
dict({ comment }) {
const [label, ...arr] = comment.split(" ");
// 选择列表
const list = arr.map((e: string, i: number) => {
const [value, label] = e.split("-");
const d: any = {
label,
value: isNaN(Number(value)) ? value : Number(value)
};
if (i > 0 && colors[i]) {
d.color = colors[i];
}
return d;
});
const d: any = {
table: {
label,
dict: list
},
form: {
label,
component: {
name: "",
options: list
}
}
};
// 默认值
if (list[0]) {
d.form.value = list[0].value;
}
// 匹配组件
d.form.component.name = arr.length > 4 ? "el-select" : "el-radio-group";
return d;
},
// 多选
dict_multiple({ comment }) {
const { table, form }: any = handler.dict({ comment });
if (!form.component.props) {
form.component.props = {};
}
if (!form.value) {
form.value = [];
}
switch (form.component.name) {
case "el-select":
form.component.props.multiple = true;
form.component.props.filterable = true;
break;
case "el-radio-group":
form.component.name = "el-checkbox-group";
break;
}
return {
table,
form
};
}
};
// 创建组件
function createComponent(item: any) {
const prop = item.propertyName;
let label = item.comment;
let d: any;
rules.forEach((r: any) => {
const s = r.test.find((e: any) => {
if (isRegExp(e)) {
return e.test(prop);
}
if (isFunction(e)) {
return e(prop);
}
if (isString(e)) {
const re = new RegExp(`${e}$`);
return re.test(prop.toLocaleLowerCase());
}
return false;
});
if (s) {
if (r.handler) {
const fn = isString(r.handler) ? handler[r.handler] : r.handler;
if (isFunction(fn)) {
d = fn(item);
}
} else {
d = {
...r,
test: undefined
};
}
}
});
function parse(v: any) {
label = label.split(" ")[0];
if (v?.name) {
return {
prop,
label,
component: v
};
} else {
return {
prop,
label,
...v
};
}
}
return {
column: parse(d?.table),
item: parse(d?.form)
};
}
// 获取页面标识
function getPageName(router: string) {
if (router.indexOf("/") === 0) {
router = router.substr(1, router.length);
}
return router ? router.replace(/\//g, "-") : "";
}
// 时间合并
function datetimeMerge({ columns, item }: any) {
if (["startTime", "startDate"].includes(item.prop)) {
const key = item.prop.replace("start", "");
if (columns.find((e: any) => e.propertyName == "end" + key)) {
item.prop = key.toLocaleLowerCase();
const isTime = item.prop == "time";
item.label = isTime ? "时间范围" : "日期范围";
item.hook = "datetimeRange";
item.component = {
name: "el-date-picker",
props: {
type: isTime ? "datetimerange" : "daterange",
valueFormat: isTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD 00:00:00",
defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]
}
};
}
}
}
// 创建文件
export async function createMenu({ router, columns, prefix, api, filePath }: any) {
const upsert: any = {
items: []
};
const table: any = {
columns: []
};
// 遍历
columns.forEach((e: any) => {
// 组件
const { item, column }: any = createComponent(e);
// 验证规则
if (!e.nullable) {
item.required = true;
}
// 忽略部分字段
if (!["createTime", "updateTime", "id", "endTime", "endDate"].includes(item.prop)) {
datetimeMerge({ columns, item });
if (!item.component) {
item.component = {
name: "el-input"
};
}
upsert.items.push(format(item));
}
if (!column.component?.name.includes("cl-editor-")) {
table.columns.push(format(column));
}
});
// 服务
const service = prefix.replace("/admin", "service").replace(/\//g, ".");
// 请求路径
const paths = api.map((e: any) => e.path);
// 权限
const permission: any = {
add: paths.includes("/add"),
del: paths.includes("/delete"),
update: paths.includes("/info") && paths.includes("/update"),
page: paths.includes("/page"),
upsert: true
};
permission.upsert = permission.add || permission.update;
// 是否有操作栏
if (permission.del || permission.upsert) {
const d: any = {
type: "op",
buttons: []
};
if (permission.upsert) {
d.buttons.push("edit");
}
if (permission.del) {
d.buttons.push("delete");
}
table.columns.push(d);
}
// 是否多选、序号
if (permission.del) {
table.columns.unshift({
type: "selection"
});
} else {
table.columns.unshift({
label: "#",
type: "index"
});
}
// 代码模板
const temp = `<template>
<cl-crud ref="Crud">
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
${permission.add ? "<!-- 新增按钮 -->\n<cl-add-btn />" : ""}
${permission.del ? "<!-- 删除按钮 -->\n<cl-multi-delete-btn />" : ""}
<cl-flex1 />
<!-- 关键字搜索 -->
<cl-search-key />
</cl-row>
<cl-row>
<!-- 数据表格 -->
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<!-- 分页控件 -->
<cl-pagination />
</cl-row>
<!-- 新增、编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</template>
<script lang="ts" name="${getPageName(router)}" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
const { service } = useCool();
// cl-upsert 配置
const Upsert = useUpsert(${JSON.stringify(upsert)});
// cl-table 配置
const Table = useTable(${JSON.stringify(table)});
// cl-crud 配置
const Crud = useCrud(
{
service: ${service}
},
(app) => {
app.refresh();
}
);
</script>`;
// 文件内容
const content = prettier.format(temp, {
parser: "vue",
useTabs: true,
tabWidth: 4,
endOfLine: "lf",
semi: true,
jsxBracketSameLine: true,
singleQuote: false,
printWidth: 100,
trailingComma: "none"
});
// 目录路径
const dir = filePath.split("/");
// 文件名
const fname = dir.pop();
// 创建目录
const path = mkdirs(`./src/modules/${dir.join("/")}`);
// 创建文件
createWriteStream(join(path, fname), {
flags: "w"
}).write(content);
}

View File

@@ -1,202 +0,0 @@
export default [
{
test: ["avatar", "img", "image", "pic", "photo", "picture", "head", "icon"],
table: {
name: "cl-image",
props: {
size: 60
}
},
form: {
name: "cl-upload"
}
},
{
test: ["avatars", "imgs", "images", "pics", "photos", "pictures", "heads", "icons"],
table: {
name: "cl-image",
props: {
size: 60
}
},
form: {
name: "cl-upload",
props: {
listType: "picture-card",
multiple: true
}
}
},
{
test: ["file", "attachment", "attach", "url", "video", "music"],
table: {
name: "cl-link"
},
form: {
name: "cl-upload",
props: {
listType: "text",
limit: 1
}
}
},
{
test: ["files", "attachments", "attachs", "urls", "videos", "musics"],
table: {
name: "cl-link"
},
form: {
name: "cl-upload",
props: {
listType: "text",
multiple: true
}
}
},
{
test: ["enable", "status"],
table: {
name: "cl-switch"
},
form: {
name: "el-switch"
}
},
{
test: ["type", "classify", "category"],
handler: "dict"
},
{
test: ["types", "classifys", "categorys"],
handler: "dict_multiple"
},
{
test: ["date"],
table: {
name: "cl-date-text",
props: {
format: "YYYY-MM-DD"
}
},
form: {
name: "el-date-picker",
props: {
type: "date",
valueFormat: "YYYY-MM-DD"
}
}
},
{
test: ["dates", "dateRange", "dateScope"],
table: {
name: "cl-date-text",
props: {
format: "YYYY-MM-DD"
}
},
form: {
component: {
name: "el-date-picker",
props: {
type: "daterange",
valueFormat: "YYYY-MM-DD"
}
}
}
},
{
test: ["time"],
form: {
name: "el-date-picker",
props: {
type: "datetime",
valueFormat: "YYYY-MM-DD HH:mm:ss"
}
}
},
{
test: ["times", "timeRange", "timeScope"],
form: {
component: {
name: "el-date-picker",
props: {
type: "datetimerange",
valueFormat: "YYYY-MM-DD HH:mm:ss",
defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]
}
}
}
},
{
test: ["star", "stars"],
table: {
name: "el-rate",
props: {
disabled: true
}
},
form: {
name: "el-rate"
}
},
{
test: ["progress", "rate", "ratio"],
table: {
name: "el-progress"
},
form: {
name: "el-slider",
props: {
style: {
width: "200px"
}
}
}
},
{
test: ["num", "price", "age", "amount"],
form: {
name: "el-input-number",
props: {
min: 0
}
}
},
{
test: ["remark", "desc"],
table: {
showOverflowTooltip: true
},
form: {
name: "el-input",
props: {
type: "textarea",
rows: 4
}
}
},
{
test: ["rich", "text", "html", "content", "introduce", "description", "desc"],
form: {
name: "cl-editor-wang"
}
},
{
test: ["code", "codes"],
form: {
name: "cl-editor-monaco"
}
},
{
test: ["createTime"],
table: {
sortable: "desc"
}
},
{
test: ["updateTime"],
table: {
sortable: "custom"
}
}
];

View File

@@ -1,12 +0,0 @@
import fs from "fs";
export function getModules() {
return new Promise((resolve, reject) => {
try {
const dirs = fs.readdirSync("./src/modules");
resolve(dirs.filter((e) => !e.includes(".")));
} catch (e) {
reject(e);
}
});
}

View File

@@ -1,54 +0,0 @@
import { readFileSync, readdirSync } from "fs";
import { extname } from "path";
function findFiles(dir: string): string[] {
const res: string[] = [];
const dirs = readdirSync(dir, {
withFileTypes: true
});
for (const d of dirs) {
if (d.isDirectory()) {
res.push(...findFiles(dir + d.name + "/"));
} else {
if (extname(d.name) == ".svg") {
const svg = readFileSync(dir + d.name)
.toString()
.replace(/(\r)|(\n)/g, "")
.replace(/<svg([^>+].*?)>/, (_: any, $2: any) => {
let width = 0;
let height = 0;
let content = $2.replace(
/(width|height)="([^>+].*?)"/g,
(_: any, s2: any, s3: any) => {
if (s2 === "width") {
width = s3;
} else if (s2 === "height") {
height = s3;
}
return "";
}
);
if (!/(viewBox="[^>+].*?")/g.test($2)) {
content += `viewBox="0 0 ${width} ${height}"`;
}
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
})
.replace("</svg>", "</symbol>");
res.push(svg);
}
}
}
return res;
}
export function createSvg(html: string) {
const res = findFiles("./src/modules/");
return html.replace(
"<body>",
`<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
${res.join("")}
</svg>`
);
}

View File

@@ -1,32 +0,0 @@
import { parse, compileScript } from "@vue/compiler-sfc";
import magicString from "magic-string";
export function createTag(code: string, id: string) {
if (/\.vue$/.test(id)) {
let s: any;
const str = () => s || (s = new magicString(code));
const { descriptor } = parse(code);
if (!descriptor.script && descriptor.scriptSetup) {
const res = compileScript(descriptor, { id });
const { name, lang }: any = res.attrs;
str().appendLeft(
0,
`<script lang="${lang}">
import { defineComponent } from 'vue'
export default defineComponent({
name: "${name}"
})
<\/script>`
);
return {
map: str().generateMap(),
code: str().toString()
};
}
}
return null;
}

View File

@@ -1,68 +0,0 @@
import fs from "fs";
import { join, sep } from "path";
// 首字母大写
export function firstUpperCase(value: string): string {
return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
return $1.toUpperCase() + $2;
});
}
// 横杠转驼峰
export function toCamel(str: string): string {
return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
return $1 + $2.toUpperCase();
});
}
// 创建目录
export function createDir(path: string) {
if (!fs.existsSync(path)) fs.mkdirSync(path);
}
// 读取文件
export function readFile(name: string) {
try {
return fs.readFileSync(name, "utf8");
} catch (e) {}
return "";
}
// 解析body
export function parseJson(req: any): Promise<any> {
return new Promise((resolve) => {
let d = "";
req.on("data", function (chunk: Buffer) {
d += chunk;
});
req.on("end", function () {
try {
resolve(JSON.parse(d));
} catch {
resolve({});
}
});
});
}
// 深度创建目录
export function mkdirs(path: string) {
const arr = path.split(sep);
let p = "";
arr.forEach((e) => {
try {
fs.statSync(join(p, e));
} catch (err) {
try {
fs.mkdirSync(join(p, e));
} catch (error) {
console.error(error);
}
}
p = join(p, e);
});
return p;
}

View File

@@ -1 +0,0 @@
package frontend

View File

@@ -1,3 +0,0 @@
module github.com/cool-team-official/cool-admin-go/frontend
go 1.18

View File

@@ -1,163 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="referer" content="never" />
<meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
/>
<title></title>
<link rel="icon" href="favicon.ico" />
<style>
html,
body,
#app {
height: 100%;
}
* {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}
.preload__wrap {
display: flex;
flex-direction: column;
height: 100%;
letter-spacing: 1px;
background-color: #2f3447;
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
z-index: 9999;
}
.preload__container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
user-select: none;
flex-grow: 1;
}
.preload__name {
font-size: 30px;
color: #fff;
letter-spacing: 5px;
font-weight: bold;
margin-bottom: 30px;
}
.preload__title {
color: #fff;
font-size: 14px;
margin: 30px 0 20px 0;
}
.preload__sub-title {
color: #ababab;
font-size: 12px;
}
.preload__footer {
text-align: center;
padding: 10px 0 20px 0;
}
.preload__footer a {
font-size: 12px;
color: #ababab;
text-decoration: none;
}
.preload__loading {
height: 30px;
width: 30px;
border-radius: 30px;
border: 7px solid currentColor;
border-bottom-color: #2f3447 !important;
position: relative;
animation: r 1s infinite cubic-bezier(0.17, 0.67, 0.83, 0.67),
bc 2s infinite ease-in;
transform: rotate(0deg);
}
@keyframes r {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.preload__loading::after,
.preload__loading::before {
content: "";
display: inline-block;
position: absolute;
bottom: -2px;
height: 7px;
width: 7px;
border-radius: 10px;
background-color: currentColor;
}
.preload__loading::after {
left: -1px;
}
.preload__loading::before {
right: -1px;
}
@keyframes bc {
0% {
color: #689cc5;
}
25% {
color: #b3b7e2;
}
50% {
color: #93dbe9;
}
75% {
color: #abbd81;
}
100% {
color: #689cc5;
}
}
</style>
</head>
<body>
<div class="preload__wrap" id="Loading">
<div class="preload__container">
<p class="preload__name">COOL-ADMIN</p>
<div class="preload__loading"></div>
<p class="preload__title">正在加载资源...</p>
<p class="preload__sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
</div>
<div class="preload__footer">
<a href="https://cool-js.com/" target="_blank"> https://cool-js.com </a>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,123 +0,0 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
upstream backend {
server midway:8001;
}
server {
listen 80;
server_name localhost;
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/
{
proxy_pass http://backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
#缓存相关配置
#proxy_cache cache_one;
#proxy_cache_key $host$request_uri$is_args$args;
#proxy_cache_valid 200 304 301 302 1h;
#持久化连接相关配置
proxy_connect_timeout 3000s;
proxy_read_timeout 86400s;
proxy_send_timeout 3000s;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
add_header X-Cache $upstream_cache_status;
#expires 12h;
}
# location /im {
# proxy_pass http://backend/im;
# proxy_connect_timeout 3600s; #配置点1
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
# proxy_send_timeout 3600s; #配置点3
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header REMOTE-HOST $remote_addr;
# #proxy_bind $remote_addr transparent;
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# # rewrite /socket/(.*) /$1 break;
# proxy_redirect off;
# }
# location /socket {
# proxy_pass http://backend/socket;
# proxy_connect_timeout 3600s; #配置点1
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
# proxy_send_timeout 3600s; #配置点3
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header REMOTE-HOST $remote_addr;
# #proxy_bind $remote_addr transparent;
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# rewrite /socket/(.*) /$1 break;
# proxy_redirect off;
# }
location /adminer/
{
proxy_pass http://adminer:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
#缓存相关配置
#proxy_cache cache_one;
#proxy_cache_key $host$request_uri$is_args$args;
#proxy_cache_valid 200 304 301 302 1h;
#持久化连接相关配置
proxy_connect_timeout 3000s;
proxy_read_timeout 86400s;
proxy_send_timeout 3000s;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
add_header X-Cache $upstream_cache_status;
#expires 12h;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

View File

@@ -1,67 +0,0 @@
{
"name": "front-next",
"version": "5.12.3",
"scripts": {
"dev": "vite --host",
"build": "vite build",
"serve": "vite preview",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
},
"dependencies": {
"@cool-vue/crud": "^6.0.3",
"@element-plus/icons-vue": "^2.0.10",
"@vueuse/core": "^9.1.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^0.27.2",
"core-js": "^3.23.5",
"echarts": "^5.3.3",
"element-plus": "^2.2.28",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"monaco-editor": "^0.34.1",
"nprogress": "^0.2.0",
"pinia": "^2.0.28",
"quill": "^1.3.7",
"socket.io-client": "^4.5.1",
"store": "^2.0.12",
"vue": "^3.2.45",
"vue-echarts": "^6.2.3",
"vue-router": "^4.1.6",
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@types/lodash-es": "^4.17.6",
"@types/mockjs": "^1.0.6",
"@types/node": "^18.0.6",
"@types/nprogress": "^0.2.0",
"@types/quill": "^2.0.10",
"@types/store": "^2.0.2",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"@vitejs/plugin-vue": "^3.0.1",
"@vitejs/plugin-vue-jsx": "^2.0.0",
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-typescript": "^5.0.8",
"@vue/compiler-sfc": "^3.2.37",
"@vue/composition-api": "^1.7.0",
"eslint": "^8.20.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.2.0",
"lodash": "^4.17.21",
"magic-string": "^0.26.2",
"prettier": "^2.7.1",
"rollup-plugin-visualizer": "^5.9.0",
"sass": "^1.53.0",
"sass-loader": "^13.0.2",
"typescript": "^4.7.4",
"vite": "^4.0.3",
"vite-plugin-compression": "^0.5.1"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,10 +0,0 @@
<template>
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<script lang="ts" setup>
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
</script>

View File

@@ -1,204 +0,0 @@
import { isDev, config } from "../config";
import { BaseService, service } from "../service";
import { Data, toCamel } from "../utils";
import { isArray, isEmpty } from "lodash-es";
// 获取标签名
function getNames(v: any) {
return [...Object.getOwnPropertyNames(v.constructor.prototype), ...Object.keys(v)].filter(
(e) => !["namespace", "constructor", "request", "permission"].includes(e)
);
}
// 标签名
const names = getNames(new BaseService());
// 创建
export async function createEps() {
// 创建描述文件
function createDts(list: any[]) {
if (!isDev) {
return false;
}
function deep(v: any) {
for (const i in v) {
if (v[i].namespace) {
v[i].namespace = v[i].namespace;
// 模块
const item: any = list.find((e: any) => e.prefix.includes(v[i].namespace));
// 接口
const api: any[] = item ? item.api : [];
// 获取方法集合
[...names, ...getNames(v[i])].forEach((e) => {
if (!api.find((a) => a.path.includes(e))) {
api.push({
path: `/${e}`
});
}
});
if (item) {
item.api = api;
} else {
list.push({
prefix: `/${v[i].namespace}`,
api
});
}
} else {
deep(v[i]);
}
}
}
deep(service);
// 本地服务
return service.request({
url: "/__cool_eps",
method: "POST",
proxy: false,
data: {
service,
list
}
});
}
// 设置
async function set(d?: any) {
const list: any[] = [];
if (isArray(d)) {
d = { d };
}
for (const i in d) {
if (isArray(d[i])) {
d[i].forEach((e: any) => {
// 分隔路径
const arr = e.prefix
.replace(/\//, "")
.replace("admin", "")
.split("/")
.filter(Boolean)
.map(toCamel);
// 遍历
function deep(d: any, i: number) {
const k = arr[i];
if (k) {
// 是否最后一个
if (arr[i + 1]) {
if (!d[k]) {
d[k] = {};
}
deep(d[k], i + 1);
} else {
// 本地不存在则创建实例
if (!d[k]) {
d[k] = new BaseService({
namespace: e.prefix.substr(1, e.prefix.length - 1)
});
}
// 创建方法
e.api.forEach((a: any) => {
// 方法名
const n = a.path.replace("/", "");
// 过滤
if (!names.includes(n)) {
// 本地不存在则创建
if (!d[k][n]) {
if (n && !/[-:]/g.test(n)) {
d[k][n] = function (data: any) {
return this.request({
url: a.path,
method: a.method,
[a.method.toLocaleLowerCase() == "post"
? "data"
: "params"]: data
});
};
}
}
}
});
// 创建权限
if (!d[k].permission) {
d[k].permission = {};
const ks = Array.from(new Set([...names, ...getNames(d[k])]));
ks.forEach((e) => {
d[k].permission[e] = `${d[k].namespace.replace(
"admin/",
""
)}/${e}`.replace(/\//g, ":");
});
}
list.push(e);
}
}
}
deep(service, 0);
});
}
}
// 缓存数据
Data.set("service", service);
createDts(list);
}
// 获取
async function getEps() {
try {
// 本地数据
let list = JSON.parse(__EPS__ || "[]").map(([prefix, name, api]: any[]) => {
return {
prefix,
name,
api: api.map(([path, method]: string[]) => {
return {
method,
path
};
})
};
});
// 接口数据
if (isDev && config.test.eps) {
await service
.request({
url: "/admin/base/open/eps"
})
.then((res) => {
if (!isEmpty(res)) {
list = res;
}
});
}
if (list) {
set(list);
}
} catch (err) {
console.error("[Eps] 获取失败!", err);
}
}
await getEps();
}

View File

@@ -1,36 +0,0 @@
import { createPinia } from "pinia";
import mitt from "mitt";
import VueECharts from "vue-echarts";
import { App } from "vue";
import { createModule } from "./module";
import { createEps } from "./eps";
import { router } from "../router";
import ElementPlus from "element-plus";
import "element-plus/theme-chalk/src/index.scss";
import { Loading } from "../utils";
export async function bootstrap(app: App) {
// pinia
app.use(createPinia());
// element-plus
app.use(ElementPlus);
// mitt
app.provide("mitt", mitt());
// charts
app.component("v-chart", VueECharts);
// 路由
app.use(router);
// 模块
const { eventLoop } = createModule(app);
// eps
await createEps();
// 加载
Loading.set([eventLoop()]);
}

View File

@@ -1,112 +0,0 @@
// @ts-nocheck
import { App } from "vue";
import { isFunction, orderBy } from "lodash-es";
import { Data, deepMerge, filename, mergeService } from "../utils";
import { service } from "../service";
import { module } from "../module";
// 扫描文件
const files: any = import.meta.glob("/src/modules/*/{config.ts,service/**,directives/**}", {
eager: true
});
// 模块列表
module.list = Data.get("modules", []);
// 解析
for (const i in files) {
// 分割
const [, , , name, action] = i.split("/");
// 文件名
const fname = filename(i);
// 文件内容
const v = files[i]?.default;
// 模块是否存在
const m = module.get(name);
// 数据
const d = m || {
name,
value: null,
services: [],
directives: []
};
switch (action) {
// 配置参数
case "config.ts":
d.value = v;
break;
// 请求服务
case "service":
const s = new v();
if (s) {
d.services?.push({
path: s.namespace,
value: s
});
}
break;
// 指令
case "directives":
d.directives?.push({ name: fname, value: v });
break;
}
if (!m) {
module.add(d);
}
}
// 创建
export function createModule(app: App) {
// 模块加载
const list = orderBy(module.list, "order").map((e) => {
const d = isFunction(e.value) ? e.value(app) : e.value;
if (d) {
Object.assign(e, d);
// 注册组件
e.components?.forEach(async (c: any) => {
const v = await (isFunction(c) ? c() : c);
const n = v.default || v;
app.component(n.name, n);
});
// 注册指令
e.directives?.forEach((v) => {
app.directive(v.name, v.value);
});
// 安装事件
if (d.install) {
d.install(app, d.options);
}
}
// 合并
deepMerge(service, mergeService(e.services || []));
return e;
});
return {
// 事件加载
async eventLoop() {
const events = {};
for (let i = 0; i < list.length; i++) {
if (list[i].onLoad) {
Object.assign(events, await list[i].onLoad(events));
}
}
}
};
}

View File

@@ -1,20 +0,0 @@
import { getUrlParam, storage } from "../utils";
import { proxy } from "./proxy";
export default {
// 根地址
host: proxy["/dev"].target,
// 请求地址
get baseUrl() {
let proxy = getUrlParam("proxy");
if (proxy) {
storage.set("proxy", proxy);
} else {
proxy = storage.get("proxy") || "dev";
}
return `/${proxy}`;
}
};

View File

@@ -1,54 +0,0 @@
import dev from "./dev";
import prod from "./prod";
// 是否开发模式
export const isDev = import.meta.env.MODE === "development";
// 配置
export const config = {
// 项目信息
app: {
name: "COOL-ADMIN",
// 菜单
menu: {
// 是否分组显示
isGroup: false,
// 自定义菜单列表
list: []
},
// 路由
router: {
// 模式
mode: "hash",
// 转场动画
transition: "slide",
// 首页组件
home: () => import("/$/demo/views/home/index.vue")
},
// 字体图标库
iconfont: []
},
// 忽略规则
ignore: {
// 不显示请求进度条
NProgress: ["/base/comm/upload", "/base/comm/uploadMode"],
// 页面不需要登录验证
token: ["/login", "/401", "/403", "/404", "/500", "/502"]
},
// 调试
test: {
token: "",
mock: false,
eps: true
},
// 当前环境
...(isDev ? dev : prod)
};
export * from "./proxy";

View File

@@ -1,9 +0,0 @@
import { proxy } from "./proxy";
export default {
// 根地址
host: proxy["/prod"].target,
// 请求地址
baseUrl: ""
};

View File

@@ -1,13 +0,0 @@
export const proxy = {
"/dev": {
target: "http://127.0.0.1:8001",
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/dev/, "")
},
"/prod": {
target: "https://show.cool-admin.com",
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/prod/, "/api")
}
};

View File

@@ -1,30 +0,0 @@
import { useEventListener } from "@vueuse/core";
import { reactive, watch } from "vue";
import { getBrowser } from "../utils";
const browser = reactive(getBrowser());
const events: (() => void)[] = [];
watch(
() => browser.screen,
() => {
events.forEach((ev) => ev());
}
);
useEventListener(window, "resize", () => {
Object.assign(browser, getBrowser());
});
export function useBrowser() {
return {
browser,
onScreenChange(ev: () => void, immediate = true) {
events.push(ev);
if (immediate) {
ev();
}
}
};
}

View File

@@ -1,56 +0,0 @@
import { Emitter } from "mitt";
import { inject, getCurrentInstance, Ref, reactive } from "vue";
import { useRoute, useRouter } from "vue-router";
import { service } from "../service";
import { Data } from "../utils";
import { useBrowser } from "./browser";
export function useService(): Eps.Service {
return Data.get("service" || service);
}
export function useRefs() {
const refs = reactive<{ [key: string]: any }>({});
function setRefs(name: string) {
return (el: any) => {
refs[name] = el;
};
}
return { refs, setRefs };
}
export function useParent(name: string, r: Ref) {
const d = getCurrentInstance();
if (d) {
let parent = d.proxy?.$.parent;
if (parent) {
while (parent && parent.type?.name != name && parent.type?.name != "cl-crud") {
parent = parent?.parent;
}
if (parent) {
if (parent.type.name == name) {
r.value = parent.proxy;
}
}
}
}
return r;
}
export function useCool() {
return {
service: useService(),
route: useRoute(),
router: useRouter(),
mitt: inject("mitt") as Emitter<any>,
...useBrowser(),
...useRefs()
};
}
export * from "./browser";

View File

@@ -1,8 +0,0 @@
export * from "./service";
export * from "./bootstrap";
export * from "./hook";
export * from "./module";
export * from "./router";
export * from "./config";
export * from "./types/index.d";
export { storage } from "./utils";

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