index.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <template>
  2. <el-row :gutter="20">
  3. <!-- 左侧树形结构 -->
  4. <el-col :span="3">
  5. <el-card shadow="never">
  6. <div class="header">
  7. <span>分类树</span>
  8. <el-button class="add-btn" size="small" type="primary" @click="addcategory">添加根分类</el-button>
  9. </div>
  10. <el-divider></el-divider>
  11. <el-tree ref="treeRef" :data="treeData" :props="treeProps" :render-content="renderTreeContent"
  12. :row-style="{height:'100px'}"
  13. highlight-current node-key="id" @node-click="handleNodeClick"/>
  14. </el-card>
  15. <!-- 树形结构下方按钮 -->
  16. <div style="margin-top: 10px; display: flex; justify-content: space-between;">
  17. <el-button size="default" @click="expandAll">展开所有</el-button>
  18. <el-button size="default" @click="collapseAll">折叠所有</el-button>
  19. </div>
  20. </el-col>
  21. <!-- 中间表单区域 -->
  22. <el-col :span="21">
  23. <el-card shadow="never">
  24. <div style="display: flex;justify-content: space-between">
  25. <h3>{{ addOrEdit }}分类</h3>
  26. <el-tag round effect="light">ID:{{form.id}}</el-tag>
  27. </div>
  28. <el-form ref="formvalidate" :model="form" :rules="rules" label-position="top" label-width="120px">
  29. <el-form-item label="分类名称" prop="name">
  30. <el-input v-model="form.name"/>
  31. </el-form-item>
  32. <el-form-item label="分类编码" prop="code">
  33. <el-input v-model="form.code"/>
  34. <div>
  35. <span style="color: #777777">分类编码 用于系统标识,只能包含字母,数字和连字符</span>
  36. </div>
  37. </el-form-item>
  38. <el-row :gutter="20">
  39. <el-col :span="12">
  40. <el-form-item label="父级分类" prop="parentId">
  41. <el-select v-model="form.parentId" placeholder="请选择父级分类">
  42. <el-option label="无(根分类)" :value="1"/>
  43. </el-select>
  44. </el-form-item>
  45. </el-col>
  46. <el-col :span="12">
  47. <el-form-item label="排序顺序" prop="sort">
  48. <el-input v-model="form.sort" :min="1" style="width: 100%;"/>
  49. <div style="color: #999; font-size: 14px; margin-top: 5px;">数值越小排序越靠前</div>
  50. </el-form-item>
  51. </el-col>
  52. </el-row>
  53. <el-form-item label="分类描述" prop="description">
  54. <el-input v-model="form.description" type="textarea"/>
  55. </el-form-item>
  56. </el-form>
  57. <!-- 关键词提示模块 -->
  58. <div style="color: #777777">关键词提示词</div>
  59. <div class="keyword-box" style="display: flex">
  60. <div class="keyword-content" style="margin-top: 20px;margin-bottom: 20px;margin-left: 20px">
  61. <span>该分类下共有 {{ templateCount }} 个提示词模板</span><br>
  62. <el-button plain size="small" style="margin-top: 10px " @click="viewKeywords">查看关联提示词</el-button>
  63. </div>
  64. </div>
  65. <div class="form-footer">
  66. <el-button v-show="showdeletebutton" type="danger" @click="deletecategory">删除分类</el-button>
  67. <div class="right-btns">
  68. <el-button>取消</el-button>
  69. <el-button type="primary" @click="saveform(formvalidate)">{{ saveoradd }}</el-button>
  70. </div>
  71. </div>
  72. </el-card>
  73. </el-col>
  74. </el-row>
  75. </template>
  76. <script lang="ts" setup>
  77. import {ref} from 'vue';
  78. import {PromptModel} from '@/api/promptwordManagement/types'
  79. import {getPromptById, addPromptCategory, putPromptCategory,deletePromptCategory} from '@/api/promptwordManagement/promptpage.js'
  80. import {onMounted} from "vue";
  81. import {ElMessage} from "element-plus/es";
  82. import type {FormInstance, FormRules} from 'element-plus'
  83. import {Document, Folder} from '@element-plus/icons-vue';
  84. const showaddpromptcategory = ref(false)
  85. const templateCount = ref(5)
  86. const saveoradd = ref('保存')
  87. const showdeletebutton = ref(false)
  88. const addOrEdit = ref('新增')
  89. let form_add = reactive<PromptModel>({
  90. id: 0,
  91. name: '',
  92. code: '',
  93. description: '',
  94. parentId: 0,
  95. sortOrder: 0,
  96. createdAt: ''
  97. })
  98. const treeData = ref([
  99. {
  100. id: 1,
  101. name: '内容创作',
  102. code: '123',
  103. description: '简单介绍',
  104. parentid: '0',
  105. sortOrder: '1',
  106. children: [
  107. {
  108. id: 2,
  109. name: '内容创作',
  110. code: '123',
  111. description: '简单介绍',
  112. parentid: '0',
  113. sortOrder: '1',
  114. },
  115. {
  116. id: 3,
  117. name: '内容创作',
  118. code: '123',
  119. description: '简单介绍',
  120. parentid: '0',
  121. sortOrder: '1',
  122. }
  123. ]
  124. },
  125. {
  126. id: 4,
  127. name: '通用回答',
  128. code: '123',
  129. description: '简单介绍',
  130. parentid: '0',
  131. sortOrder: '1',
  132. children: [
  133. {
  134. id: 5,
  135. name: '通用回答',
  136. code: '123',
  137. description: '简单介绍',
  138. parentid: '0',
  139. sortOrder: '1',
  140. },
  141. {
  142. id: 6,
  143. name: '专业回答',
  144. code: '123',
  145. description: '简单介绍',
  146. parentid: '0',
  147. sortOrder: '2',
  148. }
  149. ]
  150. },
  151. ])
  152. const treeProps = {
  153. children: 'children',
  154. label: 'name'
  155. }
  156. const addcategory = () => {
  157. saveoradd.value = '添加'
  158. addOrEdit.value = '新增'
  159. showdeletebutton.value = false
  160. formvalidate.value.resetFields()
  161. }
  162. const viewKeywords = ref()
  163. const form = ref({
  164. id:null,
  165. name: '',
  166. code: '',
  167. parentId: '',
  168. sort: null,
  169. description: ''
  170. })
  171. const rules = reactive<FormRules<PromptModel>>({
  172. name: [
  173. {required: true, message: '分类名称不能为空', trigger: 'blur'}
  174. ],
  175. code: [
  176. {required: true, message: '分类编码不能为空', trigger: 'blur'}
  177. ],
  178. parentId: [
  179. {required: true, message: '父分类不能为空', trigger: 'blur'}
  180. ],
  181. description: [
  182. {required: true, message: '分类描述不能为空', trigger: 'blur'}
  183. ],
  184. sort: [
  185. {required: true, message: '排序顺序不能为空', trigger: 'blur'}
  186. ]
  187. })
  188. const formvalidate = ref<FormInstance>()
  189. const saveform = async (formvalidate: FormInstance | undefined) => {
  190. // if (!formEl)return
  191. const valid = await formvalidate.validate()
  192. try {
  193. if (valid) {
  194. console.log('校验通过')
  195. if (addOrEdit.value === '新增') {
  196. await addPromptCategory(form.value)
  197. } else {
  198. await putPromptCategory(form.value)
  199. }
  200. }
  201. } catch (error) {
  202. ElMessage.error("出错了!")
  203. console.log('出错:', error)
  204. }
  205. }
  206. const renderTreeContent = (h, {node, data}) => {
  207. const isFolder = !data.isLeaf;
  208. return h(
  209. 'span',
  210. {
  211. style: {
  212. display: 'flex',
  213. alignItems: 'center'
  214. }
  215. },
  216. [
  217. h(ElIcon, {style: {marginRight: '6px'}}, () => isFolder ? h(Folder) : h(Document)),
  218. h('span', null, node.label)
  219. ]
  220. );
  221. }
  222. function handleNodeClick(data, node) {
  223. addOrEdit.value = '编辑'
  224. saveoradd.value = '保存'
  225. showdeletebutton.value = true
  226. const parent = node.parent
  227. if (parent && parent.level > 0) {
  228. // ✅ 如果有父节点,把它的 id 设置为 form.parentId
  229. form.value.parentId = parent.data.id
  230. } else {
  231. // 如果没有父节点(顶级节点),parentId 设为 null 或 ''
  232. form.value.parentId = '无(根分类)'
  233. console.log('该节点是顶级节点')
  234. }
  235. // 其他表单字段可选填充
  236. form.value.id=data.id
  237. form.value.name = data.name
  238. form.value.code = data.code
  239. form.value.description = data.description
  240. form.value.sort=data.sortOrder
  241. }
  242. const treeRef = ref() // 用于获取 el-tree 实例
  243. // 展开所有节点
  244. const expandAll = () => {
  245. const tree = treeRef.value
  246. if (tree) {
  247. // 设置所有节点为展开状态
  248. treeData.value.forEach(node => {
  249. tree.store.nodesMap[node.id].expanded = true
  250. })
  251. }
  252. }
  253. // 折叠所有节点(只显示根节点)
  254. const collapseAll = () => {
  255. const tree = treeRef.value
  256. if (tree) {
  257. // 设置所有节点为折叠状态
  258. treeData.value.forEach(node => {
  259. tree.store.nodesMap[node.id].expanded = false
  260. })
  261. }
  262. }
  263. // 递归删除节点
  264. const removeNode = (nodes, id) => {
  265. for (let i = 0; i < nodes.length; i++) {
  266. if (nodes[i].id === id) {
  267. // 找到节点,从数组中移除
  268. nodes.splice(i, 1);
  269. return true;
  270. }
  271. if (nodes[i].children && nodes[i].children.length) {
  272. const found = removeNode(nodes[i].children, id);
  273. if (found) return true;
  274. }
  275. }
  276. return false;
  277. };
  278. const deletecategory = async () => {
  279. const nodeIdToDelete = form.value.id;
  280. if (!nodeIdToDelete) {
  281. ElMessage.warning('请选择一个分类进行删除');
  282. return;
  283. }
  284. try {
  285. await deletePromptCategory(nodeIdToDelete); // 调用接口删除
  286. const deleted = removeNode(treeData.value, nodeIdToDelete);
  287. if (deleted) {
  288. ElMessage.success('删除成功');
  289. form.value = {
  290. id: null,
  291. name: '',
  292. code: '',
  293. parentId: '',
  294. sort: null,
  295. description: ''
  296. };
  297. addOrEdit.value = '新增';
  298. saveoradd.value = '添加';
  299. showdeletebutton.value = false;
  300. }
  301. } catch (error) {
  302. ElMessage.error('删除失败,请重试');
  303. console.error('删除失败:', error);
  304. }
  305. };
  306. onMounted(() => {
  307. // getData();
  308. })
  309. </script>
  310. <style scoped>
  311. .keyword-content {
  312. margin-top: 10px;
  313. }
  314. .keyword-box {
  315. background-color: #F5F7FA;
  316. }
  317. .header {
  318. display: flex;
  319. justify-content: space-between;
  320. align-items: center;
  321. margin-bottom: 10px;
  322. }
  323. .add-btn {
  324. font-size: 12px;
  325. }
  326. .form-footer {
  327. display: flex;
  328. justify-content: space-between;
  329. align-items: center;
  330. margin-top: 20px;
  331. }
  332. .right-btns {
  333. display: flex;
  334. gap: 10px;
  335. margin-left: auto;
  336. }
  337. .custom-tree-node {
  338. display: flex;
  339. align-items: center;
  340. }
  341. </style>