mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: Support import and export operations for quick commands (#10434)
Refs #7384
This commit is contained in:
@@ -1,11 +1,102 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Export command
|
||||
// @Success 200 {string} path
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/commands/upload [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"导出快速命令","formatEN":"export quick commands"}
|
||||
func (b *BaseApi) UploadCommandCsv(c *gin.Context) {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
files := form.File["file"]
|
||||
if len(files) == 0 {
|
||||
helper.BadRequest(c, errors.New("no such files"))
|
||||
return
|
||||
}
|
||||
uploadFile, _ := files[0].Open()
|
||||
reader := csv.NewReader(uploadFile)
|
||||
if _, err := reader.Read(); err != nil {
|
||||
helper.BadRequest(c, fmt.Errorf("read title failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
groupRepo := repo.NewIGroupRepo()
|
||||
group, _ := groupRepo.Get(groupRepo.WithByDefault(true))
|
||||
var commands []dto.CommandInfo
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
helper.BadRequest(c, fmt.Errorf("read content failed, err: %v", err))
|
||||
return
|
||||
}
|
||||
if len(record) >= 2 {
|
||||
commands = append(commands, dto.CommandInfo{
|
||||
Name: record[0],
|
||||
Type: "command",
|
||||
GroupID: group.ID,
|
||||
Command: record[1],
|
||||
GroupBelong: group.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, commands)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Export command
|
||||
// @Success 200 {string} path
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/commands/export [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"导出快速命令","formatEN":"export quick commands"}
|
||||
func (b *BaseApi) ExportCommands(c *gin.Context) {
|
||||
file, err := commandService.Export()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, file)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Import command
|
||||
// @Success 200 {string} path
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/commands/import [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"导入快速命令","formatEN":"import quick commands"}
|
||||
func (b *BaseApi) ImportCommands(c *gin.Context) {
|
||||
var req dto.CommandImport
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range req.Items {
|
||||
_ = commandService.Create(item)
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Create command
|
||||
// @Accept json
|
||||
|
||||
@@ -9,6 +9,10 @@ type SearchCommandWithPage struct {
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type CommandImport struct {
|
||||
Items []CommandOperate `json:"items"`
|
||||
}
|
||||
|
||||
type CommandOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/csv"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
@@ -17,6 +24,8 @@ type ICommandService interface {
|
||||
Create(req dto.CommandOperate) error
|
||||
Update(req dto.CommandOperate) error
|
||||
Delete(ids []uint) error
|
||||
|
||||
Export() (string, error)
|
||||
}
|
||||
|
||||
func NewICommandService() ICommandService {
|
||||
@@ -98,6 +107,28 @@ func (u *CommandService) SearchWithPage(req dto.SearchCommandWithPage) (int64, i
|
||||
return total, dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) Export() (string, error) {
|
||||
commands, err := commandRepo.List(repo.WithByType("command"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var list []csv.CommandTemplate
|
||||
for _, item := range commands {
|
||||
list = append(list, csv.CommandTemplate{
|
||||
Name: item.Name,
|
||||
Command: item.Command,
|
||||
})
|
||||
}
|
||||
tmpFileName := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/export/commands", fmt.Sprintf("1panel-commands-%s.csv", time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if _, err := os.Stat(path.Dir(tmpFileName)); err != nil {
|
||||
_ = os.MkdirAll(path.Dir(tmpFileName), constant.DirPerm)
|
||||
}
|
||||
if err := csv.ExportCommands(tmpFileName, list); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFileName, err
|
||||
}
|
||||
|
||||
func (u *CommandService) Create(req dto.CommandOperate) error {
|
||||
command, _ := commandRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))
|
||||
if command.ID != 0 {
|
||||
|
||||
@@ -230,4 +230,8 @@ MasterNodePortNotAvailable: "Node {{ .name }} port {{ .port }} connectivity veri
|
||||
ClusterMasterNotExist: "The master node of the cluster is disconnected, please delete the child nodes."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} request failed: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} request failed: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Name"
|
||||
Command: "Command"
|
||||
@@ -231,3 +231,7 @@ ClusterMasterNotExist: "El nodo principal del clúster está desconectado, elimi
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} petición fallida: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Nombre"
|
||||
Command: "Comando"
|
||||
@@ -231,4 +231,8 @@ MasterNodePortNotAvailable: "ノード {{ .name }} のポート {{ .port }} の
|
||||
ClusterMasterNotExist: "クラスタのマスターノードが切断されています。子ノードを削除してください。"
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} リクエスト失敗: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} リクエスト失敗: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "名前"
|
||||
Command: "コマンド"
|
||||
@@ -230,4 +230,8 @@ MasterNodePortNotAvailable: "노드 {{ .name }} 포트 {{ .port }} 연결성 검
|
||||
ClusterMasterNotExist: "클러스터의 마스터 노드가 연결이 끊어졌습니다. 자식 노드를 삭제하세요."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} 요청 실패: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} 요청 실패: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "이름"
|
||||
Command: "명령어"
|
||||
@@ -225,4 +225,8 @@ MasterNodePortNotAvailable: "Pengesahan kesambungan pelabuhan {{ .name }} nod {{
|
||||
ClusterMasterNotExist: "Node utama kluster terputus, sila padamkan nod anak."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} permintaan gagal: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} permintaan gagal: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Nama"
|
||||
Command: "Arahan"
|
||||
@@ -230,4 +230,8 @@ MasterNodePortNotAvailable: "A verificação de conectividade da porta {{ .port
|
||||
ClusterMasterNotExist: "O nó mestre do cluster está desconectado, por favor, exclua os nós filhos."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} solicitação falhou: {{ .err }}
|
||||
ErrReqFailed: "{{.name}} solicitação falhou: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Название"
|
||||
Command: "Команда"
|
||||
@@ -230,4 +230,8 @@ MasterNodePortNotAvailable: "Проверка подключения порта
|
||||
ClusterMasterNotExist: "Основной узел кластера отключен, пожалуйста, удалите дочерние узлы."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} запрос не удался: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} запрос не удался: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Название"
|
||||
Command: "Команда"
|
||||
@@ -229,4 +229,8 @@ MasterNodePortNotAvailable: "Düğüm {{ .name }} portu {{ .port }} bağlantı d
|
||||
ClusterMasterNotExist: "Küme ana düğümü bağlantısı kesildi, lütfen alt düğümleri silin."
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} istek başarısız: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} istek başarısız: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "Ad"
|
||||
Command: "Komut"
|
||||
@@ -241,3 +241,7 @@ ClusterMasterNotExist: "叢集主節點失聯,請刪除子節點"
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} 請求失敗: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "名稱"
|
||||
Command: "命令"
|
||||
@@ -239,4 +239,8 @@ MasterNodePortNotAvailable: "节点 {{ .name }} 端口 {{ .port }} 连通性校
|
||||
ClusterMasterNotExist: "集群主节点失联,请删除子节点"
|
||||
|
||||
#ssl
|
||||
ErrReqFailed: "{{.name}} 请求失败: {{ .err }}"
|
||||
ErrReqFailed: "{{.name}} 请求失败: {{ .err }}"
|
||||
|
||||
#command
|
||||
Name: "名称"
|
||||
Command: "命令"
|
||||
@@ -20,5 +20,8 @@ func (s *CommandRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
commandRouter.POST("/search", baseApi.SearchCommand)
|
||||
commandRouter.POST("/tree", baseApi.SearchCommandTree)
|
||||
commandRouter.POST("/update", baseApi.UpdateCommand)
|
||||
commandRouter.POST("/export", baseApi.ExportCommands)
|
||||
commandRouter.POST("/upload", baseApi.UploadCommandCsv)
|
||||
commandRouter.POST("/import", baseApi.ImportCommands)
|
||||
}
|
||||
}
|
||||
|
||||
43
core/utils/csv/command.go
Normal file
43
core/utils/csv/command.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package csv
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/i18n"
|
||||
)
|
||||
|
||||
type CommandTemplate struct {
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
func ExportCommands(filename string, commands []CommandTemplate) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := csv.NewWriter(file)
|
||||
defer writer.Flush()
|
||||
|
||||
if err := writer.Write([]string{
|
||||
i18n.GetMsgByKey("Name"),
|
||||
i18n.GetMsgByKey("Command"),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, log := range commands {
|
||||
record := []string{
|
||||
log.Name,
|
||||
log.Command,
|
||||
}
|
||||
if err := writer.Write(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -153,7 +153,7 @@ class RequestHttp {
|
||||
download<BlobPart>(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
||||
return this.service.post(url, params, _object);
|
||||
}
|
||||
upload<T>(url: string, params: object = {}, config?: AxiosRequestConfig): Promise<T> {
|
||||
upload<T>(url: string, params: object = {}, config?: AxiosRequestConfig): Promise<ResultData<T>> {
|
||||
return this.service.post(url, params, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,15 @@ import { Command } from '../interface/command';
|
||||
export const getCommandList = (type: string) => {
|
||||
return http.post<Array<Command.CommandInfo>>(`/core/commands/list`, { type: type });
|
||||
};
|
||||
export const exportCommands = () => {
|
||||
return http.post<string>(`/core/commands/export`);
|
||||
};
|
||||
export const uploadCommands = (params: FormData) => {
|
||||
return http.upload<Array<Command.CommandInfo>>(`/core/commands/upload`, params);
|
||||
};
|
||||
export const importCommands = (list: Array<Command.CommandOperate>) => {
|
||||
return http.post(`/core/commands/import`, { items: list });
|
||||
};
|
||||
export const getCommandPage = (params: SearchWithPage) => {
|
||||
return http.post<ResPage<Command.CommandInfo>>(`/core/commands/search`, params);
|
||||
};
|
||||
|
||||
@@ -1184,6 +1184,7 @@ const message = {
|
||||
fold: 'All contract',
|
||||
batchInput: 'Batch processing',
|
||||
quickCommand: 'Quick command | Quick commands',
|
||||
noSuchCommand: 'No quick command data found in the imported CSV file, please check and try again!',
|
||||
quickCommandHelper: 'You can use the quick commands at the bottom of the "Terminals -> Terminals".',
|
||||
groupDeleteHelper:
|
||||
'After the group is removed, all connections in the group will be migrated to the default group. Do you want to continue?',
|
||||
|
||||
@@ -1185,6 +1185,8 @@ const message = {
|
||||
fold: 'Contraer todo',
|
||||
batchInput: 'Procesamiento por lotes',
|
||||
quickCommand: 'Comando rápido | Comandos rápidos',
|
||||
noSuchCommand:
|
||||
'No se encontraron datos de comandos rápidos en el archivo CSV importado, ¡compruebe e inténtelo de nuevo!',
|
||||
quickCommandHelper: 'Puede usar comandos rápidos en la parte inferior de "Terminales -> Terminales".',
|
||||
groupDeleteHelper:
|
||||
'Después de eliminar el grupo, todas las conexiones pasarán al grupo predeterminado. ¿Desea continuar?',
|
||||
|
||||
@@ -1146,6 +1146,8 @@ const message = {
|
||||
fold: 'すべての契約',
|
||||
batchInput: 'バッチ処理',
|
||||
quickCommand: 'クイックコマンド|クイックコマンド',
|
||||
noSuchCommand:
|
||||
'インポートしたCSVファイルにクイックコマンドデータが見つかりませんでした。確認して再試行してください!',
|
||||
quickCommandHelper: '「端末 - >端子」の下部にあるクイックコマンドを使用できます。',
|
||||
groupDeleteHelper:
|
||||
'グループが削除された後、グループ内のすべての接続がデフォルトグループに移行されます。続けたいですか?',
|
||||
|
||||
@@ -1138,6 +1138,7 @@ const message = {
|
||||
fold: '모두 축소',
|
||||
batchInput: '배치 처리',
|
||||
quickCommand: '빠른 명령 | 빠른 명령들',
|
||||
noSuchCommand: '가져온 CSV 파일에서 빠른 명령어 데이터를 찾을 수 없습니다. 확인 후 다시 시도하세요!',
|
||||
quickCommandHelper: '"터미널 -> 터미널" 하단에서 빠른 명령을 사용할 수 있습니다.',
|
||||
groupDeleteHelper: '그룹을 제거하면 해당 그룹의 모든 연결이 기본 그룹으로 이동됩니다. 계속하시겠습니까?',
|
||||
command: '명령',
|
||||
|
||||
@@ -1174,6 +1174,7 @@ const message = {
|
||||
fold: 'Kontrak semua',
|
||||
batchInput: 'Pemprosesan kelompok',
|
||||
quickCommand: 'Arahan pantas | Arahan pantas',
|
||||
noSuchCommand: 'Tiada data arahan pantas ditemui dalam fail CSV yang diimport, sila periksa dan cuba lagi!',
|
||||
quickCommandHelper: 'Anda boleh menggunakan arahan pantas di bahagian bawah "Terminal -> Terminal".',
|
||||
groupDeleteHelper:
|
||||
'Selepas kumpulan dikeluarkan, semua sambungan dalam kumpulan akan dipindahkan ke kumpulan lalai. Adakah anda mahu meneruskan?',
|
||||
|
||||
@@ -1167,6 +1167,8 @@ const message = {
|
||||
fold: 'Contrair tudo',
|
||||
batchInput: 'Processamento em lote',
|
||||
quickCommand: 'Comando rápido | Comandos rápidos',
|
||||
noSuchCommand:
|
||||
'Nenhum dado de comando rápido encontrado no arquivo CSV importado, verifique e tente novamente!',
|
||||
quickCommandHelper: 'Você pode usar os comandos rápidos na parte inferior de "Terminais -> Terminais".',
|
||||
groupDeleteHelper:
|
||||
'Após o grupo ser removido, todas as conexões no grupo serão migradas para o grupo padrão. Você deseja continuar?',
|
||||
|
||||
@@ -1170,6 +1170,7 @@ const message = {
|
||||
fold: 'Свернуть все',
|
||||
batchInput: 'Пакетная обработка',
|
||||
quickCommand: 'Быстрая команда | Быстрые команды',
|
||||
noSuchCommand: 'В импортированном CSV-файле не найдены данные быстрых команд, проверьте и повторите попытку!',
|
||||
quickCommandHelper: 'Вы можете использовать быстрые команды внизу страницы "Терминалы -> Терминалы".',
|
||||
groupDeleteHelper:
|
||||
'После удаления группы все подключения в группе будут перемещены в группу по умолчанию. Хотите продолжить?',
|
||||
|
||||
@@ -1196,6 +1196,8 @@ const message = {
|
||||
fold: 'Tümünü daralt',
|
||||
batchInput: 'Toplu işleme',
|
||||
quickCommand: 'Hızlı komut | Hızlı komutlar',
|
||||
noSuchCommand:
|
||||
'İçe aktarılan CSV dosyasında hızlı komut verisi bulunamadı, lütfen kontrol edip tekrar deneyin!',
|
||||
quickCommandHelper: '"Terminaller -> Terminaller" altındaki hızlı komutları kullanabilirsiniz.',
|
||||
groupDeleteHelper:
|
||||
'Grup kaldırıldıktan sonra, gruptaki tüm bağlantılar varsayılan gruba taşınacaktır. Devam etmek istiyor musunuz?',
|
||||
|
||||
@@ -1128,6 +1128,7 @@ const message = {
|
||||
fold: '全部收縮',
|
||||
batchInput: '批次輸入',
|
||||
quickCommand: '快速指令',
|
||||
noSuchCommand: '導入的CSV文件中未能發現快速命令數據,請檢查後重試!',
|
||||
quickCommandHelper: '常用命令列表,用於在終端介面底部快速選擇',
|
||||
groupDeleteHelper: '移除組後,組內所有連接將遷移到 default 組內,是否繼續?',
|
||||
command: '指令',
|
||||
|
||||
@@ -1128,6 +1128,7 @@ const message = {
|
||||
fold: '全部收缩',
|
||||
batchInput: '批量输入',
|
||||
quickCommand: '快速命令',
|
||||
noSuchCommand: '导入的 csv 文件中未能发现快速命令数据,请检查后重试!',
|
||||
quickCommandHelper: '常用命令列表,用于在终端界面底部快速选择',
|
||||
groupDeleteHelper: '移除组后,组内所有连接将迁移到 default 组内,是否继续?',
|
||||
command: '命令',
|
||||
|
||||
175
frontend/src/views/terminal/command/import/index.vue
Normal file
175
frontend/src/views/terminal/command/import/index.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<DialogPro v-model="visible" :title="$t('commons.button.import')" size="large">
|
||||
<div>
|
||||
<el-upload
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
ref="uploadRef"
|
||||
class="float-left mt-2"
|
||||
:show-file-list="false"
|
||||
:limit="1"
|
||||
accept=".csv"
|
||||
:on-change="fileOnChange"
|
||||
:on-exceed="handleExceed"
|
||||
v-model:file-list="uploaderFiles"
|
||||
>
|
||||
<el-button class="float-left" type="primary">{{ $t('commons.button.upload') }}</el-button>
|
||||
</el-upload>
|
||||
|
||||
<el-button :disabled="selects.length === 0" @click="onImport" class="ml-2 mt-2">
|
||||
{{ $t('commons.button.import') }}
|
||||
</el-button>
|
||||
|
||||
<el-select
|
||||
filterable
|
||||
:placeholder="$t('terminal.groupChange')"
|
||||
v-model="currentGroup"
|
||||
@change="changeGroup"
|
||||
class="p-w-200 ml-2 mt-2"
|
||||
>
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option v-if="item.name === 'Default'" :label="$t('commons.table.default')" :value="item.id" />
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
|
||||
<el-card class="mt-2 w-full" v-loading="loading">
|
||||
<el-table :data="data" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column
|
||||
:label="$t('commons.table.name')"
|
||||
:min-width="80"
|
||||
prop="name"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('commons.table.group')"
|
||||
show-overflow-tooltip
|
||||
min-width="80"
|
||||
prop="groupBelong"
|
||||
fix
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.groupBelong === 'Default'">{{ $t('commons.table.default') }}</span>
|
||||
<span v-else>{{ row.groupBelong }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('terminal.command')"
|
||||
:min-width="120"
|
||||
prop="command"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DialogPro>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';
|
||||
import { importCommands, uploadCommands } from '@/api/modules/command';
|
||||
import { getGroupList } from '@/api/modules/group';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref();
|
||||
const selects = ref<any>([]);
|
||||
const data = ref([]);
|
||||
|
||||
const uploadRef = ref();
|
||||
const uploaderFiles = ref();
|
||||
|
||||
const currentGroup = ref();
|
||||
const groupList = ref();
|
||||
|
||||
const acceptParams = (): void => {
|
||||
visible.value = true;
|
||||
loadGroups();
|
||||
data.value = [];
|
||||
};
|
||||
|
||||
const loadGroups = async () => {
|
||||
const res = await getGroupList('command');
|
||||
groupList.value = res.data || [];
|
||||
};
|
||||
const changeGroup = () => {
|
||||
let itemGroup;
|
||||
for (const g of groupList.value) {
|
||||
if (g.id === currentGroup.value) {
|
||||
itemGroup = g;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of data.value) {
|
||||
item.groupID = currentGroup.value;
|
||||
item.groupBelong = itemGroup.name;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectionChange = (val: any) => {
|
||||
selects.value = val;
|
||||
};
|
||||
|
||||
const fileOnChange = async (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
||||
uploaderFiles.value = uploadFiles;
|
||||
if (uploaderFiles.value.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const file = uploaderFiles.value[0];
|
||||
const formData = new FormData();
|
||||
formData.append('file', file.raw);
|
||||
loading.value = true;
|
||||
await uploadCommands(formData)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
uploadRef.value!.clearFiles();
|
||||
uploaderFiles.value = [];
|
||||
data.value = res.data || [];
|
||||
if (data.value.length === 0) {
|
||||
MsgError(i18n.global.t('terminal.noSuchCommand'));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
uploadRef.value!.clearFiles();
|
||||
uploaderFiles.value = [];
|
||||
});
|
||||
};
|
||||
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
uploadRef.value!.clearFiles();
|
||||
const file = files[0] as UploadRawFile;
|
||||
file.uid = genFileId();
|
||||
uploadRef.value!.handleStart(file);
|
||||
};
|
||||
|
||||
const onImport = async () => {
|
||||
loading.value = true;
|
||||
importCommands(selects.value)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
visible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
@@ -14,6 +14,15 @@
|
||||
<el-button type="primary" plain :disabled="selects.length === 0" @click="batchDelete(null)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
|
||||
<el-button-group>
|
||||
<el-button @click="onImport">
|
||||
{{ $t('commons.button.import') }}
|
||||
</el-button>
|
||||
<el-button @click="onExport">
|
||||
{{ $t('commons.button.export') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
<template #rightToolBar>
|
||||
<el-select v-model="group" @change="search()" clearable class="p-w-200">
|
||||
@@ -85,6 +94,7 @@
|
||||
|
||||
<OpDialog ref="opRef" @search="search" />
|
||||
<OperateDialog @search="search" ref="dialogRef" />
|
||||
<ImportDialog @search="search" ref="importDialogRef" />
|
||||
<GroupDialog @search="loadGroups" ref="dialogGroupRef" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -93,11 +103,13 @@
|
||||
import { Command } from '@/api/interface/command';
|
||||
import GroupDialog from '@/components/group/index.vue';
|
||||
import OperateDialog from '@/views/terminal/command/operate/index.vue';
|
||||
import { editCommand, deleteCommand, getCommandPage } from '@/api/modules/command';
|
||||
import ImportDialog from '@/views/terminal/command/import/index.vue';
|
||||
import { editCommand, deleteCommand, getCommandPage, exportCommands } from '@/api/modules/command';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { getGroupList } from '@/api/modules/group';
|
||||
import { downloadFile } from '@/utils/util';
|
||||
|
||||
const loading = ref();
|
||||
const data = ref();
|
||||
@@ -115,6 +127,7 @@ const info = ref();
|
||||
const group = ref<string>('');
|
||||
const dialogRef = ref();
|
||||
const opRef = ref();
|
||||
const importDialogRef = ref();
|
||||
|
||||
const acceptParams = () => {
|
||||
search();
|
||||
@@ -150,6 +163,24 @@ const updateGroup = async (row: any) => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
const onExport = async () => {
|
||||
loading.value = true;
|
||||
await exportCommands()
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
loading.value = false;
|
||||
downloadFile(res.data, 'local');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onImport = () => {
|
||||
importDialogRef.value.acceptParams();
|
||||
};
|
||||
|
||||
const batchDelete = async (row: Command.CommandInfo | null) => {
|
||||
let names = [];
|
||||
let ids = [];
|
||||
|
||||
Reference in New Issue
Block a user