refactor: refactor the method for delete selected keys
This commit is contained in:
parent
bce4e2323e
commit
f98229b9fa
|
@ -234,21 +234,8 @@ func (b *browserService) CloseConnection(name string) (resp types.JSResp) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a redis client from local cache or create a new open
|
func (b *browserService) createRedisClient(selConn types.ConnectionConfig) (client redis.UniversalClient, err error) {
|
||||||
// if db >= 0, will also switch to db index
|
hook := redis2.NewHook(selConn.Name, func(cmd string, cost int64) {
|
||||||
func (b *browserService) getRedisClient(connName string, db int) (item *connectionItem, err error) {
|
|
||||||
var ok bool
|
|
||||||
var client redis.UniversalClient
|
|
||||||
if item, ok = b.connMap[connName]; ok {
|
|
||||||
client = item.client
|
|
||||||
} else {
|
|
||||||
selConn := Connection().getConnection(connName)
|
|
||||||
if selConn == nil {
|
|
||||||
err = fmt.Errorf("no match connection \"%s\"", connName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hook := redis2.NewHook(connName, func(cmd string, cost int64) {
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
//last := strings.LastIndex(cmd, ":")
|
//last := strings.LastIndex(cmd, ":")
|
||||||
//if last != -1 {
|
//if last != -1 {
|
||||||
|
@ -256,13 +243,13 @@ func (b *browserService) getRedisClient(connName string, db int) (item *connecti
|
||||||
//}
|
//}
|
||||||
b.cmdHistory = append(b.cmdHistory, cmdHistoryItem{
|
b.cmdHistory = append(b.cmdHistory, cmdHistoryItem{
|
||||||
Timestamp: now.UnixMilli(),
|
Timestamp: now.UnixMilli(),
|
||||||
Server: connName,
|
Server: selConn.Name,
|
||||||
Cmd: cmd,
|
Cmd: cmd,
|
||||||
Cost: cost,
|
Cost: cost,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
client, err = Connection().createRedisClient(selConn.ConnectionConfig)
|
client, err = Connection().createRedisClient(selConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("create conenction error: %s", err.Error())
|
err = fmt.Errorf("create conenction error: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -270,8 +257,7 @@ func (b *browserService) getRedisClient(connName string, db int) (item *connecti
|
||||||
|
|
||||||
_ = client.Do(b.ctx, "CLIENT", "SETNAME", url.QueryEscape(selConn.Name)).Err()
|
_ = client.Do(b.ctx, "CLIENT", "SETNAME", url.QueryEscape(selConn.Name)).Err()
|
||||||
// add hook to each node in cluster mode
|
// add hook to each node in cluster mode
|
||||||
var cluster *redis.ClusterClient
|
if cluster, ok := client.(*redis.ClusterClient); ok {
|
||||||
if cluster, ok = client.(*redis.ClusterClient); ok {
|
|
||||||
err = cluster.ForEachShard(b.ctx, func(ctx context.Context, cli *redis.Client) error {
|
err = cluster.ForEachShard(b.ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||||
cli.AddHook(hook)
|
cli.AddHook(hook)
|
||||||
return nil
|
return nil
|
||||||
|
@ -284,10 +270,27 @@ func (b *browserService) getRedisClient(connName string, db int) (item *connecti
|
||||||
client.AddHook(hook)
|
client.AddHook(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = client.Ping(b.ctx).Result(); err != nil && err != redis.Nil {
|
if _, err = client.Ping(b.ctx).Result(); err != nil && !errors.Is(err, redis.Nil) {
|
||||||
err = errors.New("can not connect to redis server:" + err.Error())
|
err = errors.New("can not connect to redis server:" + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a redis client from local cache or create a new open
|
||||||
|
// if db >= 0, will also switch to db index
|
||||||
|
func (b *browserService) getRedisClient(server string, db int) (item *connectionItem, err error) {
|
||||||
|
var ok bool
|
||||||
|
var client redis.UniversalClient
|
||||||
|
if item, ok = b.connMap[server]; ok {
|
||||||
|
client = item.client
|
||||||
|
} else {
|
||||||
|
selConn := Connection().getConnection(server)
|
||||||
|
if selConn == nil {
|
||||||
|
err = fmt.Errorf("no match connection \"%s\"", server)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client, err = b.createRedisClient(selConn.ConnectionConfig)
|
||||||
ctx, cancelFunc := context.WithCancel(b.ctx)
|
ctx, cancelFunc := context.WithCancel(b.ctx)
|
||||||
item = &connectionItem{
|
item = &connectionItem{
|
||||||
client: client,
|
client: client,
|
||||||
|
@ -300,7 +303,7 @@ func (b *browserService) getRedisClient(connName string, db int) (item *connecti
|
||||||
if item.stepSize <= 0 {
|
if item.stepSize <= 0 {
|
||||||
item.stepSize = consts.DEFAULT_LOAD_SIZE
|
item.stepSize = consts.DEFAULT_LOAD_SIZE
|
||||||
}
|
}
|
||||||
b.connMap[connName] = item
|
b.connMap[server] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUG: go-redis might not be executing commands on the corresponding database
|
// BUG: go-redis might not be executing commands on the corresponding database
|
||||||
|
@ -315,7 +318,7 @@ func (b *browserService) getRedisClient(connName string, db int) (item *connecti
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.db = db
|
item.db = db
|
||||||
b.connMap[connName].db = db
|
b.connMap[server].db = db
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -328,12 +331,12 @@ func (b *browserService) loadDBSize(ctx context.Context, client redis.UniversalC
|
||||||
}
|
}
|
||||||
|
|
||||||
// save current scan cursor
|
// save current scan cursor
|
||||||
func (b *browserService) setClientCursor(connName string, db int, cursor uint64) {
|
func (b *browserService) setClientCursor(server string, db int, cursor uint64) {
|
||||||
if _, ok := b.connMap[connName]; ok {
|
if _, ok := b.connMap[server]; ok {
|
||||||
if cursor == 0 {
|
if cursor == 0 {
|
||||||
delete(b.connMap[connName].cursor, db)
|
delete(b.connMap[server].cursor, db)
|
||||||
} else {
|
} else {
|
||||||
b.connMap[connName].cursor[db] = cursor
|
b.connMap[server].cursor[db] = cursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1940,6 +1943,89 @@ func (b *browserService) DeleteOneKey(server string, db int, k any) (resp types.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteKeys delete keys sync with notification
|
||||||
|
func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo string) (resp types.JSResp) {
|
||||||
|
// connect a new connection to export keys
|
||||||
|
conf := Connection().getConnection(server)
|
||||||
|
if conf == nil {
|
||||||
|
resp.Msg = fmt.Sprintf("no connection profile named: %s", server)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var client redis.UniversalClient
|
||||||
|
var err error
|
||||||
|
var connConfig = conf.ConnectionConfig
|
||||||
|
connConfig.LastDB = db
|
||||||
|
if client, err = b.createRedisClient(connConfig); err != nil {
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancelFunc := context.WithCancel(b.ctx)
|
||||||
|
defer client.Close()
|
||||||
|
defer cancelFunc()
|
||||||
|
|
||||||
|
cancelEvent := "delete:stop:" + serialNo
|
||||||
|
runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
|
||||||
|
cancelFunc()
|
||||||
|
})
|
||||||
|
processEvent := "deleting:" + serialNo
|
||||||
|
total := len(ks)
|
||||||
|
var failed atomic.Int64
|
||||||
|
var canceled bool
|
||||||
|
var deletedKeys = make([]any, 0, total)
|
||||||
|
var mutex sync.Mutex
|
||||||
|
del := func(ctx context.Context, cli redis.UniversalClient) error {
|
||||||
|
for i, k := range ks {
|
||||||
|
// emit progress per second
|
||||||
|
param := map[string]any{
|
||||||
|
"total": total,
|
||||||
|
"progress": i + 1,
|
||||||
|
"processing": k,
|
||||||
|
}
|
||||||
|
runtime.EventsEmit(b.ctx, processEvent, param)
|
||||||
|
|
||||||
|
key := strutil.DecodeRedisKey(k)
|
||||||
|
delErr := cli.Del(ctx, key).Err()
|
||||||
|
// do some sleep to prevent blocking the Redis server
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
if err != nil {
|
||||||
|
failed.Add(1)
|
||||||
|
} else {
|
||||||
|
// save deleted key
|
||||||
|
mutex.Lock()
|
||||||
|
deletedKeys = append(deletedKeys, k)
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
if errors.Is(delErr, context.Canceled) || canceled {
|
||||||
|
canceled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cluster, ok := client.(*redis.ClusterClient); ok {
|
||||||
|
// cluster mode
|
||||||
|
err = cluster.ForEachMaster(ctx, func(ctx context.Context, cli *redis.Client) error {
|
||||||
|
return del(ctx, cli)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
err = del(ctx, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.EventsOff(ctx, cancelEvent)
|
||||||
|
resp.Success = true
|
||||||
|
resp.Data = struct {
|
||||||
|
Canceled bool `json:"canceled"`
|
||||||
|
Deleted any `json:"deleted"`
|
||||||
|
Failed int64 `json:"failed"`
|
||||||
|
}{
|
||||||
|
Canceled: canceled,
|
||||||
|
Deleted: deletedKeys,
|
||||||
|
Failed: failed.Load(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ExportKey export keys
|
// ExportKey export keys
|
||||||
func (b *browserService) ExportKey(server string, db int, ks []any, path string) (resp types.JSResp) {
|
func (b *browserService) ExportKey(server string, db int, ks []any, path string) (resp types.JSResp) {
|
||||||
// connect a new connection to export keys
|
// connect a new connection to export keys
|
||||||
|
@ -1952,12 +2038,12 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string)
|
||||||
var err error
|
var err error
|
||||||
var connConfig = conf.ConnectionConfig
|
var connConfig = conf.ConnectionConfig
|
||||||
connConfig.LastDB = db
|
connConfig.LastDB = db
|
||||||
if client, err = Connection().createRedisClient(connConfig); err != nil {
|
if client, err = b.createRedisClient(connConfig); err != nil {
|
||||||
resp.Msg = err.Error()
|
resp.Msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: add cancel handle
|
|
||||||
ctx, cancelFunc := context.WithCancel(b.ctx)
|
ctx, cancelFunc := context.WithCancel(b.ctx)
|
||||||
|
defer client.Close()
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
|
|
||||||
file, err := os.Create(path)
|
file, err := os.Create(path)
|
||||||
|
@ -1970,7 +2056,8 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string)
|
||||||
writer := csv.NewWriter(file)
|
writer := csv.NewWriter(file)
|
||||||
defer writer.Flush()
|
defer writer.Flush()
|
||||||
|
|
||||||
runtime.EventsOnce(ctx, "export:stop:"+path, func(data ...any) {
|
cancelEvent := "export:stop:" + path
|
||||||
|
runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
|
||||||
cancelFunc()
|
cancelFunc()
|
||||||
})
|
})
|
||||||
processEvent := "exporting:" + path
|
processEvent := "exporting:" + path
|
||||||
|
@ -1987,7 +2074,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string)
|
||||||
|
|
||||||
key := strutil.DecodeRedisKey(k)
|
key := strutil.DecodeRedisKey(k)
|
||||||
content, dumpErr := client.Dump(ctx, key).Bytes()
|
content, dumpErr := client.Dump(ctx, key).Bytes()
|
||||||
if errors.Is(dumpErr, context.Canceled) {
|
if errors.Is(dumpErr, context.Canceled) || canceled {
|
||||||
canceled = true
|
canceled = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1998,6 +2085,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtime.EventsOff(ctx, cancelEvent)
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Data = struct {
|
resp.Data = struct {
|
||||||
Canceled bool `json:"canceled"`
|
Canceled bool `json:"canceled"`
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, reactive, ref, watch } from 'vue'
|
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
||||||
import useDialog from 'stores/dialog'
|
import useDialog from 'stores/dialog'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { isEmpty, map, size } from 'lodash'
|
import { isEmpty, map, size } from 'lodash'
|
||||||
|
@ -70,6 +70,7 @@ const onConfirmDelete = async () => {
|
||||||
try {
|
try {
|
||||||
deleting.value = true
|
deleting.value = true
|
||||||
const { server, db, key, affectedKeys } = deleteForm
|
const { server, db, key, affectedKeys } = deleteForm
|
||||||
|
await nextTick()
|
||||||
browserStore.deleteKeys(server, db, affectedKeys).catch((e) => {})
|
browserStore.deleteKeys(server, db, affectedKeys).catch((e) => {})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$message.error(e.message)
|
$message.error(e.message)
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
"remove_tip": "{type} \"{name}\" will be deleted",
|
"remove_tip": "{type} \"{name}\" will be deleted",
|
||||||
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
||||||
"delete_key_succ": "\"{key}\" has been deleted",
|
"delete_key_succ": "\"{key}\" has been deleted",
|
||||||
"deleting_key": "Deleting key: {key} ({index}/{count})",
|
"deleting_key": "Deleting key({index}/{count}): {key}",
|
||||||
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
|
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
|
||||||
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
||||||
"handle_succ": "Success!",
|
"handle_succ": "Success!",
|
||||||
|
@ -276,7 +276,7 @@
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"save_file": "Export Path",
|
"save_file": "Export Path",
|
||||||
"save_file_tip": "Select the export file save path",
|
"save_file_tip": "Select the export file save path",
|
||||||
"exporting": "Exporting key: {key} ({index}/{count})",
|
"exporting": "Exporting key({index}/{count}): {key}",
|
||||||
"export_completed": "Export process has been completed, {success} successed, {fail} failed"
|
"export_completed": "Export process has been completed, {success} successed, {fail} failed"
|
||||||
},
|
},
|
||||||
"ttl": {
|
"ttl": {
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
"remove_tip": "{type} \"{name}\" 将会被删除",
|
"remove_tip": "{type} \"{name}\" 将会被删除",
|
||||||
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
||||||
"delete_key_succ": "{key} 已被删除",
|
"delete_key_succ": "{key} 已被删除",
|
||||||
"deleting_key": "正在删除键:{key} ({index}/{count})",
|
"deleting_key": "正在删除键({index}/{count}):{key}",
|
||||||
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
|
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
|
||||||
"rename_binary_key_fail": "不支持重命名二进制键名",
|
"rename_binary_key_fail": "不支持重命名二进制键名",
|
||||||
"handle_succ": "操作成功",
|
"handle_succ": "操作成功",
|
||||||
|
@ -275,7 +275,7 @@
|
||||||
"export": "确认导出",
|
"export": "确认导出",
|
||||||
"save_file": "导出路径",
|
"save_file": "导出路径",
|
||||||
"save_file_tip": "选择保存文件路径",
|
"save_file_tip": "选择保存文件路径",
|
||||||
"exporting": "正在导出键:{key} ({index}/{count})",
|
"exporting": "正在导出键({index}/{count}):{key}",
|
||||||
"export_completed": "已完成导出操作,成功{success}个,失败{fail}个"
|
"export_completed": "已完成导出操作,成功{success}个,失败{fail}个"
|
||||||
},
|
},
|
||||||
"ttl": {
|
"ttl": {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import {
|
||||||
CloseConnection,
|
CloseConnection,
|
||||||
ConvertValue,
|
ConvertValue,
|
||||||
DeleteKey,
|
DeleteKey,
|
||||||
DeleteOneKey,
|
DeleteKeys,
|
||||||
ExportKey,
|
ExportKey,
|
||||||
FlushDB,
|
FlushDB,
|
||||||
GetCmdHistory,
|
GetCmdHistory,
|
||||||
|
@ -1974,35 +1974,52 @@ const useBrowserStore = defineStore('browser', {
|
||||||
*/
|
*/
|
||||||
async deleteKeys(server, db, keys) {
|
async deleteKeys(server, db, keys) {
|
||||||
const delMsgRef = $message.loading('', { duration: 0, closable: true })
|
const delMsgRef = $message.loading('', { duration: 0, closable: true })
|
||||||
let progress = 0
|
let deleted = []
|
||||||
let count = size(keys)
|
|
||||||
let deletedCount = 0
|
|
||||||
let failCount = 0
|
let failCount = 0
|
||||||
|
let canceled = false
|
||||||
|
const serialNo = Date.now().valueOf().toString()
|
||||||
|
const eventName = 'deleting:' + serialNo
|
||||||
|
const cancelEvent = 'deleting:' + serialNo
|
||||||
try {
|
try {
|
||||||
for (const key of keys) {
|
let maxProgress = 0
|
||||||
delMsgRef.content = i18nGlobal.t('dialogue.deleting_key', {
|
EventsOn(eventName, ({ total, progress, processing }) => {
|
||||||
key: decodeRedisKey(key),
|
// update delete progress
|
||||||
index: ++progress,
|
if (progress > maxProgress) {
|
||||||
count,
|
maxProgress = progress
|
||||||
})
|
|
||||||
const { success } = await DeleteOneKey(server, db, key)
|
|
||||||
if (success) {
|
|
||||||
this._deleteKeyNode(server, db, key, false)
|
|
||||||
deletedCount += 1
|
|
||||||
} else {
|
|
||||||
failCount += 1
|
|
||||||
}
|
}
|
||||||
|
const k = decodeRedisKey(processing)
|
||||||
|
delMsgRef.content = i18nGlobal.t('dialogue.deleting_key', {
|
||||||
|
key: k,
|
||||||
|
index: maxProgress,
|
||||||
|
count: total,
|
||||||
|
})
|
||||||
|
// this._deleteKeyNode(server, db, k, false)
|
||||||
|
})
|
||||||
|
delMsgRef.onClose = () => {
|
||||||
|
EventsEmit(cancelEvent)
|
||||||
|
}
|
||||||
|
const { data, success, msg } = await DeleteKeys(server, db, keys, serialNo)
|
||||||
|
if (success) {
|
||||||
|
canceled = get(data, 'canceled', false)
|
||||||
|
deleted = get(data, 'deleted', [])
|
||||||
|
failCount = get(data, 'failed', 0)
|
||||||
|
} else {
|
||||||
|
$message.error(msg)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
delMsgRef.destroy()
|
delMsgRef.destroy()
|
||||||
|
EventsOff(eventName)
|
||||||
// clear checked keys
|
// clear checked keys
|
||||||
const tab = useTabStore()
|
const tab = useTabStore()
|
||||||
tab.setCheckedKeys(server)
|
tab.setCheckedKeys(server)
|
||||||
}
|
}
|
||||||
// refresh model data
|
// refresh model data
|
||||||
|
const deletedCount = size(deleted)
|
||||||
this._tidyNode(server, db, '', true)
|
this._tidyNode(server, db, '', true)
|
||||||
this._updateDBMaxKeys(server, db, -deletedCount)
|
this._updateDBMaxKeys(server, db, -deletedCount)
|
||||||
if (failCount <= 0) {
|
if (canceled) {
|
||||||
|
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
||||||
|
} else if (failCount <= 0) {
|
||||||
// no fail
|
// no fail
|
||||||
$message.success(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
$message.success(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
||||||
} else if (failCount >= deletedCount) {
|
} else if (failCount >= deletedCount) {
|
||||||
|
@ -2012,6 +2029,25 @@ const useBrowserStore = defineStore('browser', {
|
||||||
// some fail
|
// some fail
|
||||||
$message.warn(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
$message.warn(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: update tree view
|
||||||
|
// if (!isEmpty(deleted)) {
|
||||||
|
// let updateDeleted = []
|
||||||
|
// let count = size(deleted)
|
||||||
|
// for (const k of deleted) {
|
||||||
|
// updateDeleted.push(k)
|
||||||
|
// console.log(count)
|
||||||
|
// count -= 1
|
||||||
|
// if (size(updateDeleted) > 100 || count <= 0) {
|
||||||
|
// for (const dk of updateDeleted) {
|
||||||
|
// this._deleteKeyNode(server, db, dk, false)
|
||||||
|
// await nextTick()
|
||||||
|
// }
|
||||||
|
// updateDeleted = []
|
||||||
|
// console.warn('updateDeleted:', updateDeleted)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue