mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-25 08:33:53 +08:00
updata:智控台页面添加语言切换,支持中英繁。
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
<HeaderBar/>
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">设备管理</h2>
|
||||
<h2 class="page-title">{{ $t('device.management') }}</h2>
|
||||
<div class="right-operations">
|
||||
<el-input placeholder="请输入设备型号或Mac地址查询" v-model="searchKeyword" class="search-input"
|
||||
<el-input :placeholder="$t('device.searchPlaceholder')" v-model="searchKeyword" class="search-input"
|
||||
@keyup.enter.native="handleSearch" clearable/>
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button class="btn-search" @click="handleSearch">{{ $t('device.search') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,23 +17,23 @@
|
||||
<el-card class="device-card" shadow="never">
|
||||
<el-table ref="deviceTable" :data="paginatedDeviceList" class="transparent-table"
|
||||
:header-cell-class-name="headerCellClassName" v-loading="loading"
|
||||
element-loading-text="拼命加载中"
|
||||
:element-loading-text="$t('message.loading')"
|
||||
element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备型号" prop="model" align="center">
|
||||
<el-table-column :label="$t('device.model')" prop="model" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getFirmwareTypeName(scope.row.model) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="固件版本" prop="firmwareVersion" align="center"></el-table-column>
|
||||
<el-table-column label="Mac地址" prop="macAddress" align="center"></el-table-column>
|
||||
<el-table-column label="绑定时间" prop="bindTime" align="center"></el-table-column>
|
||||
<el-table-column label="最近对话" prop="lastConversation" align="center"></el-table-column>
|
||||
<el-table-column label="备注" align="center">
|
||||
<el-table-column :label="$t('device.firmwareVersion')" prop="firmwareVersion" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('device.macAddress')" prop="macAddress" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('device.bindTime')" prop="bindTime" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('device.lastConversation')" prop="lastConversation" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('device.remark')" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-show="row.isEdit"
|
||||
@@ -51,52 +51,52 @@
|
||||
style="cursor: pointer;"
|
||||
></i>
|
||||
<span @click="row.isEdit = true">
|
||||
{{ row.remark || '—' }}
|
||||
{{ row.remark || '-' }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="OTA升级" align="center">
|
||||
<el-table-column :label="$t('device.autoUpdate')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.otaSwitch" size="mini" active-color="#13ce66" inactive-color="#ff4949"
|
||||
@change="handleOtaSwitchChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column :label="$t('device.operation')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="handleUnbind(scope.row.device_id)">
|
||||
解绑
|
||||
</el-button>
|
||||
{{ $t('device.unbind') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isCurrentPageAllSelected ? '取消全选' : '全选' }}
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" class="add-device-btn" @click="handleAddDevice">
|
||||
验证码绑定
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" class="add-device-btn" @click="handleManualAddDevice">
|
||||
手动添加
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelected">解绑</el-button>
|
||||
</div>
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isCurrentPageAllSelected ? $t('device.deselectAll') : $t('device.selectAll') }}
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" class="add-device-btn" @click="handleAddDevice">
|
||||
{{ $t('device.bindWithCode') }}
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" class="add-device-btn" @click="handleManualAddDevice">
|
||||
{{ $t('device.manualAdd') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelected">{{ $t('device.unbind') }}</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">首页</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">上一页</button>
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('dictManagement.itemsPerPage').replace('{items}', item)" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">{{ $t('dictManagement.firstPage') }}</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">{{ $t('dictManagement.prevPage') }}</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">下一页</button>
|
||||
<span class="total-text">共{{ deviceList.length }}条记录</span>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">{{ $t('dictManagement.nextPage') }}</button>
|
||||
<span class="total-text">{{ $t('dictManagement.totalRecords').replace('{total}', deviceList.length) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -194,8 +194,8 @@ export default {
|
||||
const res = await Api.dict.getDictDataByType('FIRMWARE_TYPE')
|
||||
this.firmwareTypes = res.data
|
||||
} catch (error) {
|
||||
console.error('获取固件类型失败:', error)
|
||||
this.$message.error(error.message || '获取固件类型失败')
|
||||
console.error(this.$t('device.getFirmwareTypeFailed') + ':', error)
|
||||
this.$message.error(error.message || this.$t('device.getFirmwareTypeFailed'))
|
||||
}
|
||||
},
|
||||
handlePageSizeChange(val) {
|
||||
@@ -218,15 +218,15 @@ export default {
|
||||
const selectedDevices = this.paginatedDeviceList.filter(device => device.selected);
|
||||
if (selectedDevices.length === 0) {
|
||||
this.$message.warning({
|
||||
message: '请至少选择一条记录',
|
||||
message: this.$t('device.selectAtLeastOne'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.$confirm(`确认要解绑选中的 ${selectedDevices.length} 台设备吗?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('device.confirmBatchUnbind').replace('{count}', selectedDevices.length), this.$t('message.warning'), {
|
||||
confirmButtonText: this.$t('button.ok'),
|
||||
cancelButtonText: this.$t('button.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const deviceIds = selectedDevices.map(device => device.device_id);
|
||||
@@ -240,7 +240,7 @@ export default {
|
||||
if (data.code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(data.msg || '解绑失败');
|
||||
reject(data.msg || this.$t('device.bindFailed'));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -248,14 +248,14 @@ export default {
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
this.$message.success({
|
||||
message: `成功解绑 ${deviceIds.length} 台设备`,
|
||||
message: this.$t('device.batchUnbindSuccess').replace('{count}', deviceIds.length),
|
||||
showClose: true
|
||||
});
|
||||
this.fetchBindDevices(this.currentAgentId);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error({
|
||||
message: error || '批量解绑过程中出现错误',
|
||||
message: error || this.$t('device.batchUnbindError'),
|
||||
showClose: true
|
||||
});
|
||||
});
|
||||
@@ -271,7 +271,7 @@ export default {
|
||||
|
||||
const text = (row.remark || '').trim();
|
||||
if (text.length > 64) {
|
||||
this.$message.warning('备注不能超过 64 字符');
|
||||
this.$message.warning(this.$t('device.remarkTooLong'));
|
||||
return;
|
||||
}
|
||||
if (text === row._originalRemark) {
|
||||
@@ -282,10 +282,10 @@ export default {
|
||||
this.updateDeviceInfo(row.device_id, { alias: text }, (ok, resp) => {
|
||||
if (ok) {
|
||||
row._originalRemark = text;
|
||||
this.$message.success('备注已保存');
|
||||
this.$message.success(this.$t('device.remarkSaved'));
|
||||
} else {
|
||||
row.remark = row._originalRemark;
|
||||
this.$message.error(resp.msg || '备注保存失败');
|
||||
this.$message.error(resp.msg || this.$t('device.remarkSaveFailed'));
|
||||
}
|
||||
row._submitting = false;
|
||||
});
|
||||
@@ -303,23 +303,23 @@ export default {
|
||||
this.submitRemark(row);
|
||||
},
|
||||
handleUnbind(device_id) {
|
||||
this.$confirm('确认要解绑该设备吗?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('device.confirmUnbind'), this.$t('message.warning'), {
|
||||
confirmButtonText: this.$t('button.ok'),
|
||||
cancelButtonText: this.$t('button.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
Api.device.unbindDevice(device_id, ({data}) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: '设备解绑成功',
|
||||
showClose: true
|
||||
});
|
||||
this.$message.success({
|
||||
message: this.$t('device.unbindSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.fetchBindDevices(this.$route.query.agentId);
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '设备解绑失败',
|
||||
showClose: true
|
||||
});
|
||||
this.$message.error({
|
||||
message: data.msg || this.$t('device.unbindFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -363,7 +363,7 @@ export default {
|
||||
this.activeSearchKeyword = "";
|
||||
this.searchKeyword = "";
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取设备列表失败');
|
||||
this.$message.error(data.msg || this.$t('device.getListFailed'));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -385,11 +385,11 @@ export default {
|
||||
handleOtaSwitchChange(row) {
|
||||
this.updateDeviceInfo(row.device_id, {autoUpdate: row.otaSwitch ? 1 : 0}, (result, {msg}) => {
|
||||
if (result) {
|
||||
this.$message.success(row.otaSwitch ? '已设置成自动升级' : '已关闭自动升级');
|
||||
this.$message.success(row.otaSwitch ? this.$t('device.autoUpdateEnabled') : this.$t('device.autoUpdateDisabled'));
|
||||
return;
|
||||
}
|
||||
row.otaSwitch = !row.otaSwitch
|
||||
this.$message.error(msg || '操作失败')
|
||||
this.$message.error(msg || this.$t('message.error'))
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">字典管理</h2>
|
||||
<h2 class="page-title">{{ $t('dictManagement.pageTitle') }}</h2>
|
||||
<div class="action-group">
|
||||
<div class="search-group">
|
||||
<el-input placeholder="请输入字典值标签查询" v-model="search" class="search-input" clearable
|
||||
<el-input :placeholder="$t('dictManagement.searchPlaceholder')" v-model="search" class="search-input" clearable
|
||||
@keyup.enter.native="handleSearch" style="width: 240px" />
|
||||
<el-button class="btn-search" @click="handleSearch">
|
||||
搜索
|
||||
{{ $t('dictManagement.search') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,22 +21,22 @@
|
||||
<!-- 左侧字典类型列表 -->
|
||||
<div class="dict-type-panel">
|
||||
<div class="dict-type-header">
|
||||
<el-button type="success" size="mini" @click="showAddDictTypeDialog">新增字典类型</el-button>
|
||||
<el-button type="success" size="mini" @click="showAddDictTypeDialog">{{ $t('dictManagement.addDictType') }}</el-button>
|
||||
<el-button type="danger" size="mini" @click="batchDeleteDictType"
|
||||
:disabled="selectedDictTypes.length === 0">
|
||||
批量删除字典类型
|
||||
{{ $t('dictManagement.batchDeleteDictType') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table ref="dictTypeTable" :data="dictTypeList" style="width: 100%" v-loading="dictTypeLoading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)" @row-click="handleDictTypeRowClick"
|
||||
@selection-change="handleDictTypeSelectionChange" :row-class-name="tableRowClassName"
|
||||
class="dict-type-table">
|
||||
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||
<el-table-column label="字典类型名称" prop="dictName" align="center"></el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
class="dict-type-table" :header-cell-class-name="headerCellClassName">
|
||||
<el-table-column type="selection" width="70" align="center" :cell-class-name="selectionCellClassName"></el-table-column>
|
||||
<el-table-column :label="$t('dictManagement.dictTypeName')" prop="dictName" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('dictManagement.operation')" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click.stop="editDictType(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" @click.stop="editDictType(scope.row)">{{ $t('dictManagement.edit') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -50,23 +50,23 @@
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)" class="data-table"
|
||||
header-row-class-name="table-header">
|
||||
<el-table-column label="选择" align="center" width="55">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="70">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字典标签" prop="dictLabel" align="center"></el-table-column>
|
||||
<el-table-column label="字典值" prop="dictValue" align="center"></el-table-column>
|
||||
<el-table-column label="排序" prop="sort" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center" width="180px">
|
||||
<el-table-column :label="$t('dictManagement.dictLabel')" prop="dictLabel" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('dictManagement.dictValue')" prop="dictValue" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('dictManagement.sort')" prop="sort" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('dictManagement.operation')" align="center" width="180px">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="editDictData(scope.row)"
|
||||
class="edit-btn">
|
||||
修改
|
||||
{{ $t('dictManagement.edit') }}
|
||||
</el-button>
|
||||
<el-button type="text" size="mini" @click="deleteDictData(scope.row)"
|
||||
class="delete-btn">
|
||||
删除
|
||||
{{ $t('dictManagement.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -74,36 +74,36 @@
|
||||
<div class="table-footer">
|
||||
<div class="batch-actions">
|
||||
<el-button size="mini" type="primary" @click="selectAllDictData">
|
||||
{{ isAllDictDataSelected ? '取消全选' : '全选' }}
|
||||
{{ isAllDictDataSelected ? $t('dictManagement.deselectAll') : $t('dictManagement.selectAll') }}
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" @click="showAddDictDataDialog" class="add-btn">
|
||||
新增字典数据
|
||||
{{ $t('dictManagement.addDictData') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDeleteDictData">
|
||||
批量删除字典数据
|
||||
{{ $t('dictManagement.batchDeleteDictData') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`"
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('dictManagement.itemsPerPage', { items: item })"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
首页
|
||||
{{ $t('dictManagement.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
上一页
|
||||
{{ $t('dictManagement.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
下一页
|
||||
{{ $t('dictManagement.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<span class="total-text">{{ $t('dictManagement.totalRecords', { total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -211,7 +211,7 @@ export default {
|
||||
return row === this.selectedDictType ? 'current-row' : ''
|
||||
},
|
||||
showAddDictTypeDialog() {
|
||||
this.dictTypeDialogTitle = '新增字典类型'
|
||||
this.dictTypeDialogTitle = this.$t('dictManagement.addDictType')
|
||||
this.dictTypeForm = {
|
||||
id: null,
|
||||
dictName: '',
|
||||
@@ -220,7 +220,7 @@ export default {
|
||||
this.dictTypeDialogVisible = true
|
||||
},
|
||||
editDictType(row) {
|
||||
this.dictTypeDialogTitle = '编辑字典类型'
|
||||
this.dictTypeDialogTitle = this.$t('dictManagement.editDictType')
|
||||
this.dictTypeForm = { ...row }
|
||||
this.dictTypeDialogVisible = true
|
||||
},
|
||||
@@ -228,7 +228,7 @@ export default {
|
||||
const api = formData.id ? dictApi.updateDictType : dictApi.addDictType
|
||||
api(formData, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('保存成功')
|
||||
this.$message.success(this.$t('dictManagement.saveSuccess'))
|
||||
this.dictTypeDialogVisible = false
|
||||
this.loadDictTypeList()
|
||||
}
|
||||
@@ -236,19 +236,19 @@ export default {
|
||||
},
|
||||
batchDeleteDictType() {
|
||||
if (this.selectedDictTypes.length === 0) {
|
||||
this.$message.warning('请选择要删除的字典类型')
|
||||
this.$message.warning(this.$t('dictManagement.selectDictTypeToDelete'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('确定要删除选中的字典类型吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('dictManagement.confirmDeleteDictType'), this.$t('dictManagement.confirm'), {
|
||||
confirmButtonText: this.$t('dictManagement.confirm'),
|
||||
cancelButtonText: this.$t('dictManagement.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const ids = this.selectedDictTypes.map(item => item.id)
|
||||
dictApi.deleteDictType(ids, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.$message.success(this.$t('dictManagement.deleteSuccess'))
|
||||
this.loadDictTypeList()
|
||||
}
|
||||
})
|
||||
@@ -273,8 +273,8 @@ export default {
|
||||
}))
|
||||
this.total = data.data.total
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取字典数据失败')
|
||||
}
|
||||
this.$message.error(data.msg || this.$t('dictManagement.getDictDataFailed'))
|
||||
}
|
||||
this.dictDataLoading = false
|
||||
})
|
||||
},
|
||||
@@ -286,10 +286,10 @@ export default {
|
||||
},
|
||||
showAddDictDataDialog() {
|
||||
if (!this.selectedDictType) {
|
||||
this.$message.warning('请先选择字典类型')
|
||||
this.$message.warning(this.$t('dictManagement.selectDictTypeFirst'))
|
||||
return
|
||||
}
|
||||
this.dictDataDialogTitle = '新增字典数据'
|
||||
this.dictDataDialogTitle = this.$t('dictManagement.addDictData')
|
||||
this.dictDataForm = {
|
||||
id: null,
|
||||
dictTypeId: this.selectedDictType.id,
|
||||
@@ -300,7 +300,7 @@ export default {
|
||||
this.dictDataDialogVisible = true
|
||||
},
|
||||
editDictData(row) {
|
||||
this.dictDataDialogTitle = '编辑字典数据'
|
||||
this.dictDataDialogTitle = this.$t('dictManagement.editDictData')
|
||||
this.dictDataForm = { ...row }
|
||||
this.dictDataDialogVisible = true
|
||||
},
|
||||
@@ -308,21 +308,21 @@ export default {
|
||||
const api = formData.id ? dictApi.updateDictData : dictApi.addDictData
|
||||
api(formData, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('保存成功')
|
||||
this.$message.success(this.$t('dictManagement.saveSuccess'))
|
||||
this.dictDataDialogVisible = false
|
||||
this.loadDictDataList(formData.dictTypeId)
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteDictData(row) {
|
||||
this.$confirm('确定要删除该字典数据吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('dictManagement.confirmDeleteDictData'), this.$t('dictManagement.confirm'), {
|
||||
confirmButtonText: this.$t('dictManagement.confirm'),
|
||||
cancelButtonText: this.$t('dictManagement.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
dictApi.deleteDictData([row.id], ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.$message.success(this.$t('dictManagement.deleteSuccess'))
|
||||
this.loadDictDataList(row.dictTypeId)
|
||||
}
|
||||
})
|
||||
@@ -331,19 +331,19 @@ export default {
|
||||
batchDeleteDictData() {
|
||||
const selectedRows = this.dictDataList.filter(row => row.selected)
|
||||
if (selectedRows.length === 0) {
|
||||
this.$message.warning('请选择要删除的字典数据')
|
||||
this.$message.warning(this.$t('dictManagement.selectDictDataToDelete'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm(`确定要删除选中的${selectedRows.length}个字典数据吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('dictManagement.confirmBatchDeleteDictData', { count: selectedRows.length }), this.$t('dictManagement.confirm'), {
|
||||
confirmButtonText: this.$t('dictManagement.confirm'),
|
||||
cancelButtonText: this.$t('dictManagement.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const ids = selectedRows.map(item => item.id)
|
||||
dictApi.deleteDictData(ids, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.$message.success(this.$t('dictManagement.deleteSuccess'))
|
||||
this.loadDictDataList(this.selectedDictType.id)
|
||||
}
|
||||
})
|
||||
@@ -363,6 +363,14 @@ export default {
|
||||
this.currentPage = 1;
|
||||
this.loadDictDataList(this.selectedDictType?.id);
|
||||
},
|
||||
|
||||
// 更新选择列表头翻译文本
|
||||
updateSelectionHeaderText() {
|
||||
const thElement = document.querySelector(`.el-table__header th:nth-child(1) .cell`);
|
||||
if (thElement) {
|
||||
thElement.setAttribute('data-content', this.$t('modelConfig.select'));
|
||||
}
|
||||
},
|
||||
goFirst() {
|
||||
this.currentPage = 1;
|
||||
this.loadDictDataList(this.selectedDictType?.id);
|
||||
@@ -382,8 +390,38 @@ export default {
|
||||
goToPage(page) {
|
||||
this.currentPage = page;
|
||||
this.loadDictDataList(this.selectedDictType?.id);
|
||||
},
|
||||
// 表头单元格样式类名,用于选择列
|
||||
headerCellClassName({ columnIndex }) {
|
||||
if (columnIndex === 0) {
|
||||
return 'custom-selection-header';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// 单元格样式类名,用于设置选择列表头的翻译文本
|
||||
selectionCellClassName({ row, column, rowIndex, columnIndex }) {
|
||||
// 只对表头行设置data-content
|
||||
if (rowIndex === undefined) {
|
||||
setTimeout(() => {
|
||||
this.updateSelectionHeaderText();
|
||||
}, 0);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 在组件挂载后确保表头翻译文本正确显示
|
||||
setTimeout(() => {
|
||||
this.updateSelectionHeaderText();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
updated() {
|
||||
// 在组件更新后重新设置表头翻译文本
|
||||
this.updateSelectionHeaderText();
|
||||
},
|
||||
|
||||
computed: {
|
||||
pageCount() {
|
||||
return Math.ceil(this.total / this.pageSize);
|
||||
@@ -823,6 +861,29 @@ export default {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table .custom-selection-header .cell .el-checkbox__inner {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table .custom-selection-header .cell::before {
|
||||
content: attr(data-content);
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
color: black;
|
||||
margin-top: 0;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-selection-header .cell {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.el-table thead) {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">{{ modelTypeText }}</h2>
|
||||
<h2 class="page-title">{{ $t('modelConfig.' + activeTab) }}</h2>
|
||||
<div class="action-group">
|
||||
<div class="search-group">
|
||||
<el-input placeholder="请输入模型名称查询" v-model="search" class="search-input" clearable
|
||||
<el-input :placeholder="$t('modelConfig.searchPlaceholder')" v-model="search" class="search-input" clearable
|
||||
@keyup.enter.native="handleSearch" style="width: 240px" />
|
||||
<el-button class="btn-search" @click="handleSearch">
|
||||
搜索
|
||||
{{ $t('modelConfig.search') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,73 +22,73 @@
|
||||
<el-menu :default-active="activeTab" class="nav-panel" @select="handleMenuSelect"
|
||||
style="background-size: cover; background-position: center;">
|
||||
<el-menu-item index="vad">
|
||||
<span class="menu-text">语言活动检测</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.vad') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="asr">
|
||||
<span class="menu-text">语音识别</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.asr') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="llm">
|
||||
<span class="menu-text">大语言模型</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.llm') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="vllm">
|
||||
<span class="menu-text">视觉大模型</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.vllm') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="intent">
|
||||
<span class="menu-text">意图识别</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.intent') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="tts">
|
||||
<span class="menu-text">语音合成</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.tts') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="memory">
|
||||
<span class="menu-text">记忆</span>
|
||||
<span class="menu-text">{{ $t('modelConfig.memory') }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="content-area">
|
||||
<el-card class="model-card" shadow="never">
|
||||
<el-table ref="modelTable" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中"
|
||||
<el-table ref="modelTable" style="width: 100%" v-loading="loading" :element-loading-text="$t('modelConfig.loading')"
|
||||
element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.7)"
|
||||
:header-cell-style="{ background: 'transparent' }" :data="modelList" class="data-table"
|
||||
header-row-class-name="table-header" :header-cell-class-name="headerCellClassName"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||
<el-table-column label="模型ID" prop="id" align="center"></el-table-column>
|
||||
<el-table-column label="模型名称" prop="modelName" align="center"></el-table-column>
|
||||
<el-table-column label="提供商" align="center">
|
||||
<el-table-column type="selection" width="55" align="center" :cell-class-name="selectionCellClassName"></el-table-column>
|
||||
<el-table-column :label="$t('modelConfig.modelId')" prop="id" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('modelConfig.modelName')" prop="modelName" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('modelConfig.provider')" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.configJson.type || '未知' }}
|
||||
{{ scope.row.configJson.type || $t('modelConfig.unknown') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否启用" align="center">
|
||||
<el-table-column :label="$t('modelConfig.isEnabled')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.isEnabled" class="custom-switch" :active-value="1" :inactive-value="0"
|
||||
@change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否默认" align="center">
|
||||
<el-table-column :label="$t('modelConfig.isDefault')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.isDefault" class="custom-switch" :active-value="1" :inactive-value="0"
|
||||
@change="handleDefaultChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="activeTab === 'tts'" label="音色管理" align="center">
|
||||
<el-table-column v-if="activeTab === 'tts'" :label="$t('modelConfig.voiceManagement')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="openTtsDialog(scope.row)" class="voice-management-btn">
|
||||
音色管理
|
||||
{{ $t('modelConfig.voiceManagement') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="180px">
|
||||
<el-table-column :label="$t('modelConfig.action')" align="center" width="180px">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="editModel(scope.row)" class="edit-btn">
|
||||
修改
|
||||
{{ $t('modelConfig.edit') }}
|
||||
</el-button>
|
||||
<el-button type="text" size="mini" @click="duplicateModel(scope.row)" class="edit-btn">
|
||||
创建副本
|
||||
{{ $t('modelConfig.duplicate') }}
|
||||
</el-button>
|
||||
<el-button type="text" size="mini" @click="deleteModel(scope.row)" class="delete-btn">
|
||||
删除
|
||||
{{ $t('modelConfig.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -97,32 +97,32 @@
|
||||
<div class="batch-actions">
|
||||
<el-button size="mini" type="primary" @click="selectAll">
|
||||
{{ isAllSelected ?
|
||||
'取消全选' : '全选' }}
|
||||
$t('modelConfig.deselectAll') : $t('modelConfig.selectAll') }}
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" @click="addModel" class="add-btn">
|
||||
新增
|
||||
{{ $t('modelConfig.add') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDelete">
|
||||
删除
|
||||
{{ $t('modelConfig.delete') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`" :value="item">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('modelConfig.itemsPerPage', { items: item })" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">首页</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">上一页</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">{{ $t('modelConfig.firstPage') }}</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">{{ $t('modelConfig.prevPage') }}</button>
|
||||
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">下一页</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">{{ $t('modelConfig.nextPage') }}</button>
|
||||
<span class="total-text">{{ $t('modelConfig.totalRecords', { total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -173,19 +173,22 @@ export default {
|
||||
created() {
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 在组件挂载后确保表头翻译文本正确显示
|
||||
setTimeout(() => {
|
||||
this.updateSelectionHeaderText();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
updated() {
|
||||
// 在组件更新后重新设置表头翻译文本
|
||||
this.updateSelectionHeaderText();
|
||||
},
|
||||
|
||||
computed: {
|
||||
modelTypeText() {
|
||||
const map = {
|
||||
vad: '语言活动检测模型(VAD)',
|
||||
asr: '语音识别模型(ASR)',
|
||||
llm: '大语言模型(LLM)',
|
||||
vllm: '视觉大模型(VLLM)',
|
||||
intent: '意图识别模型(Intent)',
|
||||
tts: '语音合成模型(TTS)',
|
||||
memory: '记忆模型(Memory)'
|
||||
}
|
||||
return map[this.activeTab] || '模型配置'
|
||||
return this.$t('modelConfig.' + this.activeTab) || this.$t('modelConfig.modelConfig')
|
||||
},
|
||||
pageCount() {
|
||||
return Math.ceil(this.total / this.pageSize);
|
||||
@@ -208,6 +211,13 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 更新选择列表头翻译文本
|
||||
updateSelectionHeaderText() {
|
||||
const thElement = document.querySelector(`.el-table__header th:nth-child(1) .cell`);
|
||||
if (thElement) {
|
||||
thElement.setAttribute('data-content', this.$t('modelConfig.select'));
|
||||
}
|
||||
},
|
||||
handlePageSizeChange(val) {
|
||||
this.pageSize = val;
|
||||
this.currentPage = 1;
|
||||
@@ -224,6 +234,19 @@ export default {
|
||||
}
|
||||
return '';
|
||||
},
|
||||
selectionCellClassName({ row, column, rowIndex, columnIndex }) {
|
||||
// 只对表头行设置data-content
|
||||
if (rowIndex === undefined) {
|
||||
// 使用setTimeout确保DOM已经渲染完成
|
||||
setTimeout(() => {
|
||||
const thElement = document.querySelector(`.el-table__header th:nth-child(1) .cell`);
|
||||
if (thElement) {
|
||||
thElement.setAttribute('data-content', this.$t('modelConfig.select'));
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
handleMenuSelect(index) {
|
||||
this.activeTab = index;
|
||||
this.currentPage = 1; // 重置到第一页
|
||||
@@ -237,13 +260,13 @@ export default {
|
||||
// 批量删除
|
||||
batchDelete() {
|
||||
if (this.selectedModels.length === 0) {
|
||||
this.$message.warning('请先选择要删除的模型')
|
||||
this.$message.warning(this.$t('modelConfig.selectModelsFirst'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('确定要删除选中的模型吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('modelConfig.confirmBatchDelete'), this.$t('common.prompt'), {
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const deletePromises = this.selectedModels.map(model =>
|
||||
@@ -258,19 +281,19 @@ export default {
|
||||
Promise.all(deletePromises).then(results => {
|
||||
if (results.every(Boolean)) {
|
||||
this.$message.success({
|
||||
message: '批量删除成功',
|
||||
message: this.$t('modelConfig.batchDeleteSuccess'),
|
||||
showClose: true
|
||||
})
|
||||
this.loadData()
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: '部分删除失败',
|
||||
message: this.$t('modelConfig.partialDeleteFailed'),
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message.info('已取消删除')
|
||||
this.$message.info(this.$t('modelConfig.deleteCancelled'))
|
||||
})
|
||||
},
|
||||
addModel() {
|
||||
@@ -287,9 +310,9 @@ export default {
|
||||
},
|
||||
// 删除单个模型
|
||||
deleteModel(model) {
|
||||
this.$confirm('确定要删除该模型吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('modelConfig.confirmDelete'), this.$t('common.prompt'), {
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
Api.model.deleteModel(
|
||||
@@ -297,20 +320,20 @@ export default {
|
||||
({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: '删除成功',
|
||||
message: this.$t('modelConfig.deleteSuccess'),
|
||||
showClose: true
|
||||
})
|
||||
this.loadData()
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '删除失败',
|
||||
message: data.msg || this.$t('modelConfig.deleteFailed'),
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}).catch(() => {
|
||||
this.$message.info('已取消删除')
|
||||
this.$message.info(this.$t('modelConfig.deleteCancelled'))
|
||||
})
|
||||
},
|
||||
handleCurrentChange(page) {
|
||||
@@ -325,11 +348,11 @@ export default {
|
||||
Api.model.addModel({modelType, provideCode, formData},
|
||||
({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('创建副本成功');
|
||||
this.$message.success(this.$t('modelConfig.duplicateSuccess'));
|
||||
this.loadData();
|
||||
this.editDialogVisible = false;
|
||||
} else {
|
||||
this.$message.error(data.msg || '创建副本失败');
|
||||
this.$message.error(data.msg || this.$t('modelConfig.duplicateFailed'));
|
||||
}
|
||||
done && done(); // 调用done回调关闭加载状态
|
||||
})
|
||||
@@ -339,11 +362,11 @@ export default {
|
||||
{ modelType, provideCode, id, formData },
|
||||
({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('保存成功');
|
||||
this.$message.success(this.$t('modelConfig.saveSuccess'));
|
||||
this.loadData();
|
||||
this.editDialogVisible = false;
|
||||
} else {
|
||||
this.$message.error(data.msg || '保存失败');
|
||||
this.$message.error(data.msg || this.$t('modelConfig.saveFailed'));
|
||||
}
|
||||
done && done(); // 调用done回调关闭加载状态
|
||||
}
|
||||
@@ -381,13 +404,13 @@ export default {
|
||||
Api.model.addModel(params, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: '新增成功',
|
||||
message: this.$t('modelConfig.addSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.loadData();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '新增失败',
|
||||
message: data.msg || this.$t('modelConfig.addFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -432,7 +455,7 @@ export default {
|
||||
this.modelList = data.data.list;
|
||||
this.total = data.data.total;
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取模型列表失败');
|
||||
this.$message.error(data.msg || this.$t('modelConfig.fetchModelsFailed'));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -448,13 +471,13 @@ export default {
|
||||
newStatus,
|
||||
({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(newStatus === 1 ? '启用成功' : '禁用成功')
|
||||
this.$message.success(newStatus === 1 ? this.$t('modelConfig.enableSuccess') : this.$t('modelConfig.disableSuccess'))
|
||||
// 保持新状态
|
||||
model.isEnabled = newStatus
|
||||
} else {
|
||||
// 操作失败时恢复原状态
|
||||
model.isEnabled = originalStatus
|
||||
this.$message.error(data.msg || '操作失败')
|
||||
this.$message.error(data.msg || this.$t('modelConfig.operationFailed'))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -462,7 +485,7 @@ export default {
|
||||
handleDefaultChange(model) {
|
||||
Api.model.setDefaultModel(model.id, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success('设置默认模型成功')
|
||||
this.$message.success(this.$t('modelConfig.setDefaultSuccess'))
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
@@ -764,14 +787,25 @@ export default {
|
||||
}
|
||||
|
||||
::v-deep .el-table .custom-selection-header .cell::before {
|
||||
content: '选择';
|
||||
content: attr(data-content);
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 0;
|
||||
line-height: 32px; /* 设置合适的行高,确保文本完整显示 */
|
||||
color: black;
|
||||
margin-top: 23px;
|
||||
margin-top: 0; /* 移除可能导致偏移的上边距 */
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-selection-header .cell {
|
||||
position: relative;
|
||||
}
|
||||
/* 已移除可能影响文本显示的空伪元素 */
|
||||
|
||||
::v-deep .el-table__body .el-checkbox__inner {
|
||||
display: inline-block !important;
|
||||
background: #e6edfa;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">固件管理</h2>
|
||||
<h2 class="page-title">{{ $t('otaManagement.firmwareManagement') }}</h2>
|
||||
<div class="right-operations">
|
||||
<el-input placeholder="请输入固件名称查询" v-model="searchName" class="search-input"
|
||||
<el-input :placeholder="$t('otaManagement.searchPlaceholder')" v-model="searchName" class="search-input"
|
||||
@keyup.enter.native="handleSearch" clearable />
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button class="btn-search" @click="handleSearch">{{ $t('otaManagement.search') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,44 +16,44 @@
|
||||
<div class="content-area">
|
||||
<el-card class="params-card" shadow="never">
|
||||
<el-table ref="paramsTable" :data="paramsList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-text="Loading" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)"
|
||||
:header-cell-class-name="headerCellClassName">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="固件名称" prop="firmwareName" align="center"></el-table-column>
|
||||
<el-table-column label="固件类型" prop="type" align="center">
|
||||
<el-table-column :label="$t('otaManagement.firmwareName')" prop="firmwareName" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('otaManagement.firmwareType')" prop="type" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getFirmwareTypeName(scope.row.type) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="版本号" prop="version" align="center"></el-table-column>
|
||||
<el-table-column label="文件大小" prop="size" align="center">
|
||||
<el-table-column :label="$t('otaManagement.version')" prop="version" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('otaManagement.fileSize')" prop="size" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ formatFileSize(scope.row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" align="center"
|
||||
<el-table-column :label="$t('otaManagement.remark')" prop="remark" align="center"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="创建时间" prop="createDate" align="center">
|
||||
<el-table-column :label="$t('otaManagement.createTime')" prop="createDate" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ formatDate(scope.row.createDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" prop="updateDate" align="center">
|
||||
<el-table-column :label="$t('otaManagement.updateTime')" prop="updateDate" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ formatDate(scope.row.updateDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column :label="$t('otaManagement.action')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text"
|
||||
@click="downloadFirmware(scope.row)">下载</el-button>
|
||||
<el-button size="mini" type="text" @click="editParam(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteParam(scope.row)">删除</el-button>
|
||||
@click="downloadFirmware(scope.row)">{{ $t('otaManagement.download') }}</el-button>
|
||||
<el-button size="mini" type="text" @click="editParam(scope.row)">{{ $t('otaManagement.edit') }}</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteParam(scope.row)">{{ $t('otaManagement.delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -61,33 +61,33 @@
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
{{ isAllSelected ? $t('otaManagement.deselectAll') : $t('otaManagement.selectAll') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="showAddDialog"
|
||||
style="background: #5bc98c;border: None;">新增</el-button>
|
||||
style="background: #5bc98c;border: None;">{{ $t('otaManagement.addNew') }}</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete"
|
||||
@click="deleteSelectedParams">删除</el-button>
|
||||
@click="deleteSelectedParams">{{ $t('otaManagement.delete') }}</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`"
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('otaManagement.itemsPerPage', { items: item })"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
首页
|
||||
{{ $t('otaManagement.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
上一页
|
||||
{{ $t('otaManagement.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
下一页
|
||||
{{ $t('otaManagement.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<span class="total-text">{{ $t('otaManagement.totalRecords', { total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<!-- 新增/编辑固件对话框 -->
|
||||
<firmware-dialog :title="dialogTitle" :visible.sync="dialogVisible" :form="firmwareForm"
|
||||
:firmware-types="firmwareTypes" @submit="handleSubmit" @cancel="dialogVisible = false" />
|
||||
:firmware-types="firmwareTypes" @submit="handleSubmit" @cancel="dialogVisible = false" />
|
||||
<el-footer>
|
||||
<version-footer />
|
||||
</el-footer>
|
||||
@@ -110,6 +110,7 @@ import FirmwareDialog from "@/components/FirmwareDialog.vue";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import VersionFooter from "@/components/VersionFooter.vue";
|
||||
import { formatDate, formatFileSize } from "@/utils/format";
|
||||
import i18n from '@/i18n';
|
||||
|
||||
export default {
|
||||
components: { HeaderBar, FirmwareDialog, VersionFooter },
|
||||
@@ -193,7 +194,7 @@ export default {
|
||||
this.paramsList = [];
|
||||
this.total = 0;
|
||||
this.$message.error({
|
||||
message: res?.data?.msg || '获取固件列表失败',
|
||||
message: res?.data?.msg || this.$t('otaManagement.getFirmwareListFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -210,7 +211,7 @@ export default {
|
||||
});
|
||||
},
|
||||
showAddDialog() {
|
||||
this.dialogTitle = "新增固件";
|
||||
this.dialogTitle = this.$t('otaManagement.addFirmware');
|
||||
// 完全重置表单数据
|
||||
this.firmwareForm = {
|
||||
id: null,
|
||||
@@ -230,7 +231,7 @@ export default {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
editParam(row) {
|
||||
this.dialogTitle = "编辑固件";
|
||||
this.dialogTitle = this.$t('otaManagement.editFirmware');
|
||||
this.firmwareForm = { ...row };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
@@ -241,14 +242,14 @@ export default {
|
||||
res = res.data;
|
||||
if (res.code === 0) {
|
||||
this.$message.success({
|
||||
message: "修改成功",
|
||||
message: this.$t('otaManagement.updateSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.fetchFirmwareList();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: res.msg || "修改失败",
|
||||
message: res.msg || this.$t('otaManagement.updateFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -259,14 +260,14 @@ export default {
|
||||
res = res.data;
|
||||
if (res.code === 0) {
|
||||
this.$message.success({
|
||||
message: "新增成功",
|
||||
message: this.$t('otaManagement.addSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.fetchFirmwareList();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: res.msg || "新增失败",
|
||||
message: res.msg || this.$t('otaManagement.addFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -278,7 +279,7 @@ export default {
|
||||
const selectedRows = this.firmwareList.filter(row => row.selected);
|
||||
if (selectedRows.length === 0) {
|
||||
this.$message.warning({
|
||||
message: "请先选择需要删除的固件",
|
||||
message: this.$t('otaManagement.selectFirmwareFirst'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
@@ -291,23 +292,23 @@ export default {
|
||||
|
||||
if (Array.isArray(row) && row.length === 0) {
|
||||
this.$message.warning({
|
||||
message: "请先选择需要删除的参数",
|
||||
message: this.$t('otaManagement.selectFirmwareFirst'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const paramCount = params.length;
|
||||
this.$confirm(`确定要删除选中的${paramCount}个固件吗?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('otaManagement.confirmBatchDelete', { paramCount }), 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true
|
||||
}).then(() => {
|
||||
const ids = params.map(param => param.id);
|
||||
if (ids.some(id => !id)) {
|
||||
this.$message.error({
|
||||
message: '存在无效的参数ID',
|
||||
message: this.$t('otaManagement.invalidFirmwareId'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
@@ -317,13 +318,13 @@ export default {
|
||||
res = res.data;
|
||||
if (res.code === 0) {
|
||||
this.$message.success({
|
||||
message: `成功删除${paramCount}个固件`,
|
||||
message: this.$t('otaManagement.batchDeleteSuccess', { paramCount }),
|
||||
showClose: true
|
||||
});
|
||||
this.fetchFirmwareList();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: res.msg || '删除失败,请重试',
|
||||
message: res.msg || this.$t('otaManagement.deleteFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -332,13 +333,13 @@ export default {
|
||||
if (action === 'cancel') {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '已取消删除操作',
|
||||
message: this.$t('otaManagement.operationCancelled'),
|
||||
duration: 1000
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '操作已关闭',
|
||||
message: this.$t('otaManagement.operationClosed'),
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
@@ -372,7 +373,7 @@ export default {
|
||||
},
|
||||
downloadFirmware(firmware) {
|
||||
if (!firmware || !firmware.id) {
|
||||
this.$message.error('固件信息不完整');
|
||||
this.$message.error(this.$t('otaManagement.incompleteFirmwareInfo'));
|
||||
return;
|
||||
}
|
||||
// 先获取下载链接
|
||||
@@ -382,7 +383,7 @@ export default {
|
||||
const baseUrl = process.env.VUE_APP_API_BASE_URL || '';
|
||||
window.open(`${window.location.origin}${baseUrl}/otaMag/download/${uuid}`);
|
||||
} else {
|
||||
this.$message.error('获取下载链接失败');
|
||||
this.$message.error(this.$t('otaManagement.getDownloadUrlFailed'));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -394,7 +395,7 @@ export default {
|
||||
this.firmwareTypes = res.data
|
||||
} catch (error) {
|
||||
console.error('获取固件类型失败:', error)
|
||||
this.$message.error(error.message || '获取固件类型失败')
|
||||
this.$message.error(error.message || this.$t('otaManagement.getFirmwareTypesFailed'))
|
||||
}
|
||||
},
|
||||
getFirmwareTypeName(type) {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">参数管理</h2>
|
||||
<h2 class="page-title">{{ $t('paramManagement.pageTitle') }}</h2>
|
||||
<div class="right-operations">
|
||||
<el-input placeholder="请输入参数编码或备注查询" v-model="searchCode" class="search-input"
|
||||
<el-input :placeholder="$t('paramManagement.searchPlaceholder')" v-model="searchCode" class="search-input"
|
||||
@keyup.enter.native="handleSearch" clearable />
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button class="btn-search" @click="handleSearch">{{ $t('paramManagement.search') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,33 +16,33 @@
|
||||
<div class="content-area">
|
||||
<el-card class="params-card" shadow="never">
|
||||
<el-table ref="paramsTable" :data="paramsList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-text="Loading" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)"
|
||||
:header-cell-class-name="headerCellClassName">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参数编码" prop="paramCode" align="center"></el-table-column>
|
||||
<el-table-column label="参数值" prop="paramValue" align="center" show-overflow-tooltip>
|
||||
<el-table-column :label="$t('paramManagement.paramCode')" prop="paramCode" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('paramManagement.paramValue')" prop="paramValue" align="center" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<div v-if="isSensitiveParam(scope.row.paramCode)">
|
||||
<span v-if="!scope.row.showValue">{{ maskSensitiveValue(scope.row.paramValue)
|
||||
}}</span>
|
||||
<span v-else>{{ scope.row.paramValue }}</span>
|
||||
<el-button size="mini" type="text" @click="toggleSensitiveValue(scope.row)">
|
||||
{{ scope.row.showValue ? '隐藏' : '查看' }}
|
||||
{{ scope.row.showValue ? $t('paramManagement.hide') : $t('paramManagement.view') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<span v-else>{{ scope.row.paramValue }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column :label="$t('paramManagement.remark')" prop="remark" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('paramManagement.operation')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="editParam(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteParam(scope.row)">删除</el-button>
|
||||
<el-button size="mini" type="text" @click="editParam(scope.row)">{{ $t('paramManagement.edit') }}</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteParam(scope.row)">{{ $t('paramManagement.delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -50,32 +50,32 @@
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
{{ isAllSelected ? $t('paramManagement.deselectAll') : $t('paramManagement.selectAll') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="showAddDialog">新增</el-button>
|
||||
<el-button size="mini" type="success" @click="showAddDialog">{{ $t('paramManagement.add') }}</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete"
|
||||
@click="deleteSelectedParams">删除</el-button>
|
||||
@click="deleteSelectedParams">{{ $t('paramManagement.delete') }}</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`"
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}${$t('paramManagement.itemsPerPage')}`"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
首页
|
||||
{{ $t('paramManagement.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
上一页
|
||||
{{ $t('paramManagement.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
下一页
|
||||
{{ $t('paramManagement.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<span class="total-text">{{ $t('paramManagement.totalRecords', { total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -97,6 +97,7 @@ import Api from "@/apis/api";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import ParamDialog from "@/components/ParamDialog.vue";
|
||||
import VersionFooter from "@/components/VersionFooter.vue";
|
||||
import i18n from '@/i18n';
|
||||
export default {
|
||||
components: { HeaderBar, ParamDialog, VersionFooter },
|
||||
data() {
|
||||
@@ -170,7 +171,7 @@ export default {
|
||||
this.total = data.data.total;
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '获取参数列表失败',
|
||||
message: data.msg || this.$t('paramManagement.getParamsListFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -188,7 +189,7 @@ export default {
|
||||
});
|
||||
},
|
||||
showAddDialog() {
|
||||
this.dialogTitle = "新增参数";
|
||||
this.dialogTitle = this.$t('paramManagement.addParam');
|
||||
this.paramForm = {
|
||||
id: null,
|
||||
paramCode: "",
|
||||
@@ -198,119 +199,121 @@ export default {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
editParam(row) {
|
||||
this.dialogTitle = "编辑参数";
|
||||
this.paramForm = { ...row };
|
||||
this.dialogTitle = this.$t('paramManagement.editParam');
|
||||
this.paramForm = {
|
||||
id: row.id,
|
||||
paramCode: row.paramCode,
|
||||
paramValue: row.paramValue,
|
||||
remark: row.remark
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
||||
handleSubmit({ form, done }) {
|
||||
handleSubmit(form) {
|
||||
if (form.id) {
|
||||
// 编辑
|
||||
// 更新参数
|
||||
Api.admin.updateParam(form, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: "修改成功",
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.fetchParams();
|
||||
}
|
||||
done && done();
|
||||
});
|
||||
} else {
|
||||
// 新增
|
||||
Api.admin.addParam(form, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: "新增成功",
|
||||
message: this.$t('paramManagement.updateSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.fetchParams();
|
||||
}
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
deleteSelectedParams() {
|
||||
const selectedRows = this.paramsList.filter(row => row.selected);
|
||||
if (selectedRows.length === 0) {
|
||||
this.$message.warning({
|
||||
message: "请先选择需要删除的参数",
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.deleteParam(selectedRows);
|
||||
},
|
||||
deleteParam(row) {
|
||||
// 处理单个参数或参数数组
|
||||
const params = Array.isArray(row) ? row : [row];
|
||||
|
||||
if (Array.isArray(row) && row.length === 0) {
|
||||
this.$message.warning({
|
||||
message: "请先选择需要删除的参数",
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const paramCount = params.length;
|
||||
this.$confirm(`确定要删除选中的${paramCount}个参数吗?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true
|
||||
}).then(() => {
|
||||
const ids = params.map(param => param.id);
|
||||
if (ids.some(id => isNaN(id))) {
|
||||
this.$message.error({
|
||||
message: '存在无效的参数ID',
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Api.admin.deleteParam(ids, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: `成功删除${paramCount}个参数`,
|
||||
showClose: true
|
||||
});
|
||||
this.fetchParams();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '删除失败,请重试',
|
||||
message: data.msg || this.$t('paramManagement.updateFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}).catch(action => {
|
||||
if (action === 'cancel') {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '已取消删除操作',
|
||||
duration: 1000
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '操作已关闭',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 新增参数
|
||||
Api.admin.addParam(form, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.dialogVisible = false;
|
||||
this.fetchParams();
|
||||
this.$message.success({
|
||||
message: this.$t('paramManagement.addSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || this.$t('paramManagement.addFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
deleteSelectedParams() {
|
||||
const selectedParams = this.paramsList.filter(row => row.selected);
|
||||
if (selectedParams.length === 0) {
|
||||
this.$message.warning({
|
||||
message: this.$t('paramManagement.selectParamsFirst'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.deleteParams(selectedParams);
|
||||
},
|
||||
deleteParam(row) {
|
||||
if (!row.id) {
|
||||
this.$message.warning({
|
||||
message: this.$t('paramManagement.selectParamsFirst'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.deleteParams([row]);
|
||||
},
|
||||
deleteParams(params) {
|
||||
const paramCount = params.length;
|
||||
const paramIds = params.map(param => param.id).filter(id => id);
|
||||
if (paramIds.length === 0) {
|
||||
this.$message.error({
|
||||
message: this.$t('paramManagement.invalidParamId'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$confirm(this.$t('paramManagement.confirmBatchDelete', { paramCount }), 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
Api.admin.deleteParams(paramIds, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.fetchParams();
|
||||
this.$message.success({
|
||||
message: this.$t('paramManagement.batchDeleteSuccess', { paramCount }),
|
||||
showClose: true
|
||||
});
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || this.$t('paramManagement.deleteFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: this.$t('paramManagement.operationCancelled'),
|
||||
duration: 1000
|
||||
});
|
||||
});
|
||||
},
|
||||
headerCellClassName({ columnIndex }) {
|
||||
if (columnIndex === 0) {
|
||||
return "custom-selection-header";
|
||||
goToPage(page) {
|
||||
if (page !== this.currentPage) {
|
||||
this.currentPage = page;
|
||||
this.fetchParams();
|
||||
}
|
||||
return "";
|
||||
},
|
||||
goFirst() {
|
||||
this.currentPage = 1;
|
||||
this.fetchParams();
|
||||
if (this.currentPage !== 1) {
|
||||
this.currentPage = 1;
|
||||
this.fetchParams();
|
||||
}
|
||||
},
|
||||
goPrev() {
|
||||
if (this.currentPage > 1) {
|
||||
@@ -324,22 +327,22 @@ export default {
|
||||
this.fetchParams();
|
||||
}
|
||||
},
|
||||
goToPage(page) {
|
||||
this.currentPage = page;
|
||||
this.fetchParams();
|
||||
},
|
||||
isSensitiveParam(paramCode) {
|
||||
return this.sensitive_keys.some(key => paramCode.toLowerCase().includes(key.toLowerCase()));
|
||||
return this.sensitive_keys.some(key => paramCode.toLowerCase().includes(key));
|
||||
},
|
||||
maskSensitiveValue(value) {
|
||||
if (!value) return '';
|
||||
if (value.length <= 8) return '****';
|
||||
return value.substring(0, 4) + '****' + value.substring(value.length - 4);
|
||||
if (value.length <= 4) {
|
||||
return '****';
|
||||
}
|
||||
return value.substring(0, 2) + '****' + value.substring(value.length - 2);
|
||||
},
|
||||
toggleSensitiveValue(row) {
|
||||
this.$set(row, 'showValue', !row.showValue);
|
||||
row.showValue = !row.showValue;
|
||||
},
|
||||
},
|
||||
headerCellClassName() {
|
||||
return 'header-cell';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">字段管理</h2>
|
||||
<h2 class="page-title">{{ $t('header.providerManagement') }}</h2>
|
||||
<div class="right-operations">
|
||||
<el-dropdown trigger="click" @command="handleSelectModelType" @visible-change="handleDropdownVisibleChange">
|
||||
<el-button class="category-btn">
|
||||
类别筛选 {{ selectedModelTypeLabel }}<i class="el-icon-arrow-down el-icon--right"
|
||||
{{ $t('providerManagement.categoryFilter') }} {{ selectedModelTypeLabel }}<i class="el-icon-arrow-down el-icon--right"
|
||||
:class="{ 'rotate-down': DropdownVisible }"></i>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="">全部</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in modelTypes" :key="item.value" :command="item.value">
|
||||
<el-dropdown-item command="">{{ $t('common.all') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in translatedModelTypes" :key="item.value" :command="item.value">
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-input placeholder="请输入供应器名称查询" v-model="searchName" class="search-input" @keyup.enter.native="handleSearch"
|
||||
<el-input :placeholder="$t('providerManagement.searchPlaceholder')" v-model="searchName" class="search-input" @keyup.enter.native="handleSearch"
|
||||
clearable />
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button class="btn-search" @click="handleSearch">{{ $t('common.search') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,23 +30,23 @@
|
||||
<el-table ref="providersTable" :data="filteredProvidersList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)" :header-cell-class-name="headerCellClassName">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="类别" prop="modelType" align="center" width="200">
|
||||
<el-table-column :label="$t('providerManagement.category')" prop="modelType" align="center" width="200">
|
||||
<template slot="header" slot-scope="scope">
|
||||
<el-dropdown trigger="click" @command="handleSelectModelType"
|
||||
@visible-change="isDropdownOpen = $event">
|
||||
<span class="dropdown-trigger" :class="{ 'active': isDropdownOpen }">
|
||||
类别{{ selectedModelTypeLabel }} <i class="dropdown-arrow"
|
||||
{{ $t('providerManagement.category') }}{{ selectedModelTypeLabel }} <i class="dropdown-arrow"
|
||||
:class="{ 'is-active': isDropdownOpen }"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="">全部</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in modelTypes" :key="item.value" :command="item.value">
|
||||
<el-dropdown-item command="">{{ $t('common.all') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in translatedModelTypes" :key="item.value" :command="item.value">
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -58,25 +58,25 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="供应器编码" prop="providerCode" align="center" width="150"></el-table-column>
|
||||
<el-table-column label="名称" prop="name" align="center"></el-table-column>
|
||||
<el-table-column label="字段配置" align="center">
|
||||
<el-table-column :label="$t('providerManagement.providerCode')" prop="providerCode" align="center" width="150"></el-table-column>
|
||||
<el-table-column :label="$t('common.name')" prop="name" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('providerManagement.fieldConfig')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-popover placement="top-start" width="400" trigger="hover">
|
||||
<div v-for="field in scope.row.fields" :key="field.key" class="field-item">
|
||||
<span class="field-label">{{ field.label }}:</span>
|
||||
<span class="field-type">{{ field.type }}</span>
|
||||
<span v-if="isSensitiveField(field.key)" class="sensitive-tag">敏感</span>
|
||||
<span v-if="isSensitiveField(field.key)" class="sensitive-tag">{{ $t('common.sensitive') }}</span>
|
||||
</div>
|
||||
<el-button slot="reference" size="mini" type="text">查看字段</el-button>
|
||||
<el-button slot="reference" size="mini" type="text">{{ $t('providerManagement.viewFields') }}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sort" align="center" width="80"></el-table-column>
|
||||
<el-table-column label="操作" align="center" width="180">
|
||||
<el-table-column :label="$t('common.sort')" prop="sort" align="center" width="80"></el-table-column>
|
||||
<el-table-column :label="$t('common.action')" align="center" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="editProvider(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteProvider(scope.row)">删除</el-button>
|
||||
<el-button size="mini" type="text" @click="editProvider(scope.row)">{{ $t('common.edit') }}</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteProvider(scope.row)">{{ $t('common.delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -84,31 +84,31 @@
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
{{ isAllSelected ? $t('common.deselectAll') : $t('common.selectAll') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="showAddDialog">新增</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelectedProviders">删除
|
||||
<el-button size="mini" type="success" @click="showAddDialog">{{ $t('common.add') }}</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelectedProviders">{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`" :value="item">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('common.perPage', { number: item })" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
首页
|
||||
{{ $t('common.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
上一页
|
||||
{{ $t('common.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
下一页
|
||||
{{ $t('common.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<span class="total-text">{{ $t('common.totalRecords', { number: total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -140,14 +140,14 @@ export default {
|
||||
searchModelType: "",
|
||||
providersList: [],
|
||||
modelTypes: [
|
||||
{ value: "ASR", label: "语音识别" },
|
||||
{ value: "TTS", label: "语音合成" },
|
||||
{ value: "LLM", label: "大语言模型" },
|
||||
{ value: "VLLM", label: "视觉大语言模型" },
|
||||
{ value: "Intent", label: "意图识别" },
|
||||
{ value: "Memory", label: "记忆模块" },
|
||||
{ value: "VAD", label: "语音活动检测" },
|
||||
{ value: "Plugin", label: "插件工具" }
|
||||
{ value: "ASR", labelKey: 'providerManagement.modelType.ASR' },
|
||||
{ value: "TTS", labelKey: 'providerManagement.modelType.TTS' },
|
||||
{ value: "LLM", labelKey: 'providerManagement.modelType.LLM' },
|
||||
{ value: "VLLM", labelKey: 'providerManagement.modelType.VLLM' },
|
||||
{ value: "Intent", labelKey: 'providerManagement.modelType.Intent' },
|
||||
{ value: "Memory", labelKey: 'providerManagement.modelType.Memory' },
|
||||
{ value: "VAD", labelKey: 'providerManagement.modelType.VAD' },
|
||||
{ value: "Plugin", labelKey: 'providerManagement.modelType.Plugin' }
|
||||
],
|
||||
currentPage: 1,
|
||||
loading: false,
|
||||
@@ -174,10 +174,16 @@ export default {
|
||||
this.fetchProviders();
|
||||
},
|
||||
computed: {
|
||||
translatedModelTypes() {
|
||||
return this.modelTypes.map(type => ({
|
||||
value: type.value,
|
||||
label: this.$t(type.labelKey)
|
||||
}));
|
||||
},
|
||||
selectedModelTypeLabel() {
|
||||
if (!this.searchModelType) return "(全部)";
|
||||
if (!this.searchModelType) return `(${this.$t('providerManagement.all')})`;
|
||||
const selectedType = this.modelTypes.find(item => item.value === this.searchModelType);
|
||||
return selectedType ? `(${selectedType.label})` : "";
|
||||
return selectedType ? `(${this.$t(selectedType.labelKey)})` : "";
|
||||
},
|
||||
pageCount() {
|
||||
return Math.ceil(this.total / this.pageSize);
|
||||
@@ -259,7 +265,7 @@ export default {
|
||||
});
|
||||
},
|
||||
showAddDialog() {
|
||||
this.dialogTitle = "新增供应器";
|
||||
this.dialogTitle = this.$t('common.addProvider');
|
||||
this.providerForm = {
|
||||
id: null,
|
||||
modelType: "",
|
||||
@@ -271,7 +277,7 @@ export default {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
editProvider(row) {
|
||||
this.dialogTitle = "编辑供应器";
|
||||
this.dialogTitle = this.$t('common.editProvider');
|
||||
this.providerForm = {
|
||||
...row,
|
||||
fields: JSON.parse(JSON.stringify(row.fields))
|
||||
@@ -287,9 +293,9 @@ export default {
|
||||
if (data.code === 0) {
|
||||
this.fetchProviders(); // 刷新表格
|
||||
this.$message.success({
|
||||
message: "修改成功",
|
||||
showClose: true
|
||||
});
|
||||
message: this.$t('common.updateSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -298,9 +304,9 @@ export default {
|
||||
if (data.code === 0) {
|
||||
this.fetchProviders(); // 刷新表格
|
||||
this.$message.success({
|
||||
message: "新增成功",
|
||||
showClose: true
|
||||
});
|
||||
message: this.$t('common.addSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.total += 1;
|
||||
}
|
||||
});
|
||||
@@ -313,7 +319,7 @@ export default {
|
||||
const selectedRows = this.providersList.filter(row => row.selected);
|
||||
if (selectedRows.length === 0) {
|
||||
this.$message.warning({
|
||||
message: "请先选择需要删除的供应器",
|
||||
message: this.$t('providerManagement.selectToDelete'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
@@ -324,9 +330,9 @@ export default {
|
||||
const providers = Array.isArray(row) ? row : [row];
|
||||
const providerCount = providers.length;
|
||||
|
||||
this.$confirm(`确定要删除选中的${providerCount}个供应器吗?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('providerManagement.confirmDelete', { count: providerCount }), this.$t('common.warning'), {
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
const ids = providers.map(provider => provider.id);
|
||||
@@ -337,12 +343,12 @@ export default {
|
||||
this.fetchProviders(); // 刷新表格
|
||||
|
||||
this.$message.success({
|
||||
message: `成功删除${providerCount}个参数`,
|
||||
message: this.$t('common.deleteSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '删除失败,请重试',
|
||||
message: data.msg || this.$t('common.deleteFailure'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -350,7 +356,7 @@ export default {
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '已取消删除',
|
||||
message: this.$t('common.deleteCancelled'),
|
||||
showClose: true,
|
||||
duration: 1000
|
||||
});
|
||||
@@ -369,7 +375,7 @@ export default {
|
||||
},
|
||||
getModelTypeLabel(type) {
|
||||
const typeItem = this.modelTypes.find(item => item.value === type);
|
||||
return typeItem ? typeItem.label : type;
|
||||
return typeItem ? this.$t(typeItem.labelKey) : type;
|
||||
},
|
||||
isSensitiveField(fieldKey) {
|
||||
if (typeof fieldKey !== 'string') return false;
|
||||
@@ -388,6 +394,26 @@ export default {
|
||||
}
|
||||
return "";
|
||||
},
|
||||
selectionCellClassName() {
|
||||
return "custom-selection-cell";
|
||||
},
|
||||
updateSelectionHeaderText() {
|
||||
// 确保表格已渲染
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.providersTable && this.$refs.providersTable.$el) {
|
||||
const headerCheckbox = this.$refs.providersTable.$el.querySelector('.custom-selection-header .el-checkbox .el-checkbox__label');
|
||||
if (headerCheckbox) {
|
||||
headerCheckbox.textContent = this.$t('modelConfig.select');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.updateSelectionHeaderText();
|
||||
},
|
||||
updated() {
|
||||
this.updateSelectionHeaderText();
|
||||
},
|
||||
goFirst() {
|
||||
this.currentPage = 1;
|
||||
this.fetchProviders();
|
||||
@@ -875,4 +901,26 @@ export default {
|
||||
.dropdown-trigger.active {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/* 确保选择列标题样式正确 */
|
||||
.custom-selection-header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.custom-selection-header .el-checkbox) {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.custom-selection-header .el-checkbox__label) {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.custom-selection-cell) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">服务端管理</h2>
|
||||
<h2 class="page-title">{{ $t('serverSideManager.pageTitle') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
@@ -11,19 +11,19 @@
|
||||
<div class="content-area">
|
||||
<el-card class="params-card" shadow="never">
|
||||
<el-table ref="paramsTable" :data="paramsList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
:element-loading-text="$t('serverSideManager.loading')" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)" :header-cell-class-name="headerCellClassName">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="ws地址" prop="address" align="center"></el-table-column>
|
||||
<el-table-column label="操作" prop="operator" align="center" show-overflow-tooltip>
|
||||
<el-table-column :label="$t('serverSideManager.wsAddress')" prop="address" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('serverSideManager.operation')" prop="operator" align="center" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-button size="medium" type="text" @click="emitAction(scope.row, actionMap.restart)">重启</el-button>
|
||||
<el-button size="medium" type="text" @click="emitAction(scope.row, actionMap.restart)">{{ $t('serverSideManager.restart') }}</el-button>
|
||||
<el-button size="medium" type="text"
|
||||
@click="emitAction(scope.row, actionMap.update_config)">更新配置</el-button>
|
||||
@click="emitAction(scope.row, actionMap.update_config)">{{ $t('serverSideManager.updateConfig') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -44,33 +44,20 @@ import Api from "@/apis/api";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import ParamDialog from "@/components/ParamDialog.vue";
|
||||
import VersionFooter from "@/components/VersionFooter.vue";
|
||||
import i18n from '@/i18n';
|
||||
|
||||
export default {
|
||||
components: { HeaderBar, ParamDialog, VersionFooter },
|
||||
data() {
|
||||
return {
|
||||
paramsList: [],
|
||||
actionMap: {
|
||||
restart: {
|
||||
value: 'restart',
|
||||
title: "重启服务端",
|
||||
message: "确定要重启服务端吗?",
|
||||
confirmText: "重启",
|
||||
},
|
||||
update_config: {
|
||||
value: 'update_config',
|
||||
title: "更新配置",
|
||||
message: "确定要更新配置吗?",
|
||||
confirmText: "更新",
|
||||
}
|
||||
},
|
||||
currentPage: 1,
|
||||
loading: false,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
total: 0,
|
||||
dialogVisible: false,
|
||||
dialogTitle: "新增参数",
|
||||
dialogTitle: '',
|
||||
isAllSelected: false,
|
||||
sensitive_keys: ["api_key", "personal_access_token", "access_token", "token", "secret", "access_key_secret", "secret_key"],
|
||||
paramForm: {
|
||||
@@ -84,6 +71,10 @@ export default {
|
||||
created() {
|
||||
this.fetchParams();
|
||||
},
|
||||
mounted() {
|
||||
this.getServerList();
|
||||
this.dialogTitle = this.$t('paramManagement.addParam');
|
||||
},
|
||||
|
||||
computed: {
|
||||
pageCount() {
|
||||
@@ -104,6 +95,22 @@ export default {
|
||||
}
|
||||
return pages;
|
||||
},
|
||||
actionMap() {
|
||||
return {
|
||||
restart: {
|
||||
value: 'restart',
|
||||
title: this.$t('serverSideManager.restartServer'),
|
||||
message: this.$t('serverSideManager.confirmRestart'),
|
||||
confirmText: this.$t('serverSideManager.restart'),
|
||||
},
|
||||
update_config: {
|
||||
value: 'update_config',
|
||||
title: this.$t('serverSideManager.updateConfigTitle'),
|
||||
message: this.$t('serverSideManager.confirmUpdateConfig'),
|
||||
confirmText: this.$t('serverSideManager.updateConfig'),
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePageSizeChange(val) {
|
||||
@@ -122,7 +129,7 @@ export default {
|
||||
this.total = data.data.length;
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '获取参数列表失败',
|
||||
message: data.msg || this.$t('serverSideManager.getServerListFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -136,6 +143,7 @@ export default {
|
||||
// 弹开询问框
|
||||
this.$confirm(actionItem.message, actionItem.title, {
|
||||
confirmButtonText: actionItem.confirmText, // 确认按钮文本
|
||||
cancelButtonText: this.$t('common.cancel') // 取消按钮文本
|
||||
}).then(() => {
|
||||
// 用户点击了确认按钮
|
||||
Api.admin.sendWsServerAction({
|
||||
@@ -144,13 +152,13 @@ export default {
|
||||
}, ({ data }) => {
|
||||
if (data.code !== 0) {
|
||||
this.$message.error({
|
||||
message: data.msg || '操作失败',
|
||||
message: data.msg || this.$t('serverSideManager.operationFailed'),
|
||||
showClose: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$message.success({
|
||||
message: `${actionItem.title}成功`,
|
||||
message: actionItem.value === 'restart' ? this.$t('serverSideManager.restartSuccess') : this.$t('serverSideManager.updateConfigSuccess'),
|
||||
showClose: true
|
||||
})
|
||||
})
|
||||
@@ -450,26 +458,7 @@ export default {
|
||||
|
||||
.el-table__body-wrapper {
|
||||
max-height: calc(var(--table-max-height) - 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-loading-mask) {
|
||||
background-color: rgba(255, 255, 255, 0.6) !important;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .circular) {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .path) {
|
||||
stroke: #6b8cff;
|
||||
}
|
||||
|
||||
:deep(.el-loading-text) {
|
||||
color: #6b8cff !important;
|
||||
font-size: 14px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">用户管理</h2>
|
||||
<h2 class="page-title">{{ $t('header.userManagement') }}</h2>
|
||||
<div class="right-operations">
|
||||
<el-input placeholder="请输入手机号码查询" v-model="searchPhone" class="search-input" clearable
|
||||
<el-input :placeholder="$t('user.searchPhone')" v-model="searchPhone" class="search-input" clearable
|
||||
@keyup.enter.native="handleSearch" />
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button class="btn-search" @click="handleSearch">{{ $t('user.search') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,29 +18,29 @@
|
||||
<el-table ref="userTable" :data="userList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户Id" prop="userid" align="center"></el-table-column>
|
||||
<el-table-column label="手机号码" prop="mobile" align="center"></el-table-column>
|
||||
<el-table-column label="设备数量" prop="deviceCount" align="center"></el-table-column>
|
||||
<el-table-column label="注册时间" prop="createDate" align="center"></el-table-column>
|
||||
<el-table-column label="状态" prop="status" align="center">
|
||||
<el-table-column :label="$t('user.userid')" prop="userid" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('user.mobile')" prop="mobile" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('user.deviceCount')" prop="deviceCount" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('user.createDate')" prop="createDate" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('user.status')" prop="status" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
|
||||
<el-tag v-else type="danger">禁用</el-tag>
|
||||
<el-tag v-if="scope.row.status === 1" type="success">{{ $t('user.normal') }}</el-tag>
|
||||
<el-tag v-else type="danger">{{ $t('user.disabled') }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column :label="$t('modelConfig.action')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="resetPassword(scope.row)">重置密码</el-button>
|
||||
<el-button size="mini" type="text" @click="resetPassword(scope.row)">{{ $t('user.resetPassword') }}</el-button>
|
||||
<el-button size="mini" type="text" v-if="scope.row.status === 1"
|
||||
@click="handleChangeStatus(scope.row, 0)">禁用账户</el-button>
|
||||
@click="handleChangeStatus(scope.row, 0)">{{ $t('user.disableAccount') }}</el-button>
|
||||
<el-button size="mini" type="text" v-if="scope.row.status === 0"
|
||||
@click="handleChangeStatus(scope.row, 1)">恢复账号</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteUser(scope.row)">删除用户</el-button>
|
||||
@click="handleChangeStatus(scope.row, 1)">{{ $t('user.enableAccount') }}</el-button>
|
||||
<el-button size="mini" type="text" @click="deleteUser(scope.row)">{{ $t('user.deleteUser') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -48,32 +48,32 @@
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
{{ isAllSelected ? $t('user.deselectAll') : $t('user.selectAll') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" icon="el-icon-circle-check" @click="batchEnable">启用</el-button>
|
||||
<el-button size="mini" type="success" icon="el-icon-circle-check" @click="batchEnable">{{ $t('user.enable') }}</el-button>
|
||||
<el-button size="mini" type="warning" @click="batchDisable"><i
|
||||
class="el-icon-remove-outline rotated-icon"></i>禁用</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDelete">删除</el-button>
|
||||
class="el-icon-remove-outline rotated-icon"></i>{{ $t('user.disable') }}</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDelete">{{ $t('user.delete') }}</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`" :value="item">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" :class="['page-size-select', { 'page-size-select-en': isEnglish }]">
|
||||
<el-option v-for="item in pageSizeOptions" :key="item" :label="$t('modelConfig.itemsPerPage', { count: item })" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
首页
|
||||
{{ $t('modelConfig.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
上一页
|
||||
{{ $t('modelConfig.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button> <button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
下一页
|
||||
{{ $t('modelConfig.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">共{{ total }}条记录</span>
|
||||
<span class="total-text">{{ $t('modelConfig.totalRecords', { count: total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -93,6 +93,7 @@ import Api from "@/apis/api";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import VersionFooter from "@/components/VersionFooter.vue";
|
||||
import ViewPasswordDialog from "@/components/ViewPasswordDialog.vue";
|
||||
import i18n from '@/i18n';
|
||||
export default {
|
||||
components: { HeaderBar, ViewPasswordDialog, VersionFooter },
|
||||
data() {
|
||||
@@ -116,6 +117,9 @@ export default {
|
||||
pageCount() {
|
||||
return Math.ceil(this.total / this.pageSize);
|
||||
},
|
||||
isEnglish() {
|
||||
return i18n.locale === 'en';
|
||||
},
|
||||
visiblePages() {
|
||||
const pages = [];
|
||||
const maxVisible = 3;
|
||||
@@ -172,19 +176,19 @@ export default {
|
||||
batchDelete() {
|
||||
const selectedUsers = this.userList.filter(user => user.selected);
|
||||
if (selectedUsers.length === 0) {
|
||||
this.$message.warning("请先选择需要删除的用户");
|
||||
this.$message.warning(this.$t('user.selectUsersFirst'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.$confirm(`确定要删除选中的${selectedUsers.length}个用户吗?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
this.$confirm(this.$t('user.confirmDeleteSelected', { count: selectedUsers.length }), "警告", {
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在删除中...",
|
||||
text: this.$t('user.deleting'),
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
@@ -656,6 +660,16 @@ export default {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.page-size-select-en {
|
||||
width: 130px;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input__suffix) {
|
||||
right: 6px;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">声纹识别</h2>
|
||||
<h2 class="page-title">{{ $t('voicePrint.pageTitle') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
@@ -11,23 +11,23 @@
|
||||
<div class="content-area">
|
||||
<el-card class="voice-print-card" shadow="never">
|
||||
<el-table ref="paramsTable" :data="voicePrintList" class="transparent-table" v-loading="loading"
|
||||
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table-column label="姓名" prop="sourceName" align="center"></el-table-column>
|
||||
<el-table-column label="描述" prop="introduce" align="center"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="createDate" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
:element-loading-text="$t('voicePrint.loading')" element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table-column :label="$t('voicePrint.name')" prop="sourceName" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('voicePrint.description')" prop="introduce" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('voicePrint.createTime')" prop="createDate" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('voicePrint.action')" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="editVoicePrint(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" @click="editVoicePrint(scope.row)">{{ $t('voicePrint.edit') }}</el-button>
|
||||
<el-button size="mini" type="text"
|
||||
@click="deleteVoicePrint(scope.row.id)">删除</el-button>
|
||||
@click="deleteVoicePrint(scope.row.id)">{{ $t('voicePrint.delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="success" @click="showAddDialog">新增</el-button>
|
||||
<el-button size="mini" type="success" @click="showAddDialog">{{ $t('voicePrint.add') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
voicePrintList: [],
|
||||
loading: false,
|
||||
dialogVisible: false,
|
||||
dialogTitle: "添加说话人",
|
||||
dialogTitle: this.$t('voicePrint.addSpeaker'),
|
||||
isAllSelected: false,
|
||||
paramForm: {
|
||||
id: null,
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
}));
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '获取声纹列表失败',
|
||||
message: data.msg || this.$t('voicePrint.fetchFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
);
|
||||
},
|
||||
showAddDialog() {
|
||||
this.dialogTitle = "添加说话人";
|
||||
this.dialogTitle = this.$t('voicePrint.addSpeaker');
|
||||
this.paramForm = {
|
||||
id: null,
|
||||
audioId: '',
|
||||
@@ -105,7 +105,7 @@ export default {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
editVoicePrint(row) {
|
||||
this.dialogTitle = "编辑说话人";
|
||||
this.dialogTitle = this.$t('voicePrint.editSpeaker');
|
||||
this.paramForm = { ...row };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
@@ -116,7 +116,7 @@ export default {
|
||||
Api.agent.updateAgentVoicePrint(form, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: "修改成功",
|
||||
message: this.$t('voicePrint.updateSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
@@ -134,7 +134,7 @@ export default {
|
||||
}, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: "新增成功",
|
||||
message: this.$t('voicePrint.addSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
@@ -146,22 +146,22 @@ export default {
|
||||
},
|
||||
// 删除按钮
|
||||
deleteVoicePrint(id) {
|
||||
this.$confirm(`确定要删除选中的此声纹吗?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('voicePrint.confirmDelete'), this.$t('voicePrint.warning'), {
|
||||
confirmButtonText: this.$t('voicePrint.confirm'),
|
||||
cancelButtonText: this.$t('voicePrint.cancel'),
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true
|
||||
}).then(() => {
|
||||
Api.agent.deleteAgentVoicePrint(id, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: `成功删除此声纹`,
|
||||
message: this.$t('voicePrint.deleteSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.fetchVoicePrints();
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '删除失败,请重试',
|
||||
message: data.msg || this.$t('voicePrint.deleteFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
@@ -170,13 +170,13 @@ export default {
|
||||
if (action === 'cancel') {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '已取消删除操作',
|
||||
message: this.$t('voicePrint.cancelDelete'),
|
||||
duration: 1000
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '操作已关闭',
|
||||
message: this.$t('voicePrint.closeOperation'),
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
.login-text {
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
font-size: 25px;
|
||||
text-align: left;
|
||||
color: #3d4566;
|
||||
}
|
||||
|
||||
@@ -8,20 +8,17 @@
|
||||
<div class="add-device">
|
||||
<div class="add-device-bg">
|
||||
<div class="hellow-text" style="margin-top: 30px;">
|
||||
你好,小智
|
||||
你好小智
|
||||
</div>
|
||||
<div class="hellow-text">
|
||||
让我们度过
|
||||
<div style="display: inline-block;color: #5778FF;">
|
||||
美好的一天!
|
||||
</div>
|
||||
让我们度过美好的一天!
|
||||
</div>
|
||||
<div class="hi-hint">
|
||||
Hello, Let's have a wonderful day!
|
||||
let's have a wonderful day!
|
||||
</div>
|
||||
<div class="add-device-btn">
|
||||
<div class="left-add" @click="showAddDialog">
|
||||
添加智能体
|
||||
{{ $t('home.addAgent') }}
|
||||
</div>
|
||||
<div style="width: 23px;height: 13px;background: #5778ff;margin-left: -10px;" />
|
||||
<div class="right-add">
|
||||
@@ -152,21 +149,21 @@ export default {
|
||||
},
|
||||
// 删除智能体
|
||||
handleDeleteAgent(agentId) {
|
||||
this.$confirm('确定要删除该智能体吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$t('home.confirmDeleteAgent'), '提示', {
|
||||
confirmButtonText: this.$t('button.ok'),
|
||||
cancelButtonText: this.$t('button.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
Api.agent.deleteAgent(agentId, (res) => {
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success({
|
||||
message: '删除成功',
|
||||
message: this.$t('home.deleteSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
this.fetchAgentList(); // 刷新列表
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: res.data.msg || '删除失败',
|
||||
message: res.data.msg || this.$t('home.deleteFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,17 +14,37 @@
|
||||
<div class="login-box" @keyup.enter="login">
|
||||
<div style="display: flex;align-items: center;gap: 20px;margin-bottom: 39px;padding: 0 30px;">
|
||||
<img loading="lazy" alt="" src="@/assets/login/hi.png" style="width: 34px;height: 34px;" />
|
||||
<div class="login-text">登录</div>
|
||||
<div class="login-text">{{ $t('login.title') }}</div>
|
||||
|
||||
<div class="login-welcome">
|
||||
WELCOME TO LOGIN
|
||||
{{ $t('login.welcome') }}
|
||||
</div>
|
||||
|
||||
<!-- 语言切换下拉菜单 -->
|
||||
<el-dropdown trigger="click" class="title-language-dropdown" @visible-change="handleLanguageDropdownVisibleChange">
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-document"></i>
|
||||
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': languageDropdownVisible }"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="changeLanguage('zh_CN')">
|
||||
{{ $t('language.zhCN') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="changeLanguage('zh_TW')">
|
||||
{{ $t('language.zhTW') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="changeLanguage('en')">
|
||||
{{ $t('language.en') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div style="padding: 0 30px;">
|
||||
<!-- 用户名登录 -->
|
||||
<template v-if="!isMobileLogin">
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/username.png" />
|
||||
<el-input v-model="form.username" placeholder="请输入用户名" />
|
||||
<el-input v-model="form.username" :placeholder="$t('login.usernamePlaceholder')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,51 +53,52 @@
|
||||
<div class="input-box">
|
||||
<div style="display: flex; align-items: center; width: 100%;">
|
||||
<el-select v-model="form.areaCode" style="width: 220px; margin-right: 10px;">
|
||||
<el-option v-for="item in mobileAreaList" :key="item.key" :label="`${item.name} (${item.key})`"
|
||||
<el-option v-for="item in mobileAreaList" :key="item.key" :label="`${item.name} (${item.key})`"
|
||||
:value="item.key" />
|
||||
</el-select>
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号码" />
|
||||
<el-input v-model="form.mobile" :placeholder="$t('login.mobilePlaceholder')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
|
||||
<el-input v-model="form.password" placeholder="请输入密码" type="password" show-password />
|
||||
<el-input v-model="form.password" :placeholder="$t('login.passwordPlaceholder')" type="password" show-password />
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
|
||||
<el-input v-model="form.captcha" :placeholder="$t('login.captchaPlaceholder')" style="flex: 1;" />
|
||||
</div>
|
||||
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
|
||||
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
|
||||
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
|
||||
</div>
|
||||
<div
|
||||
style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;display: flex;justify-content: space-between;margin-top: 20px;">
|
||||
<div v-if="allowUserRegister" style="cursor: pointer;" @click="goToRegister">新用户注册</div>
|
||||
<div style="cursor: pointer;" @click="goToForgetPassword" v-if="enableMobileRegister">忘记密码?</div>
|
||||
<div v-if="allowUserRegister" style="cursor: pointer;" @click="goToRegister">{{ $t('login.register') }}</div>
|
||||
<div style="cursor: pointer;" @click="goToForgetPassword" v-if="enableMobileRegister">{{ $t('login.forgetPassword') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-btn" @click="login">登录</div>
|
||||
<div class="login-btn" @click="login">{{ $t('login.login') }}</div>
|
||||
|
||||
<!-- 登录方式切换按钮 -->
|
||||
<div class="login-type-container" v-if="enableMobileRegister">
|
||||
<el-tooltip content="手机号码登录" placement="bottom">
|
||||
<el-button :type="isMobileLogin ? 'primary' : 'default'" icon="el-icon-mobile" circle
|
||||
@click="switchLoginType('mobile')"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="用户名登录" placement="bottom">
|
||||
<el-button :type="!isMobileLogin ? 'primary' : 'default'" icon="el-icon-user" circle
|
||||
@click="switchLoginType('username')"></el-button>
|
||||
</el-tooltip>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<el-tooltip :content="$t('login.mobileLogin')" placement="bottom">
|
||||
<el-button :type="isMobileLogin ? 'primary' : 'default'" icon="el-icon-mobile" circle
|
||||
@click="switchLoginType('mobile')"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('login.usernameLogin')" placement="bottom">
|
||||
<el-button :type="!isMobileLogin ? 'primary' : 'default'" icon="el-icon-user" circle
|
||||
@click="switchLoginType('username')"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 14px;color: #979db1;">
|
||||
登录即同意
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
||||
和
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《隐私政策》</div>
|
||||
{{ $t('login.agreeTo') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('login.userAgreement') }}</div>
|
||||
{{ $t('login.and') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('login.privacyPolicy') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
@@ -93,6 +114,7 @@ import Api from '@/apis/api';
|
||||
import VersionFooter from '@/components/VersionFooter.vue';
|
||||
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from '@/utils';
|
||||
import { mapState } from 'vuex';
|
||||
import { changeLanguage } from '@/i18n';
|
||||
|
||||
export default {
|
||||
name: 'login',
|
||||
@@ -119,7 +141,8 @@ export default {
|
||||
},
|
||||
captchaUuid: '',
|
||||
captchaUrl: '',
|
||||
isMobileLogin: false
|
||||
isMobileLogin: false,
|
||||
languageDropdownVisible: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -149,6 +172,21 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 切换语言下拉菜单的可见状态变化
|
||||
handleLanguageDropdownVisibleChange(visible) {
|
||||
this.languageDropdownVisible = visible;
|
||||
},
|
||||
|
||||
// 切换语言
|
||||
changeLanguage(lang) {
|
||||
changeLanguage(lang);
|
||||
this.languageDropdownVisible = false;
|
||||
this.$message.success({
|
||||
message: this.$t('message.success'),
|
||||
showClose: true
|
||||
});
|
||||
},
|
||||
|
||||
// 切换登录方式
|
||||
switchLoginType(type) {
|
||||
this.isMobileLogin = type === 'mobile';
|
||||
@@ -226,6 +264,26 @@ export default {
|
||||
|
||||
.login-type-container {
|
||||
margin: 10px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-language-dropdown {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.language-dropdown {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.rotate-down {
|
||||
transform: rotate(180deg);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.el-icon-arrow-down {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
<!-- 修改标题部分 -->
|
||||
<div style="display: flex;align-items: center;gap: 20px;margin-bottom: 39px;padding: 0 30px;">
|
||||
<img loading="lazy" alt="" src="@/assets/login/hi.png" style="width: 34px;height: 34px;" />
|
||||
<div class="login-text">注册</div>
|
||||
<div class="login-text">{{ $t('register.title') }}</div>
|
||||
<div class="login-welcome">
|
||||
WELCOME TO REGISTER
|
||||
{{ $t('register.welcome') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<!-- 用户名/手机号输入框 -->
|
||||
<div class="input-box" v-if="!enableMobileRegister">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/username.png" />
|
||||
<el-input v-model="form.username" placeholder="请输入用户名" />
|
||||
<el-input v-model="form.username" :placeholder="$t('register.usernamePlaceholder')" />
|
||||
</div>
|
||||
|
||||
<!-- 手机号注册部分 -->
|
||||
@@ -38,14 +38,14 @@
|
||||
<el-option v-for="item in mobileAreaList" :key="item.key" :label="`${item.name} (${item.key})`"
|
||||
:value="item.key" />
|
||||
</el-select>
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号码" />
|
||||
<el-input v-model="form.mobile" :placeholder="$t('register.mobilePlaceholder')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
|
||||
<el-input v-model="form.captcha" :placeholder="$t('register.captchaPlaceholder')" style="flex: 1;" />
|
||||
</div>
|
||||
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
|
||||
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
|
||||
@@ -56,12 +56,12 @@
|
||||
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" />
|
||||
<el-input v-model="form.mobileCaptcha" placeholder="请输入手机验证码" style="flex: 1;" maxlength="6" />
|
||||
<el-input v-model="form.mobileCaptcha" :placeholder="$t('register.mobileCaptchaPlaceholder')" style="flex: 1;" maxlength="6" />
|
||||
</div>
|
||||
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
|
||||
@click="sendMobileCaptcha">
|
||||
<span>
|
||||
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
|
||||
{{ countdown > 0 ? `${countdown}秒后重试` : $t('register.sendCaptcha') }}
|
||||
</span>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -70,13 +70,13 @@
|
||||
<!-- 密码输入框 -->
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
|
||||
<el-input v-model="form.password" placeholder="请输入密码" type="password" show-password />
|
||||
<el-input v-model="form.password" :placeholder="$t('register.passwordPlaceholder')" type="password" show-password />
|
||||
</div>
|
||||
|
||||
<!-- 新增确认密码 -->
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
|
||||
<el-input v-model="form.confirmPassword" placeholder="请确认密码" type="password" show-password />
|
||||
<el-input v-model="form.confirmPassword" :placeholder="$t('register.confirmPasswordPlaceholder')" type="password" show-password />
|
||||
</div>
|
||||
|
||||
<!-- 验证码部分保持相同 -->
|
||||
@@ -84,7 +84,7 @@
|
||||
style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
|
||||
<el-input v-model="form.captcha" :placeholder="$t('register.captchaPlaceholder')" style="flex: 1;" />
|
||||
</div>
|
||||
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
|
||||
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
|
||||
@@ -92,20 +92,20 @@
|
||||
|
||||
<!-- 修改底部链接 -->
|
||||
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;margin-top: 20px;">
|
||||
<div style="cursor: pointer;" @click="goToLogin">已有账号?立即登录</div>
|
||||
<div style="cursor: pointer;" @click="goToLogin">{{ $t('register.goToLogin') }}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 修改按钮文本 -->
|
||||
<div class="login-btn" @click="register">立即注册</div>
|
||||
<div class="login-btn" @click="register">{{ $t('register.registerButton') }}</div>
|
||||
|
||||
<!-- 保持相同的协议声明 -->
|
||||
<div style="font-size: 14px;color: #979db1;">
|
||||
注册即同意
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
||||
和
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《隐私政策》</div>
|
||||
{{ $t('register.agreeTo') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('register.userAgreement') }}</div>
|
||||
{{ $t('register.and') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('register.privacyPolicy') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
@@ -124,6 +124,9 @@ import VersionFooter from '@/components/VersionFooter.vue';
|
||||
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from '@/utils';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
// 导入语言切换功能
|
||||
import { changeLanguage } from '@/i18n';
|
||||
|
||||
export default {
|
||||
name: 'register',
|
||||
components: {
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<!-- 修改标题部分 -->
|
||||
<div style="display: flex;align-items: center;gap: 20px;margin-bottom: 39px;padding: 0 30px;">
|
||||
<img loading="lazy" alt="" src="@/assets/login/hi.png" style="width: 34px;height: 34px;" />
|
||||
<div class="login-text">重置密码</div>
|
||||
<div class="login-text">{{ $t('retrievePassword.title') }}</div>
|
||||
<div class="login-welcome">
|
||||
PASSWORD RETRIEVE
|
||||
{{ $t('retrievePassword.subtitle') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
<el-option v-for="item in mobileAreaList" :key="item.key" :label="`${item.name} (${item.key})`"
|
||||
:value="item.key" />
|
||||
</el-select>
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号码" />
|
||||
<el-input v-model="form.mobile" :placeholder="$t('retrievePassword.mobilePlaceholder')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
|
||||
<el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
|
||||
<el-input v-model="form.captcha" :placeholder="$t('retrievePassword.captchaPlaceholder')" style="flex: 1;" />
|
||||
</div>
|
||||
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
|
||||
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
|
||||
@@ -48,12 +48,12 @@
|
||||
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
|
||||
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" />
|
||||
<el-input v-model="form.mobileCaptcha" placeholder="请输入手机验证码" style="flex: 1;" maxlength="6" />
|
||||
<el-input v-model="form.mobileCaptcha" :placeholder="$t('retrievePassword.mobileCaptchaPlaceholder')" style="flex: 1;" maxlength="6" />
|
||||
</div>
|
||||
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
|
||||
@click="sendMobileCaptcha">
|
||||
<span>
|
||||
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
|
||||
{{ countdown > 0 ? `${countdown}秒后重试` : $t('retrievePassword.sendCaptcha') }}
|
||||
</span>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -61,30 +61,30 @@
|
||||
<!-- 新密码 -->
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
|
||||
<el-input v-model="form.newPassword" placeholder="请输入新密码" type="password" show-password />
|
||||
<el-input v-model="form.newPassword" :placeholder="$t('retrievePassword.newPasswordPlaceholder')" type="password" show-password />
|
||||
</div>
|
||||
|
||||
<!-- 确认新密码 -->
|
||||
<div class="input-box">
|
||||
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
|
||||
<el-input v-model="form.confirmPassword" placeholder="请确认新密码" type="password" show-password />
|
||||
<el-input v-model="form.confirmPassword" :placeholder="$t('retrievePassword.confirmPasswordPlaceholder')" type="password" show-password />
|
||||
</div>
|
||||
|
||||
<!-- 修改底部链接 -->
|
||||
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;margin-top: 20px;">
|
||||
<div style="cursor: pointer;" @click="goToLogin">返回登录</div>
|
||||
<div style="cursor: pointer;" @click="goToLogin">{{ $t('retrievePassword.goToLogin') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 修改按钮文本 -->
|
||||
<div class="login-btn" @click="retrievePassword">立即修改</div>
|
||||
<div class="login-btn" @click="retrievePassword">{{ $t('retrievePassword.resetButton') }}</div>
|
||||
|
||||
<!-- 保持相同的协议声明 -->
|
||||
<div style="font-size: 14px;color: #979db1;">
|
||||
同意
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
||||
和
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《隐私政策》</div>
|
||||
{{ $t('retrievePassword.agreeTo') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('register.userAgreement') }}</div>
|
||||
{{ $t('register.and') }}
|
||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">{{ $t('register.privacyPolicy') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -104,6 +104,9 @@ import VersionFooter from '@/components/VersionFooter.vue';
|
||||
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from '@/utils';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
// 导入语言切换功能
|
||||
import { changeLanguage } from '@/i18n';
|
||||
|
||||
export default {
|
||||
name: 'retrieve',
|
||||
components: {
|
||||
@@ -125,7 +128,7 @@ export default {
|
||||
mobile: '',
|
||||
captcha: '',
|
||||
captchaId: '',
|
||||
smsCode: '',
|
||||
mobileCaptcha: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<HeaderBar />
|
||||
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">角色配置</h2>
|
||||
<h2 class="page-title">{{ $t('roleConfig.title') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="main-wrapper">
|
||||
@@ -18,10 +18,10 @@
|
||||
<div class="header-actions">
|
||||
<div class="hint-text">
|
||||
<img loading="lazy" src="@/assets/home/info.png" alt="">
|
||||
<span>保存配置后,需要重启设备,新的配置才会生效。</span>
|
||||
<span>{{ $t('roleConfig.restartNotice') }}</span>
|
||||
</div>
|
||||
<el-button type="primary" class="save-btn" @click="saveConfig">保存配置</el-button>
|
||||
<el-button class="reset-btn" @click="resetConfig">重置</el-button>
|
||||
<el-button type="primary" class="save-btn" @click="saveConfig">{{ $t('roleConfig.saveConfig') }}</el-button>
|
||||
<el-button class="reset-btn" @click="resetConfig">{{ $t('roleConfig.reset') }}</el-button>
|
||||
<button class="custom-close-btn" @click="goToHome">
|
||||
×
|
||||
</button>
|
||||
@@ -33,10 +33,10 @@
|
||||
<div class="form-content">
|
||||
<div class="form-grid">
|
||||
<div class="form-column">
|
||||
<el-form-item label="助手昵称:">
|
||||
<el-form-item :label="$t('roleConfig.agentName') + ':'">
|
||||
<el-input v-model="form.agentName" class="form-input" maxlength="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色模版:">
|
||||
<el-form-item :label="$t('roleConfig.roleTemplate') + ':'">
|
||||
<div class="template-container">
|
||||
<div v-for="(template, index) in templates" :key="`template-${index}`" class="template-item"
|
||||
:class="{ 'template-loading': loadingTemplate }" @click="selectTemplate(template)">
|
||||
@@ -44,39 +44,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色介绍:">
|
||||
<el-input type="textarea" rows="9" resize="none" placeholder="请输入内容" v-model="form.systemPrompt"
|
||||
<el-form-item :label="$t('roleConfig.roleIntroduction') + ':'">
|
||||
<el-input type="textarea" rows="9" resize="none" :placeholder="$t('roleConfig.pleaseEnterContent')" v-model="form.systemPrompt"
|
||||
maxlength="2000" show-word-limit class="form-textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="记忆:">
|
||||
<el-form-item :label="$t('roleConfig.memory') + ':'">
|
||||
<el-input type="textarea" rows="6" resize="none" v-model="form.summaryMemory" maxlength="2000"
|
||||
show-word-limit class="form-textarea"
|
||||
:disabled="form.model.memModelId !== 'Memory_mem_local_short'" />
|
||||
</el-form-item>
|
||||
<el-form-item label="语言编码:" style="display: none;">
|
||||
<el-input v-model="form.langCode" placeholder="请输入语言编码,如:zh_CN" maxlength="10" show-word-limit
|
||||
<el-form-item :label="$t('roleConfig.languageCode') + ':'" style="display: none;">
|
||||
<el-input v-model="form.langCode" :placeholder="$t('roleConfig.pleaseEnterLangCode')" maxlength="10" show-word-limit
|
||||
class="form-input" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交互语种:" style="display: none;">
|
||||
<el-input v-model="form.language" placeholder="请输入交互语种,如:中文" maxlength="10" show-word-limit
|
||||
<el-form-item :label="$t('roleConfig.interactionLanguage') + ':'" style="display: none;">
|
||||
<el-input v-model="form.language" :placeholder="$t('roleConfig.pleaseEnterLangName')" maxlength="10" show-word-limit
|
||||
class="form-input" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="form-column">
|
||||
<div class="model-row">
|
||||
<el-form-item label="语音活动检测(VAD)" class="model-item">
|
||||
<el-form-item :label="$t('roleConfig.vad')" class="model-item">
|
||||
<div class="model-select-wrapper">
|
||||
<el-select v-model="form.model.vadModelId" filterable placeholder="请选择" class="form-select"
|
||||
<el-select v-model="form.model.vadModelId" filterable :placeholder="$t('roleConfig.pleaseSelect')" class="form-select"
|
||||
@change="handleModelChange('VAD', $event)">
|
||||
<el-option v-for="(item, optionIndex) in modelOptions['VAD']"
|
||||
:key="`option-vad-${optionIndex}`" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="语音识别(ASR)" class="model-item">
|
||||
<el-form-item :label="$t('roleConfig.asr')" class="model-item">
|
||||
<div class="model-select-wrapper">
|
||||
<el-select v-model="form.model.asrModelId" filterable placeholder="请选择" class="form-select"
|
||||
<el-select v-model="form.model.asrModelId" filterable :placeholder="$t('roleConfig.pleaseSelect')" class="form-select"
|
||||
@change="handleModelChange('ASR', $event)">
|
||||
<el-option v-for="(item, optionIndex) in modelOptions['ASR']"
|
||||
:key="`option-asr-${optionIndex}`" :label="item.label" :value="item.value" />
|
||||
@@ -84,11 +84,11 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item v-for="(model, index) in models.slice(2)" :key="`model-${index}`" :label="model.label"
|
||||
<el-form-item v-for="(model, index) in models.slice(2)" :key="`model-${index}`" :label="$t('roleConfig.' + model.type.toLowerCase())"
|
||||
class="model-item">
|
||||
<div class="model-select-wrapper">
|
||||
<el-select v-model="form.model[model.key]" filterable placeholder="请选择" class="form-select"
|
||||
@change="handleModelChange(model.type, $event)">
|
||||
<el-select v-model="form.model[model.key]" filterable :placeholder="$t('roleConfig.pleaseSelect')" class="form-select"
|
||||
@change="handleModelChange(model.type, $event)">
|
||||
<el-option v-for="(item, optionIndex) in modelOptions[model.type]" v-if="!item.isHidden"
|
||||
:key="`option-${index}-${optionIndex}`" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
@@ -104,20 +104,20 @@
|
||||
</el-tooltip>
|
||||
<el-button class="edit-function-btn" @click="openFunctionDialog"
|
||||
:class="{ 'active-btn': showFunctionDialog }">
|
||||
编辑功能
|
||||
{{ $t('roleConfig.editFunctions') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="model.type === 'Memory' && form.model.memModelId !== 'Memory_nomem'"
|
||||
class="chat-history-options">
|
||||
<el-radio-group v-model="form.chatHistoryConf" @change="updateChatHistoryConf">
|
||||
<el-radio-button :label="1">上报文字</el-radio-button>
|
||||
<el-radio-button :label="2">上报文字+语音</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-radio-button :label="1">{{ $t('roleConfig.reportText') }}</el-radio-button>
|
||||
<el-radio-button :label="2">{{ $t('roleConfig.reportTextVoice') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色音色">
|
||||
<el-select v-model="form.ttsVoiceId" placeholder="请选择" class="form-select">
|
||||
<el-form-item :label="$t('roleConfig.voiceType')">
|
||||
<el-select v-model="form.ttsVoiceId" :placeholder="$t('roleConfig.pleaseSelect')" class="form-select">
|
||||
<el-option v-for="(item, index) in voiceOptions" :key="`voice-${index}`" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
@@ -139,6 +139,7 @@
|
||||
import Api from '@/apis/api';
|
||||
import FunctionDialog from "@/components/FunctionDialog.vue";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import i18n from '@/i18n';
|
||||
|
||||
export default {
|
||||
name: 'RoleConfigPage',
|
||||
@@ -166,13 +167,13 @@ export default {
|
||||
}
|
||||
},
|
||||
models: [
|
||||
{ label: '语音活动检测(VAD)', key: 'vadModelId', type: 'VAD' },
|
||||
{ label: '语音识别(ASR)', key: 'asrModelId', type: 'ASR' },
|
||||
{ label: '大语言模型(LLM)', key: 'llmModelId', type: 'LLM' },
|
||||
{ label: '视觉大模型(VLLM)', key: 'vllmModelId', type: 'VLLM' },
|
||||
{ label: '意图识别(Intent)', key: 'intentModelId', type: 'Intent' },
|
||||
{ label: '记忆(Memory)', key: 'memModelId', type: 'Memory' },
|
||||
{ label: '语音合成(TTS)', key: 'ttsModelId', type: 'TTS' }
|
||||
{ label: this.$t('roleConfig.vad'), key: 'vadModelId', type: 'VAD' },
|
||||
{ label: this.$t('roleConfig.asr'), key: 'asrModelId', type: 'ASR' },
|
||||
{ label: this.$t('roleConfig.llm'), key: 'llmModelId', type: 'LLM' },
|
||||
{ label: this.$t('roleConfig.vllm'), key: 'vllmModelId', type: 'VLLM' },
|
||||
{ label: this.$t('roleConfig.intent'), key: 'intentModelId', type: 'Intent' },
|
||||
{ label: this.$t('roleConfig.memory'), key: 'memModelId', type: 'Memory' },
|
||||
{ label: this.$t('roleConfig.tts'), key: 'ttsModelId', type: 'TTS' }
|
||||
],
|
||||
llmModeTypeMap: new Map(),
|
||||
modelOptions: {},
|
||||
@@ -221,21 +222,21 @@ export default {
|
||||
Api.agent.updateAgentConfig(this.$route.query.agentId, configData, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success({
|
||||
message: '配置保存成功',
|
||||
message: i18n.t('roleConfig.saveSuccess'),
|
||||
showClose: true
|
||||
});
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || '配置保存失败',
|
||||
message: data.msg || i18n.t('roleConfig.saveFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
resetConfig() {
|
||||
this.$confirm('确定要重置配置吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(i18n.t('roleConfig.confirmReset'), i18n.t('message.info'), {
|
||||
confirmButtonText: i18n.t('button.ok'),
|
||||
cancelButtonText: i18n.t('button.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.form = {
|
||||
@@ -260,7 +261,7 @@ export default {
|
||||
}
|
||||
this.currentFunctions = [];
|
||||
this.$message.success({
|
||||
message: '配置已重置',
|
||||
message: i18n.t('roleConfig.resetSuccess'),
|
||||
showClose: true
|
||||
})
|
||||
}).catch(() => {
|
||||
@@ -271,7 +272,7 @@ export default {
|
||||
if (data.code === 0) {
|
||||
this.templates = data.data;
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取模板列表失败');
|
||||
this.$message.error(data.msg || i18n.t('roleConfig.fetchTemplatesFailed'));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -281,12 +282,12 @@ export default {
|
||||
try {
|
||||
this.applyTemplateData(template);
|
||||
this.$message.success({
|
||||
message: `「${template.agentName}」模板已应用`,
|
||||
message: `${template.agentName}${i18n.t('roleConfig.templateApplied')}`,
|
||||
showClose: true
|
||||
});
|
||||
} catch (error) {
|
||||
this.$message.error({
|
||||
message: '应用模板失败',
|
||||
message: i18n.t('roleConfig.applyTemplateFailed'),
|
||||
showClose: true
|
||||
});
|
||||
console.error('应用模板失败:', error);
|
||||
@@ -361,7 +362,7 @@ export default {
|
||||
this.updateIntentOptionsVisibility();
|
||||
});
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取配置失败');
|
||||
this.$message.error(data.msg || i18n.t('roleConfig.fetchConfigFailed'));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -381,7 +382,7 @@ export default {
|
||||
this.updateIntentOptionsVisibility();
|
||||
}
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取模型列表失败');
|
||||
this.$message.error(data.msg || i18n.t('roleConfig.fetchModelsFailed'));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -457,7 +458,7 @@ export default {
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
this.$message.error(data.msg || '获取插件列表失败');
|
||||
this.$message.error(data.msg || i18n.t('roleConfig.fetchPluginsFailed'));
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user