prompt-management.blade.php 16 KB

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