diff --git a/.gitignore b/.gitignore index 7b36447d2..e45925676 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,4 @@ tools/NcfDesktopApp.GUI/安全性与隐私中允许* tools/NcfDesktopApp.GUI/publish-self-contained/* /.github/appmod/custom-tasks/ *.tmp +/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Generated/Senparc.Xncf.XncfBuilder.DynamicContentGenerator/MultiFileCodeGenerator/BuildXncfAppService.Generated.cs diff --git a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml index 51fae8409..cfebf9b14 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml @@ -1,4 +1,4 @@ -@page +@page @model Senparc.Xncf.AIKernel.Areas.AIKernel.Pages.Index @{ ViewData["Title"] = "AIKernel 首页"; @@ -15,13 +15,32 @@ 首页 } -
-
- 添加 - 导入 NeuCharAI 云端模型算力 -
+
+
+ @* 查询区:按名称(别名)查询 *@ +
+
+ + + 查询 + 重置 +
+
+ + @* 工具栏 *@ +
+ 添加 + 导入 NeuCharAI 云端模型算力 +
- + @* 表格区域 *@ +
+ @* alias *@ @@ -76,34 +95,35 @@ @* 操作 *@ - + -
- -
+ @* 分页:首页、上一页、下一页、尾页、跳转、每页 5/10/15/20/50/100 条,默认 10 *@ +
+ + +
+
- @* dialog for 添加模型 *@ - - - - + @* 添加模型弹框 *@ + + + + - + @@ -143,8 +163,8 @@ - - + + @@ -158,7 +178,7 @@ - + @@ -174,18 +194,19 @@ - - 取 消 - 确 定 - + - @* dialog for 编辑模型 *@ - - - - + + @* 编辑模型弹框 *@ + + + + - + @* NeuCharAI = 4, *@ @* OpenAI = 8, *@ @* AzureOpenAI = 16, *@ @@ -246,7 +267,7 @@ - + @@ -265,27 +286,30 @@ - - 取 消 - 确 定 - + - - - - - - - - - -

   可从 https://www.neuchar.com/Developer/AiApp 页面中,点击【显示 ApiKey】按钮查看到 DeveloperId 及 ApiKey,如果您有多项,可分批次导入。

-
- - 取消 - 确定 - + @* 导入 NeuCharAI 云端模型算力弹框 *@ + + + + + + + + +
+ + 可从 NeuChar 开发者中心 页面中,点击【显示 ApiKey】查看 DeveloperId 及 ApiKey,多项可分批次导入。 +
+
+
diff --git a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml index 94a7e5d5f..c8a48a466 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml @@ -1,4 +1,4 @@ -@page +@page @model Senparc.Xncf.AIKernel.Areas.AIVector.Pages.Index @{ ViewData["Title"] = "AIVector 首页"; @@ -15,12 +15,31 @@ 首页 } -
-
- 添加 -
+
+
+ @* 查询区:按向量数据库名称(关键字)查询 *@ +
+
+ + + 查询 + 重置 +
+
+ + @* 工具栏 *@ +
+ 添加 +
- + @* 表格区域 *@ +
+ @* alias *@ @@ -55,34 +74,44 @@ @* 操作 *@ - + -
- - +
+ @* 分页:首页、上一页、下一页、尾页、跳转、每页条数 5/10/15/20/50/100,默认 10 *@ +
+ + +
@* dialog for 添加向量数据库 *@ - - - - + + + + - + @@ -93,40 +122,39 @@ - @* - - - - - - - - - *@ - - + + - - + + - - + + - - 取 消 - 确 定 - + @* dialog for 编辑向量数据库 *@ - - - - + + + + - + @@ -137,37 +165,25 @@ - @* - - - - - - - - - *@ - - - + + - - + + - - + + - - @* use switch *@ + - - 取 消 - 确 定 - +
diff --git a/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIVectorAppService.cs b/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIVectorAppService.cs index 0d59ca651..b06359b16 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIVectorAppService.cs +++ b/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIVectorAppService.cs @@ -45,13 +45,12 @@ public AIVectorAppService( protected virtual Expression> GetListWhere(AIVector_GetListRequest request) { SenparcExpressionHelper helper = new(); - //helper.ValueCompare - // .AndAlso(!request.Alias.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.Alias, request.Alias)) - // .AndAlso(!request.DeploymentName.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.DeploymentName, request.DeploymentName)) - // .AndAlso(!request.Endpoint.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.Endpoint, request.Endpoint)) - // .AndAlso(!request.OrganizationId.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.OrganizationId, request.OrganizationId)) - // .AndAlso(request.Show != null, z => z.Show == request.Show) - // ; + helper.ValueCompare + .AndAlso(!request.Alias.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.Alias, $"%{request.Alias}%")) + .AndAlso(!request.Name.IsNullOrWhiteSpace(), z => EF.Functions.Like(z.Name, $"%{request.Name}%")) + //.AndAlso(request.VectorDBType != 0, z => z.VectorDBType == request.VectorDBType) + .AndAlso(request.Show != null, z => z.Show == request.Show) + ; return helper.BuildWhereExpression(); } diff --git a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIKernel/index.css b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIKernel/index.css index 1d2a75483..b1f2a90c5 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIKernel/index.css +++ b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIKernel/index.css @@ -1,9 +1,157 @@ -.d-flex{ +.d-flex { display: flex; } +.justify-content-between { justify-content: space-between; } +.align-items-center { align-items: center; } + +/* 页面整体 */ +.aikernel-index-page { + padding: 20px 24px; + background: #f0f2f5; + min-height: 100%; + box-sizing: border-box; +} + +.aikernel-main { + background: #fff; + border-radius: 8px; + padding: 20px 24px; + box-shadow: 0 1px 4px rgba(0,0,0,0.06); +} + +/* 查询区 */ +.aikernel-query-section { + margin-bottom: 20px; + padding: 16px 18px; + background: #fafbfc; + border: 1px solid #e8eaec; + border-radius: 6px; +} + +.aikernel-query-row { display: flex; + align-items: center; + flex-wrap: wrap; + gap: 12px; } -.justify-content-between{ - justify-content: space-between; + +.aikernel-query-label { + flex: 0 0 auto; + font-size: 14px; + color: #606266; + margin: 0; } -.align-items-center{ + +.aikernel-query-input { + width: 260px; +} + +.aikernel-query-row .el-button { margin-left: 0; } + +/* 工具栏 */ +.aikernel-toolbar { + display: flex; align-items: center; + gap: 12px; + margin-bottom: 16px; +} + +.aikernel-toolbar .el-button { margin: 0; } + +/* 表格:限制高度,超出显示纵向滚动条,内容控制在页面内 */ +.aikernel-table-wrap { + margin-bottom: 16px; + max-height: calc(100vh - 280px); + overflow-y: auto; + overflow-x: auto; +} + +.aikernel-table-wrap .el-table { + margin-bottom: 0; +} + +.aikernel-table-wrap .el-table th { + background: #f5f7fa; + color: #303133; + font-weight: 600; +} + +/* 分页 */ +.aikernel-pagination-wrap { + display: flex; + justify-content: flex-end; + padding-top: 12px; + border-top: 1px solid #ebeef5; +} + +.aikernel-pagination-wrap .el-pagination { + flex-wrap: nowrap; + white-space: nowrap; +} + +/* 弹框通用(添加/编辑/导入 NeuCharAI) */ +.aikernel-form-dialog .el-dialog__header { + padding: 18px 24px 14px; + border-bottom: 1px solid #ebeef5; +} + +.aikernel-form-dialog .el-dialog__title { + font-size: 16px; + font-weight: 600; + color: #303133; +} + +.aikernel-form-dialog .el-dialog__body { + padding: 24px 24px 20px; +} + +.aikernel-dialog-form .el-form-item { + margin-bottom: 18px; +} + +.aikernel-dialog-form .el-form-item__label { + font-size: 13px; + color: #606266; + line-height: 1.4; + padding-bottom: 6px; +} + +.aikernel-dialog-form .el-form-item__content .el-input, +.aikernel-dialog-form .el-form-item__content .el-select { + width: 100%; +} + +.aikernel-dialog-footer { + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 14px 24px 18px; + border-top: 1px solid #ebeef5; +} + +.aikernel-dialog-footer .el-button { + min-width: 80px; +} + +/* 导入 NeuCharAI 说明 */ +.aikernel-neuchar-tip { + font-size: 13px; + color: #606266; + line-height: 1.6; + padding: 12px 14px; + background: #f4f4f5; + border-radius: 4px; + margin-top: 8px; +} + +.aikernel-neuchar-tip i { + margin-right: 6px; + color: #909399; +} + +.aikernel-neuchar-tip a { + color: #409eff; + text-decoration: none; +} + +.aikernel-neuchar-tip a:hover { + text-decoration: underline; } \ No newline at end of file diff --git a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIVector/index.css b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIVector/index.css index 3733b6d84..1e61be917 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIVector/index.css +++ b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIVector/index.css @@ -1,9 +1,141 @@ -.d-flex{ +.d-flex{ display: flex; } +.justify-content-between{ justify-content: space-between; } +.align-items-center{ align-items: center; } + +/* 页面整体 */ +.aivector-index-page { + padding: 20px 24px; + background: #f0f2f5; + min-height: 100%; + box-sizing: border-box; +} + +.aivector-main { + background: #fff; + border-radius: 8px; + padding: 20px 24px; + box-shadow: 0 1px 4px rgba(0,0,0,0.06); +} + +/* 查询区 */ +.aivector-query-section { + margin-bottom: 20px; + padding: 16px 18px; + background: #fafbfc; + border: 1px solid #e8eaec; + border-radius: 6px; +} + +.aivector-query-row { display: flex; + align-items: center; + flex-wrap: wrap; + gap: 12px; } -.justify-content-between{ - justify-content: space-between; + +.aivector-query-label { + flex: 0 0 auto; + font-size: 14px; + color: #606266; + margin: 0; } -.align-items-center{ + +.aivector-query-input { + width: 260px; +} + +.aivector-query-row .el-button { + margin-left: 0; +} + +/* 工具栏 */ +.aivector-toolbar { + display: flex; align-items: center; -} \ No newline at end of file + gap: 12px; + margin-bottom: 16px; +} + +.aivector-toolbar .el-button { + margin: 0; +} + +/* 表格区域 */ +.aivector-table-wrap { + margin-bottom: 16px; + max-height: calc(100vh - 280px); + overflow-y: auto; + overflow-x: auto; +} + +.aivector-table-wrap .el-table { + margin-bottom: 0; +} + +.aivector-table-wrap .el-table th { + background: #f5f7fa; + color: #303133; + font-weight: 600; +} + +/* 分页 */ +.aivector-pagination-wrap { + display: flex; + justify-content: flex-end; + padding-top: 12px; + border-top: 1px solid #ebeef5; +} + +.aivector-pagination-wrap .el-pagination { + flex-wrap: nowrap; + white-space: nowrap; +} + +/* 弹框样式(新增 / 编辑向量数据库) */ +.aivector-form-dialog .el-dialog__header { + padding: 18px 24px 14px; + border-bottom: 1px solid #ebeef5; +} + +.aivector-form-dialog .el-dialog__title { + font-size: 16px; + font-weight: 600; + color: #303133; +} + +.aivector-form-dialog .el-dialog__body { + padding: 24px 24px 20px; +} + +.aivector-dialog-form .el-form-item { + margin-bottom: 18px; +} + +.aivector-dialog-form .el-form-item__label { + font-size: 13px; + color: #606266; + line-height: 1.4; + padding-bottom: 6px; +} + +.aivector-dialog-form .el-form-item__content .el-input, +.aivector-dialog-form .el-form-item__content .el-select, +.aivector-dialog-form .el-form-item__content .el-textarea { + width: 100%; +} + +.aivector-dialog-footer { + display: flex; + justify-content: flex-end; + padding: 14px 24px 18px; + border-top: 1px solid #ebeef5; +} + +.aivector-dialog-footer .el-button { + min-width: 80px; +} + +.aivector-dialog-footer .el-button + .el-button { + margin-left: 12px; +} + diff --git a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIKernel/index.js b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIKernel/index.js index e4e1599de..72c156657 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIKernel/index.js +++ b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIKernel/index.js @@ -2,9 +2,10 @@ var app = new Vue({ el: "#app", data() { return { + keyword: '', page: { page: 1, - size: 10 + size: 8 }, tableLoading: true, tableData: [], @@ -105,6 +106,14 @@ var app = new Vue({ } } }, + watch: { + 'addForm.aiPlatform'(val) { + if (val === '512') this.addForm.endpoint = this.addForm.endpoint || 'https://api.deepseek.com'; + }, + 'editForm.aiPlatform'(val) { + if (val === '512') this.editForm.endpoint = this.editForm.endpoint || 'https://api.deepseek.com'; + } + }, mounted() { //wait page load setTimeout(async () => { @@ -115,6 +124,24 @@ var app = new Vue({ async init() { await this.getDataList(); }, + handleSearch() { + this.page.page = 1; + this.getDataList(); + }, + resetCondition() { + this.keyword = ''; + this.page.page = 1; + this.getDataList(); + }, + handlePageSizeChange(val) { + this.page.size = val; + this.page.page = 1; + this.getDataList(); + }, + handlePageChange(val) { + this.page.page = val; + this.getDataList(); + }, async handleSizeChange(val) { this.page.size = val; await this.getDataList(); @@ -124,17 +151,21 @@ var app = new Vue({ await this.getDataList(); }, async getDataList() { - this.tableLoading = true - await service.post('/api/Senparc.Xncf.AIKernel/AIModelAppService/Xncf.AIKernel_AIModelAppService.GetPagedListAsync', { - "page": this.page.page, - "size": this.page.size, - }) - .then(res => { - console.log(res) - this.tableData = res.data.data.data; - this.total = res.data.data.total; - this.tableLoading = false - }) + this.tableLoading = true; + var alias = this.keyword ? (String(this.keyword).trim() || null) : null; + if (alias && alias.indexOf('%') === -1) alias = '%' + alias + '%'; + var payload = { page: this.page.page, size: this.page.size }; + if (alias) payload.alias = alias; + try { + var res = await service.post('/api/Senparc.Xncf.AIKernel/AIModelAppService/Xncf.AIKernel_AIModelAppService.GetPagedListAsync', payload); + var data = res && res.data && res.data.data; + this.tableData = (data && Array.isArray(data.data)) ? data.data : []; + this.total = (data && typeof data.total === 'number') ? data.total : 0; + } catch (e) { + this.tableData = []; + this.total = 0; + } + this.tableLoading = false; }, addModel() { this.addFormDialogVisible = true; @@ -143,17 +174,22 @@ var app = new Vue({ this.neuCharFormDialogVisible = true; // 显示对话框 }, copyInfo(key) { - // 把结果复制到剪切板 - const input = document.createElement('input') - input.setAttribute('readonly', 'readonly') - input.setAttribute('value', key) - document.body.appendChild(input) - input.select() - input.setSelectionRange(0, 9999) - if (document.execCommand('copy')) { - document.execCommand('copy') - //提示时展示'******'+key的后4位 - this.$message.success(`已复制【******${key.slice(-4)}】!`) + if (key == null || key === '') { + this.$message.warning('无 Api Key 可复制'); + return; + } + var input = document.createElement('input'); + input.setAttribute('readonly', 'readonly'); + input.setAttribute('value', key); + document.body.appendChild(input); + input.select(); + input.setSelectionRange(0, 9999); + try { + if (document.execCommand('copy')) { + this.$message.success('已复制【******' + key.slice(-4) + '】!'); + } + } finally { + document.body.removeChild(input); } }, async addModelSubmit() { diff --git a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIVector/index.js b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIVector/index.js index 9455b85fc..04ce3ac53 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIVector/index.js +++ b/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIVector/index.js @@ -2,6 +2,7 @@ var app = new Vue({ el: "#app", data() { return { + keyword: '', page: { page: 1, size: 10 @@ -76,17 +77,29 @@ var app = new Vue({ }, async handleSizeChange(val) { this.page.size = val; + this.page.page = 1; await this.getDataList(); }, async handleCurrentChange(val) { this.page.page = val; await this.getDataList(); }, + async handleSearch() { + this.page.page = 1; + await this.getDataList(); + }, + async resetCondition() { + this.keyword = ''; + this.page.page = 1; + this.page.size = 10; + await this.getDataList(); + }, async getDataList() { this.tableLoading = true await service.post('/api/Senparc.Xncf.AIKernel/AIVectorAppService/Xncf.AIKernel_AIVectorAppService.GetPagedListAsync', { "page": this.page.page, "size": this.page.size, + "name": this.keyword }) .then(res => { console.log(res) diff --git a/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml b/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml index b6a08459d..cd8a4d298 100644 --- a/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml @@ -1,4 +1,4 @@ -@page +@page @model Senparc.Xncf.FileManager.Areas.FileManager.Pages.Index @{ ViewData["Title"] = "FileManager 首页"; @@ -12,23 +12,21 @@ } @section style{ + } -
-
- -
- 文件夹 - 新建 -
- +
+
+ -
-
- 上传文件 - 刷新文件夹 +
+
+ + + + 查询 + 重置 + + 新建 + + 新建文件夹 + + + + 上传 + + 上传文件 + 上传文件夹 + + +
+ @* 添加标签 *@ + 下载 + 复制到 + 移动到 +
-
- + - - - - + + + + - - + + - - + + - - + + - + -
+
-
+
- - - - - - - - - + +
+ + + + + + + + +
取 消 确 定
+ + +
+ + +
{{ editNoteDialog.form.fileName || '—' }}
+
+ + + +
+
+ + 取 消 + 确 定 + +
+ + +
+ +
+ + + + {{ data.name }} + + +
+
+ 当前选择: + {{ moveCopyDialog.selectedFolderName }} +
+
+ +
@section scripts{ @@ -170,8 +257,10 @@ return { tableData: [], tableLoading: false, - page: { page: 1, size: 10 }, + page: { page: 1, size: 8 }, total: 0, + keyword: '', + folderKeyword: '', currentFolderId: null, folderPath: [], folderTree: [{ id: null, name: '根目录', hasChildren: true }], @@ -180,10 +269,32 @@ children: 'children', isLeaf: function (data) { return data && data.hasChildren === false; } }, + multipleSelection: [], uploadDialog: { visible: false, fileList: [], uploading: false }, - createFolderDialog: { visible: false, loading: false, form: { name: '', description: '' } } + createFolderDialog: { visible: false, loading: false, form: { name: '', description: '' } }, + editNoteDialog: { visible: false, loading: false, form: { id: null, fileName: '', note: '' } }, + moveCopyDialog: { + visible: false, + mode: 'move', + selectedFolderId: null, + selectedFolderName: '根目录', + treeData: [{ id: null, name: '根目录', hasChildren: true }], + folderKeyword: '', + recentFolderId: null, + recentFolderName: '', + submitting: false + } }; }, + computed: { + filteredTableData: function () { + var k = (this.keyword || '').toLowerCase(); + if (!k) return this.tableData; + return this.tableData.filter(function (row) { + return (row.fileName || '').toLowerCase().indexOf(k) >= 0; + }); + } + }, created: function () { this.getList(); }, @@ -208,12 +319,163 @@ url += '&folderId=' + this.currentFolderId; } var res = await axios.get(url); - this.tableData = res.data.data; - this.total = res.data.data.totalCount; + var d = res.data.data || res.data; + var list = []; + if (Array.isArray(d)) { + list = d; + } else if (d && typeof d === 'object') { + var keys = Object.keys(d).filter(function (k) { + return k !== 'TotalCount' && k !== 'PageIndex' && k !== 'PageCount' && k !== 'SkipCount' && isNaN(Number(k)) === false; + }); + keys.sort(function (a, b) { return Number(a) - Number(b); }); + list = keys.map(function (k) { return d[k]; }); + } + this.tableData = list; + this.total = (res.data.data && res.data.data.totalCount) || (d && (d.TotalCount !== undefined ? d.TotalCount : d.totalCount)) || (res.data.TotalCount) || (res.data.totalCount) || 0; + this.tableData.forEach(function (row) { + if (row.updaterDisplay === undefined) row.updaterDisplay = '—'; + }); } finally { this.tableLoading = false; } }, + doSearch: function () { + this.page.page = 1; + this.getList(); + }, + handleSelectionChange: function (val) { + this.multipleSelection = val || []; + }, + handleNewCommand: function (cmd) { + if (cmd === 'createFolder') this.showCreateFolderDialog(); + }, + handleUploadCommand: function (cmd) { + if (cmd === 'uploadFile') this.showUploadDialog(); + else if (cmd === 'uploadFolder') this.$message.info('上传文件夹功能开发中'); + }, + batchAddTag: function () { this.$message.info('添加标签功能开发中'); }, + batchDownload: function () { + this.multipleSelection.forEach(function (row) { + window.open('/Admin/FileManager/Index?handler=Download&id=' + row.id); + }); + }, + batchCopyTo: function () { this.openMoveCopyDialog('copy'); }, + batchMoveTo: function () { this.openMoveCopyDialog('move'); }, + openMoveCopyDialog: function (mode) { + this.moveCopyDialog.mode = mode; + this.moveCopyDialog.visible = true; + this.moveCopyDialog.selectedFolderId = null; + this.moveCopyDialog.selectedFolderName = '根目录'; + this.moveCopyDialog.treeData = [{ id: null, name: '根目录', hasChildren: true }]; + this.moveCopyDialog.folderKeyword = ''; + this.moveCopyDialog.submitting = false; + }, + closeMoveCopyDialog: function () { + this.moveCopyDialog.visible = false; + }, + onMoveCopyDialogOpen: function () { + this.$nextTick(function () { + if (this.$refs.moveCopyTree && typeof this.$refs.moveCopyTree.setCurrentKey === 'function') { + this.$refs.moveCopyTree.setCurrentKey(null); + } + }); + }, + loadMoveCopyFolderChildren: async function (node, resolve) { + var self = this; + try { + var parentId = node.level === 0 ? null : node.data.id; + var url = '/Admin/FileManager/Index?handler=Folders'; + if (parentId != null) { url += '&parentId=' + parentId; } + var res = await axios.get(url); + var list = (res.data && res.data.data) ? res.data.data : (res.data || []); + resolve((list || []).map(function (f) { + return { id: f.id, name: f.name, description: f.description, parentId: f.parentId, hasChildren: true }; + })); + } catch (e) { + console.error(e); + resolve([]); + } + }, + onMoveCopyFolderClick: function (data) { + this.moveCopyDialog.selectedFolderId = data.id === null ? null : data.id; + this.moveCopyDialog.selectedFolderName = data.name || '根目录'; + }, + useRecentPath: function () { + if (!this.moveCopyDialog.recentFolderName) return; + this.moveCopyDialog.selectedFolderId = this.moveCopyDialog.recentFolderId; + this.moveCopyDialog.selectedFolderName = this.moveCopyDialog.recentFolderName; + if (this.$refs.moveCopyTree && typeof this.$refs.moveCopyTree.setCurrentKey === 'function') { + this.$refs.moveCopyTree.setCurrentKey(this.moveCopyDialog.recentFolderId); + } + }, + confirmMoveCopy: async function () { + this.moveCopyDialog.submitting = true; + try { + var targetFolderId = this.moveCopyDialog.selectedFolderId; + var ids = this.multipleSelection.map(function (r) { return r.id; }); + if (ids.length === 0) { + this.$message.warning('请先选择要' + (this.moveCopyDialog.mode === 'move' ? '移动' : '复制') + '的文件'); + return; + } + var url = this.moveCopyDialog.mode === 'move' ? '/Admin/FileManager/Index?handler=Move' : '/Admin/FileManager/Index?handler=Copy'; + await axios.post(url, { fileIds: ids, targetFolderId: targetFolderId }); + this.$message.success(this.moveCopyDialog.mode === 'move' ? '移动成功' : '复制成功'); + this.moveCopyDialog.recentFolderId = targetFolderId; + this.moveCopyDialog.recentFolderName = this.moveCopyDialog.selectedFolderName; + this.moveCopyDialog.visible = false; + this.getList(); + } catch (e) { + this.$message.info(this.moveCopyDialog.mode === 'move' ? '移动功能开发中' : '复制功能开发中'); + } finally { + this.moveCopyDialog.submitting = false; + } + }, + goParent: function () { + if (this.folderPath.length > 0) { + var parent = this.folderPath.length === 1 ? null : this.folderPath[this.folderPath.length - 2].id; + this.enterFolder(parent); + } else { + this.enterFolder(null); + } + }, + copyNavPath: function () { + var path = '根目录'; + if (this.folderPath.length > 0) { + path = this.folderPath.map(function (p) { return p.name; }).join(' / '); + } + var ta = document.createElement('textarea'); + ta.value = path; + document.body.appendChild(ta); + ta.select(); + try { + document.execCommand('copy'); + this.$message.success('已复制路径'); + } catch (e) { this.$message.error('复制失败'); } + document.body.removeChild(ta); + }, + openPermission: function (row) { this.$message.info('权限管理功能开发中'); }, + openShare: function (row) { this.$message.info('分享链接功能开发中'); }, + handleRowMore: function (cmd, row) { + if (cmd === 'editNote') this.editNote(row); + else if (cmd === 'delete') this.deleteFile(row); + }, + editNote: function (row) { + this.editNoteDialog.form = { id: row.id, fileName: row.fileName || '', note: row.description || '' }; + this.editNoteDialog.visible = true; + }, + submitEditNote: async function () { + this.editNoteDialog.loading = true; + try { + await axios.post('/Admin/FileManager/Index?handler=EditNote', { id: this.editNoteDialog.form.id, note: this.editNoteDialog.form.note }); + this.$message.success('更新成功'); + this.editNoteDialog.visible = false; + this.getList(); + } catch (e) { + this.$message.error('更新失败'); + } finally { + this.editNoteDialog.loading = false; + } + }, handleCurrentChange: function (val) { this.page.page = val; this.getList(); diff --git a/src/Extensions/Senparc.Xncf.FileManager/wwwroot/css/Admin/FileManager/index.css b/src/Extensions/Senparc.Xncf.FileManager/wwwroot/css/Admin/FileManager/index.css index b4c8ae423..fa7e49d10 100644 --- a/src/Extensions/Senparc.Xncf.FileManager/wwwroot/css/Admin/FileManager/index.css +++ b/src/Extensions/Senparc.Xncf.FileManager/wwwroot/css/Admin/FileManager/index.css @@ -1,9 +1,198 @@ -.filter-container { - padding-bottom: 10px; +/* FileManager 左右布局 */ +.file-manager-layout { + display: flex; + gap: 0; + min-height: 560px; + background: #f5f7fa; +} + +/* 左侧文件夹面板 */ +.folder-panel { + width: 260px; + min-width: 260px; + background: #fff; + border-right: 1px solid #e4e7ed; + display: flex; + flex-direction: column; +} + +.folder-panel-header { + padding: 16px; + border-bottom: 1px solid #ebeef5; +} + +.folder-panel-title { + font-size: 16px; + font-weight: 600; + color: #303133; +} + +.folder-search { + padding: 12px; + border-bottom: 1px solid #ebeef5; +} + +.folder-search .el-input { + width: 100%; +} + +.folder-tree-wrap { + flex: 1; + overflow: auto; + padding: 8px 0; +} + +.folder-tree-wrap .custom-tree-node { + display: inline-flex; + align-items: center; +} + +.folder-tree-wrap .tree-node-label { + margin-left: 6px; +} + +/* 右侧内容区 */ +.content-panel { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + background: #fff; + margin: 0; + padding: 16px 20px; +} + +/* 查询区与工具栏同一行 */ +.query-toolbar-row { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.query-input { + width: 260px; +} + +/* 搜索框内图标垂直居中 */ +.query-toolbar-row .el-input__prefix { + display: flex; + align-items: center; + left: 10px; +} + +.query-toolbar-row .query-input-prefix { + margin: 0; + line-height: 1; +} + +.toolbar-selection { + display: inline-flex; + align-items: center; + gap: 8px; + margin-left: 8px; + padding-left: 12px; + border-left: 1px solid #dcdfe6; +} + +/* 新建下拉项中图标与文字间距 */ +.query-toolbar-row .el-dropdown-menu__item .el-icon-folder { + margin-right: 6px; +} + +/* 导航栏 */ +.nav-bar { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; + padding: 8px 0; + border-bottom: 1px solid #ebeef5; +} + +.nav-back { + color: #606266; + font-size: 13px; + text-decoration: none; + white-space: nowrap; +} + +.nav-back:hover { + color: #409eff; +} + +.nav-breadcrumb { + flex: 1; + font-size: 13px; +} + +.nav-copy { + cursor: pointer; + color: #909399; + font-size: 16px; +} + +.nav-copy:hover { + color: #409eff; +} + +/* 表格:行悬停时显示操作图标 */ +.file-table .el-table__row .row-actions { + visibility: hidden; +} + +.file-table .el-table__row:hover .row-actions { + visibility: visible; +} + +.file-table .col-row-actions { + padding: 8px 0; } -.pagination-container { - padding: 20px 0; +.row-actions { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.row-actions .action-icon, +.row-actions .action-icon-wrap { + cursor: pointer; + color: #909399; + font-size: 16px; +} + +.row-actions .action-icon:hover, +.row-actions .action-icon-wrap:hover { + color: #409eff; +} + +.file-icon { + margin-right: 8px; + color: #409eff; +} + +/* 更新时间列不换行,单行完整显示 */ +.file-table .col-upload-time .cell { + white-space: nowrap; +} + +.file-table .cell-upload-time { + white-space: nowrap; +} + +/* 分页 */ +.pagination-wrap { + margin-top: 16px; + padding: 12px 0; + display: flex; + justify-content: flex-end; +} + +/* 对话框与上传 */ +.filter-container { + padding-bottom: 10px; } .el-upload-dragger { @@ -16,4 +205,190 @@ .el-dialog .el-upload { width: auto; -} \ No newline at end of file +} + +/* ========== 新建文件夹 / 编辑备注 弹窗统一样式 ========== */ +.file-manager-dialog.el-dialog { + border-radius: 8px; + overflow: hidden; +} + +.file-manager-dialog .el-dialog__header { + padding: 16px 20px 12px; + border-bottom: 1px solid #ebeef5; + background: #fafafa; +} + +.file-manager-dialog .el-dialog__title { + font-size: 16px; + font-weight: 600; + color: #303133; +} + +.file-manager-dialog .el-dialog__body { + padding: 24px 20px 20px; +} + +.file-manager-dialog .dialog-body-inner { + min-height: 60px; +} + +.file-manager-dialog .dialog-form .el-form-item { + margin-bottom: 20px; +} + +.file-manager-dialog .dialog-form .el-form-item__label { + color: #606266; + line-height: 40px; + padding-right: 16px; +} + +.file-manager-dialog .dialog-form .el-input__inner, +.file-manager-dialog .dialog-form .el-textarea__inner { + border-radius: 4px; +} + +.file-manager-dialog .dialog-form .el-textarea__inner { + padding: 10px 12px; + line-height: 1.6; + resize: vertical; + min-height: 88px; +} + +.file-manager-dialog .el-dialog__footer { + padding: 12px 20px 16px; + border-top: 1px solid #ebeef5; + text-align: right; +} + +.file-manager-dialog .dialog-footer .el-button { + min-width: 72px; +} + +/* 编辑备注 - 文件名只读展示 */ +.dialog-edit-note .edit-note-filename { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: #f5f7fa; + border-radius: 4px; + color: #606266; + font-size: 13px; + line-height: 1.5; + max-width: 100%; + word-break: break-all; +} + +.dialog-edit-note .edit-note-filename .el-icon-document { + color: #909399; + flex-shrink: 0; +} + +/* ========== 移动到 / 复制到 弹窗 ========== */ +.dialog-move-copy .el-dialog__body { + padding: 16px 20px 12px; +} + +.dialog-move-copy .el-dialog__footer { + padding: 0; +} + +.move-copy-body { + display: flex; + flex-direction: column; + gap: 12px; +} + +.move-copy-search { + flex-shrink: 0; +} + +.move-copy-search-input { + width: 100%; +} + +.move-copy-search .el-input__prefix { + display: flex; + align-items: center; +} + +.move-copy-tree-wrap { + border: 1px solid #ebeef5; + border-radius: 4px; + padding: 12px; + min-height: 240px; + max-height: 320px; + overflow: auto; + background: #fafafa; +} + +.dialog-move-copy .move-copy-tree-wrap .custom-tree-node { + display: inline-flex; + align-items: center; +} + +.dialog-move-copy .move-copy-tree-wrap .tree-node-label { + margin-left: 6px; +} + +.move-copy-current { + flex-shrink: 0; + padding: 10px 12px; + background: #f0f9ff; + border-radius: 4px; + border: 1px solid #d9ecff; + font-size: 13px; +} + +.move-copy-current-label { + color: #606266; + margin-right: 6px; +} + +.move-copy-current-value { + color: #303133; + font-weight: 500; +} + +.move-copy-footer { + display: flex; + flex-direction: column; + gap: 14px; + padding: 14px 20px 16px; + border-top: 1px solid #ebeef5; + background: #fafafa; +} + +.move-copy-recent { + font-size: 13px; + color: #606266; +} + +.move-copy-recent-label { + margin-right: 8px; +} + +.move-copy-recent-path { + color: #303133; + margin-right: 8px; +} + +.move-copy-use-recent { + padding: 0 4px; + font-size: 13px; +} + +.move-copy-recent-empty { + color: #909399; +} + +.move-copy-actions { + display: flex; + justify-content: flex-end; + gap: 10px; +} + +.move-copy-actions .el-button { + min-width: 72px; +} diff --git a/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml b/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml index a2ac9e88e..40e98cf73 100644 --- a/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml @@ -11,39 +11,48 @@ @section breadcrumbs { 扩展模块 - 知识库管理管理 - 知识库管理列表 + 知识库管理 + 知识库列表 } -
+
- - - - - 查询 - 重置 - - -
- - - 新增 - - - - - - - -
- -
-
-
-
+ @* 查询区:按知识库名称查询 *@ +
+
+ + + 查询 + 重置 +
+
+ + @* 工具栏:添加按钮、列筛选 *@ +
+ 添加 + + + + + 列筛选 +
- + + @* 表格区域 *@ +
+ @@ -61,94 +70,109 @@ {{formaTableTime(scope.row.addTime)}} - + +
- - + @* 分页:首页、上一页、下一页、尾页、跳转、每页条数 5/10/15/20/50/100,默认 10 *@ +
+ + +
+ + width="560px" + custom-class="kb-form-dialog" + class="kb-form-dialog-scope"> - - - - - - - + size="small" + label-position="top" + class="kb-form-dialog__form"> + + - +
+
模型与向量库
+ + + + + - - - - + + + + + - - - - - - - - + + + + + +
- - + +
-