修复日程提醒功能
This commit is contained in:
@@ -1 +1 @@
|
|||||||
{"pid":54204,"startedAt":1784192140605}
|
{"pid":4264,"startedAt":1784510683025}
|
||||||
@@ -22,6 +22,14 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
host: '127.0.0.1',
|
||||||
port: 4001,
|
port: 4001,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://127.0.0.1:9000',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+168
-109
@@ -1,109 +1,168 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"server/models"
|
"server/models"
|
||||||
|
|
||||||
beego "github.com/beego/beego/v2/server/web"
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApiReminderController struct {
|
type ApiReminderController struct {
|
||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// AckReminder GET /api/schedule/reminder/ack
|
// AckReminder GET /api/schedule/reminder/ack
|
||||||
// 邮件/Bark 客户端访问此接口进行提醒确认
|
// 邮件/Bark 客户端访问此接口进行提醒确认。
|
||||||
func (c *ApiReminderController) AckReminder() {
|
// backend 与 platform 使用各自的提醒表,这里按 token 自动识别来源。
|
||||||
token := c.GetString("token")
|
func (c *ApiReminderController) AckReminder() {
|
||||||
if token == "" {
|
token := c.GetString("token")
|
||||||
c.Ctx.Output.SetStatus(400)
|
if token == "" {
|
||||||
_ = c.Ctx.Output.Body([]byte("Invalid request: missing token"))
|
c.Ctx.Output.SetStatus(400)
|
||||||
return
|
_ = c.Ctx.Output.Body([]byte("Invalid request: missing token"))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
var reminder models.PlatformScheduleReminder
|
|
||||||
err := models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
type reminderState struct {
|
||||||
Filter("ack_token", token).
|
AckStatus int8
|
||||||
Filter("is_deleted", 0).
|
ScheduleID uint64
|
||||||
One(&reminder)
|
RemindStatus int8
|
||||||
if err != nil {
|
}
|
||||||
c.Ctx.Output.SetStatus(404)
|
|
||||||
_ = c.Ctx.Output.Body([]byte("Error: reminder task not found or token has expired"))
|
var state reminderState
|
||||||
return
|
table := ""
|
||||||
}
|
|
||||||
|
var backendReminder models.BackendScheduleReminder
|
||||||
if reminder.AckStatus == 1 {
|
if err := models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
// 已经确认过了,直接显示已确认成功的 HTML
|
Filter("ack_token", token).
|
||||||
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
Filter("is_deleted", 0).
|
||||||
_ = c.Ctx.Output.Body([]byte(`
|
One(&backendReminder); err == nil {
|
||||||
<!DOCTYPE html>
|
state = reminderState{
|
||||||
<html>
|
AckStatus: backendReminder.AckStatus,
|
||||||
<head>
|
ScheduleID: backendReminder.ScheduleID,
|
||||||
<meta charset="utf-8">
|
RemindStatus: backendReminder.RemindStatus,
|
||||||
<title>确认收到提醒</title>
|
}
|
||||||
<style>
|
table = "backend"
|
||||||
body { font-family: sans-serif; text-align: center; padding: 50px; background: #f5f7fa; color: #303133; }
|
} else {
|
||||||
.card { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1); display: inline-block; max-width: 400px; }
|
var platformReminder models.PlatformScheduleReminder
|
||||||
h2 { color: #67C23A; }
|
if err := models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||||
</style>
|
Filter("ack_token", token).
|
||||||
</head>
|
Filter("is_deleted", 0).
|
||||||
<body>
|
One(&platformReminder); err != nil {
|
||||||
<div class="card">
|
c.Ctx.Output.SetStatus(404)
|
||||||
<h2>提示</h2>
|
_ = c.Ctx.Output.Body([]byte("Error: reminder task not found or token has expired"))
|
||||||
<p>该日程提醒在此之前已确认过了。</p>
|
return
|
||||||
<p style="color: #909399; font-size: 14px;">无需重复点击,感谢您的使用!</p>
|
}
|
||||||
</div>
|
state = reminderState{
|
||||||
</body>
|
AckStatus: platformReminder.AckStatus,
|
||||||
</html>
|
ScheduleID: platformReminder.ScheduleID,
|
||||||
`))
|
RemindStatus: platformReminder.RemindStatus,
|
||||||
return
|
}
|
||||||
}
|
table = "platform"
|
||||||
|
}
|
||||||
// 更新确认状态为已确认,置 remind_status 为已结束(2)
|
|
||||||
now := time.Now()
|
if state.AckStatus == 1 {
|
||||||
reminder.AckStatus = 1
|
writeReminderAckAlreadyConfirmed(c)
|
||||||
reminder.AckTime = &now
|
return
|
||||||
reminder.RemindStatus = 2
|
}
|
||||||
reminder.UpdateTime = now
|
|
||||||
|
now := time.Now()
|
||||||
_, err = models.Orm.Update(&reminder, "AckStatus", "AckTime", "RemindStatus", "UpdateTime")
|
var err error
|
||||||
if err != nil {
|
if table == "backend" {
|
||||||
c.Ctx.Output.SetStatus(500)
|
_, err = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
_ = c.Ctx.Output.Body([]byte("Database error, please try again later"))
|
Filter("ack_token", token).
|
||||||
return
|
Filter("is_deleted", 0).
|
||||||
}
|
Update(map[string]interface{}{
|
||||||
|
"AckStatus": int8(1),
|
||||||
// 统一关闭该日程下的所有其他待提醒/提醒中渠道,防止重复打扰
|
"AckTime": now,
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
"RemindStatus": int8(2),
|
||||||
Filter("ScheduleID", reminder.ScheduleID).
|
"UpdateTime": now,
|
||||||
Filter("RemindStatus__in", 0, 1).
|
})
|
||||||
Update(map[string]interface{}{
|
} else {
|
||||||
"RemindStatus": int8(2),
|
_, err = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||||
"UpdateTime": now,
|
Filter("ack_token", token).
|
||||||
})
|
Filter("is_deleted", 0).
|
||||||
|
Update(map[string]interface{}{
|
||||||
// 成功确认
|
"AckStatus": int8(1),
|
||||||
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
"AckTime": now,
|
||||||
_ = c.Ctx.Output.Body([]byte(`
|
"RemindStatus": int8(2),
|
||||||
<!DOCTYPE html>
|
"UpdateTime": now,
|
||||||
<html>
|
})
|
||||||
<head>
|
}
|
||||||
<meta charset="utf-8">
|
if err != nil {
|
||||||
<title>确认成功</title>
|
c.Ctx.Output.SetStatus(500)
|
||||||
<style>
|
_ = c.Ctx.Output.Body([]byte("Database error, please try again later"))
|
||||||
body { font-family: sans-serif; text-align: center; padding: 50px; background: #f5f7fa; color: #303133; }
|
return
|
||||||
.card { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1); display: inline-block; max-width: 400px; }
|
}
|
||||||
h2 { color: #67C23A; }
|
|
||||||
</style>
|
// 只关闭命中来源表中、同一日程的其他待提醒记录,避免两套表互相影响。
|
||||||
</head>
|
if table == "backend" {
|
||||||
<body>
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
<div class="card">
|
Filter("schedule_id", state.ScheduleID).
|
||||||
<h2>确认成功</h2>
|
Filter("remind_status__in", 0, 1).
|
||||||
<p>您已成功确认收到该日程提醒!</p>
|
Update(map[string]interface{}{
|
||||||
<p style="color: #909399; font-size: 14px;">系统已停止向您重复推送,感谢您的配合。</p>
|
"RemindStatus": int8(2),
|
||||||
</div>
|
"UpdateTime": now,
|
||||||
</body>
|
})
|
||||||
</html>
|
} else {
|
||||||
`))
|
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||||
}
|
Filter("schedule_id", state.ScheduleID).
|
||||||
|
Filter("remind_status__in", 0, 1).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"RemindStatus": int8(2),
|
||||||
|
"UpdateTime": now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
writeReminderAckSuccess(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeReminderAckAlreadyConfirmed(c *ApiReminderController) {
|
||||||
|
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
_ = c.Ctx.Output.Body([]byte(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>确认收到提醒</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; text-align: center; padding: 50px; background: #f5f7fa; color: #303133; }
|
||||||
|
.card { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1); display: inline-block; max-width: 400px; }
|
||||||
|
h2 { color: #67C23A; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h2>提示</h2>
|
||||||
|
<p>该日程提醒在此之前已确认过了。</p>
|
||||||
|
<p style="color: #909399; font-size: 14px;">无需重复点击,感谢您的使用!</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeReminderAckSuccess(c *ApiReminderController) {
|
||||||
|
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
_ = c.Ctx.Output.Body([]byte(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>确认成功</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; text-align: center; padding: 50px; background: #f5f7fa; color: #303133; }
|
||||||
|
.card { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1); display: inline-block; max-width: 400px; }
|
||||||
|
h2 { color: #67C23A; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h2>确认成功</h2>
|
||||||
|
<p>您已成功确认收到该日程提醒!</p>
|
||||||
|
<p style="color: #909399; font-size: 14px;">系统已停止向您重复推送,感谢您的配合。</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"server/models"
|
"server/models"
|
||||||
"server/pkg/jwtutil"
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/client/orm"
|
||||||
beego "github.com/beego/beego/v2/server/web"
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,18 +24,18 @@ type AppReminderController struct {
|
|||||||
func (c *AppReminderController) appClaims() (*jwtutil.Claims, error) {
|
func (c *AppReminderController) appClaims() (*jwtutil.Claims, error) {
|
||||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||||
if auth == "" {
|
if auth == "" {
|
||||||
return nil, fmt.Errorf("未登录")
|
return nil, orm.ErrNoRows
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(auth, " ", 2)
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
return nil, fmt.Errorf("认证信息格式错误")
|
return nil, orm.ErrNoRows
|
||||||
}
|
}
|
||||||
claims, err := jwtutil.ParseToken(parts[1])
|
claims, err := jwtutil.ParseToken(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("无效的token")
|
return nil, err
|
||||||
}
|
}
|
||||||
if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" {
|
if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" {
|
||||||
return nil, fmt.Errorf("无权访问")
|
return nil, orm.ErrNoRows
|
||||||
}
|
}
|
||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
@@ -71,7 +72,7 @@ type appSchedulePayload struct {
|
|||||||
func (c *AppReminderController) GetList() {
|
func (c *AppReminderController) GetList() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +88,9 @@ func (c *AppReminderController) GetList() {
|
|||||||
pageSize = 20
|
pageSize = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
qs := models.Orm.QueryTable(new(models.PlatformSchedule)).
|
qs := models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
Filter("user_id", claims.UserID)
|
Filter("user_id", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId)
|
||||||
|
|
||||||
if keyword != "" {
|
if keyword != "" {
|
||||||
qs = qs.Filter("content__contains", keyword)
|
qs = qs.Filter("content__contains", keyword)
|
||||||
@@ -96,17 +98,20 @@ func (c *AppReminderController) GetList() {
|
|||||||
|
|
||||||
total, _ := qs.Count()
|
total, _ := qs.Count()
|
||||||
|
|
||||||
var schedules []models.PlatformSchedule
|
var schedules []models.BackendSchedule
|
||||||
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&schedules)
|
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&schedules)
|
||||||
if err != nil {
|
if err != nil && err != orm.ErrNoRows {
|
||||||
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if schedules == nil {
|
||||||
|
schedules = []models.BackendSchedule{}
|
||||||
|
}
|
||||||
|
|
||||||
list := make([]map[string]interface{}, 0, len(schedules))
|
list := make([]map[string]interface{}, 0, len(schedules))
|
||||||
for _, s := range schedules {
|
for _, s := range schedules {
|
||||||
var reminders []models.PlatformScheduleReminder
|
var reminders []models.BackendScheduleReminder
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", s.ID).
|
Filter("schedule_id", s.ID).
|
||||||
Filter("is_deleted", 0).
|
Filter("is_deleted", 0).
|
||||||
All(&reminders)
|
All(&reminders)
|
||||||
@@ -166,7 +171,7 @@ func (c *AppReminderController) GetList() {
|
|||||||
func (c *AppReminderController) GetDetail() {
|
func (c *AppReminderController) GetDetail() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,24 +182,25 @@ func (c *AppReminderController) GetDetail() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schedule models.PlatformSchedule
|
var schedule models.BackendSchedule
|
||||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
Filter("id", id).
|
Filter("id", id).
|
||||||
Filter("user_id", claims.UserID).
|
Filter("user_id", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId).
|
||||||
One(&schedule)
|
One(&schedule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(404, 404, "日程未找到")
|
c.jsonErr(404, 404, "日程未找到")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var reminders []models.PlatformScheduleReminder
|
var reminders []models.BackendScheduleReminder
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", schedule.ID).
|
Filter("schedule_id", schedule.ID).
|
||||||
Filter("is_deleted", 0).
|
Filter("is_deleted", 0).
|
||||||
All(&reminders)
|
All(&reminders)
|
||||||
|
|
||||||
channels := make([]string, 0, len(reminders))
|
channels := make([]string, 0, len(reminders))
|
||||||
var first models.PlatformScheduleReminder
|
var first models.BackendScheduleReminder
|
||||||
for _, r := range reminders {
|
for _, r := range reminders {
|
||||||
channels = append(channels, r.RemindChannel)
|
channels = append(channels, r.RemindChannel)
|
||||||
first = r
|
first = r
|
||||||
@@ -236,7 +242,7 @@ func (c *AppReminderController) GetDetail() {
|
|||||||
func (c *AppReminderController) Create() {
|
func (c *AppReminderController) Create() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +281,8 @@ func (c *AppReminderController) Create() {
|
|||||||
title = "日程提醒"
|
title = "日程提醒"
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule := models.PlatformSchedule{
|
schedule := models.BackendSchedule{
|
||||||
|
Tid: claims.TenantId,
|
||||||
Title: title,
|
Title: title,
|
||||||
Content: content,
|
Content: content,
|
||||||
ScheduleTime: schedTime,
|
ScheduleTime: schedTime,
|
||||||
@@ -298,7 +305,8 @@ func (c *AppReminderController) Create() {
|
|||||||
|
|
||||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||||
|
|
||||||
reminder := models.PlatformScheduleReminder{
|
reminder := models.BackendScheduleReminder{
|
||||||
|
Tid: claims.TenantId,
|
||||||
ScheduleID: uint64(schedID),
|
ScheduleID: uint64(schedID),
|
||||||
RemindChannel: ch,
|
RemindChannel: ch,
|
||||||
AdvanceMinutes: p.AdvanceMinutes,
|
AdvanceMinutes: p.AdvanceMinutes,
|
||||||
@@ -336,7 +344,7 @@ func (c *AppReminderController) Create() {
|
|||||||
func (c *AppReminderController) Update() {
|
func (c *AppReminderController) Update() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,18 +372,19 @@ func (c *AppReminderController) Update() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schedule models.PlatformSchedule
|
var schedule models.BackendSchedule
|
||||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
Filter("id", id).
|
Filter("id", id).
|
||||||
Filter("user_id", claims.UserID).
|
Filter("user_id", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId).
|
||||||
One(&schedule)
|
One(&schedule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(404, 404, "日程未找到")
|
c.jsonErr(404, 404, "日程未找到")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var reminders []models.PlatformScheduleReminder
|
var reminders []models.BackendScheduleReminder
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", id).
|
Filter("schedule_id", id).
|
||||||
Filter("is_deleted", 0).
|
Filter("is_deleted", 0).
|
||||||
All(&reminders)
|
All(&reminders)
|
||||||
@@ -414,7 +423,7 @@ func (c *AppReminderController) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 软删除旧提醒,重建
|
// 软删除旧提醒,重建
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", id).
|
Filter("schedule_id", id).
|
||||||
Update(map[string]interface{}{
|
Update(map[string]interface{}{
|
||||||
"IsDeleted": 1,
|
"IsDeleted": 1,
|
||||||
@@ -432,7 +441,8 @@ func (c *AppReminderController) Update() {
|
|||||||
|
|
||||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||||
|
|
||||||
reminder := models.PlatformScheduleReminder{
|
reminder := models.BackendScheduleReminder{
|
||||||
|
Tid: claims.TenantId,
|
||||||
ScheduleID: id,
|
ScheduleID: id,
|
||||||
RemindChannel: ch,
|
RemindChannel: ch,
|
||||||
AdvanceMinutes: p.AdvanceMinutes,
|
AdvanceMinutes: p.AdvanceMinutes,
|
||||||
@@ -470,7 +480,7 @@ func (c *AppReminderController) Update() {
|
|||||||
func (c *AppReminderController) Delete() {
|
func (c *AppReminderController) Delete() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,18 +491,19 @@ func (c *AppReminderController) Delete() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schedule models.PlatformSchedule
|
var schedule models.BackendSchedule
|
||||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
Filter("id", id).
|
Filter("id", id).
|
||||||
Filter("user_id", claims.UserID).
|
Filter("user_id", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId).
|
||||||
One(&schedule)
|
One(&schedule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(404, 404, "日程未找到")
|
c.jsonErr(404, 404, "日程未找到")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var reminders []models.PlatformScheduleReminder
|
var reminders []models.BackendScheduleReminder
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", id).
|
Filter("schedule_id", id).
|
||||||
Filter("is_deleted", 0).
|
Filter("is_deleted", 0).
|
||||||
All(&reminders)
|
All(&reminders)
|
||||||
@@ -512,9 +523,9 @@ func (c *AppReminderController) Delete() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.Orm.QueryTable(new(models.PlatformSchedule)).Filter("id", id).Delete()
|
_, err = models.Orm.QueryTable(new(models.BackendSchedule)).Filter("id", id).Delete()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", id).
|
Filter("schedule_id", id).
|
||||||
Update(map[string]interface{}{
|
Update(map[string]interface{}{
|
||||||
"IsDeleted": 1,
|
"IsDeleted": 1,
|
||||||
@@ -530,7 +541,7 @@ func (c *AppReminderController) Delete() {
|
|||||||
func (c *AppReminderController) ToggleComplete() {
|
func (c *AppReminderController) ToggleComplete() {
|
||||||
claims, err := c.appClaims()
|
claims, err := c.appClaims()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(401, 401, err.Error())
|
c.jsonErr(401, 401, "未登录或无权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,18 +552,19 @@ func (c *AppReminderController) ToggleComplete() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schedule models.PlatformSchedule
|
var schedule models.BackendSchedule
|
||||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
Filter("id", id).
|
Filter("id", id).
|
||||||
Filter("user_id", claims.UserID).
|
Filter("user_id", claims.UserID).
|
||||||
|
Filter("tid", claims.TenantId).
|
||||||
One(&schedule)
|
One(&schedule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.jsonErr(404, 404, "日程未找到")
|
c.jsonErr(404, 404, "日程未找到")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var reminders []models.PlatformScheduleReminder
|
var reminders []models.BackendScheduleReminder
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
Filter("schedule_id", id).
|
Filter("schedule_id", id).
|
||||||
Filter("is_deleted", 0).
|
Filter("is_deleted", 0).
|
||||||
All(&reminders)
|
All(&reminders)
|
||||||
|
|||||||
@@ -210,13 +210,13 @@ func (c *BackendReminderController) GetReminderDetail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"id": schedule.ID,
|
"id": schedule.ID,
|
||||||
"title": schedule.Title,
|
"title": schedule.Title,
|
||||||
"content": schedule.Content,
|
"content": schedule.Content,
|
||||||
"schedule_time": schedule.ScheduleTime.Format("2006-01-02 15:04:05"),
|
"schedule_time": schedule.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||||
"remind_channels": channels,
|
"remind_channels": channels,
|
||||||
"receiver_targets": targets,
|
"receiver_targets": targets,
|
||||||
"is_finished": isFinished,
|
"is_finished": isFinished,
|
||||||
}
|
}
|
||||||
if first.ID > 0 {
|
if first.ID > 0 {
|
||||||
data["advance_minutes"] = first.AdvanceMinutes
|
data["advance_minutes"] = first.AdvanceMinutes
|
||||||
@@ -295,16 +295,16 @@ func (c *BackendReminderController) CreateReminder() {
|
|||||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||||
|
|
||||||
reminder := models.BackendScheduleReminder{
|
reminder := models.BackendScheduleReminder{
|
||||||
Tid: claims.TenantId,
|
Tid: claims.TenantId,
|
||||||
ScheduleID: uint64(schedID),
|
ScheduleID: uint64(schedID),
|
||||||
RemindChannel: ch,
|
RemindChannel: ch,
|
||||||
AdvanceMinutes: p.AdvanceMinutes,
|
AdvanceMinutes: p.AdvanceMinutes,
|
||||||
NextRemindTime: firstSendTime,
|
NextRemindTime: firstSendTime,
|
||||||
ReceiverUserID: uint64(claims.UserID),
|
ReceiverUserID: uint64(claims.UserID),
|
||||||
ReceiverTarget: target,
|
ReceiverTarget: target,
|
||||||
RemindStatus: 0,
|
RemindStatus: 0,
|
||||||
CreateTime: time.Now(),
|
CreateTime: time.Now(),
|
||||||
UpdateTime: time.Now(),
|
UpdateTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ch == "EMAIL" || ch == "BARK" {
|
if ch == "EMAIL" || ch == "BARK" {
|
||||||
@@ -433,16 +433,16 @@ func (c *BackendReminderController) UpdateReminder() {
|
|||||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||||
|
|
||||||
reminder := models.BackendScheduleReminder{
|
reminder := models.BackendScheduleReminder{
|
||||||
Tid: claims.TenantId,
|
Tid: claims.TenantId,
|
||||||
ScheduleID: id,
|
ScheduleID: id,
|
||||||
RemindChannel: ch,
|
RemindChannel: ch,
|
||||||
AdvanceMinutes: p.AdvanceMinutes,
|
AdvanceMinutes: p.AdvanceMinutes,
|
||||||
NextRemindTime: firstSendTime,
|
NextRemindTime: firstSendTime,
|
||||||
ReceiverUserID: schedule.UserID,
|
ReceiverUserID: schedule.UserID,
|
||||||
ReceiverTarget: target,
|
ReceiverTarget: target,
|
||||||
RemindStatus: 0,
|
RemindStatus: 0,
|
||||||
CreateTime: time.Now(),
|
CreateTime: time.Now(),
|
||||||
UpdateTime: time.Now(),
|
UpdateTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ch == "EMAIL" || ch == "BARK" {
|
if ch == "EMAIL" || ch == "BARK" {
|
||||||
@@ -703,13 +703,20 @@ func (c *BackendReminderController) TestReminder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dummyToken := "test-token-for-verification"
|
dummyToken := "test-token-for-verification"
|
||||||
reminder := &models.PlatformScheduleReminder{
|
reminder := &models.BackendScheduleReminder{
|
||||||
|
Tid: claims.TenantId,
|
||||||
RemindChannel: ch,
|
RemindChannel: ch,
|
||||||
ReceiverUserID: uint64(claims.UserID),
|
ReceiverUserID: uint64(claims.UserID),
|
||||||
AckToken: &dummyToken,
|
AckToken: &dummyToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
success, sendErr := sender.Send(context.Background(), reminder, "[测试]"+p.Title, p.Content)
|
success, sendErr := sender.Send(context.Background(), services.ReminderData{
|
||||||
|
RemindChannel: reminder.RemindChannel,
|
||||||
|
ReceiverUserID: reminder.ReceiverUserID,
|
||||||
|
ReceiverTarget: reminder.ReceiverTarget,
|
||||||
|
AckToken: reminder.AckToken,
|
||||||
|
Tid: reminder.Tid,
|
||||||
|
}, "[测试]"+p.Title, p.Content)
|
||||||
msg := "发送成功"
|
msg := "发送成功"
|
||||||
if !success {
|
if !success {
|
||||||
msg = "发送失败"
|
msg = "发送失败"
|
||||||
|
|||||||
+636
-631
File diff suppressed because it is too large
Load Diff
+388
-371
@@ -1,371 +1,388 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"server/models"
|
"server/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReminderSender 提醒发送接口
|
type ReminderData struct {
|
||||||
type ReminderSender interface {
|
RemindChannel string
|
||||||
Send(ctx context.Context, reminder *models.PlatformScheduleReminder, title, content string) (success bool, err error)
|
ReceiverUserID uint64
|
||||||
}
|
ReceiverTarget *string
|
||||||
|
AckToken *string
|
||||||
// SMSSender 短信发送实现
|
Tid int
|
||||||
type SMSSender struct{}
|
}
|
||||||
|
|
||||||
func (s *SMSSender) Send(ctx context.Context, reminder *models.PlatformScheduleReminder, title, content string) (bool, error) {
|
// ReminderSender 提醒发送接口
|
||||||
backendURL, apiKey, err := getDefaultSystemSMSConfig()
|
type ReminderSender interface {
|
||||||
if err != nil {
|
Send(ctx context.Context, reminder ReminderData, title, content string) (success bool, err error)
|
||||||
return false, err
|
}
|
||||||
}
|
|
||||||
phone := ""
|
// SMSSender 短信发送实现
|
||||||
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
type SMSSender struct{}
|
||||||
phone = *reminder.ReceiverTarget
|
|
||||||
} else {
|
func (s *SMSSender) Send(ctx context.Context, reminder ReminderData, title, content string) (bool, error) {
|
||||||
var user models.AdminUser
|
backendURL, apiKey, err := getDefaultSystemSMSConfig()
|
||||||
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Phone != nil {
|
if err != nil {
|
||||||
phone = *user.Phone
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
phone := ""
|
||||||
if phone == "" {
|
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
||||||
return false, fmt.Errorf("未配置手机号")
|
phone = *reminder.ReceiverTarget
|
||||||
}
|
} else {
|
||||||
|
var user models.AdminUser
|
||||||
enqueueURL := strings.TrimRight(backendURL, "/") + "/api/v1/business/outbound-tasks"
|
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Phone != nil {
|
||||||
payload := map[string]interface{}{
|
phone = *user.Phone
|
||||||
"phone": phone,
|
}
|
||||||
"content": title + ": " + content,
|
}
|
||||||
}
|
if phone == "" {
|
||||||
bs, _ := json.Marshal(payload)
|
return false, fmt.Errorf("未配置手机号")
|
||||||
|
}
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", enqueueURL, bytes.NewReader(bs))
|
enqueueURL := strings.TrimRight(backendURL, "/") + "/api/v1/business/outbound-tasks"
|
||||||
if err != nil {
|
payload := map[string]interface{}{
|
||||||
return false, err
|
"phone": phone,
|
||||||
}
|
"content": title + ": " + content,
|
||||||
req.Header.Set("Content-Type", "application/json")
|
}
|
||||||
req.Header.Set("X-Api-Key", apiKey)
|
bs, _ := json.Marshal(payload)
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
if err != nil {
|
req, err := http.NewRequestWithContext(ctx, "POST", enqueueURL, bytes.NewReader(bs))
|
||||||
return false, err
|
if err != nil {
|
||||||
}
|
return false, err
|
||||||
defer resp.Body.Close()
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
if resp.StatusCode != http.StatusOK {
|
req.Header.Set("X-Api-Key", apiKey)
|
||||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
||||||
return false, fmt.Errorf("网关返回HTTP状态码: %d, 返回内容: %s", resp.StatusCode, string(bodyBytes))
|
resp, err := client.Do(req)
|
||||||
}
|
if err != nil {
|
||||||
|
return false, err
|
||||||
return true, nil
|
}
|
||||||
}
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// EmailSender 邮件发送实现
|
if resp.StatusCode != http.StatusOK {
|
||||||
type EmailSender struct{}
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
return false, fmt.Errorf("网关返回HTTP状态码: %d, 返回内容: %s", resp.StatusCode, string(bodyBytes))
|
||||||
func (s *EmailSender) Send(ctx context.Context, reminder *models.PlatformScheduleReminder, title, content string) (bool, error) {
|
}
|
||||||
emails, err := ListSystemEmails()
|
|
||||||
if err != nil || len(emails) == 0 {
|
return true, nil
|
||||||
return false, fmt.Errorf("未配置系统邮箱")
|
}
|
||||||
}
|
|
||||||
emailCfg := emails[0]
|
// EmailSender 邮件发送实现
|
||||||
if emailCfg.FromAddress == "" || emailCfg.Host == "" {
|
type EmailSender struct{}
|
||||||
return false, fmt.Errorf("未配置系统邮箱")
|
|
||||||
}
|
func (s *EmailSender) Send(ctx context.Context, reminder ReminderData, title, content string) (bool, error) {
|
||||||
|
emails, err := ListSystemEmails()
|
||||||
toEmail := ""
|
if err != nil || len(emails) == 0 {
|
||||||
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
return false, fmt.Errorf("未配置系统邮箱")
|
||||||
toEmail = *reminder.ReceiverTarget
|
}
|
||||||
} else {
|
emailCfg := emails[0]
|
||||||
var user models.AdminUser
|
if emailCfg.FromAddress == "" || emailCfg.Host == "" {
|
||||||
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Email != nil {
|
return false, fmt.Errorf("未配置系统邮箱")
|
||||||
toEmail = *user.Email
|
}
|
||||||
}
|
|
||||||
}
|
toEmail := ""
|
||||||
if toEmail == "" {
|
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
||||||
return false, fmt.Errorf("未配置收件邮箱")
|
toEmail = *reminder.ReceiverTarget
|
||||||
}
|
} else {
|
||||||
|
var user models.AdminUser
|
||||||
sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn")
|
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", reminder.ReceiverUserID).One(&user); err == nil && user.Email != nil {
|
||||||
ackToken := ""
|
toEmail = *user.Email
|
||||||
if reminder.AckToken != nil {
|
}
|
||||||
ackToken = *reminder.AckToken
|
}
|
||||||
}
|
if toEmail == "" {
|
||||||
|
return false, fmt.Errorf("未配置收件邮箱")
|
||||||
// 构造 HTML 邮件
|
}
|
||||||
htmlBody := fmt.Sprintf(`
|
|
||||||
<div style="font-family: Arial, sans-serif; padding: 20px; border: 1px solid #eee; border-radius: 5px; max-width: 600px; margin: 0 auto;">
|
sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn")
|
||||||
<h2 style="color: #409EFF; margin-bottom: 20px;">日程提醒:%s</h2>
|
ackToken := ""
|
||||||
<p style="font-size: 16px; line-height: 1.6; color: #333;">%s</p>
|
if reminder.AckToken != nil {
|
||||||
<hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;" />
|
ackToken = *reminder.AckToken
|
||||||
`, title, content)
|
}
|
||||||
|
|
||||||
if ackToken != "" {
|
// 构造 HTML 邮件
|
||||||
ackURL := fmt.Sprintf("%s/api/schedule/reminder/ack?token=%s", strings.TrimRight(sysDomain, "/"), ackToken)
|
htmlBody := fmt.Sprintf(`
|
||||||
htmlBody += fmt.Sprintf(`
|
<div style="font-family: Arial, sans-serif; padding: 20px; border: 1px solid #eee; border-radius: 5px; max-width: 600px; margin: 0 auto;">
|
||||||
<div style="text-align: center; margin-top: 30px;">
|
<h2 style="color: #409EFF; margin-bottom: 20px;">日程提醒:%s</h2>
|
||||||
<a href="%s" target="_blank" style="background-color: #409EFF; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold; display: inline-block;">
|
<p style="font-size: 16px; line-height: 1.6; color: #333;">%s</p>
|
||||||
收到,确认此提醒
|
<hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;" />
|
||||||
</a>
|
`, title, content)
|
||||||
</div>
|
|
||||||
<p style="font-size: 12px; color: #999; text-align: center; margin-top: 15px;">确认收到后,系统将不再向您发送该日程的重复提醒。</p>
|
if ackToken != "" {
|
||||||
`, ackURL)
|
ackURL := fmt.Sprintf("%s/api/schedule/reminder/ack?token=%s", strings.TrimRight(sysDomain, "/"), ackToken)
|
||||||
}
|
htmlBody += fmt.Sprintf(`
|
||||||
|
<div style="text-align: center; margin-top: 30px;">
|
||||||
htmlBody += "</div>"
|
<a href="%s" target="_blank" style="background-color: #409EFF; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold; display: inline-block;">
|
||||||
|
收到,确认此提醒
|
||||||
cfg := SMTPConfig{
|
</a>
|
||||||
FromAddress: emailCfg.FromAddress,
|
</div>
|
||||||
Host: emailCfg.Host,
|
<p style="font-size: 12px; color: #999; text-align: center; margin-top: 15px;">确认收到后,系统将不再向您发送该日程的重复提醒。</p>
|
||||||
Port: emailCfg.Port,
|
`, ackURL)
|
||||||
Password: emailCfg.Password,
|
}
|
||||||
Encryption: emailCfg.Encryption,
|
|
||||||
Timeout: emailCfg.Timeout,
|
htmlBody += "</div>"
|
||||||
}
|
|
||||||
if emailCfg.FromName != nil {
|
cfg := SMTPConfig{
|
||||||
cfg.FromName = *emailCfg.FromName
|
FromAddress: emailCfg.FromAddress,
|
||||||
}
|
Host: emailCfg.Host,
|
||||||
|
Port: emailCfg.Port,
|
||||||
err = SendHTMLEmailSMTP(cfg, toEmail, title, htmlBody)
|
Password: emailCfg.Password,
|
||||||
if err != nil {
|
Encryption: emailCfg.Encryption,
|
||||||
return false, err
|
Timeout: emailCfg.Timeout,
|
||||||
}
|
}
|
||||||
|
if emailCfg.FromName != nil {
|
||||||
return true, nil
|
cfg.FromName = *emailCfg.FromName
|
||||||
}
|
}
|
||||||
|
|
||||||
// BarkSender Bark 推送实现
|
err = SendHTMLEmailSMTP(cfg, toEmail, title, htmlBody)
|
||||||
type BarkSender struct{}
|
if err != nil {
|
||||||
|
return false, err
|
||||||
func (s *BarkSender) Send(ctx context.Context, reminder *models.PlatformScheduleReminder, title, content string) (bool, error) {
|
}
|
||||||
deviceKey := ""
|
|
||||||
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
return true, nil
|
||||||
deviceKey = *reminder.ReceiverTarget
|
}
|
||||||
} else {
|
|
||||||
deviceKey = models.GetPlatformSettingValue("bark_device_key", "")
|
// BarkSender Bark 推送实现
|
||||||
}
|
type BarkSender struct{}
|
||||||
if deviceKey == "" {
|
|
||||||
return false, fmt.Errorf("Bark 设备 Key 未配置")
|
func (s *BarkSender) Send(ctx context.Context, reminder ReminderData, title, content string) (bool, error) {
|
||||||
}
|
deviceKey := ""
|
||||||
|
if reminder.ReceiverTarget != nil && *reminder.ReceiverTarget != "" {
|
||||||
serverURL := models.GetPlatformSettingValue("bark_server_url", "https://api.day.app")
|
deviceKey = *reminder.ReceiverTarget
|
||||||
sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn")
|
} else {
|
||||||
ackToken := ""
|
deviceKey = models.GetPlatformSettingValue("bark_device_key", "")
|
||||||
if reminder.AckToken != nil {
|
}
|
||||||
ackToken = *reminder.AckToken
|
if deviceKey == "" {
|
||||||
}
|
return false, fmt.Errorf("Bark 设备 Key 未配置")
|
||||||
|
}
|
||||||
baseURL := strings.TrimRight(serverURL, "/")
|
|
||||||
escapedTitle := url.PathEscape(title)
|
serverURL := models.GetPlatformSettingValue("bark_server_url", "https://api.day.app")
|
||||||
pushContent := content
|
sysDomain := models.GetPlatformSettingValue("system_domain", "https://api.yunzer.cn")
|
||||||
if ackToken != "" {
|
ackToken := ""
|
||||||
pushContent += "\n确认收到请点击→"
|
if reminder.AckToken != nil {
|
||||||
}
|
ackToken = *reminder.AckToken
|
||||||
escapedContent := url.PathEscape(pushContent)
|
}
|
||||||
|
|
||||||
barkURL := fmt.Sprintf("%s/%s/%s/%s", baseURL, deviceKey, escapedTitle, escapedContent)
|
baseURL := strings.TrimRight(serverURL, "/")
|
||||||
|
escapedTitle := url.PathEscape(title)
|
||||||
if ackToken != "" {
|
pushContent := content
|
||||||
ackURL := fmt.Sprintf("%s/api/schedule/reminder/ack?token=%s", strings.TrimRight(sysDomain, "/"), ackToken)
|
if ackToken != "" {
|
||||||
// Bark 官方推送支持 url 参数
|
pushContent += "\n确认收到请点击→"
|
||||||
barkURL += "?url=" + url.QueryEscape(ackURL)
|
}
|
||||||
}
|
escapedContent := url.PathEscape(pushContent)
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
barkURL := fmt.Sprintf("%s/%s/%s/%s", baseURL, deviceKey, escapedTitle, escapedContent)
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", barkURL, nil)
|
|
||||||
if err != nil {
|
if ackToken != "" {
|
||||||
return false, err
|
ackURL := fmt.Sprintf("%s/api/schedule/reminder/ack?token=%s", strings.TrimRight(sysDomain, "/"), ackToken)
|
||||||
}
|
// Bark 官方推送支持 url 参数
|
||||||
|
barkURL += "?url=" + url.QueryEscape(ackURL)
|
||||||
resp, err := client.Do(req)
|
}
|
||||||
if err != nil {
|
|
||||||
return false, err
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
}
|
req, err := http.NewRequestWithContext(ctx, "GET", barkURL, nil)
|
||||||
defer resp.Body.Close()
|
if err != nil {
|
||||||
|
return false, err
|
||||||
if resp.StatusCode != http.StatusOK {
|
}
|
||||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
||||||
return false, fmt.Errorf("Bark返回HTTP状态码: %d, 返回内容: %s", resp.StatusCode, string(bodyBytes))
|
resp, err := client.Do(req)
|
||||||
}
|
if err != nil {
|
||||||
|
return false, err
|
||||||
return true, nil
|
}
|
||||||
}
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// SiteMsgSender 站内信发送实现
|
if resp.StatusCode != http.StatusOK {
|
||||||
type SiteMsgSender struct{}
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
return false, fmt.Errorf("Bark返回HTTP状态码: %d, 返回内容: %s", resp.StatusCode, string(bodyBytes))
|
||||||
func (s *SiteMsgSender) Send(ctx context.Context, reminder *models.PlatformScheduleReminder, title, content string) (bool, error) {
|
}
|
||||||
now := time.Now()
|
|
||||||
msg := &models.SystemReminderList{
|
return true, nil
|
||||||
Title: title,
|
}
|
||||||
Content: content,
|
|
||||||
SenderID: 0,
|
// SiteMsgSender 站内信发送实现
|
||||||
SenderType: "system",
|
type SiteMsgSender struct{}
|
||||||
ReceiverID: reminder.ReceiverUserID,
|
|
||||||
ReceiverType: "platform", // 平台端用户
|
func (s *SiteMsgSender) Send(ctx context.Context, reminder ReminderData, title, content string) (bool, error) {
|
||||||
IsRead: 0,
|
now := time.Now()
|
||||||
CreateTime: &now,
|
msg := &models.SystemReminderList{
|
||||||
}
|
Title: title,
|
||||||
_, err := models.Orm.Insert(msg)
|
Content: content,
|
||||||
if err != nil {
|
SenderID: 0,
|
||||||
return false, err
|
SenderType: "system",
|
||||||
}
|
ReceiverID: reminder.ReceiverUserID,
|
||||||
return true, nil
|
ReceiverType: "tenant", // backend/租户用户
|
||||||
}
|
IsRead: 0,
|
||||||
|
CreateTime: &now,
|
||||||
// generateUUID 生成一个安全的随机 UUID 字符
|
TargetType: "tenant",
|
||||||
func generateUUID() string {
|
TargetTenantID: uint64(reminder.Tid),
|
||||||
b := make([]byte, 16)
|
}
|
||||||
_, _ = rand.Read(b)
|
_, err := models.Orm.Insert(msg)
|
||||||
b[6] = (b[6] & 0x0f) | 0x40
|
if err != nil {
|
||||||
b[8] = (b[8] & 0x3f) | 0x80
|
return false, err
|
||||||
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
}
|
||||||
}
|
return true, nil
|
||||||
|
}
|
||||||
// StartReminderScheduler 启动定时提醒调度器 (1分钟一次的 Ticker)
|
|
||||||
func StartReminderScheduler(stopChan chan struct{}) {
|
// generateUUID 生成一个安全的随机 UUID 字符
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
func generateUUID() string {
|
||||||
go func() {
|
b := make([]byte, 16)
|
||||||
for {
|
_, _ = rand.Read(b)
|
||||||
select {
|
b[6] = (b[6] & 0x0f) | 0x40
|
||||||
case <-ticker.C:
|
b[8] = (b[8] & 0x3f) | 0x80
|
||||||
scanAndSendReminders()
|
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||||
case <-stopChan:
|
}
|
||||||
ticker.Stop()
|
|
||||||
return
|
// StartReminderScheduler 启动定时提醒调度器 (1分钟一次的 Ticker)
|
||||||
}
|
func StartReminderScheduler(stopChan chan struct{}) {
|
||||||
}
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
}()
|
go func() {
|
||||||
}
|
for {
|
||||||
|
select {
|
||||||
func scanAndSendReminders() {
|
case <-ticker.C:
|
||||||
// 1. 生成唯一扫描批次号用于抢占锁定
|
scanAndSendReminders()
|
||||||
scanBatch := generateUUID()
|
case <-stopChan:
|
||||||
now := time.Now()
|
ticker.Stop()
|
||||||
|
return
|
||||||
// 2. 抢占待处理的数据(乐观锁防并发重复发送)
|
}
|
||||||
_, err := models.Orm.Raw(`
|
}
|
||||||
UPDATE yz_platform_schedule_reminder
|
}()
|
||||||
SET scan_lock = ?, update_time = NOW()
|
}
|
||||||
WHERE next_remind_time <= ?
|
|
||||||
AND remind_status IN (0, 1)
|
func scanAndSendReminders() {
|
||||||
AND is_deleted = 0
|
// 1. 生成唯一扫描批次号用于抢占锁定
|
||||||
AND (scan_lock = '' OR scan_lock IS NULL)
|
scanBatch := generateUUID()
|
||||||
`, scanBatch, now).Exec()
|
now := time.Now()
|
||||||
if err != nil {
|
|
||||||
return
|
// 2. 抢占待处理的数据(乐观锁防并发重复发送)
|
||||||
}
|
_, err := models.Orm.Raw(`
|
||||||
|
UPDATE yz_backend_schedule_reminder
|
||||||
// 3. 查询自己锁定成功的数据
|
SET scan_lock = ?, update_time = NOW()
|
||||||
var list []models.PlatformScheduleReminder
|
WHERE next_remind_time <= ?
|
||||||
_, err = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
AND remind_status IN (0, 1)
|
||||||
Filter("scan_lock", scanBatch).
|
AND is_deleted = 0
|
||||||
Filter("remind_status__in", 0, 1).
|
AND (scan_lock = '' OR scan_lock IS NULL)
|
||||||
Filter("is_deleted", 0).
|
`, scanBatch, now).Exec()
|
||||||
All(&list)
|
if err != nil {
|
||||||
if err != nil || len(list) == 0 {
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
// 3. 查询自己锁定成功的数据
|
||||||
// 实例分发发送
|
var list []models.BackendScheduleReminder
|
||||||
senders := map[string]ReminderSender{
|
_, err = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
"SMS": &SMSSender{},
|
Filter("scan_lock", scanBatch).
|
||||||
"EMAIL": &EmailSender{},
|
Filter("remind_status__in", 0, 1).
|
||||||
"BARK": &BarkSender{},
|
Filter("is_deleted", 0).
|
||||||
"SITE_MSG": &SiteMsgSender{},
|
All(&list)
|
||||||
}
|
if err != nil || len(list) == 0 {
|
||||||
|
return
|
||||||
for i := range list {
|
}
|
||||||
reminder := &list[i]
|
|
||||||
|
// 实例分发发送
|
||||||
// 3.1 获取日程信息(主要拿 Content,Title 统一为 "日程提醒")
|
senders := map[string]ReminderSender{
|
||||||
var schedule models.PlatformSchedule
|
"SMS": &SMSSender{},
|
||||||
err := models.Orm.QueryTable(new(models.PlatformSchedule)).
|
"EMAIL": &EmailSender{},
|
||||||
Filter("id", reminder.ScheduleID).
|
"BARK": &BarkSender{},
|
||||||
One(&schedule)
|
"SITE_MSG": &SiteMsgSender{},
|
||||||
title := "日程提醒"
|
}
|
||||||
content := "您有一个待处理的日程时间已到,请注意查收。"
|
|
||||||
if err == nil {
|
for i := range list {
|
||||||
content = schedule.Content
|
reminder := &list[i]
|
||||||
}
|
|
||||||
|
// 3.1 获取日程信息(主要拿 Content,Title 统一为 "日程提醒")
|
||||||
sender, ok := senders[reminder.RemindChannel]
|
var schedule models.BackendSchedule
|
||||||
if !ok {
|
err := models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||||
// 未知渠道,直接强制置为结束
|
Filter("id", reminder.ScheduleID).
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
One(&schedule)
|
||||||
Filter("id", reminder.ID).
|
title := "日程提醒"
|
||||||
Update(map[string]interface{}{
|
content := "您有一个待处理的日程时间已到,请注意查收。"
|
||||||
"remind_status": 2,
|
if err == nil {
|
||||||
"scan_lock": "",
|
content = schedule.Content
|
||||||
"update_time": time.Now(),
|
}
|
||||||
})
|
|
||||||
continue
|
sender, ok := senders[reminder.RemindChannel]
|
||||||
}
|
if !ok {
|
||||||
|
// 未知渠道,直接强制置为结束
|
||||||
// 执行发送
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
ctx := context.Background()
|
Filter("id", reminder.ID).
|
||||||
success, sendErr := sender.Send(ctx, reminder, title, content)
|
Update(map[string]interface{}{
|
||||||
|
"remind_status": 2,
|
||||||
// 3.2 记录发送流水日志
|
"scan_lock": "",
|
||||||
sendResult := int8(0)
|
"update_time": time.Now(),
|
||||||
var failReason *string
|
})
|
||||||
if success {
|
continue
|
||||||
sendResult = 1
|
}
|
||||||
} else if sendErr != nil {
|
|
||||||
errStr := sendErr.Error()
|
// 执行发送
|
||||||
if len(errStr) > 255 {
|
ctx := context.Background()
|
||||||
errStr = errStr[:255]
|
success, sendErr := sender.Send(ctx, ReminderData{
|
||||||
}
|
RemindChannel: reminder.RemindChannel,
|
||||||
failReason = &errStr
|
ReceiverUserID: reminder.ReceiverUserID,
|
||||||
}
|
ReceiverTarget: reminder.ReceiverTarget,
|
||||||
|
AckToken: reminder.AckToken,
|
||||||
logRow := &models.PlatformScheduleReminderSendLog{
|
Tid: reminder.Tid,
|
||||||
ReminderID: reminder.ID,
|
}, title, content)
|
||||||
SendTime: time.Now(),
|
|
||||||
SendResult: sendResult,
|
// 3.2 记录发送流水日志
|
||||||
FailReason: failReason,
|
sendResult := int8(0)
|
||||||
}
|
var failReason *string
|
||||||
_, _ = models.Orm.Insert(logRow)
|
if success {
|
||||||
|
sendResult = 1
|
||||||
// 3.3 根据发送渠道分类更新提醒状态和下一次发送时间
|
} else if sendErr != nil {
|
||||||
newSendCount := reminder.SendCount + 1
|
errStr := sendErr.Error()
|
||||||
newStatus := reminder.RemindStatus
|
if len(errStr) > 255 {
|
||||||
|
errStr = errStr[:255]
|
||||||
if reminder.RemindChannel == "SMS" || reminder.RemindChannel == "SITE_MSG" {
|
}
|
||||||
// 一次性发送:发送后直接置为结束
|
failReason = &errStr
|
||||||
newStatus = 2
|
}
|
||||||
} else {
|
|
||||||
// 重复发送渠道 EMAIL / BARK
|
logRow := &models.BackendScheduleReminderSendLog{
|
||||||
// 如果还没被 Ack,且没有达到 max_send_count,继续提醒
|
Tid: reminder.Tid,
|
||||||
if reminder.AckStatus == 0 && newSendCount < reminder.MaxSendCount {
|
ReminderID: reminder.ID,
|
||||||
newStatus = 1 // 提醒中
|
SendTime: time.Now(),
|
||||||
// 更新下次发送时间
|
SendResult: sendResult,
|
||||||
reminder.NextRemindTime = time.Now().Add(time.Duration(reminder.RepeatIntervalMinutes) * time.Minute)
|
FailReason: failReason,
|
||||||
} else {
|
}
|
||||||
// 达到最大上限或者已 Ack
|
_, _ = models.Orm.Insert(logRow)
|
||||||
newStatus = 2
|
|
||||||
}
|
// 3.3 根据发送渠道分类更新提醒状态和下一次发送时间
|
||||||
}
|
newSendCount := reminder.SendCount + 1
|
||||||
|
newStatus := reminder.RemindStatus
|
||||||
// 3.4 回写主表记录
|
|
||||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
if reminder.RemindChannel == "SMS" || reminder.RemindChannel == "SITE_MSG" {
|
||||||
Filter("id", reminder.ID).
|
// 一次性发送:发送后直接置为结束
|
||||||
Update(map[string]interface{}{
|
newStatus = 2
|
||||||
"SendCount": newSendCount,
|
} else {
|
||||||
"NextRemindTime": reminder.NextRemindTime,
|
// 重复发送渠道 EMAIL / BARK
|
||||||
"RemindStatus": newStatus,
|
// 如果还没被 Ack,且没有达到 max_send_count,继续提醒
|
||||||
"ScanLock": "", // 释放扫描锁
|
if reminder.AckStatus == 0 && newSendCount < reminder.MaxSendCount {
|
||||||
"UpdateTime": time.Now(),
|
newStatus = 1 // 提醒中
|
||||||
})
|
// 更新下次发送时间
|
||||||
}
|
reminder.NextRemindTime = time.Now().Add(time.Duration(reminder.RepeatIntervalMinutes) * time.Minute)
|
||||||
}
|
} else {
|
||||||
|
// 达到最大上限或者已 Ack
|
||||||
|
newStatus = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.4 回写主表记录
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||||
|
Filter("id", reminder.ID).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"SendCount": newSendCount,
|
||||||
|
"NextRemindTime": reminder.NextRemindTime,
|
||||||
|
"RemindStatus": newStatus,
|
||||||
|
"ScanLock": "", // 释放扫描锁
|
||||||
|
"UpdateTime": time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Generated
+170
-300
@@ -9,24 +9,14 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"@tiptap/extension-color": "^3.27.4",
|
"@tiptap/extension-color": "^3.28.0",
|
||||||
"@tiptap/extension-highlight": "^3.27.4",
|
"@tiptap/extension-image": "^3.28.0",
|
||||||
"@tiptap/extension-image": "^3.27.4",
|
"@tiptap/extension-link": "^3.28.0",
|
||||||
"@tiptap/extension-link": "^3.27.4",
|
"@tiptap/extension-text-align": "^3.28.0",
|
||||||
"@tiptap/extension-placeholder": "^3.27.4",
|
"@tiptap/extension-text-style": "^3.28.0",
|
||||||
"@tiptap/extension-subscript": "^3.27.4",
|
"@tiptap/extension-underline": "^3.28.0",
|
||||||
"@tiptap/extension-superscript": "^3.27.4",
|
"@tiptap/starter-kit": "^3.28.0",
|
||||||
"@tiptap/extension-table": "^3.27.4",
|
"@tiptap/vue-3": "^3.28.0",
|
||||||
"@tiptap/extension-table-cell": "^3.27.4",
|
|
||||||
"@tiptap/extension-table-header": "^3.27.4",
|
|
||||||
"@tiptap/extension-table-row": "^3.27.4",
|
|
||||||
"@tiptap/extension-text-align": "^3.27.4",
|
|
||||||
"@tiptap/extension-text-style": "^3.27.4",
|
|
||||||
"@tiptap/extension-typography": "^3.27.4",
|
|
||||||
"@tiptap/extension-underline": "^3.27.4",
|
|
||||||
"@tiptap/pm": "^3.27.4",
|
|
||||||
"@tiptap/starter-kit": "^3.27.4",
|
|
||||||
"@tiptap/vue-3": "^3.27.4",
|
|
||||||
"@umoteam/editor": "^10.2.1",
|
"@umoteam/editor": "^10.2.1",
|
||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
"chart": "^0.1.2",
|
"chart": "^0.1.2",
|
||||||
@@ -1174,49 +1164,49 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/core": {
|
"node_modules/@tiptap/core": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/core/-/core-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/core/-/core-3.28.0.tgz",
|
||||||
"integrity": "sha512-8W/GwlEn0JwNdpyVfTWcXwHYUpj9BWwO++YxtizmgjJzlwigSh7/xLVJMwVykuQHQ2fCq5rkUvmBRtpHOMLUQA==",
|
"integrity": "sha512-gUuD5WAYfbDxNSSJya/emh2KSzXZXLUYKW4fEnc1AQ5FE2twzh4LJ9UlKFIawigrUCAksWI5Fy1hRbv+5m4ZdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-blockquote": {
|
"node_modules/@tiptap/extension-blockquote": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-blockquote/-/extension-blockquote-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-blockquote/-/extension-blockquote-3.28.0.tgz",
|
||||||
"integrity": "sha512-d1tOHgP3R5cOE+Ot8qL/dkLXRByajgn+j6cCXHqDtmJO2wsK9knmbKQ0SEjbKrU6OgHrTnY/EotNxBEBW9HGoA==",
|
"integrity": "sha512-y8Xi6Z3AQLXedz0J8Z3kDsu7SKBmL50gKVXx3RK1Oo1cuCGhNChm3syChkAz3PLAbrPUM5rvJDSZdF2jUrDnng==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bold": {
|
"node_modules/@tiptap/extension-bold": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bold/-/extension-bold-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bold/-/extension-bold-3.28.0.tgz",
|
||||||
"integrity": "sha512-wTtJUUAxCAZ01ICH2DNlOBzzHKRQ1ZST8aRYtIhBPzqEUhnJaKGcjnDB4X49fqPi48iXaPxzhsInDl+rVUujWg==",
|
"integrity": "sha512-JhZQmr0AU741bOwjVMfuwJdK4g0TQwwPbeca9aqKHv5zvZw4i4G9G6fESVyMFc3Yag1ffpnq5EtNldHKTnMhlw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bubble-menu": {
|
"node_modules/@tiptap/extension-bubble-menu": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.28.0.tgz",
|
||||||
"integrity": "sha512-Poy7xwcD3POG5ew/TW7mYXv7m++vCchvHxPUqIfnTxBxvvvqDZkPYFWZS1lvPrSBtm1DcfUTQAgVutM5NDZ99Q==",
|
"integrity": "sha512-7AUNoHj2K4XLKCgW4uspeD/ENejPu2BeHvLTsiOoIO+XXDNSi2j0bbaIS8f2s/qnFirscwp/sbznp1qph/76qg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1227,48 +1217,48 @@
|
|||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bullet-list": {
|
"node_modules/@tiptap/extension-bullet-list": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.28.0.tgz",
|
||||||
"integrity": "sha512-rvja0N1RnwGJAVwDdbUfDIJ4NoT+KjPFaZudKiPuEMfMHfbqe4xcbbC2hsfs61JNcl2xmx+ohV6lzD9YxxJl1w==",
|
"integrity": "sha512-dSVuNiH7PFMmWGkNER9vfUb5bXUHDARvHbJ3l+APEb+ZiusVN1KFdM3nd/RsW/Rt5Gd3XwlIEU/c2Uf7cxYDcw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extension-list": "3.27.4"
|
"@tiptap/extension-list": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-code": {
|
"node_modules/@tiptap/extension-code": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-code/-/extension-code-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-code/-/extension-code-3.28.0.tgz",
|
||||||
"integrity": "sha512-aPc7opCR1ylK4m4c2lsjLsGpEBD1fLQQKWd5PbZiJvrTF8gkdGZlYLt9A6VukpxeJyHhb22Jaj4fxgKmGMeTtw==",
|
"integrity": "sha512-AUw2Acof3CQE6Q6Y22saK4lyg0nkeJ4XXniU65TdAi/9TE66TGiO4NQJJNNPgu8+VMEwl5j8VsIFK8sfTcNYtg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-code-block": {
|
"node_modules/@tiptap/extension-code-block": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-code-block/-/extension-code-block-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-code-block/-/extension-code-block-3.28.0.tgz",
|
||||||
"integrity": "sha512-a5caWfWN6Z6usy48vzJDDOhWoA6+rFFCHGpQM7jXn/7rRzYPcvBzTZUGptjEbltj4YqtrQ2tVwTJcCtbb+mknA==",
|
"integrity": "sha512-bHITDzT0umTLh+SiEVQzIXkCMt/TzbPM1+HQy9ZxgQDHAJj/vdmfNAnP3RCOrz4lETXyhNQ2b6kxeu3lWckxyA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-code-block-lowlight": {
|
"node_modules/@tiptap/extension-code-block-lowlight": {
|
||||||
@@ -1305,16 +1295,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-color": {
|
"node_modules/@tiptap/extension-color": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-color/-/extension-color-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-color/-/extension-color-3.28.0.tgz",
|
||||||
"integrity": "sha512-uGbgErKGKO4OTBGqnXOA1CGXa3IqoBaiPBGcHu/px01fS8EVFu0A7q5HS6vxFiMK6qW5x71sEpCA+FUINMAb3g==",
|
"integrity": "sha512-YbUMUdrgU04GDYoii1wuFVkiTSQoaEQh04kwgQayEQ46aUBEnnaJitWdp39aGMWYTIXD/c9K9cr5Vg8GSKcVxA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extension-text-style": "3.27.4"
|
"@tiptap/extension-text-style": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-details": {
|
"node_modules/@tiptap/extension-details": {
|
||||||
@@ -1333,16 +1323,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-document": {
|
"node_modules/@tiptap/extension-document": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-document/-/extension-document-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-document/-/extension-document-3.28.0.tgz",
|
||||||
"integrity": "sha512-7nAqgfkgb9HADBeCTnOHuTiyZuxfxvMPT3nH4OZeY+cmtkI1On3QffqlmtcUPvNbkhT3o9ehA1hVfCnQ1Ye4LQ==",
|
"integrity": "sha512-5SAKtlB1Tr/eZFhISL86HqmUup6rItvPtuPyRjmZo/VgbMwXEwiyAh1sMgFp5VNaeSMM14vJSyQpjhSaOmaBqg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-drag-handle": {
|
"node_modules/@tiptap/extension-drag-handle": {
|
||||||
@@ -1382,22 +1372,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-dropcursor": {
|
"node_modules/@tiptap/extension-dropcursor": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.28.0.tgz",
|
||||||
"integrity": "sha512-RiZasQJuUTUO3aME16Bn8eJH7cYnvhT5JCFDFq0ya/1iFI9wUQA2NJC5tb5TrZ74+sQwkYU9VzexnchM481Y9w==",
|
"integrity": "sha512-JA+dXQjjfJBpD0nVsZeddGVXRsmanESM9+8CtM35hI9wJnYMXay/B+5GUhswO20zhT3zhB2ZOTGe9knu6A8nIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extensions": "3.27.4"
|
"@tiptap/extensions": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-floating-menu": {
|
"node_modules/@tiptap/extension-floating-menu": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.28.0.tgz",
|
||||||
"integrity": "sha512-tnZywwoNDuEcUZmYYIztXl3PpIKUq+gKeaYPuZhpYEVTThU44tzK3ZuFOmd+qf2aAa1MQwxKWqUuLpNK77bwNw==",
|
"integrity": "sha512-59SECvJq3pQfeJBuydEdhQqrpl0oDlk8N1Ovs1Si3/fJa6rEQAJB2zIvcpBHky9Z+5JodI2eueDnv8eimiyGbg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1406,87 +1396,74 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@floating-ui/dom": "^1.0.0",
|
"@floating-ui/dom": "^1.0.0",
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-gapcursor": {
|
"node_modules/@tiptap/extension-gapcursor": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.28.0.tgz",
|
||||||
"integrity": "sha512-svLwSKcFhzpcJeXvxxKkRFuQpykmXrQefVhEsaXq0L95yJIIAGKMRmQC3mxKdzL2j0P9cY7V41bNVSyOAyvclw==",
|
"integrity": "sha512-Wo73kM4q8z4Va9i4+0n1cO0UEMVUVBHPqM6cAqEYXjB4T48LQkKjRsiQydCezm185rQAsmm1dyi72gtvVQfdMg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extensions": "3.27.4"
|
"@tiptap/extensions": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-hard-break": {
|
"node_modules/@tiptap/extension-hard-break": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-hard-break/-/extension-hard-break-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-hard-break/-/extension-hard-break-3.28.0.tgz",
|
||||||
"integrity": "sha512-W+Z9pmDgqjbdu3NeZOQrzA15iM4w60Yd8l2CYzxcdApPVIfYzb2S3a7+u1RqW9wnTYb6xyZjASmFNfxXS4P4cg==",
|
"integrity": "sha512-JHIFjX1luJgQ4VFEkIeXZpbc54Qiw+69P8FmlazYTh7AIJ6iLCpMFgQFELnivEAZ+/vS0lMPQubg0rCeM3UrHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-heading": {
|
"node_modules/@tiptap/extension-heading": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-heading/-/extension-heading-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-heading/-/extension-heading-3.28.0.tgz",
|
||||||
"integrity": "sha512-RgvpxzuYk6QEK+az+eiXpWvGlUso42zNcGnjyUrvskoZjS47MbhSg8ylRYQSRtXE0ETlXhAx4J7iGlGr72kyIw==",
|
"integrity": "sha512-rKjGG/Ik2lNaXBNVmONEDm5DBfGDLM1NWgSf5RRfBISrH8Lm1xqFruOB9tIaB0YUoSV3SBOiwTF9svmzvKSoRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-highlight": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-highlight/-/extension-highlight-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-STRX1qJLhTZslBF8fEE5qpTGrFd/g7Ufidjxt84p0uT8FrtcbfPUwyweeYwhZp7Iw8n+qODGYnheJTOpfaW2JA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "3.27.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.28.0.tgz",
|
||||||
"integrity": "sha512-2eQU/55nE5mhMJHALtLMuBL3dcVJUDVVT7n+uZYMaYE63BtCvC4VS08YLFSR7JZSVJIlgVAmdt5nAw0B+rEPNA==",
|
"integrity": "sha512-neBCMWprkppQUoLWyeJZDHqBw+xKX2oNhLD9MbFlhKIr092zgfP4mpCvQnSkn1OHl156KzZMp070+NyLBjgQ7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-image": {
|
"node_modules/@tiptap/extension-image": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-image/-/extension-image-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-image/-/extension-image-3.28.0.tgz",
|
||||||
"integrity": "sha512-yQ8CazyOL4z1/NbV1NLGv6DvchVhOXHH3uQ7md5VX/IGZruFpnm8IpF9MDpdAUxUFmTsXJDYyO2lWGO1PYWG8A==",
|
"integrity": "sha512-aKGCdEjyujUcQ6tEtq4EPbKxEC16QsKapv3DeZs1c/UHFiEUalbbPHBnxjGnupiLz75IR+07GYqBoZ81kSCxiA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-invisible-characters": {
|
"node_modules/@tiptap/extension-invisible-characters": {
|
||||||
@@ -1505,22 +1482,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-italic": {
|
"node_modules/@tiptap/extension-italic": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-italic/-/extension-italic-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-italic/-/extension-italic-3.28.0.tgz",
|
||||||
"integrity": "sha512-PeZT4XbyxAp7Lqo/hfA1k5LI27g1RlgS+YgXp2CeHXIrUfSpO5HlZXh02Bvb0pOdl3RFw2tEKtlHzjt8Y1+Nwg==",
|
"integrity": "sha512-Yur/ELz6dNVKQC7m8wGjtoLFxamGjJmA0rUEEOacTH6J39aFmcSxYkEGNDkYHbZFyHFYqbej6eI3VE0h08O0mg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-link": {
|
"node_modules/@tiptap/extension-link": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-link/-/extension-link-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-link/-/extension-link-3.28.0.tgz",
|
||||||
"integrity": "sha512-6K/FkNwMLWWQbNWKlycrUPTN7YcyVFdFwZncoBXe5WyarRjLTGw7ywafnCI9PDIWSq7ttzVL4NgjN2IN8kBXww==",
|
"integrity": "sha512-hy/PqSeTyl317yseFcHGE+3XVfQKQYaL4uZuj27xlrcO7MZ15drt1h9sUZy1FTBF3mr8676pQu7aoEm/Ww4ZRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"linkifyjs": "^4.3.3"
|
"linkifyjs": "^4.3.3"
|
||||||
@@ -1530,48 +1507,48 @@
|
|||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-list": {
|
"node_modules/@tiptap/extension-list": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list/-/extension-list-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list/-/extension-list-3.28.0.tgz",
|
||||||
"integrity": "sha512-A0BgmRO1RE0yLCx9w7GQITtKfS9wLE5cdngSYDiSpwulcXJhJjKm5mZ4OUZmks2VN4HO5jMl2BWCGt2NSDhA+w==",
|
"integrity": "sha512-zQ66i5DuhVOndmZ0d7H475Y5Yb+BMx+zfCjFkCzEc6WVef5MumtxBVTkGLQ0ibxUaD71s4QQZbjHWagpaTg0eQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-list-item": {
|
"node_modules/@tiptap/extension-list-item": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list-item/-/extension-list-item-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list-item/-/extension-list-item-3.28.0.tgz",
|
||||||
"integrity": "sha512-z5TVuPw2mkK0B/x+gFg3uUV7tBdaElDFg0zVgnXZCqlSVTLfIyInOOnG5LTWoAd9BdzBjGrzE3PohDcLVDDGBQ==",
|
"integrity": "sha512-GbXrnMac6knSetZY33NHwOrgXFPqH28uCklQqmOZQlsrdGqezDKk31qoEMr4GsKW9n/lViAeuc07oIzwLwCMng==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extension-list": "3.27.4"
|
"@tiptap/extension-list": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-list-keymap": {
|
"node_modules/@tiptap/extension-list-keymap": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.28.0.tgz",
|
||||||
"integrity": "sha512-on7JNDi7Eqz7UdZeZdiO83bQHo0flVDHzjmtR+v/nrCGW9H15D3CHs5+4ozLDiCvTK8tbkBuut/l9AWNxcCE/Q==",
|
"integrity": "sha512-u9Wks8c/eQAv0RkULNggOyTKDD69WVbcV9H5cbasPDUbq5XbEFVa9ajxhEGfMmQ5et3EX0QL8qBi50K0GqbIjA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extension-list": "3.27.4"
|
"@tiptap/extension-list": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-mathematics": {
|
"node_modules/@tiptap/extension-mathematics": {
|
||||||
@@ -1619,123 +1596,42 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-ordered-list": {
|
"node_modules/@tiptap/extension-ordered-list": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.28.0.tgz",
|
||||||
"integrity": "sha512-bHwLiof0FqJfWzB0act7oEKMTZatEKQ4IYCvmyF5EktjMs4kxEatkPp4Yx/1LSYSjLy1MMT7oLELyaz2FFYyXA==",
|
"integrity": "sha512-OZNYrKKL9D01ySOujlNbKlLS9/3wGgKNYeoHZUg0e+Ta8WPVvL3W1zH6TgiU5sF2ZSGUBoq3w7mdaO/iROlUkw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/extension-list": "3.27.4"
|
"@tiptap/extension-list": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-paragraph": {
|
"node_modules/@tiptap/extension-paragraph": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-paragraph/-/extension-paragraph-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-paragraph/-/extension-paragraph-3.28.0.tgz",
|
||||||
"integrity": "sha512-8Dnr1J5s/s4XYYuEF3b784NnCxLjXOlQpmGyXRxTAzW7JaOP08tIUJWVNvSMekfXc2vXa33HUbqjxyyWZEQ6LQ==",
|
"integrity": "sha512-8UMlQIjzqf6r2hSJ/VeJ00RqaOrOB1J5rTk02Vcw2pYeHkOqp+B0ff0HFu4abT9x1q4oXrBAgMsxRtgh/kR14Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-placeholder": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-placeholder/-/extension-placeholder-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-7hBoFLeddCv1WzkqB0x3coZ1Hp9WZ9wLoRXIUtUhRKMpzFq2IlTtW1iw88g9pTJnL98bCCElN4DZ4mYtaQvmgA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/extensions": "3.27.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-strike": {
|
"node_modules/@tiptap/extension-strike": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-strike/-/extension-strike-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-strike/-/extension-strike-3.28.0.tgz",
|
||||||
"integrity": "sha512-8OXwcPKuV3ToBBgyvDxH1jQdObK5FIKCGiyIim6qNWiOpi9BhM3XYD+aO1khjv8qIjtoI/DYbizF4ewj09fX2g==",
|
"integrity": "sha512-VVj2ZZU9QYqiHLcjqMqRYvuHSsokj/AgUl+6TzLrKjlWwyZ18D4H2vkyI0g70iVTKnUpZ3sEy8WA/rqjgBjqBg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-subscript": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-subscript/-/extension-subscript-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-gs0AexP7GsE9vgdkwhqYgrKu7kD96PjmgCorsGLdA60m71eTavm6j8NN1m1nvcDPO1sTpP8ACs2mhipj+WS5Iw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "3.27.4",
|
|
||||||
"@tiptap/pm": "3.27.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-superscript": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-superscript/-/extension-superscript-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-krlpnLeNBF6hVbQDrkuoXiy52v5YctoGPkLnxmftYNtloVrikRpgpetTRsFh0P6hLwv6g/g8SzoM23iMBB3S4A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "3.27.4",
|
|
||||||
"@tiptap/pm": "3.27.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-table": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-table/-/extension-table-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-ejQjqt8GjUn4YswG/SsiLr/W3LZApZGUEDW0N7NoOduE0dBZ/pVJHPuqWu33kK+phJjSNCIN+bSAkoWE01rZSw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "3.27.4",
|
|
||||||
"@tiptap/pm": "3.27.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-table-cell": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-table-cell/-/extension-table-cell-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-1B7J4ZiaXaGxT2IB3hrwtz0433bFVSWsDB+B125vt2DZQr9bgdX40GLesSPlymqOdMxKJwksCPMan2ON2LkVAg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/extension-table": "3.27.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-table-header": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-table-header/-/extension-table-header-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-V/O690Z6VcHMAWDfgVFiOCwx3eE2/HM2gH+Fp0eQe3WnajQj2DPViW4VWR/rNVl3DspHoL/EU+eEsnhcvO34Vw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/extension-table": "3.27.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-table-of-contents": {
|
"node_modules/@tiptap/extension-table-of-contents": {
|
||||||
@@ -1768,82 +1664,56 @@
|
|||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-table-row": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-table-row/-/extension-table-row-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-2RTJtcy90Tc2+HpW1JyKMTwg/zz4u6hHof4CTcJS/eXLk3g1L60DDzYvz4GzBxQvsOT3QVcbaU9QOk9A3Sx7Fg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/extension-table": "3.27.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-text": {
|
"node_modules/@tiptap/extension-text": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text/-/extension-text-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text/-/extension-text-3.28.0.tgz",
|
||||||
"integrity": "sha512-lKQH/hP4FBXsziHypd6Ywj8JFvMLM5GVkK1xsH6yApNuXbHq95rd42ZOYWpYILIBib7tlaz93z61d74UrJiuiw==",
|
"integrity": "sha512-Jqp1LgfY1mnp4TQHoy+vnHyfn6qnnAM6nHgGwbY5zA8b/Xf1Etll6pziTx9p1J1qcfAgSrnjOyAmA++H7VQqww==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-text-align": {
|
"node_modules/@tiptap/extension-text-align": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text-align/-/extension-text-align-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text-align/-/extension-text-align-3.28.0.tgz",
|
||||||
"integrity": "sha512-ArfL7GLOXCSmrmiBiaWwAf7RPHXDdxLGPru29qKDLQDthjXcNOdlwlPBbolRu7mXwlSmaYpqWGrG75/AEat3mw==",
|
"integrity": "sha512-P+KQMTtCpLFq9dRvy/fbyu2BGALulT/1HtsLbpFG4tKATaKa+cFnJ7rLZF7b3AdAMDNtXdnivxAtixAMBt2QWw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-text-style": {
|
"node_modules/@tiptap/extension-text-style": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text-style/-/extension-text-style-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-text-style/-/extension-text-style-3.28.0.tgz",
|
||||||
"integrity": "sha512-Wmj64TQXY85gc7lUNbubW32sDCnVOJlpGraMeARRC9Z0EKBX1JpAxddo/64F17bn3kzLxXVszBFyLRcNgTdU2g==",
|
"integrity": "sha512-UreMyqZSv770+CXJK/l1qeHUdaNMQAVkxSOPWpiY9FiT8B4OQyjA7WBOlWrzbvVTzfJvXWk2Y3SWgVRx+/Cd9A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tiptap/extension-typography": {
|
|
||||||
"version": "3.27.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-typography/-/extension-typography-3.27.4.tgz",
|
|
||||||
"integrity": "sha512-Sn4HZOKpcFR0m7msSGWNEunRobiXk8bs78e8+qJHa3yoxPfXsFoZySEgbD6TO7JjzQtVG927FQLCXQTzK7Q0Tg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@tiptap/core": "3.27.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-underline": {
|
"node_modules/@tiptap/extension-underline": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extension-underline/-/extension-underline-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extension-underline/-/extension-underline-3.28.0.tgz",
|
||||||
"integrity": "sha512-nRJGvRyEXDtINlHTW+C2oWcL3vmX1URVxAPpkD3Zwn5Rb/vEeOU/pk/w97I0iid816MR4iVbvl1XhbUVegK9gQ==",
|
"integrity": "sha512-rwxCS6vTh2DJkNIYQX7JSrrRSmm76e2y48aZeuZO5ShkvLWMuJ3/zqDHRxAQVDiJhuE2Cp8NrTmPYlUc9YrT0A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4"
|
"@tiptap/core": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-unique-id": {
|
"node_modules/@tiptap/extension-unique-id": {
|
||||||
@@ -1877,23 +1747,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extensions": {
|
"node_modules/@tiptap/extensions": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/extensions/-/extensions-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/extensions/-/extensions-3.28.0.tgz",
|
||||||
"integrity": "sha512-d8opkg2iGtVwJmNGIqv0blfRxnvWOJp1brz+Z8CsP4ojSS2ZtaE46d6JSQ5OeJ7nMpjhT+9wh4UQcA7OSEO59w==",
|
"integrity": "sha512-DJT1khCK+O/pT1gQlAnoKAx6zwDkgv7GtnhSfkqm1/4KHC3x+SSJnBIgBl9oGHymA+DxWbW1EULU4V3d/kT2uQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4"
|
"@tiptap/pm": "3.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/pm": {
|
"node_modules/@tiptap/pm": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/pm/-/pm-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/pm/-/pm-3.28.0.tgz",
|
||||||
"integrity": "sha512-UB8lcyomfWk7YGI2PZKNqcYXfyRA+PFj+QntlsUXyrsiA5JJIaE8SHKYjxKlGG/xtW3EtPm1b0p38T9Mk4xiFw==",
|
"integrity": "sha512-ALcpwZMUdat9gjJKlpscpoqXStoLhU246LPEVBDvJdIsoUKvUu3MrzfXik2Y8mtSGfhjtm9O2TRkWxQiFVMwsQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-changeset": "^2.4.1",
|
"prosemirror-changeset": "^2.4.1",
|
||||||
@@ -1916,35 +1786,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/starter-kit": {
|
"node_modules/@tiptap/starter-kit": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/starter-kit/-/starter-kit-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/starter-kit/-/starter-kit-3.28.0.tgz",
|
||||||
"integrity": "sha512-/sb6rFxNt5BO4hWpUwvHh+Yh1kNyCQuuz3oDpGef5HUUjSdu9p9rfNiHWIUKBadK8VXuw5es7N+UlZ4hma+gvA==",
|
"integrity": "sha512-pqvFpValV+ONtlu3l4s73ROm/tEjoGMmt6uHmSJaLbqTcMHkdWuR3flJ/5brr4IDHe1foxmNicMbRxlje7yBYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tiptap/core": "^3.27.4",
|
"@tiptap/core": "^3.28.0",
|
||||||
"@tiptap/extension-blockquote": "^3.27.4",
|
"@tiptap/extension-blockquote": "^3.28.0",
|
||||||
"@tiptap/extension-bold": "^3.27.4",
|
"@tiptap/extension-bold": "^3.28.0",
|
||||||
"@tiptap/extension-bullet-list": "^3.27.4",
|
"@tiptap/extension-bullet-list": "^3.28.0",
|
||||||
"@tiptap/extension-code": "^3.27.4",
|
"@tiptap/extension-code": "^3.28.0",
|
||||||
"@tiptap/extension-code-block": "^3.27.4",
|
"@tiptap/extension-code-block": "^3.28.0",
|
||||||
"@tiptap/extension-document": "^3.27.4",
|
"@tiptap/extension-document": "^3.28.0",
|
||||||
"@tiptap/extension-dropcursor": "^3.27.4",
|
"@tiptap/extension-dropcursor": "^3.28.0",
|
||||||
"@tiptap/extension-gapcursor": "^3.27.4",
|
"@tiptap/extension-gapcursor": "^3.28.0",
|
||||||
"@tiptap/extension-hard-break": "^3.27.4",
|
"@tiptap/extension-hard-break": "^3.28.0",
|
||||||
"@tiptap/extension-heading": "^3.27.4",
|
"@tiptap/extension-heading": "^3.28.0",
|
||||||
"@tiptap/extension-horizontal-rule": "^3.27.4",
|
"@tiptap/extension-horizontal-rule": "^3.28.0",
|
||||||
"@tiptap/extension-italic": "^3.27.4",
|
"@tiptap/extension-italic": "^3.28.0",
|
||||||
"@tiptap/extension-link": "^3.27.4",
|
"@tiptap/extension-link": "^3.28.0",
|
||||||
"@tiptap/extension-list": "^3.27.4",
|
"@tiptap/extension-list": "^3.28.0",
|
||||||
"@tiptap/extension-list-item": "^3.27.4",
|
"@tiptap/extension-list-item": "^3.28.0",
|
||||||
"@tiptap/extension-list-keymap": "^3.27.4",
|
"@tiptap/extension-list-keymap": "^3.28.0",
|
||||||
"@tiptap/extension-ordered-list": "^3.27.4",
|
"@tiptap/extension-ordered-list": "^3.28.0",
|
||||||
"@tiptap/extension-paragraph": "^3.27.4",
|
"@tiptap/extension-paragraph": "^3.28.0",
|
||||||
"@tiptap/extension-strike": "^3.27.4",
|
"@tiptap/extension-strike": "^3.28.0",
|
||||||
"@tiptap/extension-text": "^3.27.4",
|
"@tiptap/extension-text": "^3.28.0",
|
||||||
"@tiptap/extension-underline": "^3.27.4",
|
"@tiptap/extension-underline": "^3.28.0",
|
||||||
"@tiptap/extensions": "^3.27.4",
|
"@tiptap/extensions": "^3.28.0",
|
||||||
"@tiptap/pm": "^3.27.4"
|
"@tiptap/pm": "^3.28.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -1966,22 +1836,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/vue-3": {
|
"node_modules/@tiptap/vue-3": {
|
||||||
"version": "3.27.4",
|
"version": "3.28.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@tiptap/vue-3/-/vue-3-3.27.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@tiptap/vue-3/-/vue-3-3.28.0.tgz",
|
||||||
"integrity": "sha512-fTz21viWZlpZz7PPdHJde+kzYmgRXBivVLoC2KGEeltqsREa8JR0v/eUDbzeMjj6AIajlNDtmn96I3h14gZJLg==",
|
"integrity": "sha512-twb3T6hofM0ajncCSgrLYIjfn86WfR2NVXteurdNOcnkJCIMTij5OhO/xn0z7y8+lp3TMkF6H5kajUpivBpnzw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tiptap/extension-bubble-menu": "^3.27.4",
|
"@tiptap/extension-bubble-menu": "^3.28.0",
|
||||||
"@tiptap/extension-floating-menu": "^3.27.4"
|
"@tiptap/extension-floating-menu": "^3.28.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@floating-ui/dom": "^1.0.0",
|
"@floating-ui/dom": "^1.0.0",
|
||||||
"@tiptap/core": "3.27.4",
|
"@tiptap/core": "3.28.0",
|
||||||
"@tiptap/pm": "3.27.4",
|
"@tiptap/pm": "3.28.0",
|
||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -7229,7 +7099,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
|||||||
@@ -11,6 +11,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
|
"@tiptap/extension-color": "^3.28.0",
|
||||||
|
"@tiptap/extension-image": "^3.28.0",
|
||||||
|
"@tiptap/extension-link": "^3.28.0",
|
||||||
|
"@tiptap/extension-text-align": "^3.28.0",
|
||||||
|
"@tiptap/extension-text-style": "^3.28.0",
|
||||||
|
"@tiptap/extension-underline": "^3.28.0",
|
||||||
|
"@tiptap/starter-kit": "^3.28.0",
|
||||||
|
"@tiptap/vue-3": "^3.28.0",
|
||||||
"@umoteam/editor": "^10.2.1",
|
"@umoteam/editor": "^10.2.1",
|
||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
"chart": "^0.1.2",
|
"chart": "^0.1.2",
|
||||||
|
|||||||
@@ -379,9 +379,9 @@ onMounted(async () => {
|
|||||||
.welcome-section {
|
.welcome-section {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
background: linear-gradient(135deg, #3973ff 0%, #4f84ff 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 12px rgba(6, 45, 163, 0.2);
|
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.25);
|
||||||
|
|
||||||
.welcome-content {
|
.welcome-content {
|
||||||
.welcome-title {
|
.welcome-title {
|
||||||
@@ -408,63 +408,65 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
background: var(--el-bg-color);
|
background: var(--el-bg-color);
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
padding: 24px;
|
padding: 28px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 20px;
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||||
transition: all 0.3s;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-4px);
|
transform: translateY(-6px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
|
||||||
border-color: var(--el-color-primary-light-7);
|
border-color: var(--el-color-primary-light-7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon-wrapper {
|
.stat-icon-wrapper {
|
||||||
width: 56px;
|
width: 60px;
|
||||||
height: 56px;
|
height: 60px;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.el-icon {
|
.el-icon {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.income .stat-icon-wrapper {
|
&.income .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #3973ff 0%, #4f84ff 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.users .stat-icon-wrapper {
|
&.users .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.orders .stat-icon-wrapper {
|
&.orders .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active .stat-icon-wrapper {
|
&.active .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.knowledge .stat-icon-wrapper {
|
&.knowledge .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #3973ff 0%, #4f84ff 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.employees .stat-icon-wrapper {
|
&.employees .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tenants .stat-icon-wrapper {
|
&.tenants .stat-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-content {
|
.stat-content {
|
||||||
@@ -692,15 +694,16 @@ onMounted(async () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
&.operation {
|
&.operation {
|
||||||
background-color: rgba(79, 132, 255, 0.2);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
color: #4f84ff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.access {
|
&.access {
|
||||||
background-color: rgba(85, 190, 130, 0.2);
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
color: #55be82;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 4000,
|
port: 4000,
|
||||||
// 开发时前端在 5000,接口走相对路径 /platform/*、/backend/*,转发到本地 Go(当前 httpport=8081)
|
// 开发时前端在 4000,接口走相对路径 /platform/*、/backend/*,转发到本地 Go(当前 httpport=9000)
|
||||||
proxy: {
|
proxy: {
|
||||||
"/platform": {
|
"/platform": {
|
||||||
target: "http://127.0.0.1:8081",
|
target: "http://127.0.0.1:9000",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
"/backend": {
|
"/backend": {
|
||||||
target: "http://127.0.0.1:8081",
|
target: "http://127.0.0.1:9000",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
+11
-6
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name" : "uniapp",
|
"name" : "uniapp",
|
||||||
"appid" : "",
|
"appid" : "__UNI__D80959E",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.0",
|
"versionName" : "1.0.0",
|
||||||
"versionCode" : "100",
|
"versionCode" : "100",
|
||||||
"transformPx" : false,
|
"transformPx" : false,
|
||||||
"uniStatistics": {
|
"uniStatistics" : {
|
||||||
"enable": false,
|
"enable" : false,
|
||||||
"debug": false
|
"debug" : false
|
||||||
},
|
},
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus" : {
|
||||||
@@ -57,7 +57,9 @@
|
|||||||
"mp-weixin" : {
|
"mp-weixin" : {
|
||||||
"appid" : "",
|
"appid" : "",
|
||||||
"setting" : {
|
"setting" : {
|
||||||
"urlCheck" : false
|
"urlCheck" : false,
|
||||||
|
"minified" : true,
|
||||||
|
"postcss" : true
|
||||||
},
|
},
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"mergeVirtualHostAttributes" : true
|
"mergeVirtualHostAttributes" : true
|
||||||
@@ -74,5 +76,8 @@
|
|||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"mergeVirtualHostAttributes" : true
|
"mergeVirtualHostAttributes" : true
|
||||||
},
|
},
|
||||||
"vueVersion" : "3"
|
"vueVersion" : "3",
|
||||||
|
"h5" : {
|
||||||
|
"title" : "云泽系统"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<view class="header-row">
|
|
||||||
<view class="user-info">
|
|
||||||
<text class="greeting">{{ greeting }}</text>
|
|
||||||
<text class="username">{{ user?.nickname || '云泽用户' }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="avatar">
|
|
||||||
<text class="avatar-text">{{ avatarText }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="search-bar">
|
<view class="search-bar">
|
||||||
<FaIcon name="magnifying-glass" color="#909399" :size="16" />
|
<FaIcon name="magnifying-glass" color="#ffffff" :size="16" />
|
||||||
<text class="search-text">搜索功能、数据...</text>
|
<text class="search-text">搜索功能、数据...</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -84,18 +75,6 @@ import AppTabbar from '@/components/AppTabbar.vue'
|
|||||||
|
|
||||||
const user = ref(null)
|
const user = ref(null)
|
||||||
|
|
||||||
const greeting = computed(() => {
|
|
||||||
const hour = new Date().getHours()
|
|
||||||
if (hour < 12) return '早上好'
|
|
||||||
if (hour < 18) return '下午好'
|
|
||||||
return '晚上好'
|
|
||||||
})
|
|
||||||
|
|
||||||
const avatarText = computed(() => {
|
|
||||||
const name = user.value?.nickname || '云'
|
|
||||||
return name.charAt(0).toUpperCase()
|
|
||||||
})
|
|
||||||
|
|
||||||
const noteCount = ref(0)
|
const noteCount = ref(0)
|
||||||
const scheduleCount = ref(0)
|
const scheduleCount = ref(0)
|
||||||
const schedulePending = ref(0)
|
const schedulePending = ref(0)
|
||||||
@@ -199,60 +178,25 @@ function onActivityTap(item) {
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 24rpx;
|
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 20rpx;
|
||||||
background: $color-card;
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
box-shadow: $shadow-header;
|
box-shadow: 0 4rpx 16rpx rgba(79, 172, 254, 0.15);
|
||||||
}
|
position: relative;
|
||||||
|
|
||||||
.header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greeting {
|
|
||||||
display: block;
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: $color-text-muted;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
display: block;
|
|
||||||
font-size: 40rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: $color-text;
|
|
||||||
margin-top: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 88rpx;
|
|
||||||
height: 88rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: $color-primary-bg;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-text {
|
|
||||||
font-size: 34rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: $color-primary;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
background: $color-bg-page;
|
background: rgba(255, 255, 255, 0.2);
|
||||||
border-radius: $radius-md;
|
border-radius: $radius-full;
|
||||||
padding: 20rpx 24rpx;
|
padding: 16rpx 24rpx;
|
||||||
|
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-text {
|
.search-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: $color-text-muted;
|
color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-scroll {
|
.main-scroll {
|
||||||
@@ -344,8 +288,14 @@ function onActivityTap(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.quick-icon {
|
.quick-icon {
|
||||||
@include icon-box(88rpx);
|
width: 88rpx;
|
||||||
border-radius: $radius-lg;
|
height: 88rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.15) 0%, rgba(0, 242, 254, 0.15) 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-name {
|
.quick-name {
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ function onItemTap(item) {
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 6rpx;
|
width: 6rpx;
|
||||||
height: 36rpx;
|
height: 36rpx;
|
||||||
background: $color-primary;
|
background: linear-gradient(180deg, #4facfe 0%, #00f2fe 100%);
|
||||||
border-radius: 0 4rpx 4rpx 0;
|
border-radius: 0 4rpx 4rpx 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,10 +270,10 @@ function onItemTap(item) {
|
|||||||
border-radius: $radius-md;
|
border-radius: $radius-md;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: $color-primary-bg;
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.08) 0%, rgba(0, 242, 254, 0.08) 100%);
|
||||||
|
|
||||||
.item-name {
|
.item-name {
|
||||||
color: $color-primary;
|
color: #4facfe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="main-scroll">
|
<view class="main-scroll" style="margin-top:10rpx">
|
||||||
<view class="menu-group" v-for="(group, gIndex) in menuGroups" :key="gIndex">
|
<view class="menu-group" v-for="(group, gIndex) in menuGroups" :key="gIndex">
|
||||||
<view
|
<view
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
@@ -167,26 +167,55 @@ function handleLogout() {
|
|||||||
|
|
||||||
.profile-header {
|
.profile-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: calc(var(--status-bar-height, 44px) + 20rpx) $page-padding-x 36rpx;
|
padding: calc(var(--status-bar-height, 44px) + 24rpx) $page-padding-x 40rpx;
|
||||||
background: $color-primary;
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
right: -50%;
|
||||||
|
width: 400rpx;
|
||||||
|
height: 400rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30%;
|
||||||
|
left: -20%;
|
||||||
|
width: 350rpx;
|
||||||
|
height: 350rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info {
|
.profile-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 24rpx;
|
gap: 24rpx;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-avatar {
|
.profile-avatar {
|
||||||
width: 112rpx;
|
width: 120rpx;
|
||||||
height: 112rpx;
|
height: 120rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.15) 100%);
|
||||||
border: 4rpx solid rgba(255, 255, 255, 0.35);
|
border: 3rpx solid rgba(255, 255, 255, 0.4);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-text {
|
.avatar-text {
|
||||||
@@ -231,9 +260,11 @@ function handleLogout() {
|
|||||||
.profile-stats {
|
.profile-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
margin-top: 36rpx;
|
margin-top: 40rpx;
|
||||||
padding-top: 28rpx;
|
padding-top: 32rpx;
|
||||||
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-stat {
|
.profile-stat {
|
||||||
@@ -263,13 +294,21 @@ function handleLogout() {
|
|||||||
@include card;
|
@include card;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-group:active {
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 28rpx 24rpx;
|
padding: 32rpx 28rpx;
|
||||||
|
transition: background 0.2s ease, transform 0.2s ease;
|
||||||
|
|
||||||
& + & {
|
& + & {
|
||||||
border-top: 1rpx solid $color-divider;
|
border-top: 1rpx solid $color-divider;
|
||||||
@@ -277,6 +316,7 @@ function handleLogout() {
|
|||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: $color-bg-page;
|
background: $color-bg-page;
|
||||||
|
transform: translateX(4rpx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,11 +329,19 @@ function handleLogout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
@include icon-box(64rpx);
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, rgba(0, 242, 254, 0.1) 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
font-size: 28rpx;
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
color: $color-text;
|
color: $color-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,31 +354,39 @@ function handleLogout() {
|
|||||||
|
|
||||||
.menu-badge {
|
.menu-badge {
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
|
font-weight: 600;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: $color-primary;
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
padding: 4rpx 12rpx;
|
padding: 6rpx 14rpx;
|
||||||
border-radius: $radius-full;
|
border-radius: 12rpx;
|
||||||
min-width: 32rpx;
|
min-width: 36rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(79, 172, 254, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-btn {
|
.logout-btn {
|
||||||
@include card;
|
@include card;
|
||||||
height: 96rpx;
|
height: 100rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 8rpx;
|
margin-bottom: 12rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
background: linear-gradient(135deg, rgba(245, 108, 108, 0.1) 0%, rgba(245, 108, 108, 0.05) 100%);
|
||||||
|
border: 1rpx solid rgba(245, 108, 108, 0.2);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-btn-hover {
|
.logout-btn-hover {
|
||||||
background: $color-bg-page !important;
|
background: linear-gradient(135deg, rgba(245, 108, 108, 0.2) 0%, rgba(245, 108, 108, 0.1) 100%) !important;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(245, 108, 108, 0.15) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-text {
|
.logout-text {
|
||||||
font-size: 30rpx;
|
font-size: 32rpx;
|
||||||
color: $color-text-secondary;
|
font-weight: 600;
|
||||||
|
color: #f56c6c;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,29 +15,14 @@
|
|||||||
|
|
||||||
<view class="field content-field">
|
<view class="field content-field">
|
||||||
<text class="field-label">内容</text>
|
<text class="field-label">内容</text>
|
||||||
<view class="editor-wrap">
|
<editor
|
||||||
<editor
|
id="noteEditor"
|
||||||
id="noteEditor"
|
class="note-editor"
|
||||||
class="note-editor"
|
:placeholder="'记录你的想法...'"
|
||||||
:placeholder="'记录你的想法...'"
|
:value="form.content"
|
||||||
:value="form.content"
|
@ready="onEditorReady"
|
||||||
@ready="onEditorReady"
|
@input="onEditorInput"
|
||||||
@input="onEditorInput"
|
/>
|
||||||
@focus="editorFocused = true"
|
|
||||||
@blur="editorFocused = false"
|
|
||||||
/>
|
|
||||||
<view class="editor-toolbar">
|
|
||||||
<view
|
|
||||||
v-for="btn in toolbarBtns"
|
|
||||||
:key="btn.name"
|
|
||||||
class="toolbar-btn"
|
|
||||||
:class="{ active: btn.active }"
|
|
||||||
@tap="execCommand(btn)"
|
|
||||||
>
|
|
||||||
<text>{{ btn.label }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="switch-row" @tap="form.pinned = !form.pinned">
|
<view class="switch-row" @tap="form.pinned = !form.pinned">
|
||||||
@@ -71,7 +56,6 @@ import { formatDate } from '@/utils/date.js'
|
|||||||
const noteId = ref('')
|
const noteId = ref('')
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const editorCtx = ref(null)
|
const editorCtx = ref(null)
|
||||||
const editorFocused = ref(false)
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
@@ -81,28 +65,6 @@ const meta = reactive({
|
|||||||
updatedAt: 0
|
updatedAt: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const toolbarBtns = reactive([
|
|
||||||
{ name: 'bold', label: 'B', active: false, value: 'bold' },
|
|
||||||
{ name: 'italic', label: 'I', active: false, value: 'italic' },
|
|
||||||
{ name: 'underline', label: 'U', active: false, value: 'underline' },
|
|
||||||
{ name: 'strike', label: 'S', active: false, value: 'strikeThrough' },
|
|
||||||
{ name: 'header', label: 'H', active: false, value: 'header' },
|
|
||||||
{ name: 'list', label: '•', active: false, value: 'insertUnorderedList' },
|
|
||||||
{ name: 'indent', label: '→', active: false, value: 'indent' },
|
|
||||||
{ name: 'outdent', label: '←', active: false, value: 'outdent' },
|
|
||||||
{ name: 'divider', label: '—', active: false, value: 'insertHorizontalRule' },
|
|
||||||
])
|
|
||||||
|
|
||||||
onLoad(async (query) => {
|
|
||||||
if (query?.id) {
|
|
||||||
noteId.value = query.id
|
|
||||||
uni.setNavigationBarTitle({ title: '编辑笔记' })
|
|
||||||
await loadNote(query.id)
|
|
||||||
} else {
|
|
||||||
uni.setNavigationBarTitle({ title: '新建笔记' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function onEditorReady() {
|
function onEditorReady() {
|
||||||
uni.createSelectorQuery().select('#noteEditor').context((res) => {
|
uni.createSelectorQuery().select('#noteEditor').context((res) => {
|
||||||
if (res && res.context) {
|
if (res && res.context) {
|
||||||
@@ -118,14 +80,15 @@ function onEditorInput(e) {
|
|||||||
form.content = e.detail.html || ''
|
form.content = e.detail.html || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function execCommand(btn) {
|
onLoad(async (query) => {
|
||||||
if (!editorCtx.value) return
|
if (query?.id) {
|
||||||
if (btn.name === 'header') {
|
noteId.value = query.id
|
||||||
editorCtx.value.format('header', 'H2')
|
uni.setNavigationBarTitle({ title: '编辑笔记' })
|
||||||
|
await loadNote(query.id)
|
||||||
} else {
|
} else {
|
||||||
editorCtx.value.format(btn.value)
|
uni.setNavigationBarTitle({ title: '新建笔记' })
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
async function loadNote(id) {
|
async function loadNote(id) {
|
||||||
try {
|
try {
|
||||||
@@ -246,17 +209,9 @@ function onDelete() {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 0;
|
padding: 24rpx 28rpx 0 28rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
border-top: 1rpx solid $color-divider;
|
||||||
|
|
||||||
.editor-wrap {
|
|
||||||
background: $color-bg-page;
|
|
||||||
border-radius: $radius-md;
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-editor {
|
.note-editor {
|
||||||
@@ -268,32 +223,8 @@ function onDelete() {
|
|||||||
color: $color-text;
|
color: $color-text;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
|
||||||
|
|
||||||
.editor-toolbar {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8rpx;
|
|
||||||
padding: 16rpx 20rpx;
|
|
||||||
border-top: 1rpx solid $color-divider;
|
|
||||||
background: $color-card;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-btn {
|
|
||||||
padding: 10rpx 20rpx;
|
|
||||||
border-radius: $radius-sm;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: $color-text-secondary;
|
|
||||||
background: $color-bg-page;
|
background: $color-bg-page;
|
||||||
|
border-radius: $radius-md;
|
||||||
&.active {
|
|
||||||
color: $color-primary;
|
|
||||||
background: $color-primary-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder {
|
.placeholder {
|
||||||
|
|||||||
@@ -65,6 +65,38 @@
|
|||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">重复提醒间隔(分钟)</text>
|
||||||
|
<view class="number-input">
|
||||||
|
<button class="number-btn" @tap="form.repeatIntervalMinutes = Math.max(0, form.repeatIntervalMinutes - 1)" :disabled="isCompleted">−</button>
|
||||||
|
<input
|
||||||
|
v-model.number="form.repeatIntervalMinutes"
|
||||||
|
type="number"
|
||||||
|
class="number-field"
|
||||||
|
:disabled="isCompleted"
|
||||||
|
@blur="form.repeatIntervalMinutes = Math.max(0, Math.min(1440, form.repeatIntervalMinutes))"
|
||||||
|
/>
|
||||||
|
<button class="number-btn" @tap="form.repeatIntervalMinutes = Math.min(1440, form.repeatIntervalMinutes + 1)" :disabled="isCompleted">+</button>
|
||||||
|
</view>
|
||||||
|
<text class="form-tip">0表示不重复,仅发送一次</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">最多提醒次数</text>
|
||||||
|
<view class="number-input">
|
||||||
|
<button class="number-btn" @tap="form.maxSendCount = Math.max(1, form.maxSendCount - 1)" :disabled="isCompleted">−</button>
|
||||||
|
<input
|
||||||
|
v-model.number="form.maxSendCount"
|
||||||
|
type="number"
|
||||||
|
class="number-field"
|
||||||
|
:disabled="isCompleted"
|
||||||
|
@blur="form.maxSendCount = Math.max(1, Math.min(100, form.maxSendCount))"
|
||||||
|
/>
|
||||||
|
<button class="number-btn" @tap="form.maxSendCount = Math.min(100, form.maxSendCount + 1)" :disabled="isCompleted">+</button>
|
||||||
|
</view>
|
||||||
|
<text class="form-tip">最多发送的提醒次数</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!isCompleted" class="footer">
|
<view v-if="!isCompleted" class="footer">
|
||||||
@@ -99,7 +131,9 @@ const form = reactive({
|
|||||||
date: '',
|
date: '',
|
||||||
time: '',
|
time: '',
|
||||||
remindChannels: ['app'],
|
remindChannels: ['app'],
|
||||||
remindMinutes: 15
|
remindMinutes: 0,
|
||||||
|
repeatIntervalMinutes: 0,
|
||||||
|
maxSendCount: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
const remindIndex = computed(() => {
|
const remindIndex = computed(() => {
|
||||||
@@ -134,7 +168,9 @@ async function loadSchedule(id) {
|
|||||||
form.date = formatDate(d)
|
form.date = formatDate(d)
|
||||||
form.time = `${pad(d.getHours())}:${pad(d.getMinutes())}`
|
form.time = `${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
form.remindChannels = [...(item.remindChannels || [])]
|
form.remindChannels = [...(item.remindChannels || [])]
|
||||||
form.remindMinutes = item.remindMinutes ?? 15
|
form.remindMinutes = item.remindMinutes ?? 0
|
||||||
|
form.repeatIntervalMinutes = item.repeatIntervalMinutes ?? 0
|
||||||
|
form.maxSendCount = item.maxSendCount ?? 1
|
||||||
isCompleted.value = !!item.completed
|
isCompleted.value = !!item.completed
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||||
@@ -200,7 +236,9 @@ async function saveData(datetime) {
|
|||||||
content: form.content,
|
content: form.content,
|
||||||
datetime,
|
datetime,
|
||||||
remindChannels: [...form.remindChannels],
|
remindChannels: [...form.remindChannels],
|
||||||
remindMinutes: form.remindMinutes
|
remindMinutes: form.remindMinutes,
|
||||||
|
repeatIntervalMinutes: form.repeatIntervalMinutes,
|
||||||
|
maxSendCount: form.maxSendCount
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (scheduleId.value) {
|
if (scheduleId.value) {
|
||||||
@@ -334,6 +372,60 @@ async function saveData(datetime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.number-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-btn {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
border-radius: $radius-md;
|
||||||
|
background: $color-bg-page;
|
||||||
|
border: 1rpx solid $color-divider;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $color-text;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:active:not(:disabled) {
|
||||||
|
background: $color-primary-bg;
|
||||||
|
color: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-field {
|
||||||
|
flex: 1;
|
||||||
|
height: 60rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
background: $color-bg-page;
|
||||||
|
border: 1rpx solid $color-divider;
|
||||||
|
border-radius: $radius-md;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $color-text;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
display: block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: $color-text-muted;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 标准商务主题(单色蓝 + 灰阶,无渐变)
|
// 标准商务主题(蓝色系 + 灰阶)
|
||||||
$color-primary: #3c9cff;
|
$color-primary: #4facfe;
|
||||||
$color-primary-dark: #2b8ae8;
|
$color-primary-dark: #00f2fe;
|
||||||
$color-primary-bg: #ecf5ff;
|
$color-primary-bg: #e0f7ff;
|
||||||
$color-bg-page: #f5f7fa;
|
$color-bg-page: #f5f7fa;
|
||||||
$color-card: #ffffff;
|
$color-card: #ffffff;
|
||||||
$color-text: #303133;
|
$color-text: #303133;
|
||||||
|
|||||||
+15
-28
@@ -1,29 +1,16 @@
|
|||||||
import { defineConfig, loadEnv } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import uni from '@dcloudio/vite-plugin-uni'
|
import uni from '@dcloudio/vite-plugin-uni'
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig({
|
||||||
const env = loadEnv(mode, process.cwd(), '')
|
plugins: [
|
||||||
const apiTarget = (env.VITE_API_BASE_URL || 'http://localhost:9000').replace(/\/$/, '')
|
uni()
|
||||||
|
],
|
||||||
return {
|
css: {
|
||||||
plugins: [uni()],
|
preprocessorOptions: {
|
||||||
server: {
|
scss: {
|
||||||
proxy: {
|
// 关闭废弃 API 警告
|
||||||
// 勿用 /api:会与 uniapp/api/ 源码目录冲突,导致 request.js 等模块 404
|
silenceDeprecations: ['legacy-js-api', 'color-functions', 'import'],
|
||||||
'/proxy-api': {
|
}
|
||||||
target: apiTarget,
|
}
|
||||||
changeOrigin: true,
|
}
|
||||||
rewrite: (path) => path.replace(/^\/proxy-api/, '')
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
preprocessorOptions: {
|
|
||||||
scss: {
|
|
||||||
// 关闭废弃 API 警告
|
|
||||||
silenceDeprecations: ['legacy-js-api', 'color-functions', 'import']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user