This commit is contained in:
oneao committed 2025-11-25 22:46:07 +08:00
1 parent 755fc20c9a
commit a57070f860
9 files changed
+557 -93

No files matched your search

+148 -31
View File
@@ -2,78 +2,195 @@
<html>
<head>
<meta charset="UTF-8">
<title>DataGrid 单行编辑 + wdatepicker 示例</title>
<link rel="stylesheet" type="text/css" href="https://www.jeasyui.com/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="https://www.jeasyui.com/easyui/themes/icon.css">
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<title>DataGrid 多选删除 + 单行编辑示例</title>
<link rel="stylesheet" href="https://www.jeasyui.com/easyui/themes/default/easyui.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<script src="https://www.jeasyui.com/easyui/jquery.easyui.min.js"></script>
<script src="https://www.jeasyui.com/easyui/locale/easyui-lang-zh_CN.js"></script>
<script src="https://www.jeasyui.com/easyui/datagrid-scrollview.js"></script>
<style>
.datagrid-editable .textbox{ width:100% !important; }
.datagrid-editable-input{
width:100%!important;
}
</style>
</head>
<body>
<h3>DataGrid 单行编辑 + wdatepicker</h3>
<h3>DataGrid 编辑 + 多选删除 + 彻底删除 示例</h3>
<table id="dg" class="easyui-datagrid" style="width:700px;height:400px"
<table id="dg" class="easyui-datagrid" style="width:800px;height:400px"
data-options="
singleSelect:true,
idField:'id',
onClickRow: onClickRow
idField:'id',
view:scrollview,
rownumbers:true,
singleSelect:false,
autoRowHeight:false,
pagination:true,
pageSize:30,
onClickRow:onClickRow,
showPageInfo:false,
showRefresh:false
">
<thead>
<tr>
<th field="ck" checkbox="true"></th>
<th field="id" width="50">ID</th>
<th field="name" width="150" editor="text">Name</th>
<th field="birth" width="120" editor="wdatepicker">出生日期</th>
<th field="score" width="100" editor="numberbox">Score</th>
<th field="name" width="120" editor='text'>姓名</th>
<th field="birth" width="140" editor='datebox'>出生日期</th>
<th field="score" width="100" editor='text'>成绩</th>
</tr>
</thead>
</table>
<div style="margin-top:10px;">
<a href="javascript:endEditRow()" class="easyui-linkbutton">保存当前行</a>
<a href="javascript:endEditRow()" class="easyui-linkbutton" iconCls="icon-save">保存当前行</a>
<a href="javascript:removeRows()" class="easyui-linkbutton" iconCls="icon-remove">删除所选</a>
<a href="javascript:confirmDelete()" class="easyui-linkbutton" iconCls="icon-save">彻底删除</a>
</div>
<script>
var editIndex = undefined;
let editIndex = undefined;
// 初始化 datagrid 数据
// 初始化 Datagrid 数据
$('#dg').datagrid({
data: [
{id:1, name:'张三', birth:'1990-01-01', score:80},
{id:2, name:'李四', birth:'1992-03-15', score:90},
{id:3, name:'王五', birth:'1988-07-20', score:75}
]
data: createMockData(100),
onLoadSuccess: function(){
let pager = $('#dg').datagrid('getPager');
pager.pagination({
showRefresh: true, // 隐藏刷新按钮
showPageInfo: true, // 隐藏页信息
onRefresh: function(pageNumber, pageSize){
let newData = createMockData(300);
$('#dg').datagrid('loadData', newData);
$('#dg').datagrid('clearSelections');
$('#dg').datagrid('clearChecked');
}
});
}
});
// 点击行进入编辑状态
function onClickRow(index,row){
if(editIndex !== index){
if(endEditRow()){
// 点击行进入编辑模式
function onClickRow(index){
if (editIndex !== index){
if (endEditRow()){
$('#dg').datagrid('beginEdit', index);
editIndex = index;
}
}
}
// 保存当前行
// 保存当前行编辑数据
function endEditRow(){
if(editIndex === undefined){ return true; }
if($('#dg').datagrid('validateRow', editIndex)){
if (editIndex == undefined) return true;
if ($('#dg').datagrid('validateRow', editIndex)){
$('#dg').datagrid('endEdit', editIndex);
editIndex = undefined;
return true;
} else {
alert('请填写完整数据');
$.messager.alert('提示','请填写完整!');
return false;
}
}
</script>
// 生成随机数据
function createMockData(count = 100){
let data = [];
for(let i = 1; i <= count; i++){
data.push({
id: i,
name: "用户" + i,
birth: randomDate("1980-01-01", "2005-12-31"),
score: Math.floor(Math.random() * 41) + 60
});
}
return data;
}
// 生成随机日期
function randomDate(start, end){
let startTime = new Date(start).getTime();
let endTime = new Date(end).getTime();
let randomTime = startTime + Math.random() * (endTime - startTime);
let d = new Date(randomTime);
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
}
// 批量删除(不立即提交)
function removeRows() {
let rows = $('#dg').datagrid('getChecked');
if(rows.length === 0){
$.messager.alert('提示', '请先勾选要删除的行');
return;
}
// 倒序删除避免索引错乱
let indexes = rows.map(row => $('#dg').datagrid('getRowIndex', row))
.sort((a,b) => b - a);
indexes.forEach(index => $('#dg').datagrid('deleteRow', index));
$('#dg').datagrid('clearSelections');
$('#dg').datagrid('clearChecked');
}
// 获取真正删除的数据
function confirmDelete(){
let deleted = $('#dg').datagrid('getChanges','deleted');
console.log("真正需要提交到后台删除的数据:", deleted);
let updated = $('#dg').datagrid('getChanges','updated');
console.log("updated:", updated);
let inserted = $('#dg').datagrid('getChanges','inserted');
console.log("inserted:", inserted);
}
let currentCell = { rowIndex: null, field: null };
// 监听编辑器加载,记录焦点单元格
$('#dg').datagrid({
onBeginEdit: function (rowIndex, rowData) {
let editors = $(this).datagrid('getEditors', rowIndex);
editors.forEach(e => {
$(e.target).off('focus').on('focus', function () {
currentCell.rowIndex = rowIndex;
currentCell.field = e.field;
// 获取所有列 field
let fields = $('#dg').datagrid('getColumnFields', true) // true = 冻结列
.concat($('#dg').datagrid('getColumnFields', false)); // false = 普通列
let colIndex = fields.indexOf(e.field);
console.log(`👉 聚焦位置:行=${currentCell.rowIndex}, 列索引=${colIndex}, field=${currentCell.field}`);
});
// 监听粘贴事件
$(e.target).off('paste').on('paste', function (event) {
setTimeout(() => { // 延迟确保粘贴内容已写入 input
let pastedValue = $(this).val();
console.log(`📌 粘贴事件触发:行=${currentCell.rowIndex}, 列=${currentCell.field}, 内容=${pastedValue}`);
handlePaste(currentCell.rowIndex, currentCell.field, pastedValue);
}, 10);
});
});
}
});
</script>
</body>
</html>
@@ -10,6 +10,8 @@ import (
"base-go-v2/internal/utils/jwtx"
"base-go-v2/internal/utils/mapx"
"base-go-v2/internal/utils/timex"
"fmt"
"time"
)
func InitAuthRouter(r *router.Router) {
@@ -69,10 +71,14 @@ func loginDefault(c *router.Context) error {
"account", account,
"password", password,
)
start := time.Now()
user, err := db.SelectOne("user_info", db.BuildWhereString(userQuery), "")
if err != nil {
return response.Success(c).Message("查询用户失败,请稍后重试").Send()
}
duration := time.Since(start) // 耗时
fmt.Printf("登录查询: (耗时: %v)\n", duration)
if user == nil {
// 账号或密码错误
@@ -8,6 +8,8 @@ import (
"cn/oneao/base-go/utils/jwtx"
"cn/oneao/base-go/utils/response"
"context"
"fmt"
"time"
auth "cn/oneao/base-go/biz/model/auth"
@@ -25,13 +27,18 @@ func LoginDefault(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusBadRequest, err.Error())
return
}
// 处理登录
// 假设 ctx 已有,req.Account / req.Password 已有
start := time.Now()
loginUser, err := db.Client.UserInfo.
Query().
Where(userinfo.AccountEQ(req.Account),
userinfo.PasswordEQ(req.Password)).
Only(ctx)
duration := time.Since(start) // 耗时
fmt.Printf("登录查询: (耗时: %v)\n", duration)
if err != nil {
c.String(consts.StatusInternalServerError, "账号或密码错误")
return
@@ -13,7 +13,7 @@ func Error() app.HandlerFunc {
if err := c.Errors.Last(); err != nil {
// 未知错误 -> 500
c.JSON(500, ErrorResponse{500, "Internal Server Error"})
//c.JSON(500, ErrorResponse{500, "Internal Server Error"})
}
}
}
@@ -0,0 +1,32 @@
package middleware
import (
"context"
"time"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/requestid"
"go.uber.org/zap"
)
func Log() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
start := time.Now() // 记录开始时间
// 执行后续处理
ctx.Next(c)
// 计算耗时
duration := time.Since(start).Milliseconds()
hlog.Info("请求完成",
zap.String("RequestID", requestid.Get(ctx)),
zap.String("IP", ctx.ClientIP()),
zap.String("Method", string(ctx.Method())),
zap.String("Path", string(ctx.Path())),
zap.Int("Status", ctx.GetResponse().StatusCode()),
zap.Int64("Duration", duration),
)
}
}
+88 -32
View File
@@ -1,49 +1,105 @@
package log
import (
"log"
"fmt"
"os"
"path"
"sync"
"time"
"github.com/cloudwego/hertz/pkg/common/hlog"
hertzzap "github.com/hertz-contrib/logger/zap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func InitLog() {
// Customizable output directory.
var logFilePath string
dir := "./hlog"
logFilePath = dir + "/logs/"
if err := os.MkdirAll(logFilePath, 0o777); err != nil {
log.Println(err.Error())
return
}
dynamicLevel := zap.NewAtomicLevel()
// Set filename to date
logFileName := time.Now().Format("2006-01-02") + ".log"
fileName := path.Join(logFilePath, logFileName)
if _, err := os.Stat(fileName); err != nil {
if _, err := os.Create(fileName); err != nil {
log.Println(err.Error())
return
}
}
dynamicLevel.SetLevel(zap.DebugLevel)
logger := hertzzap.NewLogger(hertzzap.WithCores([]hertzzap.CoreConfig{}))
lumberjackLogger := &lumberjack.Logger{
Filename: fileName,
MaxSize: 20, // A file can be up to 20M.
MaxBackups: 5, // Save up to 5 files at the same time.
MaxAge: 10, // A file can exist for a maximum of 10 days.
Compress: true, // Compress with gzip.
}
logger.SetOutput(lumberjackLogger)
logger.SetLevel(hlog.LevelDebug)
logger := NewLogger(
WithCores([]CoreConfig{
{
Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
Ws: zapcore.AddSync(os.Stdout),
Lvl: dynamicLevel,
},
{
Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
Ws: getWriteSyncer("./logs/debug"),
Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
return lev == zap.DebugLevel
}),
},
{
Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
Ws: getWriteSyncer("./logs/info"),
Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
return lev == zap.InfoLevel
}),
},
{
Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
Ws: getWriteSyncer("./logs/warn"),
Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
return lev == zap.WarnLevel
}),
},
{
Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
Ws: getWriteSyncer("./logs/error"),
Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
return lev >= zap.ErrorLevel
}),
},
}...),
)
defer logger.Sync()
hlog.SetLogger(logger)
}
// humanEncoderConfig copy from zap
func humanEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
MessageKey: "Message",
LevelKey: "Level",
NameKey: "Name",
TimeKey: "Ts",
CallerKey: "Caller",
FunctionKey: "Func",
StacktraceKey: "Stacktrace",
LineEnding: "\n",
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
}
var (
mu sync.Mutex
currentDay = make(map[string]string)
currentSync = make(map[string]zapcore.WriteSyncer)
)
func getWriteSyncer(basePath string) zapcore.WriteSyncer {
mu.Lock()
defer mu.Unlock()
today := time.Now().Format("2006-01-02")
if day, ok := currentDay[basePath]; !ok || day != today {
currentDay[basePath] = today
file := fmt.Sprintf("%s/%s.log", basePath, today)
currentSync[basePath] = zapcore.AddSync(&lumberjack.Logger{
Filename: file,
MaxSize: 10,
MaxBackups: 50000,
MaxAge: 5,
Compress: true,
LocalTime: true,
})
}
return currentSync[basePath]
}
+268
View File
@@ -0,0 +1,268 @@
package log
import (
"context"
"fmt"
"io"
"github.com/cloudwego/hertz/pkg/common/hlog"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var _ hlog.FullLogger = (*Logger)(nil)
type Logger struct {
l *zap.Logger
config *config
}
func NewLogger(opts ...Option) *Logger {
config := defaultConfig()
// apply options
for _, opt := range opts {
opt.apply(config)
}
cores := make([]zapcore.Core, 0, len(config.coreConfigs))
for _, coreConfig := range config.coreConfigs {
cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
}
logger := zap.New(
zapcore.NewTee(cores[:]...),
config.zapOpts...)
return &Logger{
l: logger,
config: config,
}
}
// GetExtraKeys get extraKeys from logger config
func (l *Logger) GetExtraKeys() []ExtraKey {
return l.config.extraKeys
}
// PutExtraKeys add extraKeys after init
func (l *Logger) PutExtraKeys(keys ...ExtraKey) {
for _, k := range keys {
if !InArray(k, l.config.extraKeys) {
l.config.extraKeys = append(l.config.extraKeys, k)
}
}
}
func (l *Logger) Log(level hlog.Level, kvs ...interface{}) {
if len(kvs) == 0 {
return
}
// 第一个参数作为 msg
msg := fmt.Sprint(kvs[0])
// 后续参数作为 zap.Field
fields := make([]zap.Field, 0, len(kvs)-1)
for _, v := range kvs[1:] {
if f, ok := v.(zap.Field); ok {
fields = append(fields, f)
}
}
switch level {
case hlog.LevelTrace, hlog.LevelDebug:
l.l.Debug(msg, fields...)
case hlog.LevelInfo:
l.l.Info(msg, fields...)
case hlog.LevelNotice, hlog.LevelWarn:
l.l.Warn(msg, fields...)
case hlog.LevelError:
l.l.Error(msg, fields...)
case hlog.LevelFatal:
l.l.Fatal(msg, fields...)
default:
l.l.Warn(msg, fields...)
}
}
func (l *Logger) Logf(level hlog.Level, format string, kvs ...interface{}) {
logger := l.l.Sugar().With()
switch level {
case hlog.LevelTrace, hlog.LevelDebug:
logger.Debugf(format, kvs...)
case hlog.LevelInfo:
logger.Infof(format, kvs...)
case hlog.LevelNotice, hlog.LevelWarn:
logger.Warnf(format, kvs...)
case hlog.LevelError:
logger.Errorf(format, kvs...)
case hlog.LevelFatal:
logger.Fatalf(format, kvs...)
default:
logger.Warnf(format, kvs...)
}
}
func (l *Logger) CtxLogf(level hlog.Level, ctx context.Context, format string, kvs ...interface{}) {
zLevel := hLevelToZapLevel(level)
if !l.config.coreConfigs[0].Lvl.Enabled(zLevel) {
return
}
zapLogger := l.l
if len(l.config.extraKeys) > 0 {
for _, k := range l.config.extraKeys {
if l.config.extraKeyAsStr {
v := ctx.Value(string(k))
if v != nil {
zapLogger = zapLogger.With(zap.Any(string(k), v))
}
} else {
v := ctx.Value(k)
if v != nil {
zapLogger = zapLogger.With(zap.Any(string(k), v))
}
}
}
}
log := zapLogger.Sugar()
switch level {
case hlog.LevelDebug, hlog.LevelTrace:
log.Debugf(format, kvs...)
case hlog.LevelInfo:
log.Infof(format, kvs...)
case hlog.LevelNotice, hlog.LevelWarn:
log.Warnf(format, kvs...)
case hlog.LevelError:
log.Errorf(format, kvs...)
case hlog.LevelFatal:
log.Fatalf(format, kvs...)
default:
log.Warnf(format, kvs...)
}
}
func (l *Logger) Trace(v ...interface{}) {
l.Log(hlog.LevelTrace, v...)
}
func (l *Logger) Debug(v ...interface{}) {
l.Log(hlog.LevelDebug, v...)
}
func (l *Logger) Info(v ...interface{}) {
l.Log(hlog.LevelInfo, v...)
}
func (l *Logger) Notice(v ...interface{}) {
l.Log(hlog.LevelNotice, v...)
}
func (l *Logger) Warn(v ...interface{}) {
l.Log(hlog.LevelWarn, v...)
}
func (l *Logger) Error(v ...interface{}) {
l.Log(hlog.LevelError, v...)
}
func (l *Logger) Fatal(v ...interface{}) {
l.Log(hlog.LevelFatal, v...)
}
func (l *Logger) Tracef(format string, v ...interface{}) {
l.Logf(hlog.LevelTrace, format, v...)
}
func (l *Logger) Debugf(format string, v ...interface{}) {
l.Logf(hlog.LevelDebug, format, v...)
}
func (l *Logger) Infof(format string, v ...interface{}) {
l.Logf(hlog.LevelInfo, format, v...)
}
func (l *Logger) Noticef(format string, v ...interface{}) {
l.Logf(hlog.LevelWarn, format, v...)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
l.Logf(hlog.LevelWarn, format, v...)
}
func (l *Logger) Errorf(format string, v ...interface{}) {
l.Logf(hlog.LevelError, format, v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.Logf(hlog.LevelFatal, format, v...)
}
func (l *Logger) CtxTracef(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelDebug, ctx, format, v...)
}
func (l *Logger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelDebug, ctx, format, v...)
}
func (l *Logger) CtxInfof(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelInfo, ctx, format, v...)
}
func (l *Logger) CtxNoticef(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelWarn, ctx, format, v...)
}
func (l *Logger) CtxWarnf(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelWarn, ctx, format, v...)
}
func (l *Logger) CtxErrorf(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelError, ctx, format, v...)
}
func (l *Logger) CtxFatalf(ctx context.Context, format string, v ...interface{}) {
l.CtxLogf(hlog.LevelFatal, ctx, format, v...)
}
func (l *Logger) SetLevel(level hlog.Level) {
lvl := hLevelToZapLevel(level)
l.config.coreConfigs[0].Lvl = lvl
cores := make([]zapcore.Core, 0, len(l.config.coreConfigs))
for _, coreConfig := range l.config.coreConfigs {
cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
}
logger := zap.New(
zapcore.NewTee(cores[:]...),
l.config.zapOpts...)
l.l = logger
}
func (l *Logger) SetOutput(writer io.Writer) {
l.config.coreConfigs[0].Ws = zapcore.AddSync(writer)
cores := make([]zapcore.Core, 0, len(l.config.coreConfigs))
for _, coreConfig := range l.config.coreConfigs {
cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
}
logger := zap.New(
zapcore.NewTee(cores[:]...),
l.config.zapOpts...)
l.l = logger
}
// Logger is used to return an instance of *zap.Logger for custom fields, etc.
func (l *Logger) Logger() *zap.Logger {
return l.l
}
func (l *Logger) Sync() {
_ = l.l.Sync()
}
-14
View File
@@ -1,17 +1,3 @@
// Copyright 2022 CloudWeGo Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
+6 -14
View File
@@ -1,42 +1,34 @@
package main
import (
"cn/oneao/base-go/biz/router/middleware"
"cn/oneao/base-go/config"
"cn/oneao/base-go/db"
"cn/oneao/base-go/log"
"cn/oneao/base-go/utils/idgen"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/requestid"
)
func main() {
// 初始化雪花id
options := idgen.NewIdGeneratorOptions(config.App.Snowflake.WorkerID)
idgen.SetIdGenerator(options)
// 初始化数据库
db.InitDB()
// 日志
log.InitLog()
h := server.Default(
server.WithBasePath(config.App.Server.BaseUrl),
server.WithHostPorts(config.App.Server.Port),
)
// 请求 id
// 全局中间件
h.Use(requestid.New())
log.InitLog()
type User struct {
Name string
Age int
}
u := User{"tom", 20}
hlog.Info(u)
h.Use(middleware.Log())
register(h)
h.Spin()