|
|
@@ -3,24 +3,14 @@
|
|
|
<div class="codex-panel-head">
|
|
|
<span>会话</span>
|
|
|
<span class="codex-panel-head__count">{{ sessions.length }}</span>
|
|
|
- <span class="codex-panel-head__spacer"></span>
|
|
|
- <button class="codex-btn" type="button" title="导出会话列表(仅元数据,不含消息正文)" @click="onExportSessions">
|
|
|
- 导出
|
|
|
- </button>
|
|
|
- <button class="codex-btn" type="button" title="导入会话列表" @click="onImportClick">导入</button>
|
|
|
- <button class="codex-btn" type="button" :disabled="creating" @click="onCreate">
|
|
|
- <Icon icon="mdi:plus" :size="13" />
|
|
|
- 新建
|
|
|
- </button>
|
|
|
- <input
|
|
|
- ref="fileInputRef"
|
|
|
- class="codex-session__file"
|
|
|
- type="file"
|
|
|
- accept="application/json,.json"
|
|
|
- @change="onFileChange"
|
|
|
- />
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 新建会话:唯一入口,放大成整行主按钮 -->
|
|
|
+ <button class="codex-session__create" type="button" :disabled="creating" @click="onCreate">
|
|
|
+ <Icon icon="mdi:plus" :size="16" />
|
|
|
+ {{ creating ? '创建中…' : '新建会话' }}
|
|
|
+ </button>
|
|
|
+
|
|
|
<div class="codex-panel-body">
|
|
|
<div v-if="!sessions.length" class="codex-placeholder">
|
|
|
还没有会话。<br />
|
|
|
@@ -86,11 +76,10 @@
|
|
|
*/
|
|
|
import { computed, nextTick, ref } from 'vue';
|
|
|
import { Input, Modal } from 'ant-design-vue';
|
|
|
- import { downloadByData } from '@/core/utils/file/download';
|
|
|
import Icon from '@/core/components/Icon/src/Icon.vue';
|
|
|
import { useMessage } from '@/core/hooks/web/useMessage';
|
|
|
import { useCodexChatStore } from '../store/codexChatStore';
|
|
|
- import { codexTimestampTag, formatCodexRelative } from '../utils/codexFormat';
|
|
|
+ import { formatCodexRelative } from '../utils/codexFormat';
|
|
|
import type { CodexSessionMeta } from '../types/codexTypes';
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
@@ -107,7 +96,6 @@
|
|
|
const renamingId = ref('');
|
|
|
const renameDraft = ref('');
|
|
|
const renameInputRef = ref();
|
|
|
- const fileInputRef = ref<HTMLInputElement>();
|
|
|
|
|
|
const sessions = computed(() => store.sessions);
|
|
|
const activeThreadId = computed(() => store.activeThreadId);
|
|
|
@@ -138,38 +126,6 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* ---------------- 会话列表导出 / 导入 ---------------- */
|
|
|
-
|
|
|
- function onExportSessions() {
|
|
|
- if (!store.sessions.length) {
|
|
|
- showMessage('还没有会话可导出', 'warning');
|
|
|
- return;
|
|
|
- }
|
|
|
- downloadByData(
|
|
|
- store.exportSessionsJson(),
|
|
|
- `codex-sessions-${codexTimestampTag()}.json`,
|
|
|
- 'application/json;charset=utf-8',
|
|
|
- );
|
|
|
- showMessage(`已导出 ${store.sessions.length} 条会话记录`, 'success');
|
|
|
- }
|
|
|
-
|
|
|
- function onImportClick() {
|
|
|
- fileInputRef.value?.click();
|
|
|
- }
|
|
|
-
|
|
|
- async function onFileChange(event: Event) {
|
|
|
- const input = event.target as HTMLInputElement;
|
|
|
- const file = input.files?.[0];
|
|
|
- input.value = '';
|
|
|
- if (!file) return;
|
|
|
- try {
|
|
|
- const { imported, skipped } = store.importSessionsJson(await file.text());
|
|
|
- showMessage(`导入完成:新增 ${imported}、跳过 ${skipped}(重复或本机已存在)`, 'success');
|
|
|
- } catch (error: any) {
|
|
|
- showMessage(error?.message || '导入失败:文件格式不正确', 'error');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
function startRename(session: CodexSessionMeta) {
|
|
|
renamingId.value = session.threadId;
|
|
|
renameDraft.value = session.title;
|
|
|
@@ -223,15 +179,6 @@
|
|
|
flex: 1 1 auto;
|
|
|
}
|
|
|
|
|
|
- /* 导入会话列表用的隐藏 file input:不能 display:none,否则某些环境点不动 */
|
|
|
- .codex-session__file {
|
|
|
- position: absolute;
|
|
|
- width: 1px;
|
|
|
- height: 1px;
|
|
|
- opacity: 0;
|
|
|
- pointer-events: none;
|
|
|
- }
|
|
|
-
|
|
|
/* 「外部导入」标记 */
|
|
|
.codex-session__external {
|
|
|
flex: 0 0 auto;
|
|
|
@@ -302,6 +249,38 @@
|
|
|
text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
+ /* 新建会话:整行主按钮(顶栏的新建已移除,这是唯一入口) */
|
|
|
+ &__create {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 6px;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ height: 34px;
|
|
|
+ margin: var(--codex-sp-2) 10px;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--codex-primary);
|
|
|
+ background: var(--codex-primary-soft);
|
|
|
+ border: 1px dashed fade(@primary-color, 45%);
|
|
|
+ border-radius: var(--codex-radius);
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ background 0.15s ease-out,
|
|
|
+ border-color 0.15s ease-out;
|
|
|
+
|
|
|
+ &:hover:not(:disabled) {
|
|
|
+ color: #fff;
|
|
|
+ background: var(--codex-primary);
|
|
|
+ border-color: var(--codex-primary);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:disabled {
|
|
|
+ opacity: 0.6;
|
|
|
+ cursor: not-allowed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
&__meta {
|
|
|
flex: 0 0 auto;
|
|
|
font-size: 11px;
|