西藏巴青项目

index.vue 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <div class="app-container">
  3. <el-card shadow="never">
  4. <el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
  5. <el-form-item prop="keyword">
  6. <template slot="label">{{ tsT("queryKeyword") }}</template>
  7. <el-input
  8. v-model="queryParams.keyword"
  9. :placeholder="tsT('queryKeywordPh')"
  10. clearable
  11. style="width: 220px"
  12. @keyup.enter.native="handleQuery"
  13. />
  14. </el-form-item>
  15. <el-form-item prop="knowledgeTopic">
  16. <template slot="label">{{ tsT("queryTopic") }}</template>
  17. <el-select v-model="queryParams.knowledgeTopic" :placeholder="tsCommon('pleaseSelect')" clearable style="width: 150px">
  18. <el-option v-for="item in topicOptions" :key="item.value" :label="item.label" :value="item.value" />
  19. </el-select>
  20. </el-form-item>
  21. <el-form-item prop="kbSyncStatus">
  22. <template slot="label">{{ tsT("queryKbSync") }}</template>
  23. <el-select v-model="queryParams.kbSyncStatus" :placeholder="tsCommon('pleaseSelect')" clearable style="width: 130px">
  24. <el-option :label="$t('techService.status.kbNotIn')" :value="0" />
  25. <el-option :label="$t('techService.status.kbIn')" :value="1" />
  26. </el-select>
  27. </el-form-item>
  28. <el-form-item>
  29. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ tsCommon("search") }}</el-button>
  30. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ tsCommon("reset") }}</el-button>
  31. <el-button
  32. v-hasPermi="['techService:knowledge:add']"
  33. type="primary"
  34. plain
  35. icon="el-icon-plus"
  36. size="mini"
  37. @click="handleAdd"
  38. >{{ tsCommon("add") }}</el-button>
  39. </el-form-item>
  40. </el-form>
  41. </el-card>
  42. <br />
  43. <el-card shadow="never">
  44. <el-table v-loading="loading" :data="tableList" border>
  45. <el-table-column :label="tsT('colTitle')" prop="knowledgeTitle" align="center" min-width="120" :show-overflow-tooltip="true" />
  46. <el-table-column :label="tsT('colTopic')" align="center" width="110">
  47. <template slot-scope="scope">
  48. <span>{{ knowledgeTopicText(scope.row.knowledgeTopic) }}</span>
  49. </template>
  50. </el-table-column>
  51. <el-table-column :label="tsT('colIntro')" prop="introduction" align="center" min-width="140" :show-overflow-tooltip="true" />
  52. <el-table-column :label="tsCommon('colContentFile')" align="center" min-width="130" :show-overflow-tooltip="true">
  53. <template slot-scope="scope">
  54. <el-link
  55. v-if="scope.row.contentFileUrl"
  56. type="primary"
  57. :href="scope.row.contentFileUrl"
  58. target="_blank"
  59. >{{ scope.row.contentFileName }}</el-link>
  60. <span v-else>{{ $t('techService.status.dash') }}</span>
  61. </template>
  62. </el-table-column>
  63. <el-table-column :label="tsCommon('colKb')" align="center" width="100">
  64. <template slot-scope="scope">
  65. <span>{{ kbSyncStatusText(scope.row.kbSyncStatus) }}</span>
  66. </template>
  67. </el-table-column>
  68. <el-table-column :label="tsCommon('colCreate')" align="center" width="160">
  69. <template slot-scope="scope">
  70. <span>{{ scope.row.createTime ? parseTime(scope.row.createTime) : $t('techService.status.dash') }}</span>
  71. </template>
  72. </el-table-column>
  73. <el-table-column :label="tsCommon('colOp')" align="center" class-name="small-padding fixed-width" width="340" fixed="right">
  74. <template slot-scope="scope">
  75. <el-button v-hasPermi="['techService:knowledge:query']" type="text" size="mini" @click="handleView(scope.row)">{{ tsCommon("view") }}</el-button>
  76. <el-button
  77. v-hasPermi="['techService:knowledge:syncKb']"
  78. v-if="canSyncKb(scope.row)"
  79. type="text"
  80. size="mini"
  81. @click="handleSyncKb(scope.row)"
  82. >{{ tsCommon("syncKb") }}</el-button>
  83. <el-button
  84. v-hasPermi="['techService:knowledge:removeKb']"
  85. v-if="canRemoveKb(scope.row)"
  86. type="text"
  87. size="mini"
  88. @click="handleRemoveKb(scope.row)"
  89. >{{ tsCommon("removeKb") }}</el-button>
  90. <el-button
  91. v-hasPermi="['techService:knowledge:edit']"
  92. v-if="canEdit(scope.row)"
  93. type="text"
  94. size="mini"
  95. @click="handleEdit(scope.row)"
  96. >{{ tsCommon("edit") }}</el-button>
  97. <el-button
  98. v-hasPermi="['techService:knowledge:remove']"
  99. v-if="canDelete(scope.row)"
  100. type="text"
  101. size="mini"
  102. @click="handleDelete(scope.row)"
  103. >{{ tsCommon("delete") }}</el-button>
  104. </template>
  105. </el-table-column>
  106. </el-table>
  107. <pagination
  108. v-show="total > 0"
  109. :total="total"
  110. :page.sync="queryParams.pageNum"
  111. :limit.sync="queryParams.pageSize"
  112. @pagination="getList"
  113. />
  114. </el-card>
  115. <el-dialog :title="dialogTitle" :visible.sync="open" width="580px" append-to-body @close="cancel">
  116. <el-form :key="formKey" ref="form" :model="form" label-width="110px">
  117. <el-form-item prop="knowledgeTitle" :rules="formRules.knowledgeTitle">
  118. <template slot="label">{{ tsT("formTitle") }}</template>
  119. <el-input v-model="form.knowledgeTitle" :placeholder="tsT('phTitle')" maxlength="20" show-word-limit />
  120. </el-form-item>
  121. <el-form-item prop="knowledgeTopic" :rules="formRules.knowledgeTopic">
  122. <template slot="label">{{ tsT("formTopic") }}</template>
  123. <el-select v-model="form.knowledgeTopic" :placeholder="tsCommon('pleaseSelect')" style="width: 100%">
  124. <el-option v-for="item in topicOptions" :key="item.value" :label="item.label" :value="item.value" />
  125. </el-select>
  126. </el-form-item>
  127. <el-form-item prop="introduction" :rules="formRules.introduction">
  128. <template slot="label">{{ tsT("formIntro") }}</template>
  129. <el-input
  130. v-model="form.introduction"
  131. type="textarea"
  132. :rows="2"
  133. :placeholder="tsT('phIntroOptional')"
  134. maxlength="50"
  135. show-word-limit
  136. />
  137. </el-form-item>
  138. <el-form-item prop="contentFilePath" :rules="formRules.contentFilePath">
  139. <template slot="label">{{ tsT("formContent") }}</template>
  140. <el-upload
  141. class="upload-file-uploader"
  142. :action="uploadAction"
  143. :headers="uploadHeaders"
  144. :before-upload="beforeContentUpload"
  145. :on-success="handleContentUploadSuccess"
  146. :on-error="handleUploadHttpError"
  147. :show-file-list="false"
  148. >
  149. <el-button size="mini" type="primary">{{ tsCommon("pickFile") }}</el-button>
  150. </el-upload>
  151. <div class="el-upload__tip">{{ tsCommon("tipBody") }}</div>
  152. <ul v-if="form.contentFilePath" class="upload-file-list el-upload-list el-upload-list--text">
  153. <li class="el-upload-list__item ele-upload-list__item-content">
  154. <el-link :href="form.contentFileUrl || '#'" :underline="false" target="_blank">
  155. <span class="el-icon-document"> {{ form.contentFileName }} </span>
  156. </el-link>
  157. <div class="ele-upload-list__item-content-action">
  158. <el-link type="danger" :underline="false" @click="clearContentFile">{{ tsCommon("del") }}</el-link>
  159. </div>
  160. </li>
  161. </ul>
  162. </el-form-item>
  163. </el-form>
  164. <div slot="footer" class="dialog-footer">
  165. <el-button type="primary" @click="submitForm">{{ tsCommon("ok") }}</el-button>
  166. <el-button @click="cancel">{{ tsCommon("cancel") }}</el-button>
  167. </div>
  168. </el-dialog>
  169. <el-dialog :title="tsCommon('viewTitle')" :visible.sync="viewOpen" width="560px" append-to-body>
  170. <el-form label-width="110px" size="small">
  171. <el-form-item :label="tsT('formTitle')">
  172. <span>{{ viewRow.knowledgeTitle || $t('techService.status.dash') }}</span>
  173. </el-form-item>
  174. <el-form-item :label="tsT('formTopic')">
  175. <span>{{ knowledgeTopicText(viewRow.knowledgeTopic) }}</span>
  176. </el-form-item>
  177. <el-form-item :label="tsT('formIntro')">
  178. <span>{{ viewRow.introduction || $t('techService.status.dash') }}</span>
  179. </el-form-item>
  180. <el-form-item :label="tsT('formContent')">
  181. <el-link
  182. v-if="viewRow.contentFileUrl"
  183. type="primary"
  184. :href="viewRow.contentFileUrl"
  185. target="_blank"
  186. >{{ viewRow.contentFileName }}</el-link>
  187. <span v-else>{{ $t('techService.status.dash') }}</span>
  188. </el-form-item>
  189. <el-form-item :label="tsCommon('colKb')">
  190. <span>{{ kbSyncStatusText(viewRow.kbSyncStatus) }}</span>
  191. </el-form-item>
  192. <el-form-item :label="tsT('viewCreateBy')">
  193. <span>{{ viewRow.createBy || $t('techService.status.dash') }}</span>
  194. </el-form-item>
  195. <el-form-item :label="tsT('viewUpdateBy')">
  196. <span>{{ viewRow.updateBy || $t('techService.status.dash') }}</span>
  197. </el-form-item>
  198. <el-form-item :label="tsT('viewUpdateTime')">
  199. <span>{{ viewRow.updateTime ? parseTime(viewRow.updateTime) : $t('techService.status.dash') }}</span>
  200. </el-form-item>
  201. </el-form>
  202. <div slot="footer" class="dialog-footer">
  203. <el-button v-if="canEdit(viewRow)" v-hasPermi="['techService:knowledge:edit']" type="primary" @click="handleEditFromView">{{ tsCommon("edit") }}</el-button>
  204. <el-button @click="viewOpen = false">{{ tsCommon("close") }}</el-button>
  205. </div>
  206. </el-dialog>
  207. </div>
  208. </template>
  209. <script>
  210. import { getToken } from "@/utils/auth"
  211. import techServiceLocaleMixin from "@/mixins/techServiceLocaleMixin"
  212. import {
  213. listKnowledge,
  214. getKnowledge,
  215. addKnowledge,
  216. updateKnowledge,
  217. delKnowledge,
  218. syncKbKnowledge,
  219. removeKbKnowledge
  220. } from "@/api/techService/knowledge"
  221. const KB_SYNC_STATUS = { NOT_IN: 0, IN: 1 }
  222. export default {
  223. name: "TechServiceKnowledge",
  224. mixins: [techServiceLocaleMixin],
  225. data() {
  226. return {
  227. tsNs: "knowledge",
  228. loading: false,
  229. open: false,
  230. viewOpen: false,
  231. viewRow: {},
  232. dialogEdit: false,
  233. formKey: 0,
  234. total: 0,
  235. tableList: [],
  236. queryParams: {
  237. pageNum: 1,
  238. pageSize: 20,
  239. keyword: undefined,
  240. knowledgeTopic: undefined,
  241. kbSyncStatus: undefined
  242. },
  243. form: {},
  244. contentFileExts: ["pdf", "doc", "docx", "xls", "xlsx", "pptx"]
  245. }
  246. },
  247. computed: {
  248. dialogTitle() {
  249. return this.dialogEdit ? this.tsT("dialogEdit") : this.tsT("dialogAdd")
  250. },
  251. topicOptions() {
  252. return [1, 2, 3, 4, 5].map((value) => ({ label: this.tsT("topic" + value), value }))
  253. },
  254. formRules() {
  255. return {
  256. knowledgeTitle: [
  257. { required: true, message: this.tsT("ruleTitleRequired"), trigger: "blur" },
  258. { min: 1, max: 20, message: this.tsT("ruleTitleLen"), trigger: "blur" }
  259. ],
  260. knowledgeTopic: [{ required: true, message: this.tsT("ruleTopicRequired"), trigger: "change" }],
  261. introduction: [{ max: 50, message: this.tsT("ruleIntroMax"), trigger: "blur" }],
  262. contentFilePath: [{ required: true, message: this.tsT("ruleContentRequired"), trigger: "change" }]
  263. }
  264. },
  265. uploadAction() {
  266. return process.env.VUE_APP_BASE_API + "/common/upload"
  267. },
  268. uploadHeaders() {
  269. return { Authorization: "Bearer " + getToken() }
  270. }
  271. },
  272. created() {
  273. this.getList()
  274. },
  275. methods: {
  276. canEdit(row) {
  277. return row && row.kbSyncStatus === KB_SYNC_STATUS.NOT_IN
  278. },
  279. canDelete(row) {
  280. return row && row.kbSyncStatus === KB_SYNC_STATUS.NOT_IN
  281. },
  282. canSyncKb(row) {
  283. return row && row.kbSyncStatus === KB_SYNC_STATUS.NOT_IN
  284. },
  285. canRemoveKb(row) {
  286. return row && row.kbSyncStatus === KB_SYNC_STATUS.IN
  287. },
  288. getList() {
  289. this.loading = true
  290. listKnowledge(this.queryParams)
  291. .then((res) => {
  292. this.tableList = res.rows || []
  293. this.total = res.total != null ? res.total : 0
  294. })
  295. .finally(() => {
  296. this.loading = false
  297. })
  298. },
  299. handleQuery() {
  300. this.queryParams.pageNum = 1
  301. this.getList()
  302. },
  303. resetQuery() {
  304. this.queryParams.keyword = undefined
  305. this.queryParams.knowledgeTopic = undefined
  306. this.queryParams.kbSyncStatus = undefined
  307. this.resetForm("queryForm")
  308. this.handleQuery()
  309. },
  310. resetFormModel() {
  311. this.form = {
  312. id: undefined,
  313. knowledgeTitle: undefined,
  314. knowledgeTopic: undefined,
  315. introduction: undefined,
  316. contentFileUrl: undefined,
  317. contentFilePath: undefined,
  318. contentFileName: undefined
  319. }
  320. },
  321. handleAdd() {
  322. this.formKey += 1
  323. this.resetFormModel()
  324. this.dialogEdit = false
  325. this.open = true
  326. this.$nextTick(() => this.resetForm("form"))
  327. },
  328. fillFormFromApi(data) {
  329. this.form = {
  330. id: data.id,
  331. knowledgeTitle: data.knowledgeTitle,
  332. knowledgeTopic: data.knowledgeTopic,
  333. introduction: data.introduction,
  334. contentFileUrl: data.contentFileUrl,
  335. contentFilePath: data.contentFilePath,
  336. contentFileName: data.contentFileName
  337. }
  338. },
  339. handleEdit(row) {
  340. if (!this.canEdit(row)) {
  341. return
  342. }
  343. getKnowledge(row.id).then((res) => {
  344. const data = res.data || {}
  345. this.formKey += 1
  346. this.dialogEdit = true
  347. this.fillFormFromApi(data)
  348. this.open = true
  349. this.viewOpen = false
  350. this.$nextTick(() => {
  351. if (this.$refs.form) {
  352. this.$refs.form.clearValidate()
  353. }
  354. })
  355. })
  356. },
  357. handleEditFromView() {
  358. this.viewOpen = false
  359. this.handleEdit(this.viewRow)
  360. },
  361. handleView(row) {
  362. getKnowledge(row.id).then((res) => {
  363. this.viewRow = res.data || {}
  364. this.viewOpen = true
  365. })
  366. },
  367. handleSyncKb(row) {
  368. this.runSyncKbAfterConfirm(this.tsT("confirmSync"), () => syncKbKnowledge(row.id))
  369. .then(() => {
  370. this.$modal.msgSuccess(this.tsCommon("msgSyncOk"))
  371. this.getList()
  372. })
  373. .catch(() => {})
  374. },
  375. handleRemoveKb(row) {
  376. this.runRemoveKbAfterConfirm(this.tsT("confirmRemoveKb"), () => removeKbKnowledge(row.id))
  377. .then(() => {
  378. this.$modal.msgSuccess(this.tsCommon("msgRemoveKbOk"))
  379. this.getList()
  380. })
  381. .catch(() => {})
  382. },
  383. handleDelete(row) {
  384. this.$modal
  385. .confirm(this.tsT("confirmDelete", { title: row.knowledgeTitle }))
  386. .then(() => delKnowledge(row.id))
  387. .then(() => {
  388. this.$modal.msgSuccess(this.tsCommon("msgDelOk"))
  389. this.viewOpen = false
  390. this.getList()
  391. })
  392. .catch(() => {})
  393. },
  394. extOf(fileName) {
  395. if (!fileName || fileName.lastIndexOf(".") < 0) {
  396. return ""
  397. }
  398. return fileName.slice(fileName.lastIndexOf(".") + 1).toLowerCase()
  399. },
  400. beforeContentUpload(file) {
  401. const ext = this.extOf(file.name)
  402. if (!this.contentFileExts.includes(ext)) {
  403. this.$modal.msgError(this.tsCommon("errBodyFmt"))
  404. return false
  405. }
  406. if (file.name.includes(",")) {
  407. this.$modal.msgError(this.tsCommon("errComma"))
  408. return false
  409. }
  410. if (file.size / 1024 / 1024 >= 30) {
  411. this.$modal.msgError(this.tsCommon("errBodyMb"))
  412. return false
  413. }
  414. this.$modal.loading(this.tsCommon("uploadingFile"))
  415. return true
  416. },
  417. handleContentUploadSuccess(res, file) {
  418. this.$modal.closeLoading()
  419. if (res.code === 200) {
  420. this.form.contentFileUrl = res.url
  421. this.form.contentFilePath = res.fileName
  422. this.form.contentFileName = res.originalFilename || file.name
  423. this.$refs.form && this.$refs.form.validateField("contentFilePath")
  424. } else {
  425. this.$modal.msgError(res.msg || this.tsCommon("errUploadFail"))
  426. }
  427. },
  428. clearContentFile() {
  429. this.form.contentFileUrl = undefined
  430. this.form.contentFilePath = undefined
  431. this.form.contentFileName = undefined
  432. this.$refs.form && this.$refs.form.validateField("contentFilePath")
  433. },
  434. handleUploadHttpError() {
  435. this.$modal.closeLoading()
  436. this.$modal.msgError(this.tsCommon("errUploadRetry"))
  437. },
  438. buildSubmitPayload() {
  439. return {
  440. knowledgeTitle: (this.form.knowledgeTitle || "").trim(),
  441. knowledgeTopic: this.form.knowledgeTopic,
  442. introduction: this.form.introduction ? this.form.introduction.trim() : undefined,
  443. contentFileUrl: this.form.contentFileUrl,
  444. contentFilePath: this.form.contentFilePath,
  445. contentFileName: this.form.contentFileName
  446. }
  447. },
  448. submitForm() {
  449. this.$refs.form.validate((valid) => {
  450. if (!valid) {
  451. return
  452. }
  453. const payload = this.buildSubmitPayload()
  454. if (this.dialogEdit) {
  455. payload.id = this.form.id
  456. updateKnowledge(payload).then(() => {
  457. this.$modal.msgSuccess(this.tsCommon("msgModOk"))
  458. this.open = false
  459. this.getList()
  460. })
  461. } else {
  462. addKnowledge(payload).then(() => {
  463. this.$modal.msgSuccess(this.tsCommon("msgAddOk"))
  464. this.open = false
  465. this.getList()
  466. })
  467. }
  468. })
  469. },
  470. cancel() {
  471. this.open = false
  472. this.resetFormModel()
  473. }
  474. }
  475. }
  476. </script>
  477. <style scoped>
  478. .upload-file-uploader {
  479. margin-bottom: 8px;
  480. }
  481. .upload-file-list {
  482. list-style: none;
  483. padding: 0;
  484. margin: 8px 0 0;
  485. }
  486. .upload-file-list .el-upload-list__item {
  487. border: 1px solid #e4e7ed;
  488. line-height: 2;
  489. padding: 0 8px;
  490. }
  491. .ele-upload-list__item-content {
  492. display: flex;
  493. justify-content: space-between;
  494. align-items: center;
  495. }
  496. </style>