prompt-management.blade.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <div>
  2. <div class="flex flex-col gap-y-6">
  3. {{-- 统计信息 --}}
  4. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  5. <div class="bg-white rounded-lg border border-gray-200 p-4">
  6. <div class="text-sm font-medium text-gray-500">总提示词</div>
  7. <div class="mt-2 text-2xl font-semibold text-gray-900">{{ $this->getPrompts()['meta']['total'] ?? 0 }}</div>
  8. </div>
  9. </div>
  10. {{-- 筛选器 --}}
  11. <div class="bg-white rounded-lg border border-gray-200 p-4">
  12. <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
  13. {{-- 类型筛选 --}}
  14. <div>
  15. <label class="block text-sm font-medium text-gray-700 mb-2">提示词类型</label>
  16. <select
  17. wire:model.live="selectedType"
  18. class="w-full rounded-lg border-gray-300 text-sm"
  19. >
  20. <option value="">全部类型</option>
  21. <option value="question_generation">题目生成(系统)</option>
  22. <option value="question_enrich">题干补全(系统)</option>
  23. <option value="question_solution_regen">解题重写(系统)</option>
  24. <option value="题目生成">题目生成</option>
  25. <option value="掌握度评估">掌握度评估</option>
  26. <option value="技能熟练度">技能熟练度</option>
  27. <option value="质量审核">质量审核</option>
  28. </select>
  29. </div>
  30. {{-- 搜索 --}}
  31. <div class="md:col-span-2">
  32. <label class="block text-sm font-medium text-gray-700 mb-2">搜索提示词</label>
  33. <input
  34. type="text"
  35. wire:model.live.debounce.300ms="search"
  36. placeholder="搜索提示词名称或描述..."
  37. class="w-full rounded-lg border-gray-300 text-sm"
  38. />
  39. </div>
  40. </div>
  41. </div>
  42. {{-- 提示词列表 --}}
  43. <div class="bg-white rounded-lg border border-gray-200">
  44. <div class="overflow-x-auto">
  45. <table class="min-w-full divide-y divide-gray-200">
  46. <thead class="bg-gray-50">
  47. <tr>
  48. <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">模板名称</th>
  49. <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类型</th>
  50. <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">描述</th>
  51. <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
  52. <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">更新时间</th>
  53. <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
  54. </tr>
  55. </thead>
  56. <tbody class="bg-white divide-y divide-gray-200">
  57. @forelse($this->getPrompts()['data'] as $prompt)
  58. <tr class="hover:bg-gray-50">
  59. <td class="px-6 py-4 whitespace-nowrap">
  60. <div class="text-sm font-medium text-gray-900">{{ $prompt['template_name'] }}</div>
  61. <div class="text-xs text-gray-500">v{{ $prompt['version'] ?? 1 }}</div>
  62. </td>
  63. <td class="px-6 py-4 whitespace-nowrap">
  64. <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
  65. {{ $prompt['template_type'] }}
  66. </span>
  67. </td>
  68. <td class="px-6 py-4">
  69. @php
  70. $description = $prompt['description'] ?? '';
  71. $normalizedDescription = is_array($description)
  72. ? collect($description)->map(fn ($value, $key) => is_string($value) ? "{$key}: {$value}" : $key)->implode(', ')
  73. : (string) $description;
  74. @endphp
  75. <div class="text-sm text-gray-900">{{ Str::limit($normalizedDescription, 50) }}</div>
  76. </td>
  77. <td class="px-6 py-4 whitespace-nowrap">
  78. @if($prompt['is_active'] === 'yes' || $prompt['is_active'] === true)
  79. <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
  80. 启用
  81. </span>
  82. @else
  83. <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
  84. 禁用
  85. </span>
  86. @endif
  87. </td>
  88. <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  89. {{ \Carbon\Carbon::parse($prompt['updated_at'])->diffForHumans() }}
  90. </td>
  91. <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
  92. <div class="flex justify-end gap-2">
  93. <button
  94. wire:click="editPrompt({{ json_encode($prompt) }})"
  95. class="text-blue-600 hover:text-blue-900"
  96. >
  97. 编辑
  98. </button>
  99. <button
  100. wire:click="duplicatePrompt({{ json_encode($prompt) }})"
  101. class="text-green-600 hover:text-green-900"
  102. >
  103. 复制
  104. </button>
  105. <button
  106. wire:click="togglePrompt('{{ $prompt['template_name'] }}', {{ $prompt['is_active'] === 'yes' || $prompt['is_active'] === true }})"
  107. class="text-amber-600 hover:text-amber-900"
  108. >
  109. {{ $prompt['is_active'] === 'yes' || $prompt['is_active'] === true ? '禁用' : '启用' }}
  110. </button>
  111. <button
  112. wire:click="deletePrompt('{{ $prompt['template_name'] }}')"
  113. class="text-red-600 hover:text-red-900"
  114. onclick="return confirm('确定要删除这个提示词吗?此操作不可恢复。')"
  115. >
  116. 删除
  117. </button>
  118. </div>
  119. </td>
  120. </tr>
  121. @empty
  122. <tr>
  123. <td colspan="6" class="px-6 py-12 text-center text-gray-500">
  124. <div class="flex flex-col items-center">
  125. <svg class="w-12 h-12 text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  126. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
  127. </svg>
  128. <p class="text-lg font-medium">暂无提示词</p>
  129. <p class="text-sm">点击"新建提示词"开始创建</p>
  130. </div>
  131. </td>
  132. </tr>
  133. @endforelse
  134. </tbody>
  135. </table>
  136. </div>
  137. {{-- 分页 --}}
  138. @if(($this->getPrompts()['meta']['total_pages'] ?? 1) > 1)
  139. <div class="border-t border-gray-200 bg-white px-6 py-3 flex items-center justify-between">
  140. <div class="text-sm text-gray-700">
  141. 显示第 {{ (($this->getPrompts()['meta']['page'] ?? 1) - 1) * ($this->getPrompts()['meta']['per_page'] ?? 10) + 1 }} 到
  142. {{ min(($this->getPrompts()['meta']['page'] ?? 1) * ($this->getPrompts()['meta']['per_page'] ?? 10), $this->getPrompts()['meta']['total'] ?? 0) }} 条,
  143. 共 {{ $this->getPrompts()['meta']['total'] ?? 0 }} 条记录
  144. </div>
  145. <div class="flex gap-2">
  146. <button
  147. wire:click="previousPage"
  148. @if(($this->getPrompts()['meta']['page'] ?? 1) <= 1) disabled @endif
  149. class="px-3 py-1 text-sm border rounded {{ ($this->getPrompts()['meta']['page'] ?? 1) <= 1 ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white hover:bg-gray-50' }}"
  150. >
  151. 上一页
  152. </button>
  153. @for($i = 1; $i <= ($this->getPrompts()['meta']['total_pages'] ?? 1); $i++)
  154. @if($i == ($this->getPrompts()['meta']['page'] ?? 1))
  155. <button
  156. class="px-3 py-1 text-sm border rounded bg-blue-50 text-blue-600 border-blue-300"
  157. >
  158. {{ $i }}
  159. </button>
  160. @else
  161. <button
  162. wire:click="gotoPage({{ $i }})"
  163. class="px-3 py-1 text-sm border rounded bg-white hover:bg-gray-50"
  164. >
  165. {{ $i }}
  166. </button>
  167. @endif
  168. @endfor
  169. <button
  170. wire:click="nextPage"
  171. @if(($this->getPrompts()['meta']['page'] ?? 1) >= ($this->getPrompts()['meta']['total_pages'] ?? 1)) disabled @endif
  172. class="px-3 py-1 text-sm border rounded {{ ($this->getPrompts()['meta']['page'] ?? 1) >= ($this->getPrompts()['meta']['total_pages'] ?? 1) ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white hover:bg-gray-50' }}"
  173. >
  174. 下一页
  175. </button>
  176. </div>
  177. </div>
  178. @endif
  179. </div>
  180. </div>
  181. {{-- 创建/编辑模态框 --}}
  182. @if($showPromptModal)
  183. <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
  184. <div class="bg-white rounded-xl shadow-xl w-full max-w-3xl p-6">
  185. <div class="flex items-center justify-between mb-4">
  186. <h3 class="text-lg font-semibold">
  187. {{ $isEditing ? '编辑提示词' : '新建提示词' }}
  188. </h3>
  189. <button wire:click="$set('showPromptModal', false)" class="text-gray-500 hover:text-gray-700">&times;</button>
  190. </div>
  191. <div class="space-y-4">
  192. @unless($isEditing)
  193. <div>
  194. <label class="block text-sm font-medium text-gray-700 mb-1">模板名称</label>
  195. <input type="text" wire:model="form.template_name" class="w-full border rounded px-3 py-2 text-sm">
  196. </div>
  197. @endunless
  198. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  199. <div>
  200. <label class="block text-sm font-medium text-gray-700 mb-1">模板类型</label>
  201. <select wire:model="form.template_type" class="w-full border rounded px-3 py-2 text-sm">
  202. <option value="question_generation">题目生成</option>
  203. <option value="掌握度评估">掌握度评估</option>
  204. <option value="技能熟练度">技能熟练度</option>
  205. <option value="质量审核">质量审核</option>
  206. </select>
  207. </div>
  208. <div>
  209. <label class="block text-sm font-medium text-gray-700 mb-1">状态</label>
  210. <select wire:model="form.is_active" class="w-full border rounded px-3 py-2 text-sm">
  211. <option value="yes">启用</option>
  212. <option value="no">禁用</option>
  213. </select>
  214. </div>
  215. </div>
  216. <div>
  217. <label class="block text-sm font-medium text-gray-700 mb-1">模板内容</label>
  218. <textarea wire:model="form.template_content" rows="10" class="w-full border rounded px-3 py-2 text-sm font-mono"></textarea>
  219. <p class="text-xs text-gray-500 mt-1">可使用占位符:{kp_code}, {skills}, {count}, {basic_ratio}, {intermediate_ratio}, {advanced_ratio}, {type_mix}, {tags}, {skills_short}</p>
  220. </div>
  221. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  222. <div>
  223. <label class="block text-sm font-medium text-gray-700 mb-1">变量(JSON)</label>
  224. <input type="text" wire:model="form.variables" class="w-full border rounded px-3 py-2 text-sm" placeholder='["kp_code","skills"]'>
  225. </div>
  226. <div>
  227. <label class="block text-sm font-medium text-gray-700 mb-1">标签</label>
  228. <input type="text" wire:model="form.tags" class="w-full border rounded px-3 py-2 text-sm" placeholder="question_generation,math">
  229. </div>
  230. </div>
  231. <div>
  232. <label class="block text-sm font-medium text-gray-700 mb-1">描述</label>
  233. <input type="text" wire:model="form.description" class="w-full border rounded px-3 py-2 text-sm" placeholder="用于题目生成的模板">
  234. </div>
  235. </div>
  236. <div class="mt-6 flex justify-end gap-3">
  237. <button wire:click="$set('showPromptModal', false)" class="px-4 py-2 border rounded text-sm">取消</button>
  238. <button wire:click="savePrompt" class="px-4 py-2 bg-blue-600 text-white rounded text-sm hover:bg-blue-700">保存</button>
  239. </div>
  240. </div>
  241. </div>
  242. @endif
  243. </div>