feat(knowledge): 新增知识库创建功能

- 添加知识库创建表单组件 knowledgeForm
- 实现知识库创建逻辑和界面- 集成 loading 组件用于表单提交时的加载效果
- 更新路由配置,增加知识库创建页面
This commit is contained in:
陈昱达
2025-04-10 10:08:54 +08:00
parent d9cb160be9
commit 66fd48b999
12 changed files with 1312 additions and 38 deletions

View File

@@ -238,3 +238,20 @@ body .el-collapse-item__wrap {
.el-card{
border-radius: 10px;
}
.danger{
color: #F56C6C!important;
& :disabled{
color: rgba(0,0,0,.25);
}
& :hover{
color: #dd6161;
}
& :focus{
color: #dd6161;
}
& :active{
color: #dd6161;
}
}

View File

@@ -0,0 +1,11 @@
import directive from './src/directive';
import service from './src/index';
export default {
install(Vue) {
Vue.use(directive);
Vue.prototype.$loading = service;
},
directive,
service
};

View File

@@ -0,0 +1,133 @@
import Vue from 'vue';
import Loading from './loading.vue';
import { addClass, removeClass, getStyle } from 'element-ui/src/utils/dom';
import { PopupManager } from 'element-ui/src/utils/popup';
import afterLeave from 'element-ui/src/utils/after-leave';
const Mask = Vue.extend(Loading);
const loadingDirective = {};
loadingDirective.install = Vue => {
if (Vue.prototype.$isServer) return;
const toggleLoading = (el, binding) => {
if (binding.value) {
Vue.nextTick(() => {
if (binding.modifiers.fullscreen) {
el.originalPosition = getStyle(document.body, 'position');
el.originalOverflow = getStyle(document.body, 'overflow');
el.maskStyle.zIndex = PopupManager.nextZIndex();
addClass(el.mask, 'is-fullscreen');
insertDom(document.body, el, binding);
} else {
removeClass(el.mask, 'is-fullscreen');
if (binding.modifiers.body) {
el.originalPosition = getStyle(document.body, 'position');
['top', 'left'].forEach(property => {
const scroll = property === 'top' ? 'scrollTop' : 'scrollLeft';
el.maskStyle[property] = el.getBoundingClientRect()[property] +
document.body[scroll] +
document.documentElement[scroll] -
parseInt(getStyle(document.body, `margin-${ property }`), 10) +
'px';
});
['height', 'width'].forEach(property => {
el.maskStyle[property] = el.getBoundingClientRect()[property] + 'px';
});
insertDom(document.body, el, binding);
} else {
el.originalPosition = getStyle(el, 'position');
insertDom(el, el, binding);
}
}
});
} else {
afterLeave(el.instance, _ => {
if (!el.instance.hiding) return;
el.domVisible = false;
const target = binding.modifiers.fullscreen || binding.modifiers.body
? document.body
: el;
removeClass(target, 'el-loading-parent--relative');
removeClass(target, 'el-loading-parent--hidden');
el.instance.hiding = false;
}, 300, true);
el.instance.visible = false;
el.instance.hiding = true;
}
};
const insertDom = (parent, el, binding) => {
if (!el.domVisible && getStyle(el, 'display') !== 'none' && getStyle(el, 'visibility') !== 'hidden') {
Object.keys(el.maskStyle).forEach(property => {
el.mask.style[property] = el.maskStyle[property];
});
if (el.originalPosition !== 'absolute' && el.originalPosition !== 'fixed') {
addClass(parent, 'el-loading-parent--relative');
}
if (binding.modifiers.fullscreen && binding.modifiers.lock) {
addClass(parent, 'el-loading-parent--hidden');
}
el.domVisible = true;
parent.appendChild(el.mask);
Vue.nextTick(() => {
if (el.instance.hiding) {
el.instance.$emit('after-leave');
} else {
el.instance.visible = true;
}
});
el.domInserted = true;
} else if (el.domVisible && el.instance.hiding === true) {
el.instance.visible = true;
el.instance.hiding = false;
}
};
Vue.directive('loading', {
bind: function(el, binding, vnode) {
const textExr = el.getAttribute('element-loading-text');
const spinnerExr = el.getAttribute('element-loading-spinner');
const backgroundExr = el.getAttribute('element-loading-background');
const customClassExr = el.getAttribute('element-loading-custom-class');
const vm = vnode.context;
const mask = new Mask({
el: document.createElement('div'),
data: {
text: vm && vm[textExr] || textExr,
spinner: vm && vm[spinnerExr] || spinnerExr,
background: vm && vm[backgroundExr] || backgroundExr,
customClass: vm && vm[customClassExr] || customClassExr,
fullscreen: !!binding.modifiers.fullscreen
}
});
el.instance = mask;
el.mask = mask.$el;
el.maskStyle = {};
binding.value && toggleLoading(el, binding);
},
update: function(el, binding) {
el.instance.setText(el.getAttribute('element-loading-text'));
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding);
}
},
unbind: function(el, binding) {
if (el.domInserted) {
el.mask &&
el.mask.parentNode &&
el.mask.parentNode.removeChild(el.mask);
toggleLoading(el, { value: false, modifiers: binding.modifiers });
}
el.instance && el.instance.$destroy();
}
});
};
export default loadingDirective;

View File

@@ -0,0 +1,106 @@
import Vue from 'vue';
import loadingVue from './loading.vue';
import { addClass, removeClass, getStyle } from 'element-ui/src/utils/dom';
import { PopupManager } from 'element-ui/src/utils/popup';
import afterLeave from 'element-ui/src/utils/after-leave';
import merge from 'element-ui/src/utils/merge';
const LoadingConstructor = Vue.extend(loadingVue);
const defaults = {
text: null,
fullscreen: true,
body: false,
lock: false,
customClass: ''
};
let fullscreenLoading;
LoadingConstructor.prototype.originalPosition = '';
LoadingConstructor.prototype.originalOverflow = '';
LoadingConstructor.prototype.close = function() {
if (this.fullscreen) {
fullscreenLoading = undefined;
}
afterLeave(this, _ => {
const target = this.fullscreen || this.body
? document.body
: this.target;
removeClass(target, 'el-loading-parent--relative');
removeClass(target, 'el-loading-parent--hidden');
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
this.$destroy();
}, 300);
this.visible = false;
};
const addStyle = (options, parent, instance) => {
let maskStyle = {};
if (options.fullscreen) {
instance.originalPosition = getStyle(document.body, 'position');
instance.originalOverflow = getStyle(document.body, 'overflow');
maskStyle.zIndex = PopupManager.nextZIndex();
} else if (options.body) {
instance.originalPosition = getStyle(document.body, 'position');
['top', 'left'].forEach(property => {
let scroll = property === 'top' ? 'scrollTop' : 'scrollLeft';
maskStyle[property] = options.target.getBoundingClientRect()[property] +
document.body[scroll] +
document.documentElement[scroll] +
'px';
});
['height', 'width'].forEach(property => {
maskStyle[property] = options.target.getBoundingClientRect()[property] + 'px';
});
} else {
instance.originalPosition = getStyle(parent, 'position');
}
Object.keys(maskStyle).forEach(property => {
instance.$el.style[property] = maskStyle[property];
});
};
const Loading = (options = {}) => {
if (Vue.prototype.$isServer) return;
options = merge({}, defaults, options);
if (typeof options.target === 'string') {
options.target = document.querySelector(options.target);
}
options.target = options.target || document.body;
if (options.target !== document.body) {
options.fullscreen = false;
} else {
options.body = true;
}
if (options.fullscreen && fullscreenLoading) {
return fullscreenLoading;
}
let parent = options.body ? document.body : options.target;
let instance = new LoadingConstructor({
el: document.createElement('div'),
data: options
});
addStyle(options, parent, instance);
if (instance.originalPosition !== 'absolute' && instance.originalPosition !== 'fixed') {
addClass(parent, 'el-loading-parent--relative');
}
if (options.fullscreen && options.lock) {
addClass(parent, 'el-loading-parent--hidden');
}
parent.appendChild(instance.$el);
Vue.nextTick(() => {
instance.visible = true;
});
if (options.fullscreen) {
fullscreenLoading = instance;
}
return instance;
};
export default Loading;

View File

@@ -0,0 +1,44 @@
<template>
<transition name="el-loading-fade" @after-leave="handleAfterLeave">
<div v-show="visible" class="el-loading-mask" :style="{ backgroundColor: background || '' }" :class="[customClass, { 'is-fullscreen': fullscreen }]">
<!-- <div class="el-loading-spinner">-->
<PG></PG>
<!-- <div class="el-loading-spinner">-->
<!-- <svg v-if="!spinner" class="circular" viewBox="25 25 50 50">-->
<!-- <circle class="path" cx="50" cy="50" r="20" fill="none"/>-->
<!-- </svg>-->
<!-- <i v-else :class="spinner"></i>-->
<!-- <p v-if="text" class="el-loading-text">{{ text }}</p>-->
<!-- </div>-->
<!-- </div>-->
</div>
</transition>
</template>
<script>
import PG from "./pig.vue";
export default {
components: {
PG
},
data() {
return {
text: null,
spinner: null,
background: null,
fullscreen: true,
visible: false,
customClass: ""
};
},
methods: {
handleAfterLeave() {
this.$emit("after-leave");
},
setText(text) {
this.text = text;
}
}
};
</script>

View File

@@ -0,0 +1,622 @@
<template>
<div id="pig-container" class="container">
<main class="loader-container">
<div class="loader-state">
<svg class="nyan" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 47 20">
<defs>
<linearGradient id="rainbow-colors" x1="0.5" x2="0.5" y2="1">
<stop class="rainbow-color1" offset="0" />
<stop class="rainbow-color1" offset="0.167" />
<stop class="rainbow-color2" offset="0.167" />
<stop class="rainbow-color2" offset="0.335" />
<stop class="rainbow-color3" offset="0.335" />
<stop class="rainbow-color3" offset="0.5" />
<stop class="rainbow-color4" offset="0.5" />
<stop class="rainbow-color4" offset="0.669" />
<stop class="rainbow-color5" offset="0.669" />
<stop class="rainbow-color5" offset="0.833" />
<stop class="rainbow-color6" offset="0.833" />
<stop class="rainbow-color6" offset="1" />
</linearGradient>
</defs>
<g class="rainbow">
<rect />
<rect />
<rect />
<rect />
<rect />
<rect />
</g>
<g class="pig">
<g class="foot" transform="translate(52)">
<rect width="1" height="3" transform="translate(6 13)" />
<rect width="1" height="3" transform="translate(8 13)" />
<rect width="1" height="3" transform="translate(14 13)" />
<rect width="1" height="3" transform="translate(12 13)" />
</g>
<g class="tail">
<rect width="1" height="1" transform="translate(4 10)" />
<rect width="1" height="1" transform="translate(3 11)" />
</g>
<g transform="translate(52)">
<rect class="body1" width="8" height="8" transform="translate(7 6)" />
<rect class="body1" width="10" height="8" transform="translate(6 7)" />
<rect class="body1" width="12" height="6" transform="translate(5 8)" />
<rect class="body2" width="10" height="6" transform="translate(6 8)" />
<rect class="body2" width="8" height="6" transform="translate(7 7)" />
<rect class="stains" width="4" height="1" transform="translate(7 13)" />
<rect class="stains" width="2" height="1" transform="translate(8 12)" />
<rect class="stains" width="2" height="1" transform="translate(6 9)" />
<rect class="stains" width="3" height="1" transform="translate(6 8)" />
<rect class="stains" width="3" height="1" transform="translate(7 7)" />
<rect class="stains" width="1" height="1" transform="translate(14 7)" />
</g>
<g class="ears" transform="translate(52)">
<rect width="1" height="3" transform="translate(10 5)" />
<rect width="1" height="3" transform="translate(15 5)" />
</g>
<g class="snout">
<rect class="snout1" width="5" height="3" transform="translate(13 10)" />
<rect class="snout-holes" width="1" height="3" transform="translate(17 11) rotate(90)" />
<rect class="snout2" width="1" height="1" transform="translate(16 11) rotate(90)" />
</g>
<g class="eyes">
<rect width="1" height="1" transform="translate(12 9)" />
<rect width="1" height="1" transform="translate(15 9)" />
</g>
</g>
</svg>
</div>
<span class="loader-text">Loading...</span>
<div class="background-container">
<div class="stars"></div>
</div>
</main>
</div>
</template>
<script>
export default {
name: "pig",
data() {
return {};
},
props: {},
watch: {},
components: {},
filters: {},
methods: {},
created() {},
mounted() {}
};
</script>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#pig-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
overflow: hidden;
font: 0.9em "Press Start 2P";
text-transform: uppercase;
//color: #000;
background: transparent;
//background: #0a0916 radial-gradient(circle at center, #151333 0%, #0a0916 30%) no-repeat;
}
a {
color: #25364c;
text-decoration: none;
}
a:hover {
color: #ffffff;
}
.settings {
position: absolute;
z-index: 2;
top: 20px;
right: 20px;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.settings li {
list-style-type: none;
}
.settings button {
max-width: 200px;
padding: 5px 0;
font: inherit;
font-size: clamp(12px, 3vw, 16px);
color: #ffffff;
text-transform: uppercase;
text-overflow: ellipsis;
user-select: none;
white-space: nowrap;
overflow: hidden;
border: none;
transform-origin: right center;
background: none;
}
.settings button[data-skin="nyan"].active {
color: #f66b79;
}
.settings button[data-skin="devil"].active {
color: #ae1f2d;
}
.settings button[data-skin="alien"].active {
color: #319e47;
}
.settings button[data-skin="baguette"].active {
color: #fedea2;
}
.settings button.state-button {
margin-top: 20px;
}
.settings button.state-button.active {
color: #13b225;
text-decoration: none;
}
.settings button:hover {
cursor: pointer;
transform: scale(1.2);
}
@media screen and (max-height: 500px) and (max-width: 750px) {
.settings {
display: none;
flex-direction: row;
gap: 10px;
}
.settings button:hover:not(.active) {
transform: translateY(-2px);
}
}
.loader-container {
position: relative;
width: 100%;
height: 100%;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
}
.loader-container .loader-state {
width: 100%;
}
.loader-container .loader-state + .loader-text {
display: block;
user-select: none;
opacity: 0;
transform: translate(10px, calc(20px * 1.5));
animation: loader-text-blink 0.4s ease-in-out infinite alternate;
}
@keyframes loader-text-blink {
to {
opacity: 1;
}
}
@media screen and (max-height: 350px) {
.loader-container .loader-state + .loader-text {
display: none;
}
}
.loader-container .loader-state.enter-exit {
animation: appear 8s ease-in-out infinite forwards;
}
@keyframes appear {
0% {
opacity: 0;
transform: translate(-600px, 20px);
}
5%,
93% {
opacity: 0;
}
10% {
transform: translate(50px, 20px);
}
15% {
transform: translate(-50px, 20px);
}
20%,
90% {
opacity: 1;
transform: translate(0, 20px);
}
100% {
opacity: 0;
transform: translate(600px, 20px);
}
}
.loader-container .loader-state.enter-exit + .loader-text {
animation: loader-text-blink2 8s ease-in-out infinite forwards;
}
@keyframes loader-text-blink2 {
0%,
5%,
15%,
25%,
35%,
45%,
55%,
65%,
75%,
85%,
93%,
100% {
opacity: 0;
}
10%,
20%,
30%,
40%,
50%,
60%,
70%,
80%,
90% {
opacity: 1;
}
}
svg {
--flying-speed: 0.4s;
width: 100%;
max-width: 400px;
-webkit-mask-image: linear-gradient(to left, #0a0916 65%, transparent 85%);
mask-image: linear-gradient(to left, #0a0916 65%, transparent 85%);
animation: moves-like-jagger 0.4s ease-in-out infinite alternate;
}
@keyframes moves-like-jagger {
to {
transform: translateY(20px);
}
}
.rainbow rect {
width: 5px;
height: 6px;
stroke: transparent;
fill: url(#rainbow-colors);
}
.rainbow rect:nth-child(1) {
transform: translate(25px, 8.4px);
animation: over-the-rainbow-1 0.4s ease-in-out 0.0666666667s infinite alternate;
}
@keyframes over-the-rainbow-1 {
to {
transform: translate(25px, 9.0666666667px);
}
}
.rainbow rect:nth-child(2) {
transform: translate(20.2px, 8.3px);
animation: over-the-rainbow-2 0.4s ease-in-out 0.1333333333s infinite alternate;
}
@keyframes over-the-rainbow-2 {
to {
transform: translate(20.2px, 9.6333333333px);
}
}
.rainbow rect:nth-child(3) {
transform: translate(15.4px, 8.2px);
animation: over-the-rainbow-3 0.4s ease-in-out 0.2s infinite alternate;
}
@keyframes over-the-rainbow-3 {
to {
transform: translate(15.4px, 10.2px);
}
}
.rainbow rect:nth-child(4) {
transform: translate(10.6px, 8.1px);
animation: over-the-rainbow-4 0.4s ease-in-out 0.2666666667s infinite alternate;
}
@keyframes over-the-rainbow-4 {
to {
transform: translate(10.6px, 10.7666666667px);
}
}
.rainbow rect:nth-child(5) {
transform: translate(5.8px, 8px);
animation: over-the-rainbow-5 0.4s ease-in-out 0.3333333333s infinite alternate;
}
@keyframes over-the-rainbow-5 {
to {
transform: translate(5.8px, 11.3333333333px);
}
}
.rainbow rect:nth-child(6) {
transform: translate(1px, 7.9px);
animation: over-the-rainbow-6 0.4s ease-in-out 0.4s infinite alternate;
}
@keyframes over-the-rainbow-6 {
to {
transform: translate(1px, 11.9px);
}
}
.nyan .rainbow-color1 {
stop-color: #e12523;
}
.nyan .rainbow-color2 {
stop-color: #dc8d30;
}
.nyan .rainbow-color3 {
stop-color: #deeb44;
}
.nyan .rainbow-color4 {
stop-color: #01eb3e;
}
.nyan .rainbow-color5 {
stop-color: #138fe9;
}
.nyan .rainbow-color6 {
stop-color: #7127ee;
}
.devil .rainbow-color1 {
stop-color: #d71440;
}
.devil .rainbow-color2 {
stop-color: #ff6c40;
}
.devil .rainbow-color3 {
stop-color: #ffce21;
}
.devil .rainbow-color4 {
stop-color: #ffce21;
}
.devil .rainbow-color5 {
stop-color: #ff6c40;
}
.devil .rainbow-color6 {
stop-color: #d71440;
}
.alien .rainbow-color1 {
stop-color: #12a921;
}
.alien .rainbow-color2 {
stop-color: #0fce22;
}
.alien .rainbow-color3 {
stop-color: #0fef25;
}
.alien .rainbow-color4 {
stop-color: #0fef25;
}
.alien .rainbow-color5 {
stop-color: #0fce22;
}
.alien .rainbow-color6 {
stop-color: #12a921;
}
.baguette .rainbow-color1 {
stop-color: #2465de;
}
.baguette .rainbow-color2 {
stop-color: #2465de;
}
.baguette .rainbow-color3 {
stop-color: #ffffff;
}
.baguette .rainbow-color4 {
stop-color: #ffffff;
}
.baguette .rainbow-color5 {
stop-color: #f1200d;
}
.baguette .rainbow-color6 {
stop-color: #f1200d;
}
svg.nyan {
--body1: #f19183;
--body2: #fcd8d7;
--stains: #fac4b8;
--eyes: #0c0b0b;
--ears: #f19183;
--snout1: #f79197;
--snout2: #f66b79;
--snout-holes: #e73b36;
}
svg.devil {
--body1: #410a07;
--body2: #bb1626;
--stains: #790813;
--eyes: #f0d459;
--ears: #2b1d05;
--snout1: #db2839;
--snout2: #ae1f2d;
--snout-holes: #900f0b;
}
svg.alien {
--body1: #116423;
--body2: #15a031;
--stains: #0f8b27;
--eyes: #ffffff;
--ears: #3dbc56;
--snout1: #3dbc56;
--snout2: #319e47;
--snout-holes: #196e2a;
}
svg.baguette {
--body1: #f89f39;
--body2: #fdb85a;
--stains: #fedda7;
--eyes: #0c0b0b;
--ears: #dc790f;
--snout1: #ffdea1;
--snout2: #fedea2;
--snout-holes: #f98d0e;
}
.pig {
position: relative;
transform: translate(-29px);
}
.pig .body2 {
fill: var(--body2);
}
.pig:hover {
cursor: pointer;
}
.pig .body1,
.tail rect,
.foot rect {
fill: var(--body1);
}
.stains {
fill: var(--stains);
}
.snout {
stroke: transparent;
transform: translate(52px);
animation: sniff-sniff 2s ease-in-out infinite;
}
.snout .snout1 {
fill: var(--snout1);
}
.snout .snout2 {
fill: var(--snout2);
}
.snout .snout-holes {
fill: var(--snout-holes);
}
@keyframes sniff-sniff {
5%,
15%,
25% {
transform: translate(52px);
}
10%,
20% {
transform: translate(52.5px);
}
}
.tail {
transform: translate(52.25px);
transform-origin: center;
}
.tail rect:nth-child(2) {
transform: translate(3.25px, 10.5px);
animation: fairy-tail 0.4s ease-in-out infinite alternate;
}
@keyframes fairy-tail {
to {
transform: translate(3.25px, 10px);
}
}
.foot {
transform: translate(52px);
animation: hypnotic-feet 0.4s ease-in-out infinite alternate;
}
@keyframes hypnotic-feet {
to {
transform: translate(52.5px);
}
}
.ears rect {
fill: var(--ears);
}
.eyes {
transform: translate(52px);
}
.eyes rect {
fill: var(--eyes);
animation: blinky-blinky 2s step-start infinite;
}
@keyframes blinky-blinky {
5% {
opacity: 1;
}
10% {
opacity: 0;
}
}
.heart {
animation: sweet-heart 0.6s ease-out;
transform: translate(33px, 4px);
}
@keyframes sweet-heart {
to {
transform: translate(33px, 0px);
}
}
.background-container {
position: absolute;
z-index: -1;
width: 100%;
max-width: 800px;
height: 50%;
opacity: 0.8;
overflow: hidden;
-webkit-mask-image: radial-gradient(circle, #151333 0%, transparent 80%);
mask-image: radial-gradient(circle, #151333 0%, transparent 80%);
}
.background-container .stars {
background: url("https://cdn.discordapp.com/attachments/997025307255119955/1004483049406353568/stars_background.svg");
width: 1740px;
height: 100%;
animation: through-the-stars 1.2s infinite linear;
}
@keyframes through-the-stars {
to {
transform: translateX(-50%);
}
}
footer {
text-align: center;
margin: auto auto 5vh auto;
padding: 0 5vw;
font-size: 0.6em;
}
@media screen and (max-height: 350px) {
footer {
display: none;
}
}
</style>

View File

@@ -0,0 +1 @@
<el-progress :percentage="percentage" :color="customColor"></el-progress>

View File

@@ -45,6 +45,18 @@ export default [
icon: "el-icon-s-home"
},
children: [
{
path: '/knowledge/knowledge-create',
name: 'knowledge-create',
component: () => import('@/views/knowledge/detail/components/knowledgeForm.vue'),
meta: {
breadcrumb:false,
title: '知识库详情',
icon: 'el-icon-s-home',
},
},
{
path: "/knowledge/detail",
name: "detail",

View File

@@ -0,0 +1,107 @@
<template>
<div class='container index-container'>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<h3>新增知识库</h3>
</div>
<el-row >
<el-col :span='21' :offset='1'>
<el-form :model="model" ref="model" label-width="100px">
<el-form-item label="知识库名称:" prop="knowledgeName">
<el-input></el-input>
</el-form-item>
<el-form-item label="描述(可选)" prop="knowledgeName" requried>
<el-input type='textarea'></el-input>
</el-form-item>
<el-form-item label="分段模式:" prop="knowledgeName" requried>
<el-radio-group>
<el-radio label="1">通用分段模式</el-radio>
<el-radio label="1">Q&A分段模式</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-col>
<el-col :span='24' class='text-center'>
<el-button type='primary' size='medium' @click='save'>创建</el-button>
<el-button size='medium'>取消 </el-button>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import 'nprogress/nprogress.css' // progress bar style
import renderLoading from '@/components/RenderLoading/loading/index'
import NProgress from 'nprogress'
export default {
name: 'knowledgeForm',
data() {
return {}
},
props: {
knowledgeForm: {
type: Object,
default: () => {
return {
knowledgeName:''
}
}
},
knowledgeId: {
type: String
}
},
watch: {},
components: {},
filters: {},
methods: {
save(){
// 配置进度条参数
// NProgress.configure({
// showSpinner: false, // 隐藏转圈圈
// })
// setTimeout(()=>{
//
// NProgress.done();
// },5000)
let loadingDom = document.getElementById('knowledgeFormDrawer').querySelector('.el-drawer')
console.log(loadingDom)
let loading = renderLoading.service({
target:loadingDom,
})
loading.close()
},
},
created() {
},
mounted() {
},
computed: {
// model 获取 knowledgeForm
model() {
return this.knowledgeForm
}
}
}
</script>
<style scoped lang='scss'>
</style>

View File

@@ -0,0 +1,107 @@
<template>
<div class='container index-container'>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<h3>新增知识库</h3>
</div>
<el-row >
<el-col :span='21' :offset='1'>
<el-form :model="model" ref="model" label-width="100px">
<el-form-item label="知识库名称:" prop="knowledgeName">
<el-input></el-input>
</el-form-item>
<el-form-item label="描述(可选)" prop="knowledgeName" requried>
<el-input type='textarea'></el-input>
</el-form-item>
<el-form-item label="分段模式:" prop="knowledgeName" requried>
<el-radio-group>
<el-radio label="1">通用分段模式</el-radio>
<el-radio label="1">Q&A分段模式</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-col>
<el-col :span='24' class='text-center'>
<el-button type='primary' size='medium' @click='save'>创建</el-button>
<el-button size='medium'>取消 </el-button>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import 'nprogress/nprogress.css' // progress bar style
import renderLoading from '@/components/RenderLoading/loading/index'
import NProgress from 'nprogress'
export default {
name: 'knowledgeForm',
data() {
return {}
},
props: {
knowledgeForm: {
type: Object,
default: () => {
return {
knowledgeName:''
}
}
},
knowledgeId: {
type: String
}
},
watch: {},
components: {},
filters: {},
methods: {
save(){
// 配置进度条参数
// NProgress.configure({
// showSpinner: false, // 隐藏转圈圈
// })
// setTimeout(()=>{
//
// NProgress.done();
// },5000)
let loadingDom = document.getElementById('knowledgeFormDrawer').querySelector('.el-drawer')
console.log(loadingDom)
let loading = renderLoading.service({
target:loadingDom,
})
loading.close()
},
},
created() {
},
mounted() {
},
computed: {
// model 获取 knowledgeForm
model() {
return this.knowledgeForm
}
}
}
</script>
<style scoped lang='scss'>
</style>

View File

@@ -1,24 +1,61 @@
<template>
<div class='container index-container'>
<div class="container index-container">
<el-card shadow="hover">
<div slot="header" class="clearfix">
<h3>监管法规知识库</h3>
<p class='mt10 fs14' style='line-height: 20px'>
描述
</p>
<p class='mt10 fs14' style='line-height: 20px'>
分段模式
</p>
<el-button type='text' class='mt10'>修改知识库</el-button>
<el-button type='text' class='mt10'>上传知识</el-button>
<h3>{{ knowledgeName }}</h3>
<p class="mt10 fs14" style="line-height: 20px">描述{{ knowledgeDesc }}</p>
<p class="mt10 fs14" style="line-height: 20px">分段模式{{ segmentedMode }}</p>
<el-button type="text" class="mt10">修改知识库</el-button>
<el-button type="text" class="mt10">上传知识</el-button>
</div>
<div class='card-body'>
<el-empty>
<div class='mt20'>
<el-button type='primary' size='medium' class='fs14' @click='handleAddKnowledge'>立即添加</el-button>
<div class="card-body">
<el-empty v-if="!list || list.length <= 0">
<div class="mt20">
<el-button type="primary" size="medium" class="fs14" @click="handleAddKnowledge">立即添加</el-button>
</div>
</el-empty>
<div class="table-container">
<div class="">
<el-form :model="form" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="知识文件名称">
<el-input v-model="form.name" placeholder="请输入知识文件名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="知识文件来源">
<el-input v-model="form.name" placeholder="请输入知识文件名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="上传用户">
<el-input v-model="form.name" placeholder="请输入知识文件名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="关键字">
<el-input v-model="form.name" placeholder="请输入知识文件名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="上传时间">
<el-input v-model="form.name" placeholder="请输入知识文件名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20 text-center">
<el-button size="medium" type="primary">查询</el-button>
<el-button size="medium" type="primary">重置</el-button>
</el-col>
</el-row>
</el-form>
</div>
<r-table :columns="columns" :data="list" :deletion="false"></r-table>
</div>
</div>
11.
</el-card>
</div>
</template>
@@ -26,26 +63,76 @@
export default {
name: 'index',
data() {
return {}
return {
form: {},
//知识库名称
knowledgeName: '监管',
knowledgeDesc: '监管',
segmentedMode: '分段模式',
list: [{}]
}
},
props: {},
watch: {},
components: {},
filters: {},
methods: {
handleAddKnowledge(){
// 跳转
handleAddKnowledge() {
this.$router.push({
path: '/knowledge/detail/create'
})
},
/**
* @name 根据id 获取知识内容详情
* @author Chen Yuda
* @created_date 2025/4/9
* @description
**/
getKnowledgeDetail() {}
},
created() {
},
mounted() {
},
computed: {}
created() {},
mounted() {},
computed: {
columns() {
return [
{
key: '知识文件名称',
prop: 'knowledgeName',
width: '200px',
align: 'center',
render: (h, params) => {
return h('div', [h('span', params.row.knowledgeName)])
}
},
{
key: '知识文件来源',
prop: 'knowledgeDesc'
},
{
key: '召回次数',
prop: 'knowledgeDesc'
},
{
key: '上传用户',
prop: 'knowledgeDesc'
},
{
key: '上传时间',
prop: 'knowledgeDesc'
},
{
key: '操作',
prop: 'knowledgeDesc',
width: '200px',
render: (h, params) => {
return h('div', [h('span', params.row.knowledgeDesc)])
}
}
]
}
}
}
</script>
<style scoped lang='scss'>
</style>
<style scoped lang="scss"></style>

View File

@@ -12,14 +12,18 @@
<div class='card-body'>
<el-row>
<el-col :span='8'>
<el-input placeholder='输入知识库名称'>
<el-button slot="append" icon="el-icon-search"></el-button>
<el-input placeholder='输入知识库名称' v-model='nameLike'>
<el-button slot="append" icon="el-icon-search" @click='getList'></el-button>
</el-input>
</el-col>
</el-row>
<div class='ledge-list mt20'>
<el-card shadow="hover" class='item dashed' @click='createdKnowLedge'>新增知识库</el-card>
<el-card shadow="hover" class='item dashed' >
<div @click='createdKnowLedge()'>
新增知识库
</div>
</el-card>
<el-card v-for='(item,index) in datasetList' class='item' shadow="hover" :key='index'>
<div class='item-body flex '>
<div >
@@ -33,47 +37,70 @@
</div>
</div>
<div class='item-bottom mt20'>
<el-button type='text' class='fs14' @click='knowLedgeDetail'>查看详情</el-button>
<el-button type='text' class='fs14' @click='knowLedgeDetail(item)'>查看详情</el-button>
<el-button type='text' class='fs14'>修改</el-button>
<el-button type='text' class='fs14'>删除</el-button>
<el-button type='text' class='fs14 danger'>删除</el-button>
</div>
</el-card>
</div>
</div>
</el-card>
<div class='table-container'>
<div class='button-container'></div>
</div>
</div>
</template>
<script>
import {docManageDataset} from '@/api/generatedApi/index'
import knowledgeForm from '@/views/knowledge/detail/components/knowledgeForm.vue'
export default {
name: 'index',
data() {
return {
datasetList:[]
datasetList:[],
nameLike:'',
drawer:false,
direction:'ltr',
size:'50%',
withHeader:false,
drawerTitle:'创建知识库',
knowledgeId:'',
knowledgeForm:{
name:'knowledgeForm',
component:knowledgeForm
}
}
},
props: {},
watch: {},
components: {},
components: {
knowledgeForm
},
filters: {},
methods: {
getList (){
docManageDataset({nameLike:''}).then(res=>{
docManageDataset({nameLike:this.nameLike}).then(res=>{
console.log(res)
this.datasetList = res.content.content.list
})
},
createdKnowLedge() {
},
knowLedgeDetail(){
this.$router.push({
path:'/knowledge/detail'
path:'/knowledge/knowledge-create',
query:{
datasetId:''
}
})
},
knowLedgeDetail(item){
this.$router.push({
path:'/knowledge/detail',
query:{
datasetId:item.id
}
})
},
},