index.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <template>
  2. <el-dialog
  3. :title="isEdit ? '编辑知识' : '新增知识'"
  4. :visible="visible"
  5. width="760px"
  6. @close="handleClose"
  7. >
  8. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  9. <el-form-item label="来源方式">
  10. <el-radio-group v-model="form.sourceType" @change="onSourceTypeChange">
  11. <el-radio label="manual">手动输入</el-radio>
  12. <el-radio label="file">上传文件</el-radio>
  13. <el-radio label="url">网址获取</el-radio>
  14. </el-radio-group>
  15. </el-form-item>
  16. <template v-if="form.sourceType === 'manual'">
  17. <el-form-item label="标题" prop="title">
  18. <el-input v-model="form.title" placeholder="知识标题" maxlength="200" />
  19. </el-form-item>
  20. <el-form-item label="内容" prop="content">
  21. <el-input
  22. v-model="form.content"
  23. type="textarea"
  24. :rows="6"
  25. placeholder="知识内容"
  26. maxlength="5000"
  27. show-word-limit
  28. />
  29. </el-form-item>
  30. </template>
  31. <template v-else-if="form.sourceType === 'file'">
  32. <el-form-item label="文件">
  33. <el-upload
  34. ref="upload"
  35. :auto-upload="false"
  36. :limit="1"
  37. accept=".txt,.pdf,.doc,.docx,.md"
  38. :file-list="fileList"
  39. :on-change="handleFileChange"
  40. :on-remove="handleFileRemove"
  41. >
  42. <el-button size="small" type="primary">选择文件</el-button>
  43. <div slot="tip" style="color:#999;font-size:12px;margin-top:4px">支持 .txt .pdf .doc .docx .md 文件</div>
  44. </el-upload>
  45. </el-form-item>
  46. <el-form-item v-if="form.fileName" label="已选文件">
  47. <span>{{ form.fileName }}</span>
  48. </el-form-item>
  49. <el-form-item label="标题" prop="title">
  50. <el-input v-model="form.title" placeholder="知识标题" maxlength="200" />
  51. </el-form-item>
  52. </template>
  53. <template v-else-if="form.sourceType === 'url'">
  54. <el-form-item label="网址">
  55. <el-input v-model="form.sourceUrl" placeholder="请输入网址,如 https://..." style="margin-bottom:8px">
  56. <el-button slot="append" :loading="fetching" @click="handleFetchUrl">获取内容</el-button>
  57. </el-input>
  58. </el-form-item>
  59. <el-form-item v-if="form.title || form.content" label="标题">
  60. <span>{{ form.title }}</span>
  61. </el-form-item>
  62. </template>
  63. <el-form-item label="维度">
  64. <dimension-selector v-model="form.dimensionIds" :multiple="true" />
  65. </el-form-item>
  66. <el-form-item label="标签">
  67. <tag-selector v-model="form.tagIds" />
  68. </el-form-item>
  69. <el-form-item label="排序">
  70. <el-input-number v-model="form.sort" :min="0" :max="9999" />
  71. </el-form-item>
  72. <el-form-item label="状态">
  73. <el-radio-group v-model="form.status">
  74. <el-radio :label="1">启用</el-radio>
  75. <el-radio :label="0">禁用</el-radio>
  76. </el-radio-group>
  77. </el-form-item>
  78. <el-form-item label="备注">
  79. <el-input v-model="form.remark" type="textarea" :rows="2" maxlength="500" />
  80. </el-form-item>
  81. </el-form>
  82. <span slot="footer">
  83. <el-button @click="handleClose">取消</el-button>
  84. <el-button type="primary" :loading="submitting" @click="handleSubmit">保存</el-button>
  85. </span>
  86. </el-dialog>
  87. </template>
  88. <script>
  89. import DimensionSelector from '@/components/DimensionSelector'
  90. import TagSelector from '@/components/TagSelector'
  91. import { uploadKnowledgeFile, fetchUrlContent } from '@/api/dimension'
  92. export default {
  93. name: 'KnowledgeDialog',
  94. components: { DimensionSelector, TagSelector },
  95. props: {
  96. visible: { type: Boolean, default: false },
  97. knowledge: { type: Object, default: () => null }
  98. },
  99. data() {
  100. return {
  101. submitting: false,
  102. fetching: false,
  103. fileList: [],
  104. form: this.initForm(),
  105. rules: {
  106. title: [{ required: true, message: '请输入知识标题', trigger: 'blur' }],
  107. content: [{ required: true, message: '请输入知识内容', trigger: 'blur' }]
  108. }
  109. }
  110. },
  111. computed: {
  112. isEdit() {
  113. return !!this.knowledge
  114. }
  115. },
  116. watch: {
  117. knowledge(val) {
  118. if (val) {
  119. this.form = {
  120. id: val.id,
  121. title: val.title || '',
  122. content: val.content || '',
  123. dimensionIds: val.dimensionIds || [],
  124. tagIds: val.tagIds || [],
  125. sort: val.sort || 0,
  126. status: val.status !== undefined ? val.status : 1,
  127. remark: val.remark || '',
  128. fileUrl: val.fileUrl || '',
  129. fileName: val.fileName || '',
  130. sourceUrl: val.sourceUrl || '',
  131. sourceType: val.sourceType || 'manual'
  132. }
  133. if (this.form.fileUrl) {
  134. this.fileList = [{ name: this.form.fileName || '已上传文件' }]
  135. }
  136. } else {
  137. this.form = this.initForm()
  138. }
  139. }
  140. },
  141. methods: {
  142. initForm() {
  143. return {
  144. id: null,
  145. title: '',
  146. content: '',
  147. dimensionIds: [],
  148. tagIds: [],
  149. sort: 0,
  150. status: 1,
  151. remark: '',
  152. fileUrl: '',
  153. fileName: '',
  154. sourceUrl: '',
  155. sourceType: 'manual'
  156. }
  157. },
  158. onSourceTypeChange() {
  159. if (this.form.sourceType !== 'file') {
  160. this.fileList = []
  161. }
  162. },
  163. handleFileChange(file, files) {
  164. this.fileList = files.slice(-1)
  165. },
  166. handleFileRemove() {
  167. this.fileList = []
  168. this.form.fileUrl = ''
  169. this.form.fileName = ''
  170. },
  171. async handleUploadFile() {
  172. if (this.fileList.length === 0) {
  173. this.$message.warning('请先选择文件')
  174. return
  175. }
  176. const file = this.fileList[0].raw
  177. try {
  178. const res = await uploadKnowledgeFile(file)
  179. if (res.code === 200 && res.data) {
  180. this.form.fileUrl = res.data.fileUrl
  181. this.form.fileName = res.data.fileName
  182. this.$message.success('文件上传成功')
  183. } else {
  184. this.$message.error(res.message || '上传失败')
  185. }
  186. } catch (e) {
  187. this.$message.error(e.message || '上传失败')
  188. }
  189. },
  190. async handleFetchUrl() {
  191. if (!this.form.sourceUrl) {
  192. this.$message.warning('请输入网址')
  193. return
  194. }
  195. this.fetching = true
  196. try {
  197. const res = await fetchUrlContent(this.form.sourceUrl)
  198. if (res.code === 200 && res.data) {
  199. this.form.title = res.data.title || '未获取到标题'
  200. this.form.content = res.data.content || ''
  201. this.form.sourceType = 'url'
  202. this.$message.success('内容获取成功')
  203. } else {
  204. this.$message.error(res.message || '获取失败')
  205. }
  206. } catch (e) {
  207. this.$message.error(e.message || '获取失败')
  208. } finally {
  209. this.fetching = false
  210. }
  211. },
  212. handleClose() {
  213. this.$refs.form.resetFields()
  214. this.fileList = []
  215. this.$emit('update:visible', false)
  216. },
  217. async handleSubmit() {
  218. if (this.form.sourceType === 'manual') {
  219. try {
  220. await this.$refs.form.validateField(['title', 'content'])
  221. } catch {
  222. return
  223. }
  224. } else if (this.form.sourceType === 'file') {
  225. if (!this.form.title) {
  226. this.$message.warning('请填写标题')
  227. return
  228. }
  229. } else if (this.form.sourceType === 'url') {
  230. if (!this.form.sourceUrl) {
  231. this.$message.warning('请输入网址')
  232. return
  233. }
  234. if (!this.form.content) {
  235. this.$message.warning('请先点击"获取内容"')
  236. return
  237. }
  238. }
  239. this.submitting = true
  240. try {
  241. await this.$store.dispatch('dimension/saveKnowledge', this.form)
  242. this.$message.success('保存成功')
  243. this.handleClose()
  244. this.$emit('saved')
  245. } catch (e) {
  246. this.$message.error('保存失败: ' + (e.message || '未知错误'))
  247. } finally {
  248. this.submitting = false
  249. }
  250. }
  251. }
  252. }
  253. </script>