frontend code
12
.babelrc
Executable file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
||||
}
|
||||
}],
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": ["transform-vue-jsx", "transform-runtime"]
|
||||
}
|
9
.editorconfig
Executable file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
14
.gitignore
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
/dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
10
.postcssrc.js
Executable file
@ -0,0 +1,10 @@
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
"postcss-import": {},
|
||||
"postcss-url": {},
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
154
AdminLogin.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>员工登录 - OOIN智能知识库</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #1a1a1a;
|
||||
color: white;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
text-align: center;
|
||||
color: #888;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group input::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.remember-me input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
float: right;
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.support-text {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.support-link {
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo">
|
||||
<img src="admin-pages/images/Logo copy.svg" alt="OOIN Logo">
|
||||
</div>
|
||||
|
||||
<div class="login-container">
|
||||
<h1 class="login-title">员工登录</h1>
|
||||
<div class="login-subtitle">登录到OOIN数据库系统</div>
|
||||
|
||||
<form class="login-form">
|
||||
<div class="form-group">
|
||||
<label>邮箱地址</label>
|
||||
<input type="email" placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="remember-me">
|
||||
<input type="checkbox" id="remember">
|
||||
<label for="remember">记住我</label>
|
||||
<a href="#" class="forgot-password">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-button">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="support-text">
|
||||
遇到问题?<a href="#" class="support-link">联系技术支持</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
21
README.md
Executable file
@ -0,0 +1,21 @@
|
||||
# competitive_analysis_system
|
||||
|
||||
> A Vue.js project
|
||||
|
||||
## Build Setup
|
||||
|
||||
``` bash
|
||||
# install dependencies
|
||||
npm install
|
||||
|
||||
# serve with hot reload at localhost:8080
|
||||
npm run dev
|
||||
|
||||
# build for production with minification
|
||||
npm run build
|
||||
|
||||
# build for production and view the bundle analyzer report
|
||||
npm run build --report
|
||||
```
|
||||
|
||||
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
|
41
build/build.js
Executable file
@ -0,0 +1,41 @@
|
||||
'use strict'
|
||||
require('./check-versions')()
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
const ora = require('ora')
|
||||
const rm = require('rimraf')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const webpackConfig = require('./webpack.prod.conf')
|
||||
|
||||
const spinner = ora('building for production...')
|
||||
spinner.start()
|
||||
|
||||
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||
if (err) throw err
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
console.log(chalk.red(' Build failed with errors.\n'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
))
|
||||
})
|
||||
})
|
54
build/check-versions.js
Executable file
@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
const chalk = require('chalk')
|
||||
const semver = require('semver')
|
||||
const packageConfig = require('../package.json')
|
||||
const shell = require('shelljs')
|
||||
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
}
|
||||
|
||||
const versionRequirements = [
|
||||
{
|
||||
name: 'node',
|
||||
currentVersion: semver.clean(process.version),
|
||||
versionRequirement: packageConfig.engines.node
|
||||
}
|
||||
]
|
||||
|
||||
if (shell.which('npm')) {
|
||||
versionRequirements.push({
|
||||
name: 'npm',
|
||||
currentVersion: exec('npm --version'),
|
||||
versionRequirement: packageConfig.engines.npm
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
const warnings = []
|
||||
|
||||
for (let i = 0; i < versionRequirements.length; i++) {
|
||||
const mod = versionRequirements[i]
|
||||
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log()
|
||||
|
||||
for (let i = 0; i < warnings.length; i++) {
|
||||
const warning = warnings[i]
|
||||
console.log(' ' + warning)
|
||||
}
|
||||
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
BIN
build/logo.png
Executable file
After Width: | Height: | Size: 6.7 KiB |
101
build/utils.js
Executable file
@ -0,0 +1,101 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const config = require('../config')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const packageConfig = require('../package.json')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
const assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
options = options || {}
|
||||
|
||||
const cssLoader = {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
const postcssLoader = {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loader, loaderOptions) {
|
||||
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
|
||||
|
||||
if (loader) {
|
||||
loaders.push({
|
||||
loader: loader + '-loader',
|
||||
options: Object.assign({}, loaderOptions, {
|
||||
sourceMap: options.sourceMap
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: loaders,
|
||||
publicPath:'../../',
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader'].concat(loaders)
|
||||
}
|
||||
}
|
||||
|
||||
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(),
|
||||
postcss: generateLoaders(),
|
||||
less: generateLoaders('less'),
|
||||
sass: generateLoaders('sass', { indentedSyntax: true }),
|
||||
scss: generateLoaders('sass'),
|
||||
stylus: generateLoaders('stylus'),
|
||||
styl: generateLoaders('stylus')
|
||||
}
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
const output = []
|
||||
const loaders = exports.cssLoaders(options)
|
||||
|
||||
for (const extension in loaders) {
|
||||
const loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
use: loader
|
||||
})
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
exports.createNotifierCallback = () => {
|
||||
const notifier = require('node-notifier')
|
||||
|
||||
return (severity, errors) => {
|
||||
if (severity !== 'error') return
|
||||
|
||||
const error = errors[0]
|
||||
const filename = error.file && error.file.split('!').pop()
|
||||
|
||||
notifier.notify({
|
||||
title: packageConfig.name,
|
||||
message: severity + ': ' + error.name,
|
||||
subtitle: filename || '',
|
||||
})
|
||||
}
|
||||
}
|
22
build/vue-loader.conf.js
Executable file
@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
const sourceMapEnabled = isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.cssSourceMap
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: sourceMapEnabled,
|
||||
extract: isProduction
|
||||
}),
|
||||
cssSourceMap: sourceMapEnabled,
|
||||
cacheBusting: config.dev.cacheBusting,
|
||||
transformToRequire: {
|
||||
video: ['src', 'poster'],
|
||||
source: 'src',
|
||||
img: 'src',
|
||||
image: 'xlink:href'
|
||||
}
|
||||
}
|
82
build/webpack.base.conf.js
Executable file
@ -0,0 +1,82 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const vueLoaderConfig = require('./vue-loader.conf')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
context: path.resolve(__dirname, '../'),
|
||||
entry: {
|
||||
app: './src/main.js'
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': resolve('src'),
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: vueLoaderConfig
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('media/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
node: {
|
||||
// prevent webpack from injecting useless setImmediate polyfill because Vue
|
||||
// source contains it (although only uses it if it's native).
|
||||
setImmediate: false,
|
||||
// prevent webpack from injecting mocks to Node native modules
|
||||
// that does not make sense for the client
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty'
|
||||
}
|
||||
}
|
95
build/webpack.dev.conf.js
Executable file
@ -0,0 +1,95 @@
|
||||
'use strict'
|
||||
const utils = require('./utils')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const merge = require('webpack-merge')
|
||||
const path = require('path')
|
||||
const baseWebpackConfig = require('./webpack.base.conf')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
const portfinder = require('portfinder')
|
||||
|
||||
const HOST = process.env.HOST
|
||||
const PORT = process.env.PORT && Number(process.env.PORT)
|
||||
|
||||
const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: config.dev.devtool,
|
||||
|
||||
// these devServer options should be customized in /config/index.js
|
||||
devServer: {
|
||||
clientLogLevel: 'warning',
|
||||
historyApiFallback: {
|
||||
rewrites: [
|
||||
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
|
||||
],
|
||||
},
|
||||
hot: true,
|
||||
contentBase: false, // since we use CopyWebpackPlugin.
|
||||
compress: true,
|
||||
host: HOST || config.dev.host,
|
||||
port: PORT || config.dev.port,
|
||||
open: config.dev.autoOpenBrowser,
|
||||
overlay: config.dev.errorOverlay
|
||||
? { warnings: false, errors: true }
|
||||
: false,
|
||||
publicPath: config.dev.assetsPublicPath,
|
||||
proxy: config.dev.proxyTable,
|
||||
quiet: true, // necessary for FriendlyErrorsPlugin
|
||||
watchOptions: {
|
||||
poll: config.dev.poll,
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': require('../config/dev.env')
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'index.html',
|
||||
inject: true
|
||||
}),
|
||||
// copy custom static assets
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.dev.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
||||
|
||||
module.exports = new Promise((resolve, reject) => {
|
||||
portfinder.basePort = process.env.PORT || config.dev.port
|
||||
portfinder.getPort((err, port) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
// publish the new Port, necessary for e2e tests
|
||||
process.env.PORT = port
|
||||
// add port to devServer config
|
||||
devWebpackConfig.devServer.port = port
|
||||
|
||||
// Add FriendlyErrorsPlugin
|
||||
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
|
||||
compilationSuccessInfo: {
|
||||
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
|
||||
},
|
||||
onErrors: config.dev.notifyOnErrors
|
||||
? utils.createNotifierCallback()
|
||||
: undefined
|
||||
}))
|
||||
|
||||
resolve(devWebpackConfig)
|
||||
}
|
||||
})
|
||||
})
|
145
build/webpack.prod.conf.js
Executable file
@ -0,0 +1,145 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.conf')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
|
||||
const env = require('../config/prod.env')
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true,
|
||||
usePostCSS: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? config.build.devtool : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env
|
||||
}),
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
},
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
parallel: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css'),
|
||||
// Setting the following option to `false` will not extract CSS from codesplit chunks.
|
||||
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
|
||||
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
|
||||
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
|
||||
allChunks: true,
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: config.build.productionSourceMap
|
||||
? { safe: true, map: { inline: false } }
|
||||
: { safe: true }
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.build.index,
|
||||
template: 'index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}),
|
||||
// keep module.id stable when vendor modules does not change
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
// enable scope hoisting
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks (module) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
minChunks: Infinity
|
||||
}),
|
||||
// This instance extracts shared chunks from code splitted chunks and bundles them
|
||||
// in a separate chunk, similar to the vendor chunk
|
||||
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'app',
|
||||
async: 'vendor-async',
|
||||
children: true,
|
||||
minChunks: 3
|
||||
}),
|
||||
|
||||
// copy custom static assets
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.build.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' +
|
||||
config.build.productionGzipExtensions.join('|') +
|
||||
')$'
|
||||
),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
14
config/dev.env.js
Executable file
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
const merge = require('webpack-merge')
|
||||
const prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"',
|
||||
API: '"/devApi"',
|
||||
CHAT_SIMILRITY: '"0.3"',
|
||||
NODE_CHAT_TITLE:'"OOIN小助手"',
|
||||
NODE_LOGO:'"OOIN智能知识库"',
|
||||
NODE_TITLE:'"智能知识库管理"',
|
||||
BASE_URL: '"/"',
|
||||
MODEL_ID: '"eb803ca4-69b2-11ef-a5a6-fa163e378f38"',
|
||||
})
|
83
config/index.js
Executable file
@ -0,0 +1,83 @@
|
||||
'use strict'
|
||||
// Template version: 1.3.1
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
dev: {
|
||||
|
||||
// Paths
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {
|
||||
'/devApi': {
|
||||
// target: 'http://yx.yanxi:8080',
|
||||
target: 'http://180.163.88.62:30226',
|
||||
ws: false,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/devApi': ''
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Various Dev Server settings
|
||||
host: 'localhost', // can be overwritten by process.env.HOST
|
||||
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||
autoOpenBrowser: false,
|
||||
errorOverlay: true,
|
||||
notifyOnErrors: true,
|
||||
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
||||
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
|
||||
// If you have problems debugging vue-files in devtools,
|
||||
// set this to false - it *may* help
|
||||
// https://vue-loader.vuejs.org/en/options.html#cachebusting
|
||||
cacheBusting: true,
|
||||
|
||||
cssSourceMap: true
|
||||
},
|
||||
|
||||
build: {
|
||||
// Template for index.html
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
|
||||
// Paths
|
||||
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||
assetsSubDirectory: 'static',
|
||||
// assetsPublicPath: './',
|
||||
assetsPublicPath: '/rag/',
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
|
||||
productionSourceMap: true,
|
||||
// https://webpack.js.org/configuration/devtool/#production
|
||||
devtool: '#source-map',
|
||||
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css','img'],
|
||||
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
11
config/prod.env.js
Executable file
@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
API: '"/yxapi"',
|
||||
CHAT_SIMILRITY: '"0.3"',
|
||||
BASE_URL: '"/rag/"',
|
||||
NODE_CHAT_TITLE:'"言晞小助手"',
|
||||
NODE_LOGO:'"OOIN智能知识库"',
|
||||
NODE_TITLE:'"智能知识库管理"',
|
||||
MODEL_ID: '"eb803ca4-69b2-11ef-a5a6-fa163e378f38"',
|
||||
}
|
12
index.html
Executable file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>OOIN小助手</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
15504
package-lock.json
generated
Executable file
73
package.json
Executable file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "competitive_analysis_system",
|
||||
"version": "1.0.0",
|
||||
"description": "A Vue.js project",
|
||||
"author": "cycxs508@sina.com <724982540@qq.com>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
|
||||
"start": "npm run dev",
|
||||
"build": "node build/build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"ant-design-vue": "^1.7.8",
|
||||
"axios": "^0.18.0",
|
||||
"highlight.js": "9.18.1",
|
||||
"marked": "0.8.0",
|
||||
"md5": "^2.3.0",
|
||||
"moment": "^2.30.1",
|
||||
"vue": "^2.5.2",
|
||||
"vue-infinite-scroll": "^2.0.2",
|
||||
"vue-router": "^3.0.1",
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.2",
|
||||
"babel-core": "^6.22.1",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-transform-runtime": "^6.22.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.5.0",
|
||||
"babel-preset-env": "^1.3.2",
|
||||
"babel-preset-stage-2": "^6.22.0",
|
||||
"chalk": "^2.0.1",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"css-loader": "^0.28.0",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^1.1.4",
|
||||
"friendly-errors-webpack-plugin": "^1.6.1",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^4.1.0",
|
||||
"node-notifier": "^5.1.2",
|
||||
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
||||
"ora": "^1.2.0",
|
||||
"portfinder": "^1.0.13",
|
||||
"postcss-import": "^11.0.0",
|
||||
"postcss-loader": "^2.0.8",
|
||||
"postcss-url": "^7.2.1",
|
||||
"rimraf": "^2.6.0",
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"uglifyjs-webpack-plugin": "^1.1.1",
|
||||
"url-loader": "^0.5.8",
|
||||
"vue-loader": "^13.3.0",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.5.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
"webpack-dev-server": "^2.9.1",
|
||||
"webpack-merge": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
]
|
||||
}
|
37
src/App.vue
Executable file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-config-provider :locale="zh_CN">
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
</div>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import zh_CN from "ant-design-vue/lib/locale-provider/zh_CN";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
zh_CN
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
42
src/api/index.js
Executable file
@ -0,0 +1,42 @@
|
||||
import {deleteAction, deleteAction1, getAction, postAction} from "./manage";
|
||||
|
||||
// 知识库列表
|
||||
export const listPage = (params) => getAction(`/api/dataset/${params.pageNum}/${params.pageSize}`);
|
||||
// 新增知识库
|
||||
export const addDataset = (params) => postAction(`/api/dataset`, params);
|
||||
// 删除知识库
|
||||
export const deleteDataset = (params) => deleteAction(`/api/dataset`, params);
|
||||
// 知识库详情
|
||||
export const documentDetail = (documentId) => getAction(`/api/dataset/${documentId}/document`);
|
||||
// 知识库文件详情
|
||||
export const documentFileDetail = (params) => getAction(`/api/dataset/${params.documentId}/document/${params.fileId}/paragraph`);
|
||||
// 新增知识库文件
|
||||
export const addDocument = (params) => postAction(`/api/dataset/${params.id}/document`, params);
|
||||
// 删除知识库文件
|
||||
export const documentFileDelete = (params) => deleteAction1(`/api/dataset/${params.documentId}/document/${params.fileId}`, params);
|
||||
// 创建会话id
|
||||
export const chatOpen = (params) => postAction(`/api/application/chat_workflow/open`, params);
|
||||
// 获取会话记录
|
||||
export const getChatRecord = (params) => getAction(`/api/application/f3acba96-0504-11ef-9c5a-0242ac110006/chat`, params);
|
||||
// 根据会话id获取具体的对话记录
|
||||
export const getChatRecordByChatId = (params) => getAction(`/api/application/f3acba96-0504-11ef-9c5a-0242ac110006/chat/${params.chatId}/chat_record/`);
|
||||
// 根据文档ID获取参考段落信息
|
||||
export const getChatDatasetQuote = (params) => getAction(`/api/dataset/${params.datasetId}/hit_test`,params);
|
||||
//文心大模型
|
||||
export const wenxinModel = (params) => postAction(`/wenxinApi/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro`, params);
|
||||
|
||||
export default {
|
||||
listPage,
|
||||
getChatRecord,
|
||||
getChatRecordByChatId,
|
||||
addDataset,
|
||||
documentDetail,
|
||||
documentFileDetail,
|
||||
documentFileDelete,
|
||||
deleteDataset,
|
||||
addDocument,
|
||||
chatOpen,
|
||||
getChatDatasetQuote,
|
||||
wenxinModel,
|
||||
|
||||
}
|
209
src/api/manage.js
Executable file
@ -0,0 +1,209 @@
|
||||
import Vue from 'vue'
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
|
||||
axios.defaults.timeout = 60000;
|
||||
//postFormData
|
||||
export function postFormDataAction(url, formData) {
|
||||
return axios({
|
||||
url: url,
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//post
|
||||
export function postAction(url, parameter) {
|
||||
return axios({
|
||||
url: url,
|
||||
method: 'post',
|
||||
data: parameter,
|
||||
})
|
||||
}
|
||||
|
||||
//put
|
||||
export function putAction(url, parameter) {
|
||||
return axios({
|
||||
url: url,
|
||||
method: 'put',
|
||||
data: parameter
|
||||
})
|
||||
}
|
||||
|
||||
//patch
|
||||
export function patchAction(url, parameter) {
|
||||
return axios({
|
||||
url: `${url}/${parameter.id}`,
|
||||
method: 'patch',
|
||||
data: parameter
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//get
|
||||
export function getAction(url, parameter) {
|
||||
return axios({
|
||||
url: url,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteAction1(url) {
|
||||
return axios({
|
||||
url: `${url}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
//deleteAction
|
||||
export function deleteAction(url, { id }) {
|
||||
return axios({
|
||||
url: `${url}/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
export function delPostAction(url, {id}) {
|
||||
return axios({
|
||||
url: `${url}/${id}`,
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
export function getUserList(parameter) {
|
||||
return axios({
|
||||
url: api.user,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleList(parameter) {
|
||||
return axios({
|
||||
url: api.role,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getServiceList(parameter) {
|
||||
return axios({
|
||||
url: api.service,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getPermissions(parameter) {
|
||||
return axios({
|
||||
url: api.permissionNoPager,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
// id == 0 add post
|
||||
// id != 0 update put
|
||||
export function saveService(parameter) {
|
||||
return axios({
|
||||
url: api.service,
|
||||
method: parameter.id == 0 ? 'post' : 'put',
|
||||
data: parameter
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件 用于excel导出
|
||||
* @param url
|
||||
* @param parameter
|
||||
* @returns {*}
|
||||
*/
|
||||
export function downFile(url, parameter, method = 'get') {
|
||||
if (method == 'get') {
|
||||
return axios({
|
||||
url: url,
|
||||
params: parameter,
|
||||
method: method,
|
||||
responseType: 'blob'
|
||||
})
|
||||
} else {
|
||||
return axios({
|
||||
url: url,
|
||||
method: method,
|
||||
data: parameter,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param url 文件路径
|
||||
* @param fileName 文件名
|
||||
* @param parameter 请求方式
|
||||
* @returns {*}
|
||||
*/
|
||||
export function downloadFile(url, fileName, parameter) {
|
||||
return downFile(url, parameter).then((data) => {
|
||||
if (!data || data.size === 0) {
|
||||
Vue.prototype['$message'].warning('文件下载失败')
|
||||
return
|
||||
}
|
||||
if (typeof window.navigator.msSaveBlob !== 'undefined') {
|
||||
window.navigator.msSaveBlob(new Blob([data]), fileName)
|
||||
} else {
|
||||
let url = window.URL.createObjectURL(new Blob([data]))
|
||||
let link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = url
|
||||
link.setAttribute('download', fileName)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link) //下载完成移除元素
|
||||
window.URL.revokeObjectURL(url) //释放掉blob对象
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传 用于富文本上传图片
|
||||
* @param url
|
||||
* @param parameter
|
||||
* @returns {*}
|
||||
*/
|
||||
export function uploadAction(url, parameter) {
|
||||
return axios({
|
||||
url: url,
|
||||
data: parameter,
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data' // 文件上传
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件服务访问路径
|
||||
* @param avatar
|
||||
* @param subStr
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getFileAccessHttpUrl(avatar, subStr) {
|
||||
if (!subStr) subStr = 'http'
|
||||
try {
|
||||
if (avatar && avatar.startsWith(subStr)) {
|
||||
return avatar
|
||||
} else {
|
||||
if (avatar && avatar.length > 0 && avatar.indexOf('[') == -1) {
|
||||
// return window._CONFIG['staticDomainURL'] + "/" + avatar;
|
||||
return avatar
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
}
|
BIN
src/assets/Slice 13@2x.png
Executable file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/avatar.png
Executable file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/bg-footer.png
Executable file
After Width: | Height: | Size: 80 KiB |
BIN
src/assets/bg.png
Executable file
After Width: | Height: | Size: 2.2 MiB |
BIN
src/assets/btn-bg.png
Executable file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/cai-active.png
Executable file
After Width: | Height: | Size: 623 B |
BIN
src/assets/cai.png
Executable file
After Width: | Height: | Size: 528 B |
BIN
src/assets/chat-bg.png
Executable file
After Width: | Height: | Size: 99 KiB |
BIN
src/assets/chat-tip.png
Executable file
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/close-active.png
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/close.png
Executable file
After Width: | Height: | Size: 468 B |
35
src/assets/company-logo.svg
Normal file
@ -0,0 +1,35 @@
|
||||
<svg width="50" height="17" viewBox="0 0 50 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<ellipse cx="7.47419" cy="9.44087" rx="7.47419" ry="7.47431" fill="#F1F1E9"/>
|
||||
<mask id="mask0_2606_2955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="1" width="15" height="16">
|
||||
<ellipse cx="7.47419" cy="9.44111" rx="7.47419" ry="7.47431" fill="#FEFEFE"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_2606_2955)">
|
||||
<ellipse cx="11.3355" cy="9.31656" rx="4.35994" ry="4.36002" fill="#FF2C3F"/>
|
||||
</g>
|
||||
<mask id="mask1_2606_2955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="1" width="15" height="16">
|
||||
<ellipse cx="7.47419" cy="9.44111" rx="7.47419" ry="7.47431" fill="#FEFEFE"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_2606_2955)">
|
||||
<ellipse cx="11.3355" cy="9.31656" rx="4.35994" ry="4.36002" fill="#FF2C3F"/>
|
||||
</g>
|
||||
<ellipse cx="22.4224" cy="9.44111" rx="7.47419" ry="7.47431" fill="#F1F1E9"/>
|
||||
<mask id="mask2_2606_2955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="14" y="1" width="16" height="16">
|
||||
<ellipse cx="22.4224" cy="9.44111" rx="7.47419" ry="7.47431" fill="#FEFEFE"/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_2606_2955)">
|
||||
<ellipse cx="26.2838" cy="9.31631" rx="4.35994" ry="4.36002" fill="#FF2C3F"/>
|
||||
</g>
|
||||
<mask id="mask3_2606_2955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="14" y="1" width="16" height="16">
|
||||
<ellipse cx="22.4224" cy="9.44111" rx="7.47419" ry="7.47431" fill="#FEFEFE"/>
|
||||
</mask>
|
||||
<g mask="url(#mask3_2606_2955)">
|
||||
<ellipse cx="26.2838" cy="9.31631" rx="4.35994" ry="4.36002" fill="#FF2C3F"/>
|
||||
</g>
|
||||
<path d="M34.614 2.87305V16.0302H31.1914V2.87305H34.614Z" fill="#F1F1E9"/>
|
||||
<path d="M40.2713 2.87354L46.5413 10.9184H46.5758V2.87354H49.9999V16.0307H46.5758L40.3072 7.96859H40.2713V16.0307H36.8486V2.87354H40.2713Z" fill="#F1F1E9"/>
|
||||
<path d="M44.4758 0.000488281V1.67298H44.2673V0.812306C44.2673 0.707057 44.2676 0.599653 44.2687 0.490452C44.2694 0.380534 44.2709 0.280314 44.273 0.190152C44.245 0.280314 44.213 0.380893 44.1767 0.491889C44.14 0.602167 44.104 0.708134 44.0688 0.809432L43.7726 1.67298H43.5108L43.2103 0.805121C43.1758 0.704542 43.1405 0.600371 43.1053 0.491889C43.0697 0.382689 43.0381 0.283547 43.0104 0.194462C43.014 0.284624 43.0161 0.384126 43.0161 0.493326C43.0161 0.602526 43.0161 0.708853 43.0161 0.812306V1.67298H42.8076V0.000488281H43.1513L43.646 1.49624L44.1393 0.000488281H44.4758Z" fill="#F1F1E9"/>
|
||||
<path d="M46.0232 1.50294V1.67249H44.9102V0H45.9988V0.169548H45.1187V0.75147H45.9269V0.921018H45.1187V1.50294H46.0232Z" fill="#F1F1E9"/>
|
||||
<path d="M47.0559 0.0100579C47.1853 0.0265816 47.2997 0.0668132 47.3996 0.130753C47.4992 0.193974 47.5772 0.282699 47.634 0.396569C47.6905 0.510798 47.7189 0.650532 47.7189 0.816128C47.7189 0.982083 47.6894 1.12505 47.6311 1.24574C47.5736 1.36572 47.4934 1.46091 47.391 1.53168C47.2892 1.6028 47.1738 1.64627 47.0444 1.66243C47.002 1.66746 46.9671 1.67033 46.9394 1.67105C46.9125 1.67213 46.8765 1.67249 46.8316 1.67249H46.334V0H46.8431C46.888 0 46.9243 0.000718376 46.9524 0.00143684C46.98 0.0025145 47.0146 0.00538819 47.0559 0.0100579ZM47.0228 1.49001C47.1645 1.4681 47.2777 1.39949 47.3622 1.28454C47.4474 1.16959 47.4902 1.01369 47.4902 0.816128C47.4902 0.630416 47.4499 0.483858 47.3694 0.376453C47.2889 0.269408 47.1771 0.204032 47.0343 0.181042C47.0084 0.176373 46.9786 0.173499 46.9452 0.172421C46.9125 0.170625 46.8747 0.169548 46.8316 0.169548H46.5425V1.50294H46.8186C46.8625 1.50294 46.9009 1.50258 46.9337 1.5015C46.9671 1.49971 46.9969 1.49576 47.0228 1.49001Z" fill="#F1F1E9"/>
|
||||
<path d="M48.2642 1.67249H48.0557V0H48.2642V1.67249Z" fill="#F1F1E9"/>
|
||||
<path d="M49.5598 1.08051H48.9443L48.7473 1.67249H48.5244L49.1255 0H49.4074L49.9841 1.67249H49.7597L49.5598 1.08051ZM49.0019 0.91096H49.5037L49.2621 0.165237L49.0019 0.91096Z" fill="#F1F1E9"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/con-bg.png
Executable file
After Width: | Height: | Size: 2.4 MiB |
BIN
src/assets/dateset-active.png
Executable file
After Width: | Height: | Size: 748 B |
BIN
src/assets/dateset.png
Executable file
After Width: | Height: | Size: 768 B |
BIN
src/assets/default.png
Executable file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/document.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/assets/excel.png
Executable file
After Width: | Height: | Size: 544 B |
BIN
src/assets/full-screen-active.png
Executable file
After Width: | Height: | Size: 1008 B |
BIN
src/assets/full-screen.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/head-bg.png
Executable file
After Width: | Height: | Size: 153 KiB |
BIN
src/assets/icon-knowledge.png
Executable file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/logo-icon.png
Executable file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/logo.png
Executable file
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/logout.png
Executable file
After Width: | Height: | Size: 841 B |
BIN
src/assets/main-head-bg.png
Executable file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/message-sent.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/msg-bg.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/assets/no-data-img.png
Executable file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/no-data-list-btn.png
Executable file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/no-data-logo.png
Executable file
After Width: | Height: | Size: 8.6 KiB |
BIN
src/assets/no-key.png
Executable file
After Width: | Height: | Size: 582 B |
BIN
src/assets/ooin-logo.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
src/assets/pdf.png
Executable file
After Width: | Height: | Size: 556 B |
BIN
src/assets/ppt.png
Executable file
After Width: | Height: | Size: 577 B |
BIN
src/assets/red-robot.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
src/assets/robot.png
Executable file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/send.png
Executable file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/settings-active.png
Executable file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/settings.png
Executable file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/sidebar-active.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/sidebar.png
Executable file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/system.png
Executable file
After Width: | Height: | Size: 615 B |
BIN
src/assets/user.png
Executable file
After Width: | Height: | Size: 4.3 KiB |
BIN
src/assets/word.png
Executable file
After Width: | Height: | Size: 599 B |
BIN
src/assets/zan-active.png
Executable file
After Width: | Height: | Size: 618 B |
BIN
src/assets/zan.png
Executable file
After Width: | Height: | Size: 523 B |
91
src/components/chatDatasetQuote.vue
Executable file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-modal :width="685" centered :visible="visible" title="命中片段" :confirmLoading="confirmLoading"
|
||||
@cancel="handleCancel" class="form-modal">
|
||||
<ul class="menu">
|
||||
<li v-for="(item,index) in quoteList" :key="index" class="menu-li">
|
||||
{{ item.content }}
|
||||
<p class="similarity">匹配度:{{ Math.floor(item.similarity * 100) / 100 }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<template slot="footer">
|
||||
<a-button @click="handleCancel">取 消</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
model: {},
|
||||
quoteList:[],
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
props:{
|
||||
datasetQuoteList:{
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
async edit(record) {
|
||||
this.model = Object.assign({}, record)
|
||||
this.visible = true
|
||||
this.handleList()
|
||||
},
|
||||
async handleList(){
|
||||
this.quoteList = this.datasetQuoteList.filter(acc=>acc.document_name == this.model.document_name)
|
||||
},
|
||||
handleCancel(){
|
||||
this.visible = false
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
div[aria-hidden="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
.menu{
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
height: 480px;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.menu-li{
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px dashed rgba(4,24,73,0.15);
|
||||
.similarity{
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #347ae2;
|
||||
margin-bottom: 14px;
|
||||
text-align: right;
|
||||
}
|
||||
&:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
182
src/components/layouts/AdminLogin.vue
Normal file
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="logo">
|
||||
<img src="../../assets/company-logo.svg" alt="OOIN Logo" class="logo-img">
|
||||
</div>
|
||||
|
||||
<h1 class="login-title">管理员登录</h1>
|
||||
<div class="login-subtitle">登录到OOIN数据库系统</div>
|
||||
|
||||
<form class="login-form" @submit.prevent="handleLogin">
|
||||
<div class="form-group">
|
||||
<label>邮箱地址</label>
|
||||
<input type="email" v-model="email" placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" v-model="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="remember-me">
|
||||
<input type="checkbox" id="remember" v-model="rememberMe">
|
||||
<label for="remember">记住我</label>
|
||||
<a href="#" class="forgot-password" @click.prevent="handleForgotPassword">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-button">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="support-text">
|
||||
遇到问题?<a href="#" class="support-link" @click.prevent="contactSupport">联系技术支持</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminLogin',
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
password: '',
|
||||
rememberMe: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
console.log('Login attempt', {
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
rememberMe: this.rememberMe
|
||||
});
|
||||
},
|
||||
handleForgotPassword() {
|
||||
console.log('Forgot password clicked');
|
||||
},
|
||||
contactSupport() {
|
||||
console.log('Contact support clicked');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #000000;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
|
||||
.logo {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.logo-img {
|
||||
width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-title {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
background-color: #18181b;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #888;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #27272a;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
color: #888;
|
||||
|
||||
input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(#ef4444, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.support-text {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
.support-link {
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
331
src/components/layouts/BasicLayout.vue
Executable file
@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="main-head">
|
||||
<!-- <span class="main-head-title"> {{ logo }} OOIN智能知识库</span> -->
|
||||
<div class="main-head-title">
|
||||
<img src="../../assets/company-logo.svg" alt="COIN Logo" class="header-logo">
|
||||
<span>OOIN智能知识库</span>
|
||||
</div>
|
||||
<!-- <img src="../../assets/logo-icon.png" alt="" class="logo"> -->
|
||||
<div class="right">
|
||||
<!-- <a-icon type="search" class="icon-ball" />
|
||||
<a-icon type="bell" class="icon-ball" /> -->
|
||||
<img src="../../assets/avatar.png" alt="" class="icon-user">
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click="e => e.preventDefault()">
|
||||
{{ userName }}
|
||||
<!-- Marci Fumons -->
|
||||
<!-- <a-icon type="down" /> -->
|
||||
</a>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a href="javascript:;" @click="handleLogout">退出登陆</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
|
||||
<!--<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">退出登陆</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">消息</a>
|
||||
</a-menu-item>
|
||||
</a-menu> -->
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-con">
|
||||
<div class="main-left">
|
||||
<div class="main-menu">
|
||||
<a-menu style="width: 100%" :default-selected-keys="['1']" :default-open-keys="['sub1']"
|
||||
mode="inline" :selected-keys="[current]" @click="handleClick">
|
||||
<a-menu-item key="1" @click="changeMenu('list')">
|
||||
<div class="icon icon-profile" :class="current == '1' ? 'active' : ''"></div>
|
||||
<!-- <a-icon type="profile" /> -->
|
||||
知识库
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="changeMenu('setting')">
|
||||
<div class="icon icon-setting" :class="current == '2' ? 'active' : ''"></div>
|
||||
设置
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<!-- <div class="main-left-footer">
|
||||
<img src="../../assets/logout.png" class="logout"/>
|
||||
<p class="logout-title">退出</p>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="main-con-right">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: '1',
|
||||
logo: process.env.NODE_LOGO,
|
||||
userName: '',
|
||||
userRole: '',
|
||||
userToken: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Get user info from localStorage
|
||||
this.userName = localStorage.getItem('userName') || '';
|
||||
this.userRole = localStorage.getItem('userRole') || '';
|
||||
this.userToken = localStorage.getItem('userToken') || '';
|
||||
|
||||
// If no user info, redirect to login
|
||||
if (!this.userToken) {
|
||||
this.$router.push('/login');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeMenu(route) {
|
||||
if (this.$route.path !== route) {
|
||||
this.$router.push(route)
|
||||
}
|
||||
},
|
||||
handleClick(e) {
|
||||
console.log('click ', e);
|
||||
this.current = e.key;
|
||||
},
|
||||
handleLogout() {
|
||||
// Clear user info from localStorage
|
||||
localStorage.removeItem('userToken');
|
||||
localStorage.removeItem('userName');
|
||||
localStorage.removeItem('userRole');
|
||||
|
||||
// Redirect to login page
|
||||
this.$router.push('/login');
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- <script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: '1',
|
||||
logo:process.env.NODE_LOGO
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
//路由内容切换
|
||||
changeMenu(route) {
|
||||
if (this.$route.path !== route) {
|
||||
//获取路由对象并切换
|
||||
this.$router.push(route)
|
||||
}
|
||||
},
|
||||
handleClick(e) {
|
||||
console.log('click ', e);
|
||||
this.current = e.key;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script> -->
|
||||
|
||||
<style lang="less" scoped>
|
||||
.main {
|
||||
background-color: #000000; //添加整体背景颜色
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.main-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #27272a; //添加底部边框
|
||||
padding: 0 50px;
|
||||
box-sizing: border-box;
|
||||
height: 60px;
|
||||
background-color: #18181b; //添加header背景颜色
|
||||
.main-head-title{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #ef4444; //更改标题颜色
|
||||
line-height: 60px;
|
||||
|
||||
.header-logo {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
// background: url('../../assets/logo-icon.png') no-repeat center center;
|
||||
// background-size: 100% 100%;
|
||||
width: 110px;
|
||||
height: 34px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-top: 14px;
|
||||
|
||||
.icon-ball {
|
||||
font-size: 16px;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.icon-user {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.main-con {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
// flex-direction: column;
|
||||
height: 100%;
|
||||
height: calc(100% - 60px);
|
||||
background-color: #18181b; //添加主要版块背景颜色
|
||||
|
||||
.main-left {
|
||||
height: 100%;
|
||||
width: 283px;
|
||||
display: flex;
|
||||
border-right: 1px solid #27272a; //添加右侧边框
|
||||
flex-direction: column;
|
||||
background-color: #18181b; //添加左侧背景颜色
|
||||
|
||||
//add new styles
|
||||
/deep/ .ant-menu {
|
||||
background-color: #18181b !important;
|
||||
}
|
||||
/deep/ .ant-menu-item {
|
||||
color: #a1a1aa !important;
|
||||
|
||||
&:hover {
|
||||
color: #ffffff !important;
|
||||
background-color: #27272a !important;
|
||||
}
|
||||
|
||||
&.ant-menu-item-selected {
|
||||
background-color: #27272a !important;
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// /deep/ .ant-menu-item {
|
||||
// color: #a1a1aa !important;
|
||||
// background-color: #27272a !important;
|
||||
// }
|
||||
|
||||
.main-menu {
|
||||
flex: 1;
|
||||
padding-top: 52px;
|
||||
width: 283px;
|
||||
text-align: left;
|
||||
.ant-menu-inline .ant-menu-item {
|
||||
width: 100%;
|
||||
border: none !important;
|
||||
font-family: Poppins, Poppins;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #a1a1aa; //更改字体颜色
|
||||
line-height: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 58px !important;
|
||||
background: transparent; //更改背景颜色
|
||||
margin-bottom: 14px;
|
||||
|
||||
.anticon {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-profile {
|
||||
background: url('../../assets/dateset.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background: url('../../assets/dateset-active.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-setting {
|
||||
background: url('../../assets/settings.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background: url('../../assets/settings-active.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-menu-item-active {
|
||||
color: #ef4444 !important; //更改选中颜色
|
||||
}
|
||||
|
||||
.ant-menu-item-selected {
|
||||
color: #ef4444 !important; //更改选中颜色
|
||||
height: 24px !important;
|
||||
border-right: #27272a !important; //更改选中边框颜色
|
||||
}
|
||||
}
|
||||
|
||||
.main-left-footer {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin:0 auto 40px;
|
||||
cursor: pointer;
|
||||
.logout{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.logout-title{
|
||||
font-family: Poppins, Poppins;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #CE4F51; //changed color
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
margin:0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.main-con-right {
|
||||
flex: 1;
|
||||
padding: 0 56px 0 30px;
|
||||
background-color: #000000; //添加右侧背景颜色
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
230
src/components/layouts/UserLogin.vue
Normal file
@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="logo">
|
||||
<img src="../../assets/company-logo.svg" alt="OOIN Logo" class="logo-img">
|
||||
</div>
|
||||
|
||||
<h1 class="login-title">员工登录</h1>
|
||||
<div class="login-subtitle">登录到OOIN数据库系统</div>
|
||||
|
||||
<form class="login-form" @submit.prevent="handleLogin">
|
||||
<div class="form-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" v-model="username" placeholder="请输入用户名">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" v-model="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="remember-me">
|
||||
<input type="checkbox" id="remember" v-model="rememberMe">
|
||||
<label for="remember">记住我</label>
|
||||
<a href="#" class="forgot-password" @click.prevent="handleForgotPassword">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-button">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="support-text">
|
||||
遇到问题?<a href="#" class="support-link" @click.prevent="contactSupport">联系技术支持</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminLogin',
|
||||
data() {
|
||||
return {
|
||||
username: '', //把邮箱改成用户名
|
||||
password: '',
|
||||
rememberMe: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
// Mock authentication logic - in real app this would be an API call
|
||||
const users = {
|
||||
'admin1': { password: 'admin123', token: '0c2da9ad83fd142deb64c8f46f57b4a328ad6356', role: 'admin' },
|
||||
'leader1': { password: 'leader123', token: '1fbc640baf1d35d67d4d4f957a7282e3cc84121a', role: 'leader' },
|
||||
'leader2': { password: 'leader123', token: '8263ba67422324042ae5c063192dc694015ae2d2', role: 'leader' },
|
||||
'member1': { password: 'member123', token: 'bd87c833d4a659f6434bc235f299533ba6c2fec2', role: 'member' },
|
||||
'member2': { password: 'member123', token: 'fe986c6c1738bb18218415c908df11a20719bed8', role: 'member' }
|
||||
};
|
||||
|
||||
const user = users[this.username]; //改成用户名
|
||||
|
||||
if (user && user.password === this.password) {
|
||||
// Store user info in localStorage
|
||||
localStorage.setItem('userToken', user.token);
|
||||
localStorage.setItem('userName', this.username); //改成用户名
|
||||
localStorage.setItem('userRole', user.role);
|
||||
|
||||
// Redirect to main page
|
||||
this.$router.push('/');
|
||||
} else {
|
||||
// Show error message
|
||||
alert('Invalid username or password');
|
||||
}
|
||||
},
|
||||
handleForgotPassword() {
|
||||
console.log('Forgot password clicked');
|
||||
},
|
||||
contactSupport() {
|
||||
console.log('Contact support clicked');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- <script>
|
||||
export default {
|
||||
name: 'AdminLogin',
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
password: '',
|
||||
rememberMe: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
console.log('Login attempt', {
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
rememberMe: this.rememberMe
|
||||
});
|
||||
},
|
||||
handleForgotPassword() {
|
||||
console.log('Forgot password clicked');
|
||||
},
|
||||
contactSupport() {
|
||||
console.log('Contact support clicked');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script> -->
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #000000;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
|
||||
.logo {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.logo-img {
|
||||
width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-title {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
background-color: #18181b;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #888;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #27272a;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
color: #888;
|
||||
|
||||
input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(#ef4444, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.support-text {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
.support-link {
|
||||
color: #ef4444;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
17
src/main.js
Executable file
@ -0,0 +1,17 @@
|
||||
import Vue from 'vue';
|
||||
import router from './router'
|
||||
import store from './store/'
|
||||
import { VueAxios } from "@/utils/request"
|
||||
import Antd from 'ant-design-vue';
|
||||
import App from './App';
|
||||
import 'ant-design-vue/dist/antd.css';
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
Vue.use(Antd);
|
||||
Vue.use(VueAxios);
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
93
src/router/index.js
Executable file
@ -0,0 +1,93 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import index from '../views/index'
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'UserLogin',
|
||||
component: () => import('@/components/layouts/UserLogin.vue'),
|
||||
meta: { title: "员工登录" }
|
||||
},
|
||||
{
|
||||
path: '/chat',
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/chat'),
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'chat',
|
||||
name: 'chat',
|
||||
meta: { title: "知识库问询" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/chat')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'index',
|
||||
component: () => import('@/components/layouts/BasicLayout'),
|
||||
meta: { title: '知识库' },
|
||||
redirect: '/list',
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'list',
|
||||
meta: { title: "知识库" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/list/list')
|
||||
},
|
||||
{
|
||||
path: 'upload',
|
||||
name: 'upload',
|
||||
meta: { title: "上传文档" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/upload')
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
name: 'detail',
|
||||
meta: { title: "知识库详情" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/detail')
|
||||
},
|
||||
{
|
||||
path: 'history',
|
||||
name: 'history',
|
||||
meta: { title: "对话记录" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/history')
|
||||
},
|
||||
{
|
||||
path: 'historyDetail',
|
||||
name: 'historyDetail',
|
||||
meta: { title: "对话记录详情" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/history/detail')
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
name: 'setting',
|
||||
meta: { title: "对话记录详情" },
|
||||
component: () => import(/* webpackChunkName: "user" */ '@/views/setting')
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const publicPages = ['/login'];
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
const loggedIn = localStorage.getItem('userToken');
|
||||
|
||||
if (authRequired && !loggedIn) {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export default router
|
||||
|
25
src/store/getters.js
Executable file
@ -0,0 +1,25 @@
|
||||
import Vue from 'vue'
|
||||
import { USER_INFO, ENHANCE_PRE, ACCESS_TOKEN, MENU_DATA } from '@/store/mutation-types'
|
||||
const getters = {
|
||||
device: state => state.app.device,
|
||||
theme: state => state.app.theme,
|
||||
color: state => state.app.color,
|
||||
token: state => {state.user.token = Vue.ls.get(ACCESS_TOKEN); return state.user.token},
|
||||
avatar: state => {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
|
||||
username: state => state.user.info.loginName,
|
||||
nickname: state => {state.user.info.realname = Vue.ls.get(USER_INFO).username; return state.user.info.realname},
|
||||
welcome: state => state.user.welcome,
|
||||
permissionList: state => {state.user.permissionList = Vue.ls.get(MENU_DATA); return state.user.permissionList},
|
||||
userInfo: state => {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
|
||||
addRouters: state => state.permission.addRouters,
|
||||
onlAuthFields: state => {return state.online.authFields },
|
||||
enhanceJs:(state) => (code) => {
|
||||
state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
|
||||
return state.enhance.enhanceJs[code]
|
||||
},
|
||||
sysSafeMode: state => state.user.sysSafeMode,
|
||||
uploadStatus: state => state.upload.uploadStatus,
|
||||
savePosition: state => state.position.savePosition,
|
||||
}
|
||||
|
||||
export default getters
|
35
src/store/index.js
Executable file
@ -0,0 +1,35 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
// import app from './modules/app'
|
||||
// import user from './modules/user'
|
||||
// import permission from './modules/permission'
|
||||
// import enhance from './modules/enhance'
|
||||
// import online from './modules/online'
|
||||
// import upload from './modules/upload'
|
||||
// import position from './modules/position'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
// app,
|
||||
// user,
|
||||
// permission,
|
||||
// enhance,
|
||||
// online,
|
||||
// upload,
|
||||
// position,
|
||||
},
|
||||
state: {
|
||||
|
||||
},
|
||||
mutations: {
|
||||
|
||||
},
|
||||
actions: {
|
||||
|
||||
},
|
||||
getters
|
||||
})
|
29
src/store/mutation-types.js
Executable file
@ -0,0 +1,29 @@
|
||||
export const ACCESS_TOKEN = 'Access-Token'
|
||||
export const SIDEBAR_TYPE = 'SIDEBAR_TYPE'
|
||||
export const DEFAULT_THEME = 'DEFAULT_THEME'
|
||||
export const DEFAULT_LAYOUT_MODE = 'DEFAULT_LAYOUT_MODE'
|
||||
export const DEFAULT_COLOR = 'DEFAULT_COLOR'
|
||||
export const DEFAULT_COLOR_WEAK = 'DEFAULT_COLOR_WEAK'
|
||||
export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER'
|
||||
export const DEFAULT_FIXED_SIDEMENU= 'DEFAULT_FIXED_SIDEMENU'
|
||||
export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN'
|
||||
export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
|
||||
export const DEFAULT_MULTI_PAGE = 'DEFAULT_MULTI_PAGE'
|
||||
export const USER_NAME = 'Login_Username'
|
||||
export const MENU_DATA = 'menu_data'
|
||||
export const USER_INFO = 'Login_Userinfo'
|
||||
export const USER_AUTH = 'LOGIN_USER_BUTTON_AUTH'
|
||||
export const SYS_BUTTON_AUTH = 'SYS_BUTTON_AUTH'
|
||||
export const ENCRYPTED_STRING = 'ENCRYPTED_STRING'
|
||||
export const ENHANCE_PRE = 'enhance_'
|
||||
export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA'
|
||||
export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis'
|
||||
export const OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login'
|
||||
export const TENANT_ID = 'TENANT_ID'
|
||||
export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS'
|
||||
//路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||
export const CACHE_INCLUDED_ROUTES = 'CACHE_INCLUDED_ROUTES'
|
||||
export const CONTENT_WIDTH_TYPE = {
|
||||
Fluid: 'Fluid',
|
||||
Fixed: 'Fixed'
|
||||
}
|
37
src/utils/axios.js
Executable file
@ -0,0 +1,37 @@
|
||||
const VueAxios = {
|
||||
vm: {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
install(Vue, router = {}, instance) {
|
||||
if (this.installed) {
|
||||
return;
|
||||
}
|
||||
this.installed = true;
|
||||
|
||||
if (!instance) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('You have to install axios');
|
||||
return;
|
||||
}
|
||||
|
||||
Vue.axios = instance;
|
||||
|
||||
Object.defineProperties(Vue.prototype, {
|
||||
axios: {
|
||||
get: function get() {
|
||||
return instance;
|
||||
}
|
||||
},
|
||||
$http: {
|
||||
get: function get() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
VueAxios,
|
||||
// eslint-disable-next-line no-undef
|
||||
//instance as axios
|
||||
}
|
29
src/utils/index.js
Executable file
@ -0,0 +1,29 @@
|
||||
export function filterObj(obj) {
|
||||
if (!(typeof obj == 'object')) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let key in obj) {
|
||||
if (obj.hasOwnProperty(key)
|
||||
&& (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
export function formatDateTime(datetimeStr, pattern) {
|
||||
const date = new Date(datetimeStr);
|
||||
const formatter = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
pattern
|
||||
});
|
||||
|
||||
return formatter.format(date);
|
||||
}
|
53
src/utils/request.js
Executable file
@ -0,0 +1,53 @@
|
||||
import Vue from 'vue'
|
||||
import axios from 'axios'
|
||||
import store from '@/store'
|
||||
import { VueAxios } from './axios'
|
||||
// import { ACCESS_TOKEN, TENANT_ID } from '@/store/mutation-types'
|
||||
|
||||
/**
|
||||
* 【指定 axios的 baseURL】
|
||||
* 如果手工指定 baseURL: '/jeecg-boot'
|
||||
* 则映射后端域名,通过 vue.config.js
|
||||
* @type {*|string}
|
||||
*/
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: process.env.API, // api base_url
|
||||
timeout: 9000, // 请求超时时间
|
||||
})
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(config => {
|
||||
// const token = Vue.ls.get(ACCESS_TOKEN)
|
||||
// if (token) {
|
||||
// config.headers['Authorization'] = 'Bearer ' + token // 让每个请求携带自定义 token 请根据实际情况自行修改
|
||||
// }
|
||||
return config
|
||||
}, (error) => {
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use((response) => {
|
||||
if (response.data.code !== 200) {
|
||||
Vue.prototype.$Jnotification.error(
|
||||
{ message: '系统提示', description: response.data.message, duration: 4 })
|
||||
}
|
||||
if (response.status == 200) {
|
||||
return response.data
|
||||
}
|
||||
}, () => {
|
||||
return Promise.reject('error')
|
||||
})
|
||||
|
||||
const installer = {
|
||||
vm: {},
|
||||
install (Vue, router = {}) {
|
||||
Vue.use(VueAxios, router, service)
|
||||
},
|
||||
}
|
||||
|
||||
export {
|
||||
installer as VueAxios,
|
||||
service as axios,
|
||||
}
|
13
src/views/HomeViewNew.vue
Executable file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return{
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
2194
src/views/chat/index.vue
Executable file
325
src/views/detail/index.vue
Executable file
@ -0,0 +1,325 @@
|
||||
<template>
|
||||
<div class="detail">
|
||||
<div class="file-list">
|
||||
<div class="head" @click="$router.back()">
|
||||
<a-icon type="left" class="a-icon"/>
|
||||
<span class="title">返回知识库</span>
|
||||
</div>
|
||||
<p class="system-title">文档列表</p>
|
||||
<div class="file-item" v-for="(item, index) in fileList" :key="index" :class="currentFileId==item.id?'active':''"
|
||||
@click="switchFile(item.id)">
|
||||
<a-tooltip placement="top">
|
||||
<template slot="title">
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
<span class="file-name">{{ item.name }}</span>
|
||||
</a-tooltip>
|
||||
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(item.id)">
|
||||
<a-icon type="delete"/>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-content">
|
||||
<div class="file-head">
|
||||
<img src="../../assets/system.png" class="icon-file"/>
|
||||
<p class="file-title">文档内容</p>
|
||||
</div>
|
||||
<div class="file-content-box">
|
||||
<div class="file-content-item" v-for="(item, index) in currentFile" :key="index">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {documentDetail, documentFileDelete, documentFileDetail} from "../../api";
|
||||
|
||||
export default {
|
||||
name: 'detail',
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
fileList: [],
|
||||
currentFileId: '',
|
||||
currentFile: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.id = this.$route.query && (this.$route.query.id)
|
||||
this.getDetail()
|
||||
},
|
||||
methods: {
|
||||
async handleDelete(id) {
|
||||
try {
|
||||
const res = await documentFileDelete({
|
||||
fileId: id,
|
||||
documentId: this.id
|
||||
})
|
||||
if (res.code === 200) {
|
||||
this.$message.success(res.message)
|
||||
await this.getDetail()
|
||||
} else {
|
||||
this.$message.error(res.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
|
||||
},
|
||||
async getDetail() {
|
||||
try {
|
||||
const res = await documentDetail(this.id)
|
||||
if (res.code === 200) {
|
||||
this.fileList = res.data
|
||||
if (this.fileList.length) {
|
||||
this.currentFileId = this.fileList[0].id
|
||||
this.getDocumentFileDetail()
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
},
|
||||
switchFile(id) {
|
||||
this.currentFileId = id
|
||||
this.getDocumentFileDetail()
|
||||
},
|
||||
async getDocumentFileDetail() {
|
||||
try {
|
||||
const result = await documentFileDetail({
|
||||
fileId: this.currentFileId,
|
||||
documentId: this.id
|
||||
})
|
||||
if (result.code === 200) {
|
||||
this.currentFile = result.data
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.detail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
.head {
|
||||
margin-bottom: 14px;
|
||||
text-align: left;
|
||||
margin-top: 12px;
|
||||
|
||||
.a-icon {
|
||||
cursor: pointer;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: Poppins, Poppins;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #a1a1aa;
|
||||
line-height: 19px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
.system-title{
|
||||
font-family: Poppins, Poppins;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
line-height: 23px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background: #18181b;
|
||||
border-radius: 5px;
|
||||
|
||||
.file-item {
|
||||
min-height: 50px;
|
||||
height: 50px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #a1a1aa;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
padding: 0 5px 6px;
|
||||
border-bottom: 1px solid #27272a;
|
||||
|
||||
.file-name {
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
text-align: left;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2; /* 这里是超出几行省略 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
& + .file-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-content {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
background: #18181b;
|
||||
border-radius: 5px;
|
||||
padding: 46px 10px 20px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.file-head{
|
||||
border-bottom: 1px solid #27272a;
|
||||
display: flex;
|
||||
.icon-file{
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.file-title{
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
line-height: 23px;
|
||||
margin-bottom:12px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
color: #000;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-right: 3px solid #1890ff;
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
transition: transform .15s cubic-bezier(.645, .045, .355, 1), opacity .15s cubic-bezier(.645, .045, .355, 1);
|
||||
}
|
||||
}
|
||||
.file-content-box{
|
||||
// flex: 1;
|
||||
height: 480px;
|
||||
padding:16px 14px 24px 20px;
|
||||
overflow-y: scroll;
|
||||
text-align: left;
|
||||
text-indent:2em;
|
||||
margin-top: 20px;
|
||||
border: 1px solid #27272a;
|
||||
color: #ffffff;
|
||||
border-radius: 4px;
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #3f3f46; // 滑块颜色
|
||||
border-radius: 5px; // 滑块圆角
|
||||
}
|
||||
.file-content-item {
|
||||
padding: 10px 0;
|
||||
|
||||
& + .file-content-item {
|
||||
border-top: 1px dashed #27272a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//删除pop-up样式
|
||||
:deep(.ant-popover) {
|
||||
.ant-popover-content {
|
||||
.ant-popover-inner {
|
||||
background-color: #18181b !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
|
||||
|
||||
.ant-popover-inner-content {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.ant-popover-message {
|
||||
color: #ffffff !important;
|
||||
|
||||
.anticon-exclamation-circle {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-popover-buttons {
|
||||
.ant-btn {
|
||||
&.ant-btn-default {
|
||||
background: #3f3f46 !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover {
|
||||
background: #52525b !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-btn-primary {
|
||||
background: #ef4444 !important;
|
||||
border-color: #ef4444 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover {
|
||||
background: #dc2626 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-popover-arrow {
|
||||
&::after {
|
||||
background-color: #18181b !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
130
src/views/history/detail.vue
Executable file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="head">
|
||||
<a-icon type="arrow-left" class="a-icon" @click="$router.back()"/>
|
||||
<span class="title">对话记录详情</span>
|
||||
</div>
|
||||
<div>
|
||||
<a-table
|
||||
ref="table"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="false"
|
||||
:loading="loading"
|
||||
bordered
|
||||
>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {addDataset, deleteDataset, getChatRecord, getChatRecordByChatId, listPage} from "../../api";
|
||||
import moment from "moment";
|
||||
import {filterObj, formatDate, formatDateTime} from "../../utils";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
dataSource: [],
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: '用户',
|
||||
align: "center",
|
||||
dataIndex: 'problem_text',
|
||||
ellipsis: true,
|
||||
customRender: (text) => (
|
||||
<a-tooltip placement="topLeft" title={text}>
|
||||
<span>{text}</span>
|
||||
</a-tooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '回复',
|
||||
align: "center",
|
||||
dataIndex: 'answer_text',
|
||||
ellipsis: true,
|
||||
customRender: (text) => (
|
||||
<a-tooltip placement="topLeft" title={text}>
|
||||
<span>{text}</span>
|
||||
</a-tooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: "center",
|
||||
dataIndex: 'create_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: "center",
|
||||
dataIndex: 'update_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
}
|
||||
],
|
||||
chatId: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.chatId = this.$route.query && (this.$route.query.chat_id)
|
||||
if (this.chatId) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await getChatRecordByChatId({chatId: this.chatId})
|
||||
if (result.code === 200) {
|
||||
this.dataSource = result.data
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/deep/ .ant-table {
|
||||
background: #fff;
|
||||
|
||||
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
|
||||
}
|
||||
|
||||
.head {
|
||||
text-align: left;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
146
src/views/history/index.vue
Executable file
@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div>
|
||||
<p class="title">对话记录</p>
|
||||
<div>
|
||||
<a-table
|
||||
ref="table"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="false"
|
||||
:loading="loading"
|
||||
bordered
|
||||
>
|
||||
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<a-button type="link" @click="detail(record.chat_id)">详情</a-button>
|
||||
</span>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {addDataset, deleteDataset, getChatRecord, listPage} from "../../api";
|
||||
import moment from "moment";
|
||||
import {filterObj, formatDate, formatDateTime} from "../../utils";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
dataSource: [],
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: '摘要',
|
||||
align: "center",
|
||||
dataIndex: 'abstract'
|
||||
},
|
||||
{
|
||||
title: '聊天记录条数',
|
||||
align: "center",
|
||||
dataIndex: 'chat_record_count'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: "center",
|
||||
dataIndex: 'create_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: "center",
|
||||
dataIndex: 'update_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
width: 80,
|
||||
scopedSlots: {customRender: 'action'},
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
detail(chat_id) {
|
||||
this.$router.push({
|
||||
path: '/historyDetail',
|
||||
query: {
|
||||
chat_id
|
||||
}
|
||||
})
|
||||
},
|
||||
async loadData() {
|
||||
var params = {
|
||||
history_day: 7
|
||||
}//查询条件
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await getChatRecord(params)
|
||||
if (result.code === 200) {
|
||||
this.dataSource = result.data
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/deep/ .ant-table {
|
||||
background: #fff;
|
||||
|
||||
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-right: 3px solid #1890ff;
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
transition: transform .15s cubic-bezier(.645, .045, .355, 1), opacity .15s cubic-bezier(.645, .045, .355, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.head {
|
||||
text-align: left;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
130
src/views/index/index.vue
Executable file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<template v-if="isWebPage">
|
||||
<div class="main-head" >
|
||||
<img src="../../assets/logo-icon.png" alt="" class="logo">
|
||||
<div class="right">
|
||||
<a-icon type="search" class="icon-ball" />
|
||||
<a-icon type="bell" class="icon-ball" />
|
||||
<img src="../../assets/logo-icon.png" alt="" class="icon-user">
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click="e => e.preventDefault()">
|
||||
Marci Fumons <a-icon type="down" />
|
||||
</a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">退出登陆</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;">消息</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-con">
|
||||
<div class="main-menu">
|
||||
<a-menu style="width: 256px" :default-selected-keys="['1']" :default-open-keys="['sub1']" mode="inline"
|
||||
:selected-keys="[current]" @click="handleClick">
|
||||
<a-menu-item key="1">
|
||||
<a-icon type="profile" />
|
||||
知识库
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2">
|
||||
<a-icon type="setting" />
|
||||
设置
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="main-right">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<router-view />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: '1',
|
||||
isWebPage:true,
|
||||
menuList: [
|
||||
{ name: '知识库', path: '/list' },
|
||||
{ name: '对话记录', path: '/history' },
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
handleClick(e) {
|
||||
console.log('click ', e);
|
||||
this.current = e.key;
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.main-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #E6EDFF;
|
||||
padding: 0 50px;
|
||||
box-sizing: border-box;
|
||||
height: 60px;
|
||||
|
||||
.logo {
|
||||
// background: url('../../assets/logo-icon.png') no-repeat center center;
|
||||
// background-size: 100% 100%;
|
||||
width: 110px;
|
||||
height: 34px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-top: 14px;
|
||||
|
||||
.icon-ball {
|
||||
font-size: 16px;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.icon-user {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.main-con {
|
||||
display: flex;
|
||||
|
||||
.main-menu {
|
||||
width: 285px;
|
||||
text-align: left;
|
||||
|
||||
.ant-menu-inline .ant-menu-item {
|
||||
padding-left: 58px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.main-con-right {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
760
src/views/list/list.vue
Executable file
@ -0,0 +1,760 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <p class="welcome">欢迎来到OOIN小助手</p> -->
|
||||
<!-- <p class="welcome-en">Welcome to the The little assistant who speaks to each other.</p> -->
|
||||
<div class="table-con">
|
||||
<div class="head">
|
||||
<a-button type="primary" @click="handleAdd" class="add-btn">创建知识库</a-button>
|
||||
</div>
|
||||
<a-table
|
||||
ref="table"
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="ipagination"
|
||||
:loading="loading"
|
||||
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template slot="status" slot-scope="t">
|
||||
<a-icon type="check-circle" theme="twoTone" two-tone-color="#52c41a"/>
|
||||
<span>{{ t.status }}</span>
|
||||
</template>
|
||||
<template slot="openStatus" slot-scope="t">
|
||||
<a-switch checkedChildren="是" unCheckedChildren="否" v-model="t.openStatus"/>
|
||||
</template>
|
||||
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<a-button type="link" @click="handleUpload(record.id)" style="padding: 0">添加</a-button>
|
||||
<a-button type="link" @click="handleDetail(record.id)" style="padding: 0">详情</a-button>
|
||||
<!-- <a-button type="link" @click="handleChat(record)" style="padding: 0">问询</a-button> -->
|
||||
<a-button type="link" style="padding: 0" @click="handleDel(record.id)">删除</a-button>
|
||||
</span>
|
||||
</a-table>
|
||||
</div>
|
||||
<a-modal :visible="createAddVisible" @cancel="handleCancel" @ok="handleSave"
|
||||
:confirmLoading="confirmLoading">
|
||||
<a-form-model :model="model" ref="form" :rules="validatorRules">
|
||||
<a-form-model-item label="名称" prop="name">
|
||||
<a-input v-model="model.name" placeholder="请输入名称"></a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="描述" prop="desc">
|
||||
<a-input type="textarea" v-model="model.desc" placeholder="请输入描述"></a-input>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
<Upload ref="upload" @uploadSave="uploadSave"></Upload>
|
||||
<a-modal
|
||||
title="提示"
|
||||
:visible="visible"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleDelete"
|
||||
@cancel="handleDelCancel"
|
||||
>
|
||||
<div class="del-box">
|
||||
<a-icon type="info-circle" theme="twoTone" two-tone-color="red" style="font-size: 20px;"/>
|
||||
<p class="del-title">
|
||||
确定删除吗?
|
||||
</p>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {addDataset, deleteDataset, listPage} from "../../api";
|
||||
import moment from "moment";
|
||||
import {filterObj, formatDateTime} from "../../utils";
|
||||
import Upload from '../upload'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
confirmLoading: false,
|
||||
visible:false,
|
||||
delId:'',
|
||||
validatorRules: {
|
||||
name: [
|
||||
{required: true, message: '请输入知识库名称'}
|
||||
],
|
||||
desc: [
|
||||
{required: true, message: '请输入知识库描述'}
|
||||
],
|
||||
},
|
||||
defaultModel: {
|
||||
name: '',
|
||||
desc: '',
|
||||
embedding_mode_id: '42f63a3d-427e-11ef-b3ec-a8a1595801ab'
|
||||
},
|
||||
model: {},
|
||||
createAddVisible: false,
|
||||
createAddUploadVisible:false,
|
||||
|
||||
/* 分页参数 */
|
||||
ipagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
//pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return " 共" + total + "条"
|
||||
},
|
||||
//showSizeChanger: true,
|
||||
total: 0
|
||||
},
|
||||
loading: false,
|
||||
dataSource: [],
|
||||
// 查询条件
|
||||
queryParam: {name: '',},
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: 'NO',
|
||||
key: 'index',
|
||||
width: 80,
|
||||
customRender: (text, record, index) => `${index + 1}`
|
||||
},
|
||||
{
|
||||
title: '知识库名称',
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'desc'
|
||||
},
|
||||
{
|
||||
title: '文档个数',
|
||||
dataIndex: 'document_count'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'create_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'update_time',
|
||||
customRender: (text, record) => {
|
||||
return formatDateTime(text, 'yyyy-MM-dd HH:mm:ss')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: "right",
|
||||
width: 160,
|
||||
scopedSlots: {customRender: 'action'},
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
components: {Upload},
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
handleDel(id){
|
||||
this.delId = id
|
||||
this.visible = true
|
||||
},
|
||||
handleDelCancel(){
|
||||
this.visible = false
|
||||
},
|
||||
uploadSave(){
|
||||
this.loadData()
|
||||
},
|
||||
handleDetail(id) {
|
||||
this.$router.push({
|
||||
path: '/detail',
|
||||
query: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
},
|
||||
handleChat(record) {
|
||||
this.$router.push({
|
||||
path: '/chat',
|
||||
query: {
|
||||
id: record.id
|
||||
}
|
||||
})
|
||||
},
|
||||
getQueryParams() {
|
||||
var param = Object.assign({}, this.queryParam)
|
||||
param.pageNum = this.ipagination.current
|
||||
param.pageSize = this.ipagination.pageSize
|
||||
return filterObj(param)
|
||||
},
|
||||
async loadData(arg) {
|
||||
if (arg === 1) {
|
||||
this.ipagination.current = 1;
|
||||
}
|
||||
var params = this.getQueryParams()//查询条件
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await listPage(params)
|
||||
if (result.code === 200) {
|
||||
this.dataSource = result.data.records.filter(item => item.name !== 'sql');
|
||||
this.ipagination.total = this.dataSource.length;
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleSave() {
|
||||
this.$refs.form.validate(async ok => {
|
||||
if (ok) {
|
||||
this.confirmLoading = true
|
||||
try {
|
||||
const result = await addDataset(this.model)
|
||||
if (result.code === 200) {
|
||||
this.$message.success('新增成功')
|
||||
this.handleCancel()
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.confirmLoading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.createAddVisible = false
|
||||
this.model = Object.assign({}, this.defaultModel)
|
||||
this.loadData()
|
||||
},
|
||||
handleAdd() {
|
||||
this.createAddVisible = true
|
||||
this.model = Object.assign({}, this.defaultModel)
|
||||
},
|
||||
handleUpload(id) {
|
||||
this.$refs.upload.add(id)
|
||||
},
|
||||
async handleDelete() {
|
||||
try {
|
||||
const result = await deleteDataset({id:this.delId})
|
||||
if (result.code === 200) {
|
||||
this.$message.success('删除成功')
|
||||
this.handleDelCancel()
|
||||
} else {
|
||||
this.$message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
handleTableChange(pagination, filters, sorter) {
|
||||
this.ipagination = pagination
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/deep/ .ant-table {
|
||||
background: #18181b; //更改背景颜色
|
||||
|
||||
|
||||
|
||||
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
|
||||
padding: 10px;
|
||||
color: #ffffff; //更改字体颜色
|
||||
}
|
||||
|
||||
|
||||
//update table specific styles
|
||||
|
||||
.ant-table-thead > tr > th {
|
||||
background: #27272a !important;
|
||||
color: #a1a1aa !important;
|
||||
border-bottom: 1px solid #27272a;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr > td {
|
||||
background: #18181b;
|
||||
border-bottom: 1px solid #27272a;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
background: #27272a !important;
|
||||
}
|
||||
|
||||
//pagination style
|
||||
/deep/ .ant-pagination-item {
|
||||
background: #272721;
|
||||
border-color: #3f3f46;
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
&:hover {
|
||||
border-color: #ef4444;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .ant-pagination-item-active {
|
||||
background: #ef4444;
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
//button styles
|
||||
/deep/ .ant-btn-primary {
|
||||
background: #ef4444;
|
||||
border-color: #ef4444;
|
||||
&:hover {
|
||||
background: #dc2626;
|
||||
border-color: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .ant-btn-link {
|
||||
&:first-child {
|
||||
color: #ef4444;
|
||||
&:hover {
|
||||
color: #dc2626;
|
||||
}
|
||||
}
|
||||
&:not(:first-child) {
|
||||
color: #ef4444;
|
||||
&:hover {
|
||||
color: #dc2626;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.welcome{
|
||||
font-family: arial, arial;
|
||||
font-weight: 500;
|
||||
font-size: 28px;
|
||||
color: #ef4444;
|
||||
line-height: 42px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.welcome-en{
|
||||
font-family: arial, arial; //更改字体
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #a1a1aa; //更改字体颜色
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
// .title {
|
||||
// text-align: left;
|
||||
// font-size: 18px;
|
||||
// color: #000;
|
||||
// margin-bottom: 16px;
|
||||
// position: relative;
|
||||
// padding-left: 14px;
|
||||
|
||||
// &::before {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// top: 2px;
|
||||
// height: 20px;
|
||||
// left: 0;
|
||||
// bottom: 0;
|
||||
// border-right: 3px solid #1890ff;
|
||||
// transform: scaleY(1);
|
||||
// opacity: 1;
|
||||
// transition: transform .15s cubic-bezier(.645, .045, .355, 1), opacity .15s cubic-bezier(.645, .045, .355, 1);
|
||||
// }
|
||||
// }
|
||||
.table-con{
|
||||
background: #18181b; //更改背景颜色
|
||||
border-radius: 10px;
|
||||
border: 1px solid #27272a; //添加边框
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
|
||||
.head {
|
||||
text-align: left;
|
||||
margin-bottom: 0 0 32px 0;
|
||||
.add-btn{
|
||||
// width: 80px;
|
||||
height: 34px;
|
||||
background: #ef4444; //更改背景颜色
|
||||
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2); //添加阴影
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// .mr10 {
|
||||
// margin-right: 10px;
|
||||
// }
|
||||
// }
|
||||
// /deep/.ant-table-thead > tr > th{
|
||||
// background: #DCE7FF !important;
|
||||
// }
|
||||
|
||||
|
||||
:deep(.ant-modal) {
|
||||
.ant-modal-content {
|
||||
background-color: #18181b !important;
|
||||
|
||||
.ant-modal-header {
|
||||
background-color: #18181b !important;
|
||||
border-bottom: 1px solid #27272a !important;
|
||||
|
||||
.ant-modal-title {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
background-color: #18181b !important;
|
||||
|
||||
.ant-form-item-label > label {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.ant-input {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #71717a !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-textarea {
|
||||
textarea {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #71717a !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
background-color: #18181b !important;
|
||||
border-top: 1px solid #27272a !important;
|
||||
|
||||
.ant-btn-default {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #3f3f46 !important;
|
||||
border-color: #52525b !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
background-color: #ef4444 !important;
|
||||
border-color: #ef4444 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #dc2626 !important;
|
||||
border-color: #dc2626 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
color: #a1a1aa !important;
|
||||
|
||||
&:hover {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.del-box {
|
||||
display: flex;
|
||||
margin: 0 40px;
|
||||
padding: 20px 0;
|
||||
align-items: center;
|
||||
|
||||
.del-title {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #ffffff;
|
||||
line-height: 19px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
margin-bottom: 0;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
// // Add styles for action buttons
|
||||
// /deep/ {
|
||||
// .ant-btn-link {
|
||||
// &:first-child {
|
||||
// color: #60a5fa; // Blue for view button
|
||||
// &:hover {
|
||||
// color: #3b82f6;
|
||||
// }
|
||||
// }
|
||||
// &:not(:first-child) {
|
||||
// color: #ef4444; // Red for other actions
|
||||
// &:hover {
|
||||
// color: #dc2626;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/deep/ .ant-table-pagination.ant-pagination {
|
||||
//background-color: #18181b;
|
||||
//padding: 16px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
background-color: #27272a !important;
|
||||
border-top: 1px solid #27272a;
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
|
||||
|
||||
// Left side total count
|
||||
.ant-pagination-total-text {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
// New section for prev/next buttons
|
||||
.ant-pagination-prev,
|
||||
.ant-pagination-next {
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
background: #3f3f46;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
margin: 0 4px;
|
||||
|
||||
.ant-pagination-item-link {
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #52525b;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pagination-item {
|
||||
//background-color: #27272a;
|
||||
//border-color: #27272a;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background: #3f3f46;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
margin: 0 4 px;
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #52525b;
|
||||
}
|
||||
|
||||
&.ant-pagination-item-active {
|
||||
background-color: #ef4444;
|
||||
//border-color: #ef4444;
|
||||
|
||||
&:hover {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next and Prev buttons
|
||||
// .ant-pagination-prev,
|
||||
// .ant-pagination-next {
|
||||
// min-width: 32px;
|
||||
// height: 32px;
|
||||
// line-height: 32px;
|
||||
// border-radius: 4px;
|
||||
// background-color: #27272a;
|
||||
// margin: 0 4px;
|
||||
|
||||
// .ant-pagination-item-link {
|
||||
// background-color: transparent;
|
||||
// border: none;
|
||||
// color: #ffffff;
|
||||
// }
|
||||
|
||||
// &:hover {
|
||||
// background-color: #3f3f46;
|
||||
// }
|
||||
// }
|
||||
|
||||
// dropdown
|
||||
.ant-pagination-options {
|
||||
&::after {
|
||||
content: "10 条/页";
|
||||
color: #a1a1aa;
|
||||
margin-left: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
// margin-left: 16px;
|
||||
// background: #ef4444;
|
||||
// border-radius: 4px;
|
||||
// padding: 0 8px;
|
||||
// height: 32px;
|
||||
// line-height: 32px;
|
||||
// //color: #a1a1aa;
|
||||
// .ant-select-selector {
|
||||
// background-color: #ef4444 !important;
|
||||
// border: none !important;
|
||||
// border-radius: 4px !important;
|
||||
// //color: #ffffff !important;
|
||||
// height: 32px !important;
|
||||
// padding: 0 12px !important;
|
||||
// //line-height: 32px !important;
|
||||
// }
|
||||
|
||||
// .ant-select-selection-item {
|
||||
// color: #ffffff !important;
|
||||
// line-height: 30px !important;
|
||||
// }
|
||||
// .ant-select-arrow {
|
||||
// color: #ffffff;
|
||||
// right: 12px;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// All your existing styles remain exactly the same here
|
||||
// Including that closing curly brace }
|
||||
</style>
|
||||
|
||||
<!-- Add this new style block right here -->
|
||||
<style lang="less">
|
||||
.ant-modal {
|
||||
.ant-modal-content {
|
||||
background-color: #18181b !important;
|
||||
|
||||
.ant-modal-header {
|
||||
background-color: #18181b !important;
|
||||
border-bottom: 1px solid #27272a !important;
|
||||
|
||||
.ant-modal-title {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
background-color: #18181b !important;
|
||||
|
||||
.ant-form-item-label > label {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.ant-input {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #71717a !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-textarea {
|
||||
textarea {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover, &:focus {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #71717a !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
background-color: #18181b !important;
|
||||
border-top: 1px solid #27272a !important;
|
||||
|
||||
.ant-btn-default {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #3f3f46 !important;
|
||||
border-color: #52525b !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
background-color: #ef4444 !important;
|
||||
border-color: #ef4444 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #dc2626 !important;
|
||||
border-color: #dc2626 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
color: #a1a1aa !important;
|
||||
|
||||
&:hover {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
37
src/views/setting/index.vue
Executable file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div >
|
||||
<!-- <p class="welcome">欢迎来到OOIN小助手</p> -->
|
||||
<!-- <p class="welcome-en">Welcome to the The little assistant who speaks to each other.</p> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.welcome{
|
||||
font-family: arial, arial;
|
||||
font-weight: 500;
|
||||
font-size: 28px;
|
||||
color: #ef4444;
|
||||
line-height: 42px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.welcome-en{
|
||||
font-family: Poppins, Poppins;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #7C8DB5;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
</style>
|
410
src/views/upload/index.vue
Executable file
@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<a-modal title="添加知识库文档" :visible="createAddUploadVisible" @cancel="handleClose" @ok="handleSave"
|
||||
:confirmLoading="confirmLoading" width="1000px">
|
||||
<div class="upload">
|
||||
<!-- <div class="head" @click="handleClose">
|
||||
<a-icon type="arrow-left"/>
|
||||
<span class="head-title">添加知识库文档</span>
|
||||
</div> -->
|
||||
<div class="content">
|
||||
<div class="content-box">
|
||||
<!-- <p class="title">上传文档</p> -->
|
||||
<a-upload-dragger name="file" :file-list="fileList" ref="file" :accept="accept" :multiple="true" :action="uploadUrl"
|
||||
@change="handleChange">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<a-icon type="inbox" />
|
||||
</p>
|
||||
<p class="ant-upload-text">
|
||||
将文件拖拽至此区域或 <span style="color: #4e7cff;">选择文件上传</span>
|
||||
</p>
|
||||
<p class="ant-upload-hint">
|
||||
支持格式: DOCX、DOC、XLS、XLSX、PDF、PPT、PPTX
|
||||
</p>
|
||||
</a-upload-dragger>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="file-head">
|
||||
<img src="../../assets/system.png" class="icon-file" />
|
||||
<p class="file-title">文档内容</p>
|
||||
</div>
|
||||
<div class="file-box">
|
||||
<template v-if="fileList.length">
|
||||
<div class="file-item" v-for="(item, index) in fileList"
|
||||
v-if="item.status == 'done' && item.response.code == 200" :key="index">
|
||||
<span class="name">{{ item.response.data[0].name }}</span>
|
||||
<div class="file-content" v-for="(item1, index1) in item.response.data[0].content" :key="index1">
|
||||
<span class="title">{{ item1.title }}</span>
|
||||
<span class="content">{{ item1.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="default" v-else>
|
||||
<div class="default-box">
|
||||
<img src="../../assets/default.png" />
|
||||
<p class="default-title">暂无内容</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="footer">
|
||||
<a-button class="mr20" @click="handleClose">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">确定</a-button>
|
||||
</div> -->
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addDocument } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
uploadUrl: `${process.env.API}/api/dataset/document/split`,
|
||||
fileList: [],
|
||||
accept: '.docx,.doc,.pdf,.ppt,.pptx,xls,.xlsx',
|
||||
confirmLoading: false,
|
||||
createAddUploadVisible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add(id) {
|
||||
this.createAddUploadVisible = true;
|
||||
this.id = id
|
||||
this.fileList = []
|
||||
},
|
||||
handleChange(info) {
|
||||
const status = info.file.status;
|
||||
this.fileList = info.fileList;
|
||||
if (status !== 'uploading') {
|
||||
}
|
||||
if (status === 'done') {
|
||||
} else if (status === 'error') {
|
||||
this.$message.error(`${info.file.name} 上传失败.`);
|
||||
}
|
||||
},
|
||||
handleSave() {
|
||||
if(!this.fileList.length) return this.$message.error('请上传知识库文档!');
|
||||
let id = this.id
|
||||
if (!id) {
|
||||
this.$message.error('该知识不存在');
|
||||
return
|
||||
}
|
||||
let totalNum = this.fileList.length;
|
||||
let num = 0
|
||||
this.fileList.forEach(async item => {
|
||||
if (item.status == 'done' && item.response.code == 200) {
|
||||
try {
|
||||
let params = {
|
||||
id,
|
||||
name: item.response.data[0].name,
|
||||
paragraphs: item.response.data[0].content
|
||||
}
|
||||
const result = await addDocument(params)
|
||||
if (result.code == 200) {
|
||||
this.$message.success(`${item.response.data[0].name} 添加成功`);
|
||||
this.$emit('uploadSave')
|
||||
this.handleClose()
|
||||
} else {
|
||||
this.$message.error(`${item.response.data[0].name} 添加失败`);
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
num++
|
||||
if (num == totalNum) {
|
||||
setTimeout(() => {
|
||||
// this.$router.go(-1)
|
||||
this.createAddUploadVisible = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleClose() {
|
||||
this.createAddUploadVisible = false
|
||||
this.fileList = []
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
/deep/ .ant-modal {
|
||||
.ant-modal-content {
|
||||
background-color: #18181b !important;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
background-color: #18181b !important;
|
||||
border-bottom: 1px solid #27272a !important;
|
||||
.ant-modal-title {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
background-color: #27272a !important;
|
||||
border-top: 1px solid #27272a !important;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .ant-upload.ant-upload-drag {
|
||||
background-color: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
|
||||
&:hover {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
.ant-upload-text {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.ant-upload-hint {
|
||||
color: #a1a1aa !important;
|
||||
}
|
||||
|
||||
.ant-upload-drag-icon {
|
||||
color: #a1a1aa !important;
|
||||
}
|
||||
}
|
||||
|
||||
.upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 360px;
|
||||
overflow-y: auto;
|
||||
|
||||
.head {
|
||||
background: #fafafa;
|
||||
text-align: left;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
|
||||
.head-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&>.content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
margin: 0 auto;
|
||||
background: #18181b;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.right {
|
||||
width: 400px;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.file-head {
|
||||
border-bottom: 1px solid #27272a;
|
||||
display: flex;
|
||||
|
||||
.icon-file {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.file-title {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
margin-bottom: 12px;
|
||||
line-height: 23px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
// & > .title {
|
||||
// text-align: left;
|
||||
// font-size: 16px;
|
||||
// color: #000;
|
||||
// margin-bottom: 16px;
|
||||
// position: relative;
|
||||
// padding-left: 14px;
|
||||
|
||||
// &::before {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// top: 2px;
|
||||
// height: 20px;
|
||||
// left: 0;
|
||||
// bottom: 0;
|
||||
// border-right: 3px solid #1890ff;
|
||||
// transform: scaleY(1);
|
||||
// opacity: 1;
|
||||
// transition: transform .15s cubic-bezier(.645, .045, .355, 1), opacity .15s cubic-bezier(.645, .045, .355, 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
.file-box {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
position: relative;
|
||||
margin-top: 16px;
|
||||
border: 1px solid #27272a;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #3f3f46; // 滑块颜色
|
||||
border-radius: 5px; // 滑块圆角
|
||||
}
|
||||
|
||||
.file-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
|
||||
.name {
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.file-content {
|
||||
color: #a1a1aa;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-indent: 2em;
|
||||
|
||||
&+.file-content {
|
||||
border-top: 1px dashed #27272a;
|
||||
}
|
||||
.content {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 10px 20px;
|
||||
text-align: right;
|
||||
background: #fafafa;
|
||||
|
||||
.mr20 {
|
||||
margin-right: 20px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.default {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
|
||||
.default-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
/* 使图片顶部在容器中垂直居中 */
|
||||
left: 50%;
|
||||
/* 使图片左侧在容器中水平居中 */
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
img{
|
||||
width: 120px;
|
||||
height: 65px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.default-title{
|
||||
font-family: PingFang HK, PingFang HK;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: #a1a1aa;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//删除pop-up样式
|
||||
:deep(.ant-popover) {
|
||||
.ant-popover-content {
|
||||
.ant-popover-inner {
|
||||
background-color: #18181b !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
|
||||
|
||||
.ant-popover-inner-content {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.ant-popover-message {
|
||||
color: #ffffff !important;
|
||||
|
||||
.anticon {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-popover-buttons {
|
||||
.ant-btn {
|
||||
&.ant-btn-default {
|
||||
background: #27272a !important;
|
||||
border-color: #3f3f46 !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&:hover {
|
||||
background: #3f3f46 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-btn-primary {
|
||||
background: #ef4444 !important;
|
||||
border-color: #ef4444 !important;
|
||||
|
||||
&:hover {
|
||||
background: #dc2626 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-popover-arrow {
|
||||
&::after {
|
||||
background-color: #18181b !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|