提示词保存

This commit is contained in:
2025-11-13 01:06:28 +08:00
parent fc7d2ccea5
commit c652d0ddf3
49 changed files with 4072 additions and 2452 deletions

View File

@@ -0,0 +1,489 @@
<script setup>
import { ref, onMounted, reactive, h } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { EditOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
import { UserPromptApi } from '@/api/userPrompt'
import { useUserStore } from '@/stores/user'
import dayjs from 'dayjs'
const userStore = useUserStore()
// 表格数据
const dataSource = ref([])
const loading = ref(false)
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total}`,
})
// 搜索表单
const searchForm = reactive({
name: '',
category: '',
status: undefined,
})
// 编辑弹窗
const editModalVisible = ref(false)
const editForm = reactive({
id: null,
name: '',
content: '',
category: '',
status: 1,
_originalRecord: null, // 保存原始记录,用于更新时获取必需字段
})
const editFormRef = ref(null)
// 表格列定义
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
width: 200,
ellipsis: true,
},
{
title: '内容',
dataIndex: 'content',
key: 'content',
ellipsis: true,
customRender: ({ text }) => {
if (!text) return '-'
const preview = text.length > 100 ? text.substring(0, 100) + '...' : text
return h('span', { title: text }, preview)
},
},
{
title: '分类',
dataIndex: 'category',
key: 'category',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
customRender: ({ text }) => {
return h('span', {
style: {
color: text === 1 ? '#52c41a' : '#ff4d4f',
},
}, text === 1 ? '启用' : '禁用')
},
},
{
title: '使用次数',
dataIndex: 'useCount',
key: 'useCount',
width: 120,
sorter: (a, b) => (a.useCount || 0) - (b.useCount || 0),
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
width: 180,
customRender: ({ text }) => {
return text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '-'
},
},
{
title: '操作',
key: 'action',
width: 150,
fixed: 'right',
customRender: ({ record }) => {
return h('div', { style: { display: 'flex', gap: '8px' } }, [
h('a-button', {
type: 'link',
size: 'small',
onClick: () => handleEdit(record),
}, [
h(EditOutlined),
'编辑',
]),
h('a-button', {
type: 'link',
size: 'small',
danger: true,
onClick: () => handleDelete(record),
}, [
h(DeleteOutlined),
'删除',
]),
])
},
},
]
// 加载数据
async function loadData() {
loading.value = true
try {
const params = {
pageNo: pagination.current,
pageSize: pagination.pageSize,
name: searchForm.name || undefined,
category: searchForm.category || undefined,
status: searchForm.status,
}
const response = await UserPromptApi.getUserPromptPage(params)
if (response && (response.code === 0 || response.code === 200)) {
dataSource.value = response.data?.list || []
pagination.total = response.data?.total || 0
} else {
throw new Error(response?.msg || response?.message || '加载失败')
}
} catch (error) {
console.error('加载提示词列表失败:', error)
message.error(error?.message || '加载失败,请稍后重试')
} finally {
loading.value = false
}
}
// 搜索
function handleSearch() {
pagination.current = 1
loadData()
}
// 重置搜索
function handleReset() {
searchForm.name = ''
searchForm.category = ''
searchForm.status = undefined
pagination.current = 1
loadData()
}
// 新增
function handleAdd() {
editForm.id = null
editForm.name = ''
editForm.content = ''
editForm.category = ''
editForm.status = 1
editForm._originalRecord = null
editModalVisible.value = true
}
// 编辑
function handleEdit(record) {
editForm.id = record.id
editForm.name = record.name || ''
editForm.content = record.content || ''
editForm.category = record.category || ''
editForm.status = record.status ?? 1
// 保存原始记录的完整信息,用于更新时传递必需字段
editForm._originalRecord = record
editModalVisible.value = true
}
// 保存(新增/编辑)
async function handleSave() {
try {
await editFormRef.value.validate()
} catch (error) {
return
}
loading.value = true
try {
const payload = {
name: editForm.name.trim(),
content: editForm.content.trim(),
category: editForm.category.trim() || null,
status: editForm.status,
}
if (editForm.id) {
// 更新:只需要传递要修改的字段,后端会自动填充其他字段
payload.id = editForm.id
// 注意sort、useCount、isPublic 等字段后端会自动从数据库获取,无需前端传递
const response = await UserPromptApi.updateUserPrompt(payload)
if (response && (response.code === 0 || response.code === 200)) {
message.success('更新成功')
editModalVisible.value = false
loadData()
} else {
throw new Error(response?.msg || response?.message || '更新失败')
}
} else {
// 新增:需要包含所有必需字段
payload.sort = 0
payload.useCount = 0
payload.isPublic = false
const response = await UserPromptApi.createUserPrompt(payload)
if (response && (response.code === 0 || response.code === 200)) {
message.success('创建成功')
editModalVisible.value = false
loadData()
} else {
throw new Error(response?.msg || response?.message || '创建失败')
}
}
} catch (error) {
console.error('保存提示词失败:', error)
message.error(error?.message || '保存失败,请稍后重试')
} finally {
loading.value = false
}
}
// 删除
function handleDelete(record) {
Modal.confirm({
title: '确认删除',
content: `确定要删除提示词"${record.name}"吗?`,
onOk: async () => {
loading.value = true
try {
const response = await UserPromptApi.deleteUserPrompt(record.id)
if (response && (response.code === 0 || response.code === 200)) {
message.success('删除成功')
loadData()
} else {
throw new Error(response?.msg || response?.message || '删除失败')
}
} catch (error) {
console.error('删除提示词失败:', error)
message.error(error?.message || '删除失败,请稍后重试')
} finally {
loading.value = false
}
},
})
}
// 分页变化
function handleTableChange(pag) {
pagination.current = pag.current
pagination.pageSize = pag.pageSize
loadData()
}
// 初始化
onMounted(() => {
loadData()
})
</script>
<template>
<div class="style-settings-page">
<div class="page-header">
<h1 class="page-title">风格设置</h1>
<p class="page-description">管理您的提示词模板用于内容创作和风格定制</p>
</div>
<div class="page-content">
<!-- 搜索表单 -->
<div class="search-card card">
<a-form :model="searchForm" layout="inline" class="search-form">
<a-form-item label="名称">
<a-input
v-model:value="searchForm.name"
placeholder="请输入提示词名称"
allow-clear
style="width: 200px"
@pressEnter="handleSearch"
/>
</a-form-item>
<a-form-item label="分类">
<a-input
v-model:value="searchForm.category"
placeholder="请输入分类"
allow-clear
style="width: 200px"
@pressEnter="handleSearch"
/>
</a-form-item>
<a-form-item label="状态">
<a-select
v-model:value="searchForm.status"
placeholder="请选择状态"
allow-clear
style="width: 120px"
>
<a-select-option :value="1">启用</a-select-option>
<a-select-option :value="0">禁用</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">搜索</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</a-form-item>
</a-form>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<a-button type="primary" @click="handleAdd">
<template #icon>
<PlusOutlined />
</template>
新增提示词
</a-button>
</div>
<!-- 表格 -->
<div class="table-card card">
<a-table
:dataSource="dataSource"
:columns="columns"
:loading="loading"
:pagination="pagination"
rowKey="id"
@change="handleTableChange"
:scroll="{ x: 1200 }"
/>
</div>
</div>
<!-- 编辑弹窗 -->
<a-modal
v-model:visible="editModalVisible"
:title="editForm.id ? '编辑提示词' : '新增提示词'"
width="800px"
@ok="handleSave"
@cancel="editModalVisible = false"
>
<a-form
ref="editFormRef"
:model="editForm"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
>
<a-form-item
label="名称"
name="name"
:rules="[{ required: true, message: '请输入提示词名称' }]"
>
<a-input v-model:value="editForm.name" placeholder="请输入提示词名称" />
</a-form-item>
<a-form-item
label="内容"
name="content"
:rules="[{ required: true, message: '请输入提示词内容' }]"
>
<a-textarea
v-model:value="editForm.content"
placeholder="请输入提示词内容"
:rows="8"
/>
</a-form-item>
<a-form-item label="分类" name="category">
<a-input v-model:value="editForm.category" placeholder="请输入分类(可选)" />
</a-form-item>
<a-form-item
label="状态"
name="status"
:rules="[{ required: true, message: '请选择状态' }]"
>
<a-radio-group v-model:value="editForm.status">
<a-radio :value="1">启用</a-radio>
<a-radio :value="0">禁用</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<style scoped>
.style-settings-page {
padding: 24px;
min-height: calc(100vh - 70px);
background: var(--color-bg);
}
.page-header {
margin-bottom: 24px;
}
.page-title {
font-size: 24px;
font-weight: 600;
color: var(--color-text);
margin: 0 0 8px 0;
}
.page-description {
font-size: 14px;
color: var(--color-text-secondary);
margin: 0;
}
.page-content {
display: flex;
flex-direction: column;
gap: 16px;
}
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-card);
padding: 16px;
}
.search-card {
padding: 16px;
}
.search-form {
margin: 0;
}
.action-bar {
display: flex;
justify-content: flex-end;
}
.table-card {
padding: 0;
overflow: hidden;
}
:deep(.ant-table) {
background: transparent;
}
:deep(.ant-table-thead > tr > th) {
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
color: var(--color-text);
}
:deep(.ant-table-tbody > tr > td) {
border-bottom: 1px solid var(--color-border);
color: var(--color-text);
}
:deep(.ant-table-tbody > tr:hover > td) {
background: var(--color-bg);
}
:deep(.ant-pagination) {
margin: 16px;
}
</style>