index.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <script setup lang="ts">
  2. defineOptions({
  3. name: 'Role Management',
  4. })
  5. import { Plus, Upload, ArrowDown } from '@element-plus/icons-vue'
  6. import { ElButton, ElDialog, ElEmpty, ElInput, ElMessage, ElOption, ElPagination, ElSelect, ElTable, ElTableColumn, ElTag } from 'element-plus'
  7. import type { UploadFile, UploadFiles } from 'element-plus'
  8. import FileImportUploader from '@/components/Uploader/FileImportUploader.vue'
  9. import EditForm from './components/EditForm.vue'
  10. import EditLLMForm from './components/EditLLMForm.vue'
  11. import EditCallingsForm from './components/EditCallingsForm.vue'
  12. import SearchForm from './components/SearchForm.vue'
  13. import type { TAgent } from '@/types/role'
  14. import { anycallPage, updateTopFlag, type TRole } from '@/api/modules/anycallService'
  15. import { formatDateGeneral } from '@/utils'
  16. import { toast } from 'vue-sonner'
  17. const tableRef = ref()
  18. const loading = ref(false)
  19. // 控制 EditForm 显示状态
  20. const editFormVisible = ref(false)
  21. const editMode = ref<'create' | 'edit'>('create')
  22. const currentData = ref<any>(null)
  23. const currentCloneId = ref<string>()
  24. const editLLMFormVisible = ref(false)
  25. const editCallingsFormVisible = ref(false)
  26. const importedResultDialogVisible = ref(false)
  27. const importedFile = ref<UploadFile | null>(null)
  28. const importedResult = ref<{name: string, msg: string}[]>([
  29. // {name: 'test1.csv', msg: '成功'},
  30. // {name: 'test1.csv', msg: '成功'},
  31. // {name: 'test1.csv', msg: '成功'},
  32. // {name: 'test1.csv', msg: '成功'},
  33. // {name: 'test1.csv', msg: '成功'},
  34. // {name: 'test1.csv', msg: '成功'},
  35. // {name: 'test1.csv', msg: '成功'},
  36. // {name: 'test1.csv', msg: '成功'},
  37. // {name: 'test1.csv', msg: '成功'},
  38. // {name: 'test1.csv', msg: '成功'},
  39. // {name: 'test1.csv', msg: '成功'},
  40. // {name: 'test1.csv', msg: '成功'},
  41. // {name: 'test1.csv', msg: '成功'},
  42. // {name: 'test1.csv', msg: '成功'},
  43. // {name: 'test1.csv', msg: '成功'},
  44. // {name: 'test1.csv', msg: '成功'},
  45. // {name: 'test1.csv', msg: '成功'},
  46. // {name: 'test1.csv', msg: '成功'},
  47. // {name: 'test1.csv', msg: '成功'},
  48. // {name: 'test1.csv', msg: '成功'},
  49. // {name: 'test1.csv', msg: '成功'},
  50. // {name: 'test1.csv', msg: '成功'},
  51. // {name: 'test1.csv', msg: '成功'},
  52. ])
  53. // 搜索参数
  54. const searchParams = ref({
  55. // name: '',
  56. nameOrTags: '',
  57. })
  58. const dataList = ref<TAgent[]>([]);
  59. // 分页信息
  60. const pagination = ref({
  61. total: 0,
  62. size: 20,
  63. page:1,
  64. })
  65. async function fetchData() {
  66. loading.value = true
  67. const res = await anycallPage({
  68. nameOrTags: searchParams.value.nameOrTags,
  69. page: pagination.value.page,
  70. size: pagination.value.size,
  71. })
  72. if(res.code === 0){
  73. dataList.value = res.data.content
  74. pagination.value.total = res.data.total
  75. }
  76. loading.value = false
  77. }
  78. function handlePageChange(page: number) {
  79. pagination.value.page = page
  80. fetchData()
  81. }
  82. function handleSizeChange(size: number) {
  83. pagination.value.size = size
  84. pagination.value.page = 1
  85. fetchData()
  86. }
  87. function handleRefresh () {
  88. pagination.value.page = 1 // 搜索时重置到第一页
  89. fetchData()
  90. }
  91. const handleFormCancel = () => {
  92. editFormVisible.value = false
  93. };
  94. const handleRecommend = async (id: string, topFlag: boolean) => {
  95. const {code} = await updateTopFlag({
  96. id,
  97. topFlag: topFlag,
  98. })
  99. if(code === 0){
  100. ElMessage.success('操作成功')
  101. await fetchData()
  102. }
  103. }
  104. function handleUploadSuccess(res:any, file: any) {
  105. console.log(res, file)
  106. if(res.code === 0){
  107. importedResult.value = res.data as {name: string, msg: string}[]
  108. importedResultDialogVisible.value = true
  109. return
  110. }
  111. toast.error('Error', {
  112. description: res.msg || '导入失败',
  113. })
  114. }
  115. const handleCreate = () => {
  116. editMode.value = 'create'
  117. currentData.value = null
  118. editFormVisible.value = true
  119. }
  120. const handleEdit = (data: TAgent) => {
  121. editMode.value = 'edit'
  122. const gender = data.gender !== 'null' ? Number(data.gender) : 1;
  123. currentData.value = {...data, avatar: [data.photo], gender}
  124. console.log(currentData.value,33344)
  125. editFormVisible.value = true
  126. }
  127. const handleEditLLM = (data: TAgent) => {
  128. console.log(data)
  129. currentData.value = data
  130. currentCloneId.value = data.id
  131. editLLMFormVisible.value = true
  132. }
  133. const handleEditCallings = (data: TAgent) => {
  134. console.log(data)
  135. currentData.value = data
  136. currentCloneId.value = data.id
  137. editCallingsFormVisible.value = true
  138. }
  139. const handleSearch = () => {
  140. pagination.value.page = 1
  141. fetchData()
  142. }
  143. const handleReset = () => {
  144. pagination.value.page = 1
  145. pagination.value.size = 10
  146. searchParams.value.name = ''
  147. fetchData()
  148. }
  149. onMounted(async () => {
  150. await fetchData()
  151. })
  152. </script>
  153. <template>
  154. <div class="absolute-container">
  155. <div class="p-4 pb-0 bg-white dark-bg-black/50">
  156. <SearchForm v-model="searchParams" :loading="loading" @search="handleSearch" @reset="handleReset" />
  157. </div>
  158. <FaPageMain class="flex-1 overflow-auto" main-class="flex-1 flex flex-col overflow-auto">
  159. <div class="pb-4">
  160. <ElSpace>
  161. <FileImportUploader @success="handleUploadSuccess" ></FileImportUploader>
  162. <ElButton type="primary" :icon="Plus" @click="handleCreate">Create Role</ElButton>
  163. {{ importedFile?.name }}
  164. </ElSpace>
  165. </div>
  166. <ElTable
  167. v-loading="loading"
  168. ref="tableRef" :data="dataList" stripe highlight-current-row border height="100%"
  169. >
  170. <ElTableColumn label="id" prop="id" min-width="240" />
  171. <ElTableColumn label="Role Name" prop="name" min-width="220" />
  172. <ElTableColumn label="Avatar" prop="photo">
  173. <template #default="{row}">
  174. <ElImage :src="row.photo" fit="cover" class="w-12 h-12 rounded" />
  175. </template>
  176. </ElTableColumn>
  177. <ElTableColumn label="callings" prop="callings" />
  178. <ElTableColumn label="prompt" prop="prompt" min-width="340">
  179. <template #default="{row}">
  180. <ElTag type="info" v-for="(tag, index) in row.prompt.split(/\,|\,/)" :key="index" class="mb-1 mr-1">
  181. <div :title="tag">{{ tag }}</div>
  182. </ElTag>
  183. </template>
  184. </ElTableColumn>
  185. <ElTableColumn label="tags" prop="tags" min-width="340">
  186. <template #default="{row}">
  187. <ElTag type="info" v-for="(tag, index) in row.tags ?? []" :key="index" class="mb-1 mr-1">
  188. <div :title="tag">{{ tag }}</div>
  189. </ElTag>
  190. </template>
  191. </ElTableColumn>
  192. <ElTableColumn label="推荐" prop="topFlag" min-width="120">
  193. <template #default="{row}">
  194. <ElSwitch v-model="row.topFlag" @change="()=> handleRecommend(row.id, row.topFlag)" />
  195. </template>
  196. </ElTableColumn>
  197. <ElTableColumn label="Language" prop="language" min-width="180" />
  198. <ElTableColumn label="voiceName" prop="voiceName" min-width="240" />
  199. <ElTableColumn fixed="right" label="操作" min-width="140">
  200. <template #default="{row}">
  201. <el-dropdown>
  202. <span class="el-dropdown-link">
  203. Actions
  204. <el-icon class="el-icon--right">
  205. <ArrowDown />
  206. </el-icon>
  207. </span>
  208. <template #dropdown>
  209. <el-dropdown-menu>
  210. <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEdit(row)">编辑</ElButton></el-dropdown-item>
  211. <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditLLM(row)">绑定大模型</ElButton></el-dropdown-item>
  212. <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditCallings(row)">修改 callings</ElButton></el-dropdown-item>
  213. </el-dropdown-menu>
  214. </template>
  215. </el-dropdown>
  216. <!-- <ElPopconfirm title="确定推荐吗?" @confirm="handleRecommend(row.id, row.topFlag)">
  217. <template #reference>
  218. <ElButton link type="primary" size="small">
  219. 推荐
  220. </ElButton>
  221. </template>
  222. </ElPopconfirm> -->
  223. </template>
  224. </ElTableColumn>
  225. </ElTable>
  226. <div class="p-4">
  227. <ElPagination
  228. v-model:current-page="pagination.page"
  229. v-model:page-size="pagination.size"
  230. :total="pagination.total"
  231. :page-sizes="[10, 20, 50, 100]"
  232. layout="total, sizes, prev, pager, next, jumper"
  233. @size-change="handleSizeChange"
  234. @current-change="handlePageChange"
  235. />
  236. </div>
  237. </FaPageMain>
  238. <EditForm v-model:visible="editFormVisible" :model-value="currentData" :mode="editMode" @refresh="handleRefresh"
  239. @cancel="handleFormCancel" />
  240. <EditLLMForm v-model:visible="editLLMFormVisible" :cloneId="currentCloneId ?? ''" @refresh="handleRefresh"></EditLLMForm>
  241. <EditCallingsForm v-model:visible="editCallingsFormVisible" :model-value="currentData" :cloneId="currentCloneId ?? ''" @refresh="handleRefresh"></EditCallingsForm>
  242. <ElDialog title="导入结果" v-model="importedResultDialogVisible" align-center
  243. width="800" :z-index="2000" :close-on-click-modal="false">
  244. <div class="max-h-200 overflow-y-auto leading-6">
  245. <el-table :data="importedResult" stripe style="width: 100%;" height="450">
  246. <el-table-column prop="name" label="name" width="180" />
  247. <el-table-column prop="msg" label="message" min-width="180" />
  248. </el-table>
  249. </div>
  250. </ElDialog>
  251. </div>
  252. </template>
  253. <style scoped>
  254. .absolute-container {
  255. position: absolute;
  256. display: flex;
  257. flex-direction: column;
  258. width: 100%;
  259. height: 100%;
  260. }
  261. </style>