From 034645f50c89075a978ca1295b5433632f78d7d7 Mon Sep 17 00:00:00 2001
From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com>
Date: Fri, 23 Aug 2024 17:22:05 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=90=8C=E6=AD=A5?=
=?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=BA=94=E7=94=A8=E6=8E=A5=E5=8F=A3=20(#6217?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
agent/app/api/v2/app.go | 6 +-
agent/app/service/app.go | 433 ++++++++++----------
agent/i18n/lang/en.yaml | 1 +
agent/i18n/lang/zh-Hant.yaml | 1 +
agent/i18n/lang/zh.yaml | 1 +
agent/router/ro_app.go | 3 +-
frontend/src/api/modules/app.ts | 6 +-
frontend/src/lang/modules/en.ts | 1 +
frontend/src/lang/modules/tw.ts | 1 +
frontend/src/lang/modules/zh.ts | 3 +-
frontend/src/views/app-store/apps/index.vue | 32 +-
11 files changed, 266 insertions(+), 222 deletions(-)
diff --git a/agent/app/api/v2/app.go b/agent/app/api/v2/app.go
index 34f746690..bba3e87c3 100644
--- a/agent/app/api/v2/app.go
+++ b/agent/app/api/v2/app.go
@@ -72,7 +72,11 @@ func (b *BaseApi) SyncApp(c *gin.Context) {
// @Router /apps/sync/local [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
func (b *BaseApi) SyncLocalApp(c *gin.Context) {
- go appService.SyncAppListFromLocal()
+ var req dto.OperateWithTask
+ if err := helper.CheckBindAndValidate(&req, c); err != nil {
+ return
+ }
+ go appService.SyncAppListFromLocal(req.TaskID)
helper.SuccessWithOutData(c)
}
diff --git a/agent/app/service/app.go b/agent/app/service/app.go
index 144d38c1d..9249507fe 100644
--- a/agent/app/service/app.go
+++ b/agent/app/service/app.go
@@ -43,7 +43,7 @@ type IAppService interface {
SyncAppListFromRemote(taskID string) error
GetAppUpdate() (*response.AppUpdateRes, error)
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
- SyncAppListFromLocal()
+ SyncAppListFromLocal(taskID string)
GetIgnoredApp() ([]response.IgnoredApp, error)
}
@@ -489,231 +489,236 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
return
}
-func (a AppService) SyncAppListFromLocal() {
- fileOp := files.NewFileOp()
- localAppDir := constant.LocalAppResourceDir
- if !fileOp.Stat(localAppDir) {
- return
- }
+func (a AppService) SyncAppListFromLocal(TaskID string) {
var (
err error
dirEntries []os.DirEntry
localApps []model.App
)
- defer func() {
- if err != nil {
- global.LOG.Errorf("Sync local app failed %v", err)
- }
- }()
-
- global.LOG.Infof("Starting local application synchronization ...")
- dirEntries, err = os.ReadDir(localAppDir)
+ syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore, TaskID, 0)
if err != nil {
+ global.LOG.Errorf("Create sync task failed %v", err)
return
}
- for _, dirEntry := range dirEntries {
- if dirEntry.IsDir() {
- appDir := filepath.Join(localAppDir, dirEntry.Name())
- appDirEntries, err := os.ReadDir(appDir)
- if err != nil {
- global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
- continue
- }
- app, err := handleLocalApp(appDir)
- if err != nil {
- global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
- continue
- }
- var appDetails []model.AppDetail
- for _, appDirEntry := range appDirEntries {
- if appDirEntry.IsDir() {
- appDetail := model.AppDetail{
- Version: appDirEntry.Name(),
- Status: constant.AppNormal,
- }
- versionDir := filepath.Join(appDir, appDirEntry.Name())
- if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
- global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
- continue
- }
- appDetails = append(appDetails, appDetail)
- }
- }
- if len(appDetails) > 0 {
- app.Details = appDetails
- localApps = append(localApps, *app)
- } else {
- global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name}))
- }
+
+ syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore), func(t *task.Task) (err error) {
+ fileOp := files.NewFileOp()
+ localAppDir := constant.LocalAppResourceDir
+ if !fileOp.Stat(localAppDir) {
+ return nil
}
- }
-
- var (
- newApps []model.App
- deleteApps []model.App
- updateApps []model.App
- oldAppIds []uint
-
- deleteAppIds []uint
- deleteAppDetails []model.AppDetail
- newAppDetails []model.AppDetail
- updateDetails []model.AppDetail
-
- appTags []*model.AppTag
- )
-
- oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
- apps := make(map[string]model.App, len(oldApps))
- for _, old := range oldApps {
- old.Status = constant.AppTakeDown
- apps[old.Key] = old
- }
- for _, app := range localApps {
- if oldApp, ok := apps[app.Key]; ok {
- app.ID = oldApp.ID
- appDetails := make(map[string]model.AppDetail, len(oldApp.Details))
- for _, old := range oldApp.Details {
- old.Status = constant.AppTakeDown
- appDetails[old.Version] = old
- }
- for i, newDetail := range app.Details {
- version := newDetail.Version
- newDetail.Status = constant.AppNormal
- newDetail.AppId = app.ID
- oldDetail, exist := appDetails[version]
- if exist {
- newDetail.ID = oldDetail.ID
- delete(appDetails, version)
- }
- app.Details[i] = newDetail
- }
- for _, v := range appDetails {
- app.Details = append(app.Details, v)
- }
+ dirEntries, err = os.ReadDir(localAppDir)
+ if err != nil {
+ return
}
- app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
- apps[app.Key] = app
- }
-
- for _, app := range apps {
- if app.ID == 0 {
- newApps = append(newApps, app)
- } else {
- oldAppIds = append(oldAppIds, app.ID)
- if app.Status == constant.AppTakeDown {
- installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
- if len(installs) > 0 {
- updateApps = append(updateApps, app)
+ for _, dirEntry := range dirEntries {
+ if dirEntry.IsDir() {
+ appDir := filepath.Join(localAppDir, dirEntry.Name())
+ appDirEntries, err := os.ReadDir(appDir)
+ if err != nil {
+ t.Log(i18n.GetWithNameAndErr("ErrAppDirNull", dirEntry.Name(), err))
continue
}
- deleteAppIds = append(deleteAppIds, app.ID)
- deleteApps = append(deleteApps, app)
- deleteAppDetails = append(deleteAppDetails, app.Details...)
- } else {
- updateApps = append(updateApps, app)
- }
- }
-
- }
-
- tags, _ := tagRepo.All()
- tagMap := make(map[string]uint, len(tags))
- for _, tag := range tags {
- tagMap[tag.Key] = tag.ID
- }
-
- tx, ctx := getTxAndContext()
- defer tx.Rollback()
- if len(newApps) > 0 {
- if err = appRepo.BatchCreate(ctx, newApps); err != nil {
- return
- }
- }
- for _, update := range updateApps {
- if err = appRepo.Save(ctx, &update); err != nil {
- return
- }
- }
- if len(deleteApps) > 0 {
- if err = appRepo.BatchDelete(ctx, deleteApps); err != nil {
- return
- }
- if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {
- return
- }
- }
-
- if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
- return
- }
- for _, newApp := range newApps {
- if newApp.ID > 0 {
- for _, detail := range newApp.Details {
- detail.AppId = newApp.ID
- newAppDetails = append(newAppDetails, detail)
- }
- }
- }
- for _, update := range updateApps {
- for _, detail := range update.Details {
- if detail.ID == 0 {
- detail.AppId = update.ID
- newAppDetails = append(newAppDetails, detail)
- } else {
- if detail.Status == constant.AppNormal {
- updateDetails = append(updateDetails, detail)
+ app, err := handleLocalApp(appDir)
+ if err != nil {
+ t.Log(i18n.GetWithNameAndErr("LocalAppErr", dirEntry.Name(), err))
+ continue
+ }
+ var appDetails []model.AppDetail
+ for _, appDirEntry := range appDirEntries {
+ if appDirEntry.IsDir() {
+ appDetail := model.AppDetail{
+ Version: appDirEntry.Name(),
+ Status: constant.AppNormal,
+ }
+ versionDir := filepath.Join(appDir, appDirEntry.Name())
+ if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
+ t.Log(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
+ continue
+ }
+ appDetails = append(appDetails, appDetail)
+ }
+ }
+ if len(appDetails) > 0 {
+ app.Details = appDetails
+ localApps = append(localApps, *app)
} else {
- deleteAppDetails = append(deleteAppDetails, detail)
+ t.Log(i18n.GetWithName("LocalAppVersionNull", app.Name))
}
}
}
- }
- allApps := append(newApps, updateApps...)
- for _, app := range allApps {
- for _, t := range app.TagsKey {
- tagId, ok := tagMap[t]
- if ok {
- appTags = append(appTags, &model.AppTag{
- AppId: app.ID,
- TagId: tagId,
- })
+ var (
+ newApps []model.App
+ deleteApps []model.App
+ updateApps []model.App
+ oldAppIds []uint
+
+ deleteAppIds []uint
+ deleteAppDetails []model.AppDetail
+ newAppDetails []model.AppDetail
+ updateDetails []model.AppDetail
+
+ appTags []*model.AppTag
+ )
+
+ oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
+ apps := make(map[string]model.App, len(oldApps))
+ for _, old := range oldApps {
+ old.Status = constant.AppTakeDown
+ apps[old.Key] = old
+ }
+ for _, app := range localApps {
+ if oldApp, ok := apps[app.Key]; ok {
+ app.ID = oldApp.ID
+ appDetails := make(map[string]model.AppDetail, len(oldApp.Details))
+ for _, old := range oldApp.Details {
+ old.Status = constant.AppTakeDown
+ appDetails[old.Version] = old
+ }
+ for i, newDetail := range app.Details {
+ version := newDetail.Version
+ newDetail.Status = constant.AppNormal
+ newDetail.AppId = app.ID
+ oldDetail, exist := appDetails[version]
+ if exist {
+ newDetail.ID = oldDetail.ID
+ delete(appDetails, version)
+ }
+ app.Details[i] = newDetail
+ }
+ for _, v := range appDetails {
+ app.Details = append(app.Details, v)
+ }
+ }
+ app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
+ apps[app.Key] = app
+ }
+
+ for _, app := range apps {
+ if app.ID == 0 {
+ newApps = append(newApps, app)
+ } else {
+ oldAppIds = append(oldAppIds, app.ID)
+ if app.Status == constant.AppTakeDown {
+ installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
+ if len(installs) > 0 {
+ updateApps = append(updateApps, app)
+ continue
+ }
+ deleteAppIds = append(deleteAppIds, app.ID)
+ deleteApps = append(deleteApps, app)
+ deleteAppDetails = append(deleteAppDetails, app.Details...)
+ } else {
+ updateApps = append(updateApps, app)
+ }
+ }
+
+ }
+
+ tags, _ := tagRepo.All()
+ tagMap := make(map[string]uint, len(tags))
+ for _, tag := range tags {
+ tagMap[tag.Key] = tag.ID
+ }
+
+ tx, ctx := getTxAndContext()
+ defer tx.Rollback()
+ if len(newApps) > 0 {
+ if err = appRepo.BatchCreate(ctx, newApps); err != nil {
+ return
}
}
- }
-
- if len(newAppDetails) > 0 {
- if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {
- return
+ for _, update := range updateApps {
+ if err = appRepo.Save(ctx, &update); err != nil {
+ return
+ }
}
- }
-
- for _, updateAppDetail := range updateDetails {
- if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {
- return
+ if len(deleteApps) > 0 {
+ if err = appRepo.BatchDelete(ctx, deleteApps); err != nil {
+ return
+ }
+ if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {
+ return
+ }
}
- }
- if len(deleteAppDetails) > 0 {
- if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {
- return
- }
- }
-
- if len(oldAppIds) > 0 {
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
return
}
- }
-
- if len(appTags) > 0 {
- if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
- return
+ for _, newApp := range newApps {
+ if newApp.ID > 0 {
+ for _, detail := range newApp.Details {
+ detail.AppId = newApp.ID
+ newAppDetails = append(newAppDetails, detail)
+ }
+ }
}
- }
- tx.Commit()
- global.LOG.Infof("Synchronization of local applications completed")
+ for _, update := range updateApps {
+ for _, detail := range update.Details {
+ if detail.ID == 0 {
+ detail.AppId = update.ID
+ newAppDetails = append(newAppDetails, detail)
+ } else {
+ if detail.Status == constant.AppNormal {
+ updateDetails = append(updateDetails, detail)
+ } else {
+ deleteAppDetails = append(deleteAppDetails, detail)
+ }
+ }
+ }
+ }
+
+ allApps := append(newApps, updateApps...)
+ for _, app := range allApps {
+ for _, t := range app.TagsKey {
+ tagId, ok := tagMap[t]
+ if ok {
+ appTags = append(appTags, &model.AppTag{
+ AppId: app.ID,
+ TagId: tagId,
+ })
+ }
+ }
+ }
+
+ if len(newAppDetails) > 0 {
+ if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {
+ return
+ }
+ }
+
+ for _, updateAppDetail := range updateDetails {
+ if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {
+ return
+ }
+ }
+
+ if len(deleteAppDetails) > 0 {
+ if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {
+ return
+ }
+ }
+
+ if len(oldAppIds) > 0 {
+ if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
+ return
+ }
+ }
+
+ if len(appTags) > 0 {
+ if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
+ return
+ }
+ }
+ tx.Commit()
+ global.LOG.Infof("Synchronization of local applications completed")
+ return nil
+ }, nil)
+ go func() {
+ _ = syncTask.Execute()
+ }()
}
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
@@ -941,24 +946,31 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}
}
+ tx, ctx := getTxAndContext()
+ defer func() {
+ if err != nil {
+ tx.Rollback()
+ return
+ }
+ }()
if len(addAppArray) > 0 {
- if err = appRepo.BatchCreate(context.Background(), addAppArray); err != nil {
+ if err = appRepo.BatchCreate(ctx, addAppArray); err != nil {
return
}
}
if len(deleteAppArray) > 0 {
- if err = appRepo.BatchDelete(context.Background(), deleteAppArray); err != nil {
+ if err = appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
return
}
- if err = appDetailRepo.DeleteByAppIds(context.Background(), deleteIds); err != nil {
+ if err = appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
return
}
}
- if err = tagRepo.DeleteAll(context.Background()); err != nil {
+ if err = tagRepo.DeleteAll(ctx); err != nil {
return
}
if len(tags) > 0 {
- if err = tagRepo.BatchCreate(context.Background(), tags); err != nil {
+ if err = tagRepo.BatchCreate(ctx, tags); err != nil {
return
}
for _, tag := range tags {
@@ -966,7 +978,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}
}
for _, update := range updateAppArray {
- if err = appRepo.Save(context.Background(), &update); err != nil {
+ if err = appRepo.Save(ctx, &update); err != nil {
return
}
}
@@ -1011,32 +1023,33 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}
}
if len(addDetails) > 0 {
- if err = appDetailRepo.BatchCreate(context.Background(), addDetails); err != nil {
+ if err = appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
return
}
}
if len(deleteDetails) > 0 {
- if err = appDetailRepo.BatchDelete(context.Background(), deleteDetails); err != nil {
+ if err = appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
return
}
}
for _, u := range updateDetails {
- if err = appDetailRepo.Update(context.Background(), u); err != nil {
+ if err = appDetailRepo.Update(ctx, u); err != nil {
return
}
}
if len(oldAppIds) > 0 {
- if err = appTagRepo.DeleteByAppIds(context.Background(), oldAppIds); err != nil {
+ if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
return
}
}
if len(appTags) > 0 {
- if err = appTagRepo.BatchCreate(context.Background(), appTags); err != nil {
+ if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
return
}
}
+ tx.Commit()
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml
index 6e72f9494..20a363c1c 100644
--- a/agent/i18n/lang/en.yaml
+++ b/agent/i18n/lang/en.yaml
@@ -235,4 +235,5 @@ AppLink: 'Associated Application'
EnableSSL: "Enable HTTPS"
AppStore: "App Store"
TaskSync: "Sync"
+LocalApp: "Local App"
diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml
index 8453928f5..09a966996 100644
--- a/agent/i18n/lang/zh-Hant.yaml
+++ b/agent/i18n/lang/zh-Hant.yaml
@@ -237,4 +237,5 @@ AppLink: '關聯應用'
EnableSSL: "開啟 HTTPS"
AppStore: "應用商店"
TaskSync: "同步"
+LocalApp: "本地應用"
diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml
index c260dd2e1..6bb68baba 100644
--- a/agent/i18n/lang/zh.yaml
+++ b/agent/i18n/lang/zh.yaml
@@ -238,3 +238,4 @@ AppLink: "关联应用"
EnableSSL: "开启 HTTPS"
AppStore: "应用商店"
TaskSync: "同步"
+LocalApp: "本地应用"
\ No newline at end of file
diff --git a/agent/router/ro_app.go b/agent/router/ro_app.go
index feb389b15..1d8bf9685 100644
--- a/agent/router/ro_app.go
+++ b/agent/router/ro_app.go
@@ -13,7 +13,8 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
baseApi := v2.ApiGroupApp.BaseApi
{
- appRouter.POST("/sync", baseApi.SyncApp)
+ appRouter.POST("/sync/remote", baseApi.SyncApp)
+ appRouter.POST("/sync/local", baseApi.SyncLocalApp)
appRouter.GET("/checkupdate", baseApi.GetAppListUpdate)
appRouter.POST("/search", baseApi.SearchApp)
appRouter.GET("/:key", baseApi.GetApp)
diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts
index 0b5c01b5e..e5a3f6378 100644
--- a/frontend/src/api/modules/app.ts
+++ b/frontend/src/api/modules/app.ts
@@ -4,7 +4,11 @@ import { App } from '../interface/app';
import { TimeoutEnum } from '@/enums/http-enum';
export const SyncApp = (req: App.AppStoreSync) => {
- return http.post('apps/sync', req);
+ return http.post('apps/sync/remote', req);
+};
+
+export const SyncLocalApp = (req: App.AppStoreSync) => {
+ return http.post('apps/sync/local', req);
};
export const GetAppListUpdate = () => {
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index ca2472f74..7d5d5c869 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1878,6 +1878,7 @@ const message = {
supportedArchitectures: 'Architectures',
link: 'Link',
showCurrentArch: 'Architecture',
+ syncLocalApp: 'Sync Local App',
},
website: {
website: 'Website',
diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts
index 9fa83b7da..b8ae1fde4 100644
--- a/frontend/src/lang/modules/tw.ts
+++ b/frontend/src/lang/modules/tw.ts
@@ -1744,6 +1744,7 @@ const message = {
supportedArchitectures: '支持架構',
link: '鏈接',
showCurrentArch: '僅顯示當前架構',
+ syncLocalApp: '同步本地應用',
},
website: {
website: '網站',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index a00128813..15aa82db4 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -1686,7 +1686,7 @@ const message = {
noService: '无{0}',
toInstall: '去安装',
param: '参数配置',
- syncAppList: '更新应用列表',
+ syncAppList: '更新远程应用',
alreadyRun: '已安装',
less1Minute: '小于1分钟',
appOfficeWebsite: '官方网站',
@@ -1745,6 +1745,7 @@ const message = {
supportedArchitectures: '支持架构',
link: '链接',
showCurrentArch: '仅显示当前服务器架构应用',
+ syncLocalApp: '同步本地应用',
},
website: {
website: '网站',
diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue
index aa9e32c3f..5cd022049 100644
--- a/frontend/src/views/app-store/apps/index.vue
+++ b/frontend/src/views/app-store/apps/index.vue
@@ -53,11 +53,12 @@
-
-
- {{ $t('app.syncAppList') }}
-
-
+
+ {{ $t('app.syncAppList') }}
+
+
+ {{ $t('app.syncLocalApp') }}
+
@@ -174,8 +175,7 @@