Commit 0295590b authored by pengxin's avatar pengxin

Merge remote-tracking branch 'remotes/origin/ljgc-web'

# Conflicts:
#	src/api/GptController/KnowledgeManage.js
#	src/staticDict/gptStaticDict.js
#	src/views/gptTraining/knowledgeBase/card/index.vue
#	src/views/gptTraining/modelService/testOnline/components/contentView/filePreview/index.vue
#	src/views/gptTraining/modelService/testOnline/components/contentView/index.vue
#	src/views/gptTraining/modelService/testOnline/components/leftBox/index.vue
#	src/views/gptTraining/modelService/testOnline/components/rightBox/index.vue
#	src/views/gptTraining/promptProject/promptTemplate/components/dialog/createTemplate.vue
parents 49dc9a9c f56b2646
......@@ -19216,6 +19216,16 @@
"extsprintf": "^1.2.0"
}
},
"vis-data": {
"version": "7.1.9",
"resolved": "https://registry.npmmirror.com/vis-data/-/vis-data-7.1.9.tgz",
"integrity": "sha512-COQsxlVrmcRIbZMMTYwD+C2bxYCFDNQ2EHESklPiInbD/Pk3JZ6qNL84Bp9wWjYjAzXfSlsNaFtRk+hO9yBPWA=="
},
"vis-network": {
"version": "9.1.9",
"resolved": "https://registry.npmmirror.com/vis-network/-/vis-network-9.1.9.tgz",
"integrity": "sha512-Ft+hLBVyiLstVYSb69Q1OIQeh3FeUxHJn0WdFcq+BFPqs+Vq1ibMi2sb//cxgq1CP7PH4yOXnHxEH/B2VzpZYA=="
},
"vm-browserify": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz",
......
......@@ -36,6 +36,8 @@
"register-service-worker": "^1.6.2",
"sortablejs": "^1.7.0",
"v-charts": "^1.19.0",
"vis-data": "^7.1.9",
"vis-network": "^9.1.9",
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.3",
"vue-demi": "^0.14.6",
......@@ -50,6 +52,7 @@
"xe-utils": "^3.5.4",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.2.0",
"@vue/cli-plugin-eslint": "~4.2.0",
......
<template>
<div>
<div style="height:100%;width:100%">
<router-view></router-view>
</div>
</template>
......
......@@ -8,7 +8,7 @@ export default class KnowledgeManage {
static recreate (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/recreate', 'post', params, axiosOption, httpOption);
}
static searchDocs (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/searchDocs', 'post', params, axiosOption, httpOption);
}
......@@ -18,34 +18,49 @@ export default class KnowledgeManage {
static uploadDocs (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/uploadDocs', 'post', params, axiosOption, httpOption);
}
static view (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/view', 'get', params, axiosOption, httpOption);
}
static export (sender, params, fileName) {
return sender.download('/admin/app/knowledgeManage/export', params, fileName);
}
static add (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/add', 'post', params, axiosOption, httpOption);
}
static update (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/update', 'post', params, axiosOption, httpOption);
}
static delete (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/delete', 'post', params, axiosOption, httpOption);
}
static deleteDocs (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/deleteDocs', 'post', params, axiosOption, httpOption);
}
static listForTree (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/listForTree', 'post', params, axiosOption, httpOption);
}
static load (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/load', 'post', params, axiosOption, httpOption);
}
static addKnowledgeUser (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/addKnowledgeUser', 'post', params, axiosOption, httpOption);
}
static listNotInKnowledgeUser (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/listNotInKnowledgeUser', 'post', params, axiosOption, httpOption);
}
static getSysUserListByKnowledgeId (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/listKnowledgeUser', 'post', params, axiosOption, httpOption);
}
static deleteKnowledgeUser (sender, params, axiosOption, httpOption) {
return sender.doUrl('/admin/app/knowledgeManage/deleteKnowledgeUser', 'post', params, axiosOption, httpOption);
}
}
......@@ -7,6 +7,11 @@ html, body {
font-size: 14px;
font-family: Arial,"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",sans-serif;
background-color: rgb(228,240,255);
height: 100%;
width: 100%;
}
div{
word-break:break-all
}
.omit {
/* 一行省略 */
......
module.exports = {
baseUrl: 'http://192.168.0.36:8082/',
wsUrl: 'ws://192.168.0.36:7860/',
baseUrl: 'http://218.76.0.69:8082/',
wsUrl: 'ws://218.76.0.69:7860/',
projectName: '灵境大模型平台'
}
......@@ -11,7 +11,6 @@ import router from './router';
import store from './store';
import '@/staticDict/gptStaticDict.js';
import {SocketService} from '@/utils/websocket.js';
import TreeSelect from '@/components/TreeSelect';
import RichEditor from '@/components/RichEditor';
import InputNumberRange from '@/components/InputNumberRange';
......
......@@ -14,13 +14,14 @@ const routers = [
component: _import('login/index'),
name: 'root'
},
{path: '/aggregate', component: _import('welcome/index-2'), name: 'aggregate', meta: { title: '集群' }},
{
path: '/main',
component: _import('layout/index'),
name: 'main',
props: getProps,
redirect: {
name: 'welcome'
name: 'aggregate'
},
meta: {
title: '主页',
......@@ -89,6 +90,7 @@ const routers = [
// { path: 'batchOptimization', component: _import('gptTraining/promptProject/batchOptimization/index'), name: 'batchOptimization', props: getProps, meta: { title: '批量优化' } },
// 知识库
{ path: 'knowledgeBase', component: _import('gptTraining/knowledgeBase/index'), name: 'knowledgeBase', props: getProps, meta: { title: '知识库' } },
{ path: 'welcome', component: _import('welcome/index'), name: 'welcome', meta: { title: '欢迎' } }
]
}
......
......@@ -412,8 +412,13 @@ const ModeOfSpeaking = new DictionaryBase('对话模式', [
},
{
id: 3,
name: '文件对话',
name: '基于文件问答',
symbol: 'fileSession'
},
{
id: 4,
name: '知识图谱问答',
symbol: 'knowledgeGraph'
}
// {
// id: 2,
......
......@@ -36,8 +36,9 @@
</el-tooltip>
</div> -->
<div style="margin-bottom:10px">
<el-button :size="defaultFormItemSize" :plain="true" @click="handleCommand('auth',item)" >授权</el-button>
<el-button :size="defaultFormItemSize" :plain="true" @click="handleCommand('edit',item)" >编辑</el-button>
<el-button :size="defaultFormItemSize" :plain="true" @click="handleCommand('del',item)">删除</el-button>
<el-button :size="defaultFormItemSize" :plain="true" @click="handleCommand('del',item)">删除</el-button>
</div>
</div>
</el-card>
......@@ -47,6 +48,7 @@
<script>
import { KnowledgeManage } from '@/api/gptController.js';
import editOrAdd from '../dialog/editOrAdd';
import authKnowledgeUser from '../dialog/authKnowledgeUser/index';
export default {
data () {
return {
......@@ -95,6 +97,13 @@ export default {
this.refresh();
}).catch(e => { this.refresh(); });
},
auth (item) {
this.$dialog.show('授权知识库', authKnowledgeUser, {
area: ['1100px', '600px']
}, { knowledgeId: item.knowledgeId }).then(res => {
this.refresh();
}).catch(e => { this.refresh(); });
},
del (item) {
this.$confirm('是否确认删除', '提示', {
confirmButtonText: '确定',
......@@ -123,6 +132,8 @@ export default {
this.del(item)
} else if (command === 'copy') {
this.copy(item)
} else if (command === 'auth') {
this.auth(item)
}
}
}
......
<template>
<div style="position: relative;">
<el-form ref="formKnowledgeUsers" :model="formData" label-width="75px" :size="defaultFormItemSize" label-position="right" @submit.native.prevent>
<filter-box :item-width="325" :hasSearch="false">
<el-form-item label="用户状态" prop="formFilter.sysUserStatus">
<el-select class="filter-item" v-model="formData.formFilter.sysUserStatus" :clearable="true"
placeholder="用户状态" :loading="formData.sysUserStatus.impl.loading"
@visible-change="onSysUserStatusVisibleChange">
<el-option v-for="item in formData.sysUserStatus.impl.dropdownList" :key="item.id" :value="item.id" :label="item.name" />
</el-select>
</el-form-item>
<el-form-item label="用户名" prop="formFilter.sysUserLoginName">
<el-input class="filter-item" v-model="formData.formFilter.sysUserLoginName" maxlength="12"
:clearable="true" placeholder="用户名" />
</el-form-item>
<div class="search-box" style="padding-left: 8px;margin-bottom: 16px;">
<el-button slot="operator" type="default" :plain="true" :size="defaultFormItemSize" @click="onReset">重置</el-button>
<el-button slot="operator" type="primary" :plain="true" :size="defaultFormItemSize" @click="refreshFormKnowledgeUserList(true)" icon="el-icon-search">查询</el-button>
<el-button :size="defaultFormItemSize" type="primary" :plain="false" @click="onSetUser"
:disabled="selectUsers == null || selectUsers.length <= 0">授权人员</el-button>
</div>
</filter-box>
</el-form>
<el-row>
<el-col :span="24">
<el-table :data="formData.SysUser.impl.dataList" :size="defaultFormItemSize" row-key="userId" ref="userTable"
header-cell-class-name="table-header-gray" @selection-change="onTableSelectionChange">
<el-table-column label="序号" header-align="center" align="center" type="index" width="50px" :index="formData.SysUser.impl.getTableIndex" />
<el-table-column header-align="center" align="center" type="selection" width="50px" :reserve-selection="true" />
<el-table-column label="用户名" prop="loginName">
</el-table-column>
<el-table-column label="昵称" prop="showName">
</el-table-column>
<el-table-column label="账号类型">
<template slot-scope="scope">
<span>{{SysUserType.getValue(scope.row.userType)}}</span>
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<el-tag :type="getUserStatusType(scope.row.userStatus)" :size="defaultFormItemSize">{{SysUserStatus.getValue(scope.row.userStatus)}}</el-tag>
</template>
</el-table-column>
</el-table>
<el-col :span="24">
<el-row type="flex" justify="end" style="margin-top: 10px;">
<el-pagination
:total="formData.SysUser.impl.totalCount"
:current-page="formData.SysUser.impl.currentPage"
:page-size="formData.SysUser.impl.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, prev, pager, next, sizes"
@current-change="formData.SysUser.impl.onCurrentPageChange"
@size-change="formData.SysUser.impl.onPageSizeChange">
</el-pagination>
</el-row>
</el-col>
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
/* eslint-disable-next-line */
import { DropdownWidget, TableWidget } from '@/utils/widget.js';
/* eslint-disable-next-line */
import { uploadMixin, statsDateRangeMixin, cachePageMixin } from '@/core/mixins';
/* eslint-disable-next-line */
import { DictionaryController } from '@/api';
import { KnowledgeManage } from '@/api/gptController.js';
export default {
name: 'formKnowledgeUsers',
props: {
knowledgeId: {
type: [String, Number],
required: true
}
},
mixins: [uploadMixin, statsDateRangeMixin, cachePageMixin],
data () {
return {
formData: {
formFilter: {
sysUserStatus: undefined,
sysUserLoginName: undefined
},
formFilterCopy: {
sysUserStatus: undefined,
sysUserLoginName: undefined
},
sysUserStatus: {
impl: new DropdownWidget(this.loadSysUserStatusDropdownList)
},
SysUser: {
impl: new TableWidget(this.loadSysUserData, this.loadSysUserVerify, true, false)
},
isInit: false
},
selectUsers: []
}
},
methods: {
/**
* 用户状态下拉数据获取函数
*/
loadSysUserStatusDropdownList () {
return new Promise((resolve, reject) => {
let params = {};
DictionaryController.dictSysUserStatus(this, params).then(res => {
resolve(res.getList());
}).catch(e => {
reject(e);
});
});
},
/**
* 用户状态下拉框显隐
*/
onSysUserStatusVisibleChange (show) {
this.formData.sysUserStatus.impl.onVisibleChange(show).catch(e => {});
},
getUserStatusType (status) {
if (status === this.SysUserStatus.NORMAL) {
return 'success';
} else if (status === this.SysUserStatus.LOCKED) {
return 'danger';
} else {
return 'info';
}
},
onTableSelectionChange (values) {
this.selectUsers = values;
},
onSetUser () {
let params = {
knowledgeId: this.knowledgeId,
userIdListString: this.selectUsers.map((item) => {
return item.userId
}).join(',')
}
KnowledgeManage.addKnowledgeUser(this, params).then(res => {
this.$message.success('授权成功');
this.refreshFormKnowledgeUserList(true);
this.$refs.userTable.clearSelection();
this.selectUsers = [];
}).catch(e => {});
},
onCancel () {
this.$router.go(-1);
},
onReset () {
this.$refs.formKnowledgeUsers.resetFields();
this.refreshFormKnowledgeUserList(true);
},
loadSysUserData (params) {
params.knowledgeId = this.knowledgeId;
params.sysUserDtoFilter = {
loginName: this.formData.formFilterCopy.sysUserLoginName,
userStatus: this.formData.formFilterCopy.sysUserStatus
}
return new Promise((resolve, reject) => {
KnowledgeManage.listNotInKnowledgeUser(this, params).then(res => {
resolve({
dataList: res.data.dataList,
totalCount: res.data.totalCount
});
}).catch(e => {
reject(e);
});
});
},
/**
* 获取检测函数,返回true正常获取数据,返回false停止获取数据
*/
loadSysUserVerify () {
this.formData.formFilterCopy.sysUserLoginName = this.formData.formFilter.sysUserLoginName;
this.formData.formFilterCopy.sysUserStatus = this.formData.formFilter.sysUserStatus;
return true;
},
refreshFormKnowledgeUserList (reloadData = false) {
// 重新获取数据组件的数据
if (reloadData) {
this.formData.SysUser.impl.refreshTable(true, 1);
} else {
this.formData.SysUser.impl.refreshTable();
}
this.formData.sysUserStatus.impl.onVisibleChange(true).catch(e => {});
this.formData.isInit = true;
},
initFormData () {
},
formInit () {
this.initFormData();
this.refreshFormKnowledgeUserList();
}
},
computed: {
getContextHeightStyle () {
return [
{'height': (this.getClientHeight - 142) + 'px'}
]
},
...mapGetters(['getClientHeight'])
},
created () {
this.formInit();
}
}
</script>
<style>
</style>
<template>
<div style="position: relative;">
<el-form ref="formKnowledgeUser" :model="formData" label-width="75px" :size="defaultFormItemSize" label-position="right" @submit.native.prevent>
<filter-box :item-width="350" :hasSearch="false">
<el-form-item label="用户名" prop="formFilter.sysUserLoginName">
<el-input class="filter-item" v-model="formData.formFilter.sysUserLoginName" maxlength="12" :clearable="true" placeholder="输入用户名 / 昵称查询"/>
</el-form-item>
<div class="search-box" style="padding-left: 8px;margin-bottom: 16px;">
<el-button slot="operator" type="default" :size="defaultFormItemSize" :plain="true" @click="onReset">重置</el-button>
<el-button slot="operator" type="primary" :size="defaultFormItemSize" :plain="true" @click="refreshFormKnowledgeUser(true)" icon="el-icon-search">查询</el-button>
<el-button type="primary" :size="defaultFormItemSize" :plain="false" @click="onAddRow()" icon="el-icon-plus">添加用户</el-button>
</div>
</filter-box>
</el-form>
<el-row>
<el-col :span="24">
<el-table :data="formData.SysUser.impl.dataList" :size="defaultFormItemSize" @sort-change="formData.SysUser.impl.onSortChange"
header-cell-class-name="table-header-gray">
<el-table-column label="序号" header-align="center" align="center" type="index" width="50px" :index="formData.SysUser.impl.getTableIndex" />
<el-table-column label="用户名" prop="loginName">
</el-table-column>
<el-table-column label="昵称" prop="showName">
</el-table-column>
<el-table-column label="账号类型">
<template slot-scope="scope">
<span>{{SysUserType.getValue(scope.row.userType)}}</span>
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<el-tag :type="getUserStatusType(scope.row.userStatus)" :size="defaultFormItemSize">{{SysUserStatus.getValue(scope.row.userStatus)}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="80px">
<template slot-scope="scope">
<el-button class="btn-table-delete" type="text" :size="defaultFormItemSize" @click="onDeleteRow(scope.row)">
移除
</el-button>
</template>
</el-table-column>
</el-table>
<el-col :span="24">
<el-row type="flex" justify="end" style="margin-top: 10px;">
<el-pagination
:total="formData.SysUser.impl.totalCount"
:current-page="formData.SysUser.impl.currentPage"
:page-size="formData.SysUser.impl.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, prev, pager, next, sizes"
@current-change="formData.SysUser.impl.onCurrentPageChange"
@size-change="formData.SysUser.impl.onPageSizeChange">
</el-pagination>
</el-row>
</el-col>
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
/* eslint-disable-next-line */
import {TableWidget } from '@/utils/widget.js';
/* eslint-disable-next-line */
import { uploadMixin, statsDateRangeMixin, cachePageMixin } from '@/core/mixins';
/* eslint-disable-next-line */
import { KnowledgeManage } from '@/api/gptController.js';
import formKnowLedgeUserEdit from './edit.vue';
export default {
name: 'formKnowledgeUser',
props: {
knowledgeId: {
type: [String, Number],
required: true
}
},
mixins: [uploadMixin, statsDateRangeMixin, cachePageMixin],
data () {
return {
formData: {
formFilter: {
sysUserLoginName: undefined
},
SysUser: {
impl: new TableWidget(this.loadSysUserData, this.loadSysUserVerify, true, false)
},
isInit: false
}
}
},
methods: {
onReset () {
this.$refs.formKnowledgeUser.resetFields();
this.refreshFormKnowledgeUser(true);
},
getUserStatusType (status) {
if (status === this.SysUserStatus.NORMAL) {
return 'success';
} else if (status === this.SysUserStatus.LOCKED) {
return 'danger';
} else {
return 'info';
}
},
onAddRow () {
this.$dialog.show('添加授权用户', formKnowLedgeUserEdit, {
area: ['1100px', '600px']
}, {
knowledgeId: this.knowledgeId
}).catch(e => {
this.refreshFormKnowledgeUser(true);
});
},
onDeleteRow (row) {
this.$confirm('是否移除此用户?').then(res => {
let params = {
knowledgeId: this.knowledgeId,
userId: row.userId
}
return KnowledgeManage.deleteKnowledgeUser(this, params);
}).then(res => {
this.$message.success('移除授权用户成功');
this.refreshFormKnowledgeUser(true);
}).catch(e => {});
},
/**
* 用户管理数据获取函数,返回Primise
*/
loadSysUserData (params) {
return new Promise((resolve, reject) => {
params.knowledgeId = this.knowledgeId;
params.sysUserDtoFilter = {
loginName: this.formData.formFilter.sysUserLoginName
}
KnowledgeManage.getSysUserListByKnowledgeId(this, params).then(res => {
resolve({
dataList: res.data.dataList,
totalCount: res.data.totalCount
});
}).catch(e => {
reject(e);
});
});
},
/**
* 用户管理数据获取检测函数,返回true正常获取数据,返回false停止获取数据
*/
loadSysUserVerify () {
return true;
},
/**
* 更新用户管理
*/
refreshFormKnowledgeUser (reloadData = false) {
// 重新获取数据组件的数据
if (reloadData) {
this.formData.SysUser.impl.refreshTable(true, 1);
} else {
this.formData.SysUser.impl.refreshTable();
}
this.formData.isInit = true;
},
onResume () {
this.refreshFormKnowledgeUser();
},
initFormData () {
},
formInit () {
this.initFormData();
this.refreshFormKnowledgeUser();
}
},
computed: {
getTableHeight () {
return (this.getMainContextHeight - 92);
},
...mapGetters(['getMainContextHeight'])
},
created () {
this.formInit();
}
}
</script>
......@@ -31,6 +31,7 @@ import { DropdownWidget, TableWidget, UploadWidget, ChartWidget } from '@/utils/
import { MyModel } from '@/api/gptController.js';
import card from './card/index';
/* eslint-disable-next-line */
import particulars from './particulars/index';
export default {
data () {
......@@ -100,11 +101,12 @@ export default {
},
particulars (item) {
this.$dialog.show('详情', particulars, {
area: ['100%', '100%']
}, { tableItem: item }).then(res => {
this.refresh();
}).catch(e => { });
console.log('详情');
// this.$dialog.show('详情', particulars, {
// area: ['100%', '100%']
// }, { tableItem: item }).then(res => {
// this.refresh();
// }).catch(e => { });
},
refresh (reloadData = false) {
if (reloadData) {
......
......@@ -18,9 +18,9 @@
</div>
<div class="video" v-if="['mp4', 'webm', 'ogg', 'ogv'].indexOf(fileType)>-1&&url">
<video controls style="width: 100%">
<source :src="item" type="video/mp4" />
<source :src="item" type="video/ogg" />
<source :src="item" type="video/webm" />
<source :src="url" type="video/mp4" />
<source :src="url" type="video/ogg" />
<source :src="url" type="video/webm" />
您的浏览器不支持 video 标签。
</video>
</div>
......@@ -127,7 +127,6 @@ export default {
},
rendered () {
this.fullscreenLoading = false
console.log('渲染完成')
}
}
}
......
@media (prefers-color-scheme: dark) {
.markdown-body,
[data-theme="dark"] {
/*dark*/
color-scheme: dark;
--color-prettylights-syntax-comment: #8b949e;
--color-prettylights-syntax-constant: #79c0ff;
--color-prettylights-syntax-entity: #d2a8ff;
--color-prettylights-syntax-storage-modifier-import: #c9d1d9;
--color-prettylights-syntax-entity-tag: #7ee787;
--color-prettylights-syntax-keyword: #ff7b72;
--color-prettylights-syntax-string: #a5d6ff;
--color-prettylights-syntax-variable: #ffa657;
--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
--color-prettylights-syntax-invalid-illegal-bg: #8e1519;
--color-prettylights-syntax-carriage-return-text: #f0f6fc;
--color-prettylights-syntax-carriage-return-bg: #b62324;
--color-prettylights-syntax-string-regexp: #7ee787;
--color-prettylights-syntax-markup-list: #f2cc60;
--color-prettylights-syntax-markup-heading: #1f6feb;
--color-prettylights-syntax-markup-italic: #c9d1d9;
--color-prettylights-syntax-markup-bold: #c9d1d9;
--color-prettylights-syntax-markup-deleted-text: #ffdcd7;
--color-prettylights-syntax-markup-deleted-bg: #67060c;
--color-prettylights-syntax-markup-inserted-text: #aff5b4;
--color-prettylights-syntax-markup-inserted-bg: #033a16;
--color-prettylights-syntax-markup-changed-text: #ffdfb6;
--color-prettylights-syntax-markup-changed-bg: #5a1e02;
--color-prettylights-syntax-markup-ignored-text: #c9d1d9;
--color-prettylights-syntax-markup-ignored-bg: #1158c7;
--color-prettylights-syntax-meta-diff-range: #d2a8ff;
--color-prettylights-syntax-brackethighlighter-angle: #8b949e;
--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
--color-fg-default: #e6edf3;
--color-fg-muted: #848d97;
--color-fg-subtle: #6e7681;
--color-canvas-default: #0d1117;
--color-canvas-subtle: #161b22;
--color-border-default: #30363d;
--color-border-muted: #21262d;
--color-neutral-muted: rgba(110,118,129,0.4);
--color-accent-fg: #2f81f7;
--color-accent-emphasis: #1f6feb;
--color-success-fg: #3fb950;
--color-success-emphasis: #238636;
--color-attention-fg: #d29922;
--color-attention-emphasis: #9e6a03;
--color-attention-subtle: rgba(187,128,9,0.15);
--color-danger-fg: #f85149;
--color-danger-emphasis: #da3633;
--color-done-fg: #a371f7;
--color-done-emphasis: #8957e5;
}
}
@media (prefers-color-scheme: light) {
.markdown-body,
[data-theme="light"] {
/*light*/
color-scheme: light;
--color-prettylights-syntax-comment: #57606a;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #6639ba;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #1F2328;
--color-fg-muted: #656d76;
--color-fg-subtle: #6e7781;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210,18%,87%,1);
--color-neutral-muted: rgba(175,184,193,0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-success-fg: #1a7f37;
--color-success-emphasis: #1f883d;
--color-attention-fg: #9a6700;
--color-attention-emphasis: #9a6700;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #d1242f;
--color-danger-emphasis: #cf222e;
--color-done-fg: #8250df;
--color-done-emphasis: #8250df;
}
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
margin: 0;
color: var(--color-fg-default);
background-color: var(--color-canvas-default);
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.markdown-body .octicon {
display: inline-block;
fill: currentColor;
vertical-align: text-bottom;
}
.markdown-body h1:hover .anchor .octicon-link:before,
.markdown-body h2:hover .anchor .octicon-link:before,
.markdown-body h3:hover .anchor .octicon-link:before,
.markdown-body h4:hover .anchor .octicon-link:before,
.markdown-body h5:hover .anchor .octicon-link:before,
.markdown-body h6:hover .anchor .octicon-link:before {
width: 16px;
height: 16px;
content: ' ';
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}
.markdown-body details,
.markdown-body figcaption,
.markdown-body figure {
display: block;
}
.markdown-body summary {
display: list-item;
}
.markdown-body [hidden] {
display: none !important;
}
.markdown-body a {
background-color: transparent;
color: var(--color-accent-fg);
text-decoration: none;
}
.markdown-body abbr[title] {
border-bottom: none;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
.markdown-body b,
.markdown-body strong {
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body dfn {
font-style: italic;
}
.markdown-body h1 {
margin: .67em 0;
font-weight: var(--base-text-weight-semibold, 600);
padding-bottom: .3em;
font-size: 2em;
border-bottom: 1px solid var(--color-border-muted);
}
.markdown-body mark {
background-color: var(--color-attention-subtle);
color: var(--color-fg-default);
}
.markdown-body small {
font-size: 90%;
}
.markdown-body sub,
.markdown-body sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.markdown-body sub {
bottom: -0.25em;
}
.markdown-body sup {
top: -0.5em;
}
.markdown-body img {
border-style: none;
max-width: 100%;
box-sizing: content-box;
background-color: var(--color-canvas-default);
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre,
.markdown-body samp {
font-family: monospace;
font-size: 1em;
}
.markdown-body figure {
margin: 1em 40px;
}
.markdown-body hr {
box-sizing: content-box;
overflow: hidden;
background: transparent;
border-bottom: 1px solid var(--color-border-muted);
height: .25em;
padding: 0;
margin: 24px 0;
background-color: var(--color-border-default);
border: 0;
}
.markdown-body input {
font: inherit;
margin: 0;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body [type=button],
.markdown-body [type=reset],
.markdown-body [type=submit] {
-webkit-appearance: button;
appearance: button;
}
.markdown-body [type=checkbox],
.markdown-body [type=radio] {
box-sizing: border-box;
padding: 0;
}
.markdown-body [type=number]::-webkit-inner-spin-button,
.markdown-body [type=number]::-webkit-outer-spin-button {
height: auto;
}
.markdown-body [type=search]::-webkit-search-cancel-button,
.markdown-body [type=search]::-webkit-search-decoration {
-webkit-appearance: none;
appearance: none;
}
.markdown-body ::-webkit-input-placeholder {
color: inherit;
opacity: .54;
}
.markdown-body ::-webkit-file-upload-button {
-webkit-appearance: button;
appearance: button;
font: inherit;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body ::placeholder {
color: var(--color-fg-subtle);
opacity: 1;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
display: block;
width: max-content;
max-width: 100%;
overflow: auto;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body details summary {
cursor: pointer;
}
.markdown-body details:not([open])>*:not(summary) {
display: none !important;
}
.markdown-body a:focus,
.markdown-body [role=button]:focus,
.markdown-body input[type=radio]:focus,
.markdown-body input[type=checkbox]:focus {
outline: 2px solid var(--color-accent-fg);
outline-offset: -2px;
box-shadow: none;
}
.markdown-body a:focus:not(:focus-visible),
.markdown-body [role=button]:focus:not(:focus-visible),
.markdown-body input[type=radio]:focus:not(:focus-visible),
.markdown-body input[type=checkbox]:focus:not(:focus-visible) {
outline: solid 1px transparent;
}
.markdown-body a:focus-visible,
.markdown-body [role=button]:focus-visible,
.markdown-body input[type=radio]:focus-visible,
.markdown-body input[type=checkbox]:focus-visible {
outline: 2px solid var(--color-accent-fg);
outline-offset: -2px;
box-shadow: none;
}
.markdown-body a:not([class]):focus,
.markdown-body a:not([class]):focus-visible,
.markdown-body input[type=radio]:focus,
.markdown-body input[type=radio]:focus-visible,
.markdown-body input[type=checkbox]:focus,
.markdown-body input[type=checkbox]:focus-visible {
outline-offset: 0;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
line-height: 10px;
color: var(--color-fg-default);
vertical-align: middle;
background-color: var(--color-canvas-subtle);
border: solid 1px var(--color-neutral-muted);
border-bottom-color: var(--color-neutral-muted);
border-radius: 6px;
box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: var(--base-text-weight-semibold, 600);
line-height: 1.25;
}
.markdown-body h2 {
font-weight: var(--base-text-weight-semibold, 600);
padding-bottom: .3em;
font-size: 1.5em;
border-bottom: 1px solid var(--color-border-muted);
}
.markdown-body h3 {
font-weight: var(--base-text-weight-semibold, 600);
font-size: 1.25em;
}
.markdown-body h4 {
font-weight: var(--base-text-weight-semibold, 600);
font-size: 1em;
}
.markdown-body h5 {
font-weight: var(--base-text-weight-semibold, 600);
font-size: .875em;
}
.markdown-body h6 {
font-weight: var(--base-text-weight-semibold, 600);
font-size: .85em;
color: var(--color-fg-muted);
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
padding: 0 1em;
color: var(--color-fg-muted);
border-left: .25em solid var(--color-border-default);
}
.markdown-body ul,
.markdown-body ol {
margin-top: 0;
margin-bottom: 0;
padding-left: 2em;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ul ul ol,
.markdown-body ul ol ol,
.markdown-body ol ul ol,
.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body tt,
.markdown-body code,
.markdown-body samp {
font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
font-size: 12px;
word-wrap: normal;
}
.markdown-body .octicon {
display: inline-block;
overflow: visible !important;
vertical-align: text-bottom;
fill: currentColor;
}
.markdown-body input::-webkit-outer-spin-button,
.markdown-body input::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
appearance: none;
}
.markdown-body .mr-2 {
margin-right: var(--base-size-8, 8px) !important;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .absent {
color: var(--color-danger-fg);
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: none;
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre,
.markdown-body details {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body blockquote>:first-child {
margin-top: 0;
}
.markdown-body blockquote>:last-child {
margin-bottom: 0;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: var(--color-fg-default);
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 tt,
.markdown-body h1 code,
.markdown-body h2 tt,
.markdown-body h2 code,
.markdown-body h3 tt,
.markdown-body h3 code,
.markdown-body h4 tt,
.markdown-body h4 code,
.markdown-body h5 tt,
.markdown-body h5 code,
.markdown-body h6 tt,
.markdown-body h6 code {
padding: 0 .2em;
font-size: inherit;
}
.markdown-body summary h1,
.markdown-body summary h2,
.markdown-body summary h3,
.markdown-body summary h4,
.markdown-body summary h5,
.markdown-body summary h6 {
display: inline-block;
}
.markdown-body summary h1 .anchor,
.markdown-body summary h2 .anchor,
.markdown-body summary h3 .anchor,
.markdown-body summary h4 .anchor,
.markdown-body summary h5 .anchor,
.markdown-body summary h6 .anchor {
margin-left: -40px;
}
.markdown-body summary h1,
.markdown-body summary h2 {
padding-bottom: 0;
border-bottom: 0;
}
.markdown-body ul.no-list,
.markdown-body ol.no-list {
padding: 0;
list-style-type: none;
}
.markdown-body ol[type="a s"] {
list-style-type: lower-alpha;
}
.markdown-body ol[type="A s"] {
list-style-type: upper-alpha;
}
.markdown-body ol[type="i s"] {
list-style-type: lower-roman;
}
.markdown-body ol[type="I s"] {
list-style-type: upper-roman;
}
.markdown-body ol[type="1"] {
list-style-type: decimal;
}
.markdown-body div>ol:not([type]) {
list-style-type: decimal;
}
.markdown-body ul ul,
.markdown-body ul ol,
.markdown-body ol ol,
.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li>p {
margin-top: 16px;
}
.markdown-body li+li {
margin-top: .25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table th {
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body table th,
.markdown-body table td {
padding: 6px 13px;
border: 1px solid var(--color-border-default);
}
.markdown-body table td>:last-child {
margin-bottom: 0;
}
.markdown-body table tr {
background-color: var(--color-canvas-default);
border-top: 1px solid var(--color-border-muted);
}
.markdown-body table tr:nth-child(2n) {
background-color: var(--color-canvas-subtle);
}
.markdown-body table img {
background-color: transparent;
}
.markdown-body img[align=right] {
padding-left: 20px;
}
.markdown-body img[align=left] {
padding-right: 20px;
}
.markdown-body .emoji {
max-width: none;
vertical-align: text-top;
background-color: transparent;
}
.markdown-body span.frame {
display: block;
overflow: hidden;
}
.markdown-body span.frame>span {
display: block;
float: left;
width: auto;
padding: 7px;
margin: 13px 0 0;
overflow: hidden;
border: 1px solid var(--color-border-default);
}
.markdown-body span.frame span img {
display: block;
float: left;
}
.markdown-body span.frame span span {
display: block;
padding: 5px 0 0;
clear: both;
color: var(--color-fg-default);
}
.markdown-body span.align-center {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-center>span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown-body span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown-body span.align-right {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-right>span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown-body span.align-right span img {
margin: 0;
text-align: right;
}
.markdown-body span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown-body span.float-left span {
margin: 13px 0 0;
}
.markdown-body span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown-body span.float-right>span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown-body code,
.markdown-body tt {
padding: .2em .4em;
margin: 0;
font-size: 85%;
white-space: break-spaces;
background-color: var(--color-neutral-muted);
border-radius: 6px;
}
.markdown-body code br,
.markdown-body tt br {
display: none;
}
.markdown-body del code {
text-decoration: inherit;
}
.markdown-body samp {
font-size: 85%;
}
.markdown-body pre code {
font-size: 100%;
}
.markdown-body pre>code {
padding: 0;
margin: 0;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
color: var(--color-fg-default);
background-color: var(--color-canvas-subtle);
border-radius: 6px;
}
.markdown-body pre code,
.markdown-body pre tt {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body .csv-data td,
.markdown-body .csv-data th {
padding: 5px;
overflow: hidden;
font-size: 12px;
line-height: 1;
text-align: left;
white-space: nowrap;
}
.markdown-body .csv-data .blob-num {
padding: 10px 8px 9px;
text-align: right;
background: var(--color-canvas-default);
border: 0;
}
.markdown-body .csv-data tr {
border-top: 0;
}
.markdown-body .csv-data th {
font-weight: var(--base-text-weight-semibold, 600);
background: var(--color-canvas-subtle);
border-top: 0;
}
.markdown-body [data-footnote-ref]::before {
content: "[";
}
.markdown-body [data-footnote-ref]::after {
content: "]";
}
.markdown-body .footnotes {
font-size: 12px;
color: var(--color-fg-muted);
border-top: 1px solid var(--color-border-default);
}
.markdown-body .footnotes ol {
padding-left: 16px;
}
.markdown-body .footnotes ol ul {
display: inline-block;
padding-left: 16px;
margin-top: 16px;
}
.markdown-body .footnotes li {
position: relative;
}
.markdown-body .footnotes li:target::before {
position: absolute;
top: -8px;
right: -8px;
bottom: -8px;
left: -24px;
pointer-events: none;
content: "";
border: 2px solid var(--color-accent-emphasis);
border-radius: 6px;
}
.markdown-body .footnotes li:target {
color: var(--color-fg-default);
}
.markdown-body .footnotes .data-footnote-backref g-emoji {
font-family: monospace;
}
.markdown-body .pl-c {
color: var(--color-prettylights-syntax-comment);
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: var(--color-prettylights-syntax-constant);
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: var(--color-prettylights-syntax-entity);
}
.markdown-body .pl-smi,
.markdown-body .pl-s .pl-s1 {
color: var(--color-prettylights-syntax-storage-modifier-import);
}
.markdown-body .pl-ent {
color: var(--color-prettylights-syntax-entity-tag);
}
.markdown-body .pl-k {
color: var(--color-prettylights-syntax-keyword);
}
.markdown-body .pl-s,
.markdown-body .pl-pds,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-sr .pl-sra {
color: var(--color-prettylights-syntax-string);
}
.markdown-body .pl-v,
.markdown-body .pl-smw {
color: var(--color-prettylights-syntax-variable);
}
.markdown-body .pl-bu {
color: var(--color-prettylights-syntax-brackethighlighter-unmatched);
}
.markdown-body .pl-ii {
color: var(--color-prettylights-syntax-invalid-illegal-text);
background-color: var(--color-prettylights-syntax-invalid-illegal-bg);
}
.markdown-body .pl-c2 {
color: var(--color-prettylights-syntax-carriage-return-text);
background-color: var(--color-prettylights-syntax-carriage-return-bg);
}
.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: var(--color-prettylights-syntax-string-regexp);
}
.markdown-body .pl-ml {
color: var(--color-prettylights-syntax-markup-list);
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: bold;
color: var(--color-prettylights-syntax-markup-heading);
}
.markdown-body .pl-mi {
font-style: italic;
color: var(--color-prettylights-syntax-markup-italic);
}
.markdown-body .pl-mb {
font-weight: bold;
color: var(--color-prettylights-syntax-markup-bold);
}
.markdown-body .pl-md {
color: var(--color-prettylights-syntax-markup-deleted-text);
background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}
.markdown-body .pl-mi1 {
color: var(--color-prettylights-syntax-markup-inserted-text);
background-color: var(--color-prettylights-syntax-markup-inserted-bg);
}
.markdown-body .pl-mc {
color: var(--color-prettylights-syntax-markup-changed-text);
background-color: var(--color-prettylights-syntax-markup-changed-bg);
}
.markdown-body .pl-mi2 {
color: var(--color-prettylights-syntax-markup-ignored-text);
background-color: var(--color-prettylights-syntax-markup-ignored-bg);
}
.markdown-body .pl-mdr {
font-weight: bold;
color: var(--color-prettylights-syntax-meta-diff-range);
}
.markdown-body .pl-ba {
color: var(--color-prettylights-syntax-brackethighlighter-angle);
}
.markdown-body .pl-sg {
color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);
}
.markdown-body .pl-corl {
text-decoration: underline;
color: var(--color-prettylights-syntax-constant-other-reference-link);
}
.markdown-body g-emoji {
display: inline-block;
min-width: 1ch;
font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
font-size: 1em;
font-style: normal !important;
font-weight: var(--base-text-weight-normal, 400);
line-height: 1;
vertical-align: -0.075em;
}
.markdown-body g-emoji img {
width: 1em;
height: 1em;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item label {
font-weight: var(--base-text-weight-normal, 400);
}
.markdown-body .task-list-item.enabled label {
cursor: pointer;
}
.markdown-body .task-list-item+.task-list-item {
margin-top: 4px;
}
.markdown-body .task-list-item .handle {
display: none;
}
.markdown-body .task-list-item-checkbox {
margin: 0 .2em .25em -1.4em;
vertical-align: middle;
}
.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {
margin: 0 -1.6em .25em .2em;
}
.markdown-body .contains-task-list {
position: relative;
}
.markdown-body .contains-task-list:hover .task-list-item-convert-container,
.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {
display: block;
width: auto;
height: 24px;
overflow: visible;
clip: auto;
}
.markdown-body ::-webkit-calendar-picker-indicator {
filter: invert(50%);
}
.markdown-body .markdown-alert {
padding: var(--base-size-8) var(--base-size-16);
margin-bottom: 16px;
color: inherit;
border-left: .25em solid var(--color-border-default);
}
.markdown-body .markdown-alert>:first-child {
margin-top: 0;
}
.markdown-body .markdown-alert>:last-child {
margin-bottom: 0;
}
.markdown-body .markdown-alert .markdown-alert-title {
display: flex;
font-weight: var(--base-text-weight-medium, 500);
align-items: center;
line-height: 1;
}
.markdown-body .markdown-alert.markdown-alert-note {
border-left-color: var(--color-accent-emphasis);
}
.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title {
color: var(--color-accent-fg);
}
.markdown-body .markdown-alert.markdown-alert-important {
border-left-color: var(--color-done-emphasis);
}
.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title {
color: var(--color-done-fg);
}
.markdown-body .markdown-alert.markdown-alert-warning {
border-left-color: var(--color-attention-emphasis);
}
.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title {
color: var(--color-attention-fg);
}
.markdown-body .markdown-alert.markdown-alert-tip {
border-left-color: var(--color-success-emphasis);
}
.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title {
color: var(--color-success-fg);
}
.markdown-body .markdown-alert.markdown-alert-caution {
border-left-color: var(--color-danger-emphasis);
}
.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title {
color: var(--color-danger-fg);
}
<!-- 答案内容预览 -->
<template>
<vue-markdown :source="content" v-highlight class="markdown-body" :id="'myContainer'+index" >
</vue-markdown>
<vue-markdown :source="content" v-highlight class="markdown-body">
</vue-markdown>
</template>
<script>
import 'vis-network/dist/dist/vis-network.css';
// eslint-disable-next-line no-unused-vars
import { Network } from 'vis-network/dist/vis-network.esm.js';
import hljs from 'highlight.js' // 代码块高亮
// import 'highlight.js/styles/github.css' // 代码块高亮样式
import 'github-markdown-css' // 整体 markdown 样式
import './github-markdown.css' // 整体 markdown 样式
import VueMarkdown from 'vue-markdown'
import filePreview from './filePreview'
export default {
data () {
return {
result: undefined
};
},
props: ['content', 'index', 'isSuccess'],
components: {VueMarkdown},
props: ['content'],
components: { VueMarkdown },
computed: {},
directives: {
highlight: {
// 指令的定义
// 指令的定义
inserted: function (el) {
let blocks = el.querySelectorAll('pre code');
blocks.forEach((block, index) => {
/** 代码高亮 */
/** 代码高亮 */
hljs.highlightBlock(block);
/** 手动添加一个hljs类名,防止不支持的语言背景变空白 */
block.classList.add('hljs');
......@@ -40,87 +44,228 @@ export default {
},
watch: {
'isSuccess' () {
if (this.isSuccess) {
const anchorTags = document.querySelectorAll(`#myContainer${this.index} a`);
this.preview(anchorTags)
}
}
},
methods: {
isSuccess () {
this.$nextTick(() => {
const anchorTags = Object.values(this.$el.getElementsByTagName('a'));
this.preview(anchorTags)
})
},
preview (anchorTags) {
let that = this
// 遍历并输出链接的 href 属性
anchorTags.forEach((aTag) => {
let tagUrl = aTag.getAttribute('href')
let tagUrl = aTag.getAttribute('href') || ''
let isFile = ['pdf', 'jpg', 'gif', 'jpeg', 'png', 'docx', 'doc', 'xlsx', 'xls', 'mp4', 'webm', 'ogg', 'ogv', 'txt'].indexOf(tagUrl.split('.').pop().toLowerCase()) !== -1
if (isFile) {
let isFile2 = ['html', 'md', 'json', 'jsonl', 'csv', 'pdf', 'png', 'jpg', 'jpeg', 'bmp', 'eml', 'msg', 'epub', 'xlsx', 'xlsd', 'ipynb', 'odt', 'py', 'rst', 'rtf', 'srt', 'toml', 'tsv', 'docx', 'doc', 'xml', 'ppt', 'pptx', 'txt', 'htm'].indexOf(tagUrl.split('.').pop().toLowerCase()) !== -1
if (isFile2 || aTag.className === 'show_detail') { // 判断知识库以及基于文件问答 的文件类型,并添加dom以及样式 || 添加特殊a标签展示详情内容
this.setDescribeStyle(aTag)
}
aTag.addEventListener('click', function (event) {
let url;
if (isFile) {
if (isFile) { // 判断可预览文件类型
// 阻止默认点击事件
event.preventDefault();
if (tagUrl.indexOf('https') !== -1) {
url = tagUrl.replace(/^https:\/\/[^/]+\//, `https://${window.location.hostname}:${window.location.port}/`);
} else {
url = tagUrl.replace(/^http:\/\/[^/]+\//, `http://${window.location.hostname}:${window.location.port}/`);
}
that.$dialog.show('文件预览', filePreview, {
area: ['100%', '100%']
}, {url}).then(res => {
}, {url: tagUrl }).then(res => {
}).catch(e => { });
}
});
});
},
setDescribeStyle (aTag) {
let describeDom = aTag.parentNode.nextElementSibling
// let describeDom = aTag.nextElementSibling
// 在当前元素前插入新的 HTML 内容
describeDom.insertAdjacentHTML('beforebegin', `
<button style="margin-bottom:10px" type="button" class="el-button el-button--primary el-button--mini is-round"><span style="margin-right:5px">详情</span><i class="el-icon-arrow-down"></i></button>
aTag.insertAdjacentHTML('afterend', `
<button style="margin-top:10px" type="button" class="el-button el-button--primary el-button--mini is-round">
<span style="margin-right:5px">详情</span>
<i class="el-icon-arrow-down"></i>
</button>
`);
describeDom.classList.add('describe')
var buttonDom = aTag.nextElementSibling;// 下拉按钮
let describeDom = aTag.parentNode.querySelector('.describe-view') || aTag.parentNode.parentNode.querySelector('.describe-view')// 详情
this.initNetwork(describeDom, describeDom.getAttribute('data'))
let height = describeDom.scrollHeight
describeDom.setAttribute('style', `height:${height}px;`)
describeDom.previousElementSibling.addEventListener('click', function (event) {
describeDom.setAttribute('style', 'height:0px;')
buttonDom.addEventListener('click', function (event) {
if (height > 0 && (describeDom.style.height !== '0px' && describeDom.style.height !== '')) {
describeDom.setAttribute('style', 'height:0px;')
} else {
describeDom.setAttribute('style', `height:${height}px;`)
}
})
},
initNetwork (target, data) {
// 判断有没有data属性
if (target.hasAttribute('data') && target.getAttribute('data') !== null) {
const container = document.createElement('div');
container.style.width = '100%'
container.style.border = '1px solid lightgray'
container.style.height = '400px'
container.style.position = 'relative';
container.style.borderRadius = '10px'
container.style.marginBottom = '10px'
container.style.zIndex = '10000'
data = JSON.parse(data.replace(/'/g, '"')).network
data.edges = data.edges.map((item) => {
item.length = 300
return item
})
const options = {
autoResize: true, // 网络将自动检测其容器的大小调整,并相应地重绘自身
nodes: {
shape: 'circle',
widthConstraint: 100, // 设置数字,将节点的最小和最大宽度设为该值,当值设为很小的时候,label会换行,节点会保持一个最小值,里边的内容会换行
color: {
background: '#0092FF'
},
font: {
color: '#7a7a7a' // 修改节点内文字颜色为黑色
},
size: 30
},
edges: {
arrows: {
to: {
enabled: true,
scaleFactor: 0.4 // 箭头大小的缩放因子
}
},
font: {
size: 12
}
},
physics: {
enabled: true,
barnesHut: {
gravitationalConstant: -2000
}
}
};
// eslint-disable-next-line no-unused-vars
const network = new Network(container, data, options);
target.appendChild(container);
container.setAttribute('isfull', '0')
let fullDom = document.createElement('div')
fullDom.classList.add('el-icon-full-screen', 'full')
container.appendChild(fullDom)
fullDom.addEventListener('click', () => {
if (container.getAttribute('isfull') !== '0') {
container.style.height = '400px';
container.style.position = 'relative';
container.style.background = 'none'
container.setAttribute('isfull', '0')
container.style.border = '1px solid lightgray'
fullDom.classList.remove('el-icon-close')
fullDom.classList.add('el-icon-full-screen')
document.querySelector('.full').style.color = '#000'
document.querySelector('.down').style.color = '#000'
} else {
document.querySelector('.full').style.color = '#fff'
document.querySelector('.down').style.color = '#fff'
fullDom.classList.remove('el-icon-full-screen')
fullDom.classList.add('el-icon-close')
container.style.height = '100%';
container.style.position = 'fixed';
container.style.top = 0;
container.style.left = 0;
container.style.border = 'none'
container.style.background = '#00000096'
container.setAttribute('isfull', '1')
}
})
let downDom = document.createElement('div')
downDom.classList.add('el-icon-download', 'down')
downDom.addEventListener('click', () => {
this.downloadCanvas(container.querySelector('canvas'))
})
container.appendChild(downDom)
}
},
downloadCanvas (canvas) {
// 获取 Canvas 的内容作为DataURL
var dataURL = canvas.toDataURL('image/png');
// 创建一个下载链接
var downloadLink = document.createElement('a');
downloadLink.href = dataURL;
downloadLink.download = 'Image.png'; // 设置下载文件的名称
// 将下载链接添加到页面
document.body.appendChild(downloadLink);
// 模拟点击下载链接
downloadLink.click();
// 移除下载链接
document.body.removeChild(downloadLink);
}
}
}
</script>
<style scoped>
.markdown-body{
background: none !important;
font-size: 14px !important;
.markdown-body {
background: none !important;
font-size: 14px !important;
}
.markdown-body pre{
background-color: #3b3d3f;
.markdown-body pre {
background-color: #3b3d3f;
}
</style>
<style>
.describe-view {
overflow: hidden !important;
transition:all 0.5s ;
color: #000;
font-size: 14px;
background-color: #fff;
border-radius: 10px;
padding: 0 20px 0 20px;
position: relative;
}
</style>
<style>
.describe{
.vis-label {
white-space: nowrap;
overflow: hidden;
transition: 0.5s all;
color: #007fff;
font-size: 14px;
background-color: #Fff;
border-radius: 10px;
padding: 0 20px 0 20px;
text-overflow: ellipsis;
}
.full{
width: 20px;
height: 20px;
/* background: #0092FF; */
position: absolute;
right: 10px;
top: 10px;
font-size: 20px;
/* color: #fff; */
border-radius: 5px;
/* transition: all 0.5s; */
}
.down{
width: 20px;
height: 20px;
/* background: #0092FF; */
position: absolute;
right: 40px;
top: 10px;
font-size: 20px;
/* color: #fff; */
border-radius: 5px;
}
</style>
......@@ -19,7 +19,7 @@
<el-form-item label="提示词:" style="">
<div style="display: flex;flex-direction: row;align-items: center;">
<el-select v-model="promptTemplate" filterable placeholder="提示词" value-key="templateId" @change="templateControllerChange" clearable>
<el-option v-for="item in templateControllerList" :key="item.value" :label="item.label" :value="item">
<el-option v-for="item in templateControllerList" :key="item.templateId" :label="item.label" :value="item">
</el-option>
</el-select>
<el-switch @change="changePromptTemplate" style="margin-left: 10px;" v-model="isPromptTemplate" active-color="#13ce66" inactive-color="#ff4949">
......@@ -92,6 +92,26 @@
</el-collapse-item>
</el-collapse>
</el-form-item>
<!-- <el-form-item label-width="0px" v-if="form.pattern == '知识图谱问答'">
<el-collapse value="1">
<el-collapse-item name="1">
<template slot="title">知识图谱配置</template>
<el-form-item label="请选择知识图谱:" style="margin-bottom:30px">
<el-select ref="knowledgeSelect" v-model="form.knowledgeGraphConfige. knowledge_graph_name" placeholder="请选择" >
<el-option v-for="item in []" :key="item.knowledgeId" :label="item.knowledgeGraphName" :value="item.knowledgeGraphName">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="匹配知识条数:" style="margin-bottom:30px">
<el-input-number v-model="form.knowledgeGraphConfige.top_k" :min="1" :max="20"></el-input-number>
</el-form-item>
<el-form-item label="匹配精度:">
<el-slider v-model="knowledgeScoreThreshold" :format-tooltip="formatTooltip" @change="form.knowledgeGraphConfige.score_threshold = knowledgeGraphThreshold / 100"></el-slider>
</el-form-item>
</el-collapse-item>
</el-collapse>
</el-form-item> -->
</el-form>
</template>
......@@ -114,6 +134,7 @@ export default {
knowledgeList: [],
knowledgeScoreThreshold: 50,
fileThreshold: 50,
knowledgeGraphThreshold: 50,
form: {
pattern: '通用智能问答', // 对话模式
model_name: undefined, // 模型名称
......@@ -129,13 +150,19 @@ export default {
search_engine_name: 'bing',
top_k: 1
},
fileConfige: {
fileConfige: {// 文件对哈配置
top_k: 1,
score_threshold: 0.5,
knowledge_id: undefined,
// max_tokens: 0,
prompt_name: 'default'
},
knowledgeGraphConfige: {// 知识图谱对话
knowledge_graph_name: '',
top_k: 1,
score_threshold: 0.5
}
},
filesForm: {
......@@ -188,6 +215,7 @@ export default {
this.modelList = res.data.map((item) => {
return { id: item.modelId, name: item.modelName, children: item.modelVersionList === [] ? [] : item.modelVersionList.map((item2) => { return { id: item2.versionId, name: item2.versionName } }) }
})
this.getActiveModelList()
}).catch(e => {
console.log(e);
......@@ -232,7 +260,6 @@ export default {
switchModel (versionId) { // 切换模型
this.openLoading('LLM模型加载中')
ModelVersion.change(this, { versionId: versionId }).then(res => {
console.log(res);
this.loading.close();
}).catch(e => {
this.loading.close();
......
<!-- 右侧聊天界面 -->
<template>
<div class="box">
<div class="box" ref="box">
<div class="contentBox" ref="contentBox" v-if="myHistory.length > 0">
<div v-for="(item, index) in myHistory" :key="index">
<div v-for=" item in myHistory" :key="item.id">
<div class="userBox">
<div class="content">
......@@ -18,13 +18,13 @@
GPT
</div>
<div class="content">
<contentView :content="item.answer" ref="contentView" :index="index" :isSuccess="isSuccess" />
<contentView :content="item.answer" :ref="'contentView' + item.id" />
</div>
</div>
</div>
</div>
<div class="initBox" v-else>
<div class="initBox" ref="initBox" v-else>
<div class="text">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你好,<br>
我是人工智能语言模型<br>
......@@ -38,10 +38,12 @@
<el-button icon="el-icon-delete" style="margin-left:10px" @click="clear" type="danger" plain></el-button>
</div>
<div id="network"></div>
</div>
</template>
<script>
import { GetStreaming } from '@/utils/getStreaming.js';
// import codePreview from '../codePreview';
import contentView from '../contentView';
......@@ -49,7 +51,6 @@ import contentView from '../contentView';
export default {
data () {
return {
isSuccess: false,
templateController: undefined,
isPromptTemplate: true,
inputContent: null,
......@@ -81,6 +82,7 @@ export default {
// 移除监听事件 "share"
this.$bus.$off('isPromptTemplate')
},
methods: {
clear () {
......@@ -90,7 +92,6 @@ export default {
this.myHistory = []
},
submit () {
this.isSuccess = false
this.param.model_name = this.chatForm.model_name
this.param.temperature = this.chatForm.temperature
this.heistoryRotate = this.chatForm.heistoryRotate
......@@ -104,13 +105,16 @@ export default {
} else if (this.chatForm.pattern === '搜索引擎问答') {
apiUrl = '/2api/chat/search_engine_chat'
this.param = { ...this.param, ...this.chatForm.searchConfige }
} else if (this.chatForm.pattern === '文件对话') {
} else if (this.chatForm.pattern === '基于文件问答') {
apiUrl = '/2api/chat/file_chat'
this.param = { ...this.param, ...this.chatForm.fileConfige }
if (!this.chatForm.fileConfige.knowledge_id) {
this.$message.error('请先上传文件');
return
}
} else if (this.chatForm.pattern === '知识图谱问答') {
apiUrl = '/2api/chat/mapping_knowledge'
this.param = { ...this.param, ...this.chatForm.knowledgeGraphConfige }
}
if (!this.inputContent) return
this.param.query = this.inputContent;
......@@ -118,49 +122,58 @@ export default {
'role': 'user',
'content': this.inputContent,
'answer': '',
'excludeReferenceAnswer': ''
'excludeReferenceAnswer': '',
'id': this.generateRandomString(8)
})
this.inputContent = null;
this.inputContent = null;
this.param.history = []
this.myHistory.slice(0, -1).slice(-this.heistoryRotate).forEach((item) => {
this.param.history.push(...[{ role: item.role, content: item.content }, { role: 'assistant', content: item.excludeReferenceAnswer}])
this.param.history.push(...[{ role: item.role, content: item.content }, { role: 'assistant', content: item.excludeReferenceAnswer }])
})
this.$emit('submit')
let getData = new GetStreaming(apiUrl, this.param, this.onmessage, this.success)
let getData = new GetStreaming(apiUrl, this.param, this.onmessage, this.setSuccess)
getData.initeventSource()
},
success () {
this.isSuccess = true
setSuccess () {
this.$nextTick(() => {
let nowChat = this.myHistory[this.myHistory.length - 1]
this.$refs['contentView' + nowChat.id][0].isSuccess()
})
},
onmessage (data) {
let nowChat = this.myHistory[this.myHistory.length - 1]
if (this.chatForm.pattern === '通用智能问答') {
nowChat.answer += data
nowChat.excludeReferenceAnswer += data
nowChat.answer += data;
} else if (this.chatForm.pattern === '专业知识库问答') {
console.log(data);
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => {
if (item.docs) {
item.docs = item.docs.map((item2) => {
return item2.replace('出处', '知识溯源')
})
}
this.modifyContent(item)// 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs
})
} else if (this.chatForm.pattern === '搜索引擎问答') {
} else if (this.chatForm.pattern === '文件对话') {
} else if (this.chatForm.pattern === '基于文件问答') {
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => {
if (item.docs) {
item.docs = item.docs.map((item2) => {
return item2.replace('出处', '知识溯源')
})
}
this.modifyContent(item)// 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs
})
} else if (this.chatForm.pattern === '知识图谱问答') {
console.log(data);
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => {
this.modifyContent(item)// 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs
})
......@@ -169,6 +182,60 @@ export default {
this.$nextTick(() => {
this.$refs.contentBox.scrollTo(0, this.$refs.contentBox.scrollHeight)
})
},
modifyContent (item) { // 修改返回内容
if (item.docs) {
item.docs = item.docs.map((item2) => {
let tagUrl = ((item2?.match(/\]\((.*?)\)/g) || [])[0] || '').slice(2, -1)
let url = ''
// item2 = item2.replace(/\n/g, '<br>')
if (tagUrl.indexOf('https') !== -1) {
url = tagUrl.replace(/^https:\/\/[^/]+\//, `https://${window.location.hostname}:${window.location.port}/`);
item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
} else {
url = tagUrl.replace(/^http:\/\/[^/]+\//, `http://${window.location.hostname}:${window.location.port}/`);
item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
}
/// ////////出处内容包装div
var match = (item2.match(/出处(.*)\)/) || [])[0]
// 查找指定字符串的位置
var indexOfSpecifiedString = item2.indexOf(match);
if (indexOfSpecifiedString !== -1) {
// 使用 slice 获取指定字符串后面的所有内容
var contentAfterSpecifiedString = item2.slice(indexOfSpecifiedString + match.length);
item2 = item2.replace(contentAfterSpecifiedString, `<div class="describe-view">${contentAfterSpecifiedString}</div>`)
} else {
console.log('未找到匹配的指定字符串');
}
/// ////////特殊标签后包装div
// var specifiedString = '<a class="show_detail"></a>';
// var match2 = item2.match(new RegExp(specifiedString + '(.*)')) || [];
const match2 = item2.match(/<a\s+class="show_detail"\s+data="([^"]*)"><\/a>([\s\S]*)/);
if (match2 && match2.length === 3) {
var textAfterSpecifiedString = match2[2];
var data = match2[1]
// 修改文本内容
textAfterSpecifiedString = `<div class="describe-view" data="${data}" >${textAfterSpecifiedString}</div>`
item2 = item2.replace(match2[2], textAfterSpecifiedString)
}
return item2.replace('出处', '知识溯源')
})
}
},
generateRandomString (length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
const randomValues = new Uint32Array(length);
crypto.getRandomValues(randomValues);
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(randomValues[i] % charactersLength);
}
return result;
}
}
......@@ -253,4 +320,5 @@ export default {
.inputBox {
display: flex;
}</style>
}
</style>
......@@ -11,7 +11,7 @@
</el-select>
</el-form-item>
<el-form-item label="场景类型:" >
<el-radio v-model="form.scenarioType" v-for="item in ScenarioTypeDict.getList()" :label="item.id" :size="defaultFormItemSize" :key="item.id">{{ item.name }}</el-radio>
<el-radio v-model="form.scenarioType" v-for="item in ScenarioTypeDict.getList()" :label="item.id" :size="defaultFormItemSize" :key="item.id" @input="changeScenarioType">{{ item.name }}</el-radio>
</el-form-item>
<el-form-item label="变量识别符:">
<el-select v-model="form.parameterFormat" placeholder="请选择" :size="defaultFormItemSize" class="inputWidth">
......@@ -21,21 +21,23 @@
</el-form-item>
<template v-if="form.scenarioType === 0 ">
<el-form-item label="模板框架:" class="templateFramework" v-if=" !isEdit">
<el-tooltip class="item" effect="dark" :content="item.name" placement="top" v-for="item in TemplateFrameworkDict.getList()" :label="item.id" :size="defaultFormItemSize" :key="item.id">
<el-radio v-model="form.templateFramework" @input="$refs.promptInput.init()" :label="item.id">{{ item.symbol }}</el-radio>
</el-tooltip>
</el-form-item>
<el-form-item label="Prompt:" prop="templateContent">
<el-form-item label="Prompt:" prop="templateContent" key="templateContent1">
<promptInput ref="promptInput" v-model="form.templateContent" :templateFramework="form.templateFramework" :isEdit="isEdit" />
</el-form-item>
</template>
<template v-else>
<el-form-item label="正向Prompt:">
<el-form-item label="正向Prompt:" prop="templateContent" key="templateContent2">
<el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="form.templateContent" class="inputWidth">
</el-input>
</el-form-item>
<el-form-item label="负向Prompt:">
<el-form-item label="负向Prompt:" prop="negativeTemplateContent" key="negativeTemplateContent1">
<el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="form.negativeTemplateContent" class="inputWidth">
</el-input>
......@@ -58,7 +60,7 @@ export default {
interfaceAddress: '',
negativeTemplateContent: '',
negativeTemplateVariables: '',
parameterFormat: '',
parameterFormat: '{}',
scenarioType: 0,
templateContent: '',
templateFramework: 0,
......@@ -70,7 +72,8 @@ export default {
},
rules: {
'templateName': [{ required: true, message: '请选输入名称', trigger: 'blur' }],
'templateContent': [{ required: true, message: '请选输入Prompt', trigger: 'blur' }]
'templateContent': [{ required: true, message: '请选输入Prompt', trigger: 'blur' }],
'negativeTemplateContent': [{ required: true, message: '请选输入负向Prompt', trigger: 'blur' }]
}
};
......@@ -129,6 +132,13 @@ export default {
}
});
});
},
changeScenarioType (data) {
if (this.$refs.promptInput) {
this.$refs.promptInput.init()
}
this.form.templateContent = ''
this.form.negativeTemplateContent = ''
}
}
};
......
<!--prompt输入 -->
<template>
<div >
<el-input v-if="[1,2,3].indexOf(templateFramework)===-1||isEdit" type="textarea" :rows="5" placeholder="请输入内容" :value="prompt" class="inputWidth" @input="changeInput"></el-input>
<el-input v-if="[1,2,3].indexOf(templateFramework)===-1||isEdit" type="textarea" :rows="5" placeholder="请输入内容" :value="prompt" class="inputWidth" @input="changeInput2"></el-input>
<el-card class="box-card" style="width:600px" v-else>
<div class="inputBox" v-for="(item,index) in myPrompts" :key="index">
......@@ -128,9 +128,13 @@ export default {
return item.label + ':' + item.value + '\n'
}
}).join('')
console.log(promptsString);
console.log(this.templateFramework);
this.$emit('input', [1, 2, 3].indexOf(this.templateFramework) === -1 ? this.prompt : promptsString)
},
changeInput2 (data) {
this.prompt = data
this.$emit('input', this.prompt)
},
add (item) {
if (item.tableData.length === 20) return
item.tableData.push({
......
......@@ -4,7 +4,7 @@
<div class="topBox">
<div class="title">Prompt 模板</div>
<div class="instructions">
集中管理平台训练、压缩及预置的生成式大模型,支持对模型进行评估、压缩及部署
管理和查看prompt模板,支持在服务调用时组合prompt输入推理
</div>
</div>
<div class="topButtonBox">
......
......@@ -7,9 +7,9 @@
<el-form-item label="模板名称" prop="formFilter.templateName">
<el-input class="filter-item" v-model="myDataSetPage.formFilter.templateName" :clearable="true" placeholder="模板名称" />
</el-form-item>
<el-form-item label-width="0px">
<!-- <el-form-item label-width="0px">
<el-button class="add" type="primary" icon="el-icon-plus" :size="defaultFormItemSize" @click="add">创建模板</el-button>
</el-form-item>
</el-form-item> -->
</filter-box>
</el-form>
<div class="listBox">
......
<!--预置模板-->
<template>
<div class="presetTemplate">
<el-form ref="myDataSetPage" :model="myDataSetPage" label-width="75px" :size="defaultFormItemSize" label-position="right" @submit.native.prevent>
<el-form
ref="myDataSetPage"
:model="myDataSetPage"
label-width="75px"
:size="defaultFormItemSize"
label-position="right"
@submit.native.prevent
>
<filter-box :item-width="350" @search="refresh()" @reset="onReset">
<el-form-item label="模板名称" prop="formFilter.templateName">
<el-input class="filter-item" v-model="myDataSetPage.formFilter.templateName" :clearable="true" placeholder="模板名称" />
<el-input
class="filter-item"
v-model="myDataSetPage.formFilter.templateName"
:clearable="true"
placeholder="模板名称"
/>
</el-form-item>
<el-form-item label-width="0px">
<!-- <el-form-item label-width="0px">
<el-button class="add" type="primary" icon="el-icon-plus" :size="defaultFormItemSize" @click="add">创建模板</el-button>
</el-form-item>
</el-form-item> -->
</filter-box>
</el-form>
<div class="listBox">
<card :dataList='myDataSetPage.operationLog.impl.dataList' :isPreset="true" :refresh="refresh"></card>
<card
:dataList="myDataSetPage.operationLog.impl.dataList"
:isPreset="true"
:refresh="refresh"
></card>
<el-row slot="pagination" type="flex" justify="end" style="margin-top: 16px;width: 100%;">
<el-pagination :total="myDataSetPage.operationLog.impl.totalCount" :current-page="myDataSetPage.operationLog.impl.currentPage" :page-size="myDataSetPage.operationLog.impl.pageSize" :page-sizes="[10, 20, 50, 100]" layout="total, prev, pager, next, sizes" @current-change="myDataSetPage.operationLog.impl.onCurrentPageChange" @size-change="myDataSetPage.operationLog.impl.onPageSizeChange">
<el-row
slot="pagination"
type="flex"
justify="end"
style="margin-top: 16px; width: 100%"
>
<el-pagination
:total="myDataSetPage.operationLog.impl.totalCount"
:current-page="myDataSetPage.operationLog.impl.currentPage"
:page-size="myDataSetPage.operationLog.impl.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, prev, pager, next, sizes"
@current-change="myDataSetPage.operationLog.impl.onCurrentPageChange"
@size-change="myDataSetPage.operationLog.impl.onPageSizeChange"
>
</el-pagination>
</el-row>
</div>
......@@ -25,7 +54,12 @@
<script>
import { mapGetters } from 'vuex';
/* eslint-disable-next-line */
import { DropdownWidget, TableWidget, UploadWidget, ChartWidget } from '@/utils/widget.js';
import {
// DropdownWidget,
TableWidget
// UploadWidget,
// ChartWidget
} from '@/utils/widget.js';
import { TemplateController } from '@/api/gptController.js';
import createTemplate from '../components/dialog/createTemplate';
import card from '../components/card/index';
......@@ -49,20 +83,26 @@ export default {
},
operationLog: {
impl: new TableWidget(this.getwidgetData, true, true, false, undefined, false)
impl: new TableWidget(
this.getwidgetData,
true,
true,
false,
undefined,
false
)
}
}
};
},
components: {card},
components: { card },
computed: {
...mapGetters(['getMainContextHeight']),
tableHeight () {
return this.getMainContextHeight - 149 - 94 - 40 + 'px';
}
},
methods: {
......@@ -95,31 +135,49 @@ export default {
templateSource: this.myDataSetPage.formFilter.templateSource,
variable: this.myDataSetPage.formFilter.variable
}
}
};
return new Promise((resolve, reject) => {
TemplateController.listForPreset(this, params).then(res => {
resolve({
dataList: res.data.dataList,
totalCount: res.data.totalCount
TemplateController.listForPreset(this, params)
.then((res) => {
resolve({
dataList: res.data.dataList,
totalCount: res.data.totalCount
});
})
.catch((e) => {
reject(e);
});
}).catch(e => {
reject(e);
});
});
},
add () {
this.$dialog.show('创建模版', createTemplate, {
area: ['100%', '100%']
}, { isEdit: false }).then(res => {
this.refresh();
}).catch(e => { });
this.$dialog
.show(
'创建模版',
createTemplate,
{
area: ['100%', '100%']
},
{ isEdit: false }
)
.then((res) => {
this.refresh();
})
.catch((e) => { });
},
edit () {
this.$dialog.show('修改模版', createTemplate, {
area: ['100%', '100%']
}, { isEdit: true }).then(res => {
this.refresh();
}).catch(e => { });
this.$dialog
.show(
'修改模版',
createTemplate,
{
area: ['100%', '100%']
},
{ isEdit: true }
)
.then((res) => {
this.refresh();
})
.catch((e) => { });
},
refresh (reloadData = false) {
if (reloadData) {
......@@ -137,11 +195,10 @@ export default {
},
handleChange () { }
},
mounted () {
this.formInit()
this.formInit();
}
};
</script>
......@@ -224,5 +281,4 @@ export default {
align-content: flex-start;
justify-content: space-between;
}
</style>
......@@ -4,7 +4,6 @@
<slot name="title"> </slot>
<slot name="content"> </slot>
</div>
</template>
<script>
......
<template>
<div class="aggregate">
<div class="logo">
<img src="@/assets/img/logo2.png" alt="">
<span class="title">陆军装备知识图谱系统</span>
</div>
<div class="aggregateBox">
<div class="itemBox" v-for="(item,index) in itemList" :key="index" @click="goPage(item.url)">
<span class="text">
{{ item.name }}
</span>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
itemList: [
{
name: '基于NEO4J的知识图谱构建',
url: `http://${window.location.hostname}:${window.location.port}/#/main/welcome`
},
{
name: '基于大语言模型与知识图谱双轮驱动的机械维修保障智能问答系统',
url: `http://${window.location.hostname}:${window.location.port}/#/main/welcome`
},
{
name: '基于gstore的知识图谱系统',
url: `http://${window.location.hostname}:${window.location.port}/#/main/welcome`
}
]
}
},
components: {},
mounted () {
},
methods: {
goPage (url) {
location.href = url
}
}
}
</script>
<style lang="scss" scoped>
.logo {
position: absolute;
top: 20px;
left: 20px;
display: flex;
img {
height: 40px;
}
.title {
color: #fff;
font-size: 18px;
height: 40px;
line-height: 40px;
display: inline-block;
margin-left: 20px;
}
}
.aggregate {
height: 100%;
width: 100%;
background: url(~@/assets/img/login_bg_2.jpg) center center;
background-size: cover;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
}
.aggregateBox {
width: 80%;
height: 50%;
min-width: 1000px;
min-height: 400px;
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.1); /* 背景色透明度,根据需要调整 */
backdrop-filter: blur(10px); /* 模糊程度,根据需要调整 */
display: flex;
padding: 20px; /* 内边距,根据需要调整 */
justify-content: space-around;
flex-direction: row;
align-items: center;
.itemBox {
width: 400px;
cursor: pointer;
height: 200px;
background: url(~@/assets/img/cardBg.png) center center;
background-size: cover;
border-radius: 10px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 20px;
border: 1px solid #b1b1b1;
font-size: 20px;
line-height: 30px;
// color: #5b6fab;
color: #273379;
font-weight: 900;
.text {
background-color: #fff;
padding: 10px;
border-radius: 5px;
}
}
}
.aggregateBox :hover {
box-shadow: 1px 1px 8px 1px #fff;
transition: all 0.5s;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment