forked from kristjan/cjvt-valency
frontend_vue -- set up dev/prod container
This commit is contained in:
parent
2d4a6a152a
commit
e5b6f02abd
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
data/samples/
|
||||
*egg-info/
|
||||
*.pyc
|
||||
src/frontend_vue/node_modules/
|
||||
|
|
21
src/frontend_vue/README.md
Normal file
21
src/frontend_vue/README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# vue_frontend
|
||||
|
||||
> Frontend for Valency App.
|
||||
|
||||
## 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
src/frontend_vue/build/build.js
Normal file
41
src/frontend_vue/build/build.js
Normal 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
src/frontend_vue/build/check-versions.js
Normal file
54
src/frontend_vue/build/check-versions.js
Normal 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
src/frontend_vue/build/logo.png
Normal file
BIN
src/frontend_vue/build/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
101
src/frontend_vue/build/utils.js
Normal file
101
src/frontend_vue/build/utils.js
Normal 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,
|
||||
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 || '',
|
||||
icon: path.join(__dirname, 'logo.png')
|
||||
})
|
||||
}
|
||||
}
|
22
src/frontend_vue/build/vue-loader.conf.js
Normal file
22
src/frontend_vue/build/vue-loader.conf.js
Normal 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
src/frontend_vue/build/webpack.base.conf.js
Normal file
82
src/frontend_vue/build/webpack.base.conf.js
Normal 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
src/frontend_vue/build/webpack.dev.conf.js
Executable file
95
src/frontend_vue/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
src/frontend_vue/build/webpack.prod.conf.js
Normal file
145
src/frontend_vue/build/webpack.prod.conf.js
Normal 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
|
3
src/frontend_vue/config/config.json
Normal file
3
src/frontend_vue/config/config.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"api_addr": "http://localhost:5004"
|
||||
}
|
3
src/frontend_vue/config/config_dev.json
Normal file
3
src/frontend_vue/config/config_dev.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"api_addr": "http://localhost:5004"
|
||||
}
|
3
src/frontend_vue/config/config_pro.json
Normal file
3
src/frontend_vue/config/config_pro.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"api_addr": "http://193.2.76.103:5004"
|
||||
}
|
7
src/frontend_vue/config/dev.env.js
Normal file
7
src/frontend_vue/config/dev.env.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
'use strict'
|
||||
const merge = require('webpack-merge')
|
||||
const prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"'
|
||||
})
|
69
src/frontend_vue/config/index.js
Normal file
69
src/frontend_vue/config/index.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
'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: {},
|
||||
|
||||
// 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: '/',
|
||||
|
||||
/**
|
||||
* 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'],
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
4
src/frontend_vue/config/prod.env.js
Normal file
4
src/frontend_vue/config/prod.env.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
'use strict'
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
12
src/frontend_vue/index.html
Normal file
12
src/frontend_vue/index.html
Normal 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>vue_frontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
10853
src/frontend_vue/package-lock.json
generated
Normal file
10853
src/frontend_vue/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
67
src/frontend_vue/package.json
Normal file
67
src/frontend_vue/package.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "vue_frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "Frontend for Valency App.",
|
||||
"author": "voje <kristjan.voje@gmail.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": {
|
||||
"axios": "^0.18.0",
|
||||
"bootstrap-vue": "^2.0.0-rc.11",
|
||||
"sha256": "^0.2.0",
|
||||
"vue": "^2.5.2",
|
||||
"vue-cookies": "^1.5.6",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-spinner": "^1.0.3"
|
||||
},
|
||||
"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",
|
||||
"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"
|
||||
]
|
||||
}
|
1
src/frontend_vue/postcss.config.js
Normal file
1
src/frontend_vue/postcss.config.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = {};
|
9
src/frontend_vue/src/App.vue
Normal file
9
src/frontend_vue/src/App.vue
Normal file
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<router-view/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
}
|
||||
</script>
|
206
src/frontend_vue/src/components/EditSenses.vue
Normal file
206
src/frontend_vue/src/components/EditSenses.vue
Normal file
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<p class="pb-0 mb-0">Urejanje pomenov za besedo: <b>{{ hw }}</b>.</p>
|
||||
<p><small>
|
||||
Z miško kliknite na poved, nato kliknite na pomen, ki ga želite dodeliti povedi. Par poved‒pomen bo obarvan z modro. Pare lahko shranite s klikom na gumb "Shrani". Možno je dodajanje poljubnih pomenov.
|
||||
</small></p>
|
||||
<button v-on:click="cancel_all">Prekliči</button>
|
||||
<button v-on:click="save_all">Shrani</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<!-- left column: sentences -->
|
||||
<div class="my-sent col-sm-6">
|
||||
<div
|
||||
v-for="(sentence, ssj_id) in sentences"
|
||||
v-on:click="pick_ssj_id(ssj_id)"
|
||||
class="border rounded my-sentences my-pointer"
|
||||
v-bind:class="{
|
||||
'border-primary': ssj_id === picked_ssj_id
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
v-for="(word, index) in sentence.words"
|
||||
v-bind:class="{
|
||||
'text-primary': index === parseInt(sentence.hw_idx)
|
||||
}"
|
||||
>
|
||||
<span v-if="$root.mkspace(index, word)"> </span>{{ word }}
|
||||
</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col-sm-12"><small>
|
||||
<div v-if="ssj_id in local_sense_map">
|
||||
<Sense v-bind:sense="local_sense_map[ssj_id].sense"></Sense>
|
||||
</div>
|
||||
<div v-else>
|
||||
<Sense v-bind:sense="undefined"></Sense>
|
||||
</div>
|
||||
</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right column: senses -->
|
||||
<div class="col-sm-6 border rounded my-div-scroll sticky-top">
|
||||
<div
|
||||
v-for="sense in local_senses"
|
||||
class="my-pointer"
|
||||
v-on:click="picked_sense_id = sense.sense_id"
|
||||
v-bind:class="{
|
||||
'text-primary': sense.sense_id === picked_sense_id
|
||||
}"
|
||||
>
|
||||
<Sense v-bind:sense="sense"></Sense>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<textarea class="my-textarea" v-model="new_sense_desc"></textarea>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<button v-on:click="new_sense">Dodaj pomen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sense from "./Sense"
|
||||
export default {
|
||||
name: "EditSenses",
|
||||
props: ["hw", "sentences", "sens"],
|
||||
components: {
|
||||
Sense: Sense
|
||||
},
|
||||
data () { return {
|
||||
picked_ssj_id: null,
|
||||
picked_sense_id: null,
|
||||
local_senses: [], // make changes on a local copy
|
||||
local_sense_map: {}, // make changes on a local copy
|
||||
new_sense_desc: "",
|
||||
new_senses: [], // only send changes to server
|
||||
delta_sense_map: {}, // only send changes to server
|
||||
}},
|
||||
created: function() {
|
||||
// not sure if needed, maybe move to data()
|
||||
this.local_senses = this.sens.senses
|
||||
var json = JSON.stringify(this.sens.sense_map)
|
||||
this.local_sense_map = JSON.parse(json)
|
||||
for (var ssj_id in this.local_sense_map) {
|
||||
this.local_sense_map[ssj_id].sense = this.sense_id_to_sense(
|
||||
this.local_sense_map[ssj_id].sense_id)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
picked_ssj_id: function() {
|
||||
this.new_link()
|
||||
},
|
||||
picked_sense_id: function() {
|
||||
this.new_link()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pick_ssj_id: function(ssj_id) {
|
||||
this.picked_ssj_id = ssj_id
|
||||
if (ssj_id in this.local_sense_map) {
|
||||
this.picked_sense_id = this.local_sense_map[ssj_id].sense_id
|
||||
}
|
||||
|
||||
},
|
||||
new_link: function() {
|
||||
if (this.picked_ssj_id === null ||
|
||||
this.picked_sense_id === null) { return }
|
||||
this.local_sense_map[this.picked_ssj_id] = {
|
||||
sense_id: this.picked_sense_id,
|
||||
sense: this.sense_id_to_sense(this.picked_sense_id)
|
||||
}
|
||||
this.delta_sense_map[this.picked_ssj_id] = { sense_id: this.picked_sense_id }
|
||||
},
|
||||
new_sense: function(sense_id) {
|
||||
if (this.new_sense_desc === "") {
|
||||
return
|
||||
}
|
||||
var new_sense = {
|
||||
hw: this.hw,
|
||||
author: this.$root.store.username,
|
||||
desc: this.new_sense_desc,
|
||||
sense_id: "tmp_sense_id" + (new Date().getTime()),
|
||||
}
|
||||
this.local_senses.push(new_sense)
|
||||
this.new_senses.push(new_sense)
|
||||
this.new_sense_desc = ""
|
||||
},
|
||||
sense_id_to_sense: function(sense_id) {
|
||||
for (var i=0; i<this.local_senses.length; i++) {
|
||||
if (this.local_senses[i].sense_id === sense_id) {
|
||||
return this.local_senses[i]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
cancel_all: function() {
|
||||
this.$parent.state = "normal"
|
||||
},
|
||||
save_all: function() {
|
||||
const data = {
|
||||
token: this.$root.store.token,
|
||||
hw: this.hw,
|
||||
sense_map: this.delta_sense_map,
|
||||
new_senses: this.new_senses,
|
||||
}
|
||||
var component = this
|
||||
function exit_edit(component) {
|
||||
component.$parent.state = "normal"
|
||||
component.$parent.request_reload = true
|
||||
}
|
||||
|
||||
// don't update if there are no changes
|
||||
if (
|
||||
Object.keys(data.sense_map).length === 0 &&
|
||||
data.new_senses.length === 0
|
||||
) { exit_edit(component); return }
|
||||
|
||||
// exit after update
|
||||
this.$http.post(
|
||||
this.$root.store.api_addr + "/api/senses/update",
|
||||
data,
|
||||
{ headers: {
|
||||
'Content-type': 'application/json',
|
||||
}}
|
||||
).then(function () {
|
||||
exit_edit(component)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.my-div-scroll {
|
||||
margin-top: 5px;
|
||||
height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.my-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.my-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
.my-sentences {
|
||||
margin: 5px 0px 20px 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
.my-sent {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.my-sent span {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
208
src/frontend_vue/src/components/Frame.vue
Normal file
208
src/frontend_vue/src/components/Frame.vue
Normal file
|
@ -0,0 +1,208 @@
|
|||
<template>
|
||||
<!-- clicking on empty space clears highlights -->
|
||||
<div v-on:click="clearOnClick" class="container-fluid">
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
št. povedi: {{ frameData.sentences.length }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--frame slots-->
|
||||
<div class="row my-frames">
|
||||
<div class="col-sm-12">
|
||||
<span v-for="(slot, key) in frameData.slots">
|
||||
<span
|
||||
v-bind:class="{
|
||||
'my-pointer text-danger': hasHoverTid(idx=key),
|
||||
'my-underline text-danger': hasSelTid(idx=key)
|
||||
}"
|
||||
v-on:mouseover="setHid(idx=key)"
|
||||
v-on:mouseleave="setHid()"
|
||||
v-on:click="setSid(idx=key)"
|
||||
>{{ slot.functor }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--sense information-->
|
||||
<div v-if="$root.store.radio === 'three'" class="col-sm-5">
|
||||
<Sense v-bind:sense="getSense()"></Sense>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--sentences-->
|
||||
<div class="row">
|
||||
<!-- fmode: prikaz->udelezenske vloge (drugacno razvrscanje povedi) -->
|
||||
<div v-if="fmode" class="col-sm-12">
|
||||
<div v-for="hw in getAggrHws()">
|
||||
<blockquote v-for="sentence in getAggrSent(hw)">
|
||||
<span class="text-secondary"> {{ hw }}</span><br>
|
||||
<span
|
||||
v-for="(token, index) in sentence"
|
||||
v-bind:class="{
|
||||
'my-pointer text-danger': hasHoverTid(idx=null, tid=token[0]), 'my-underline text-danger': hasSelTid(idx=null, tid=token[0]),
|
||||
'text-primary': isHw(token[0]),
|
||||
}"
|
||||
v-on:mouseover="setHid(idx=null, tid=token[0])"
|
||||
v-on:mouseleave="setHid()"
|
||||
v-on:click="setSid(idx=null, tid=token[0])"
|
||||
v-bind:title="token[1].msd"
|
||||
><span v-if="$root.mkspace(index, token[1].word)"> </span>{{ token[1].word }}</span>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col-sm-12">
|
||||
<blockquote v-for="sentence in frameData.sentences">
|
||||
<span
|
||||
v-for="(token, index) in sentence"
|
||||
v-bind:class="{
|
||||
'my-pointer text-danger': hasHoverTid(idx=null, tid=token[0]), 'my-underline text-danger': hasSelTid(idx=null, tid=token[0]),
|
||||
'text-primary': isHw(token[0]),
|
||||
}"
|
||||
v-on:mouseover="setHid(idx=null, tid=token[0])"
|
||||
v-on:mouseleave="setHid()"
|
||||
v-on:click="setSid(idx=null, tid=token[0])"
|
||||
v-bind:title="token[1].msd"
|
||||
><span v-if="$root.mkspace(index, token[1].word)"> </span>{{ token[1].word }}</span>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sense from "./Sense"
|
||||
export default {
|
||||
name: "Frame",
|
||||
props: {
|
||||
frameData: {},
|
||||
sensData: {},
|
||||
fmode: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data() { return {
|
||||
hid: null, // hover functor index
|
||||
sid: null, // select functor index (click)
|
||||
}},
|
||||
components: {
|
||||
Sense: Sense
|
||||
},
|
||||
watch: {
|
||||
frameData: function () {
|
||||
this.hid = null,
|
||||
this.sid = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setHid: function (idx=null, tid=null) {
|
||||
// calling this functoin without parameters
|
||||
// resets hid
|
||||
if (tid === null) {
|
||||
this.hid = idx
|
||||
return
|
||||
}
|
||||
for (var i=0; i<this.frameData.slots.length; i++) {
|
||||
if (this.frameData.slots[i].tids.includes(tid)) {
|
||||
this.hid = i
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
clearOnClick: function (event) {
|
||||
if (event.target.tagName !== "SPAN") {
|
||||
this.sid = null
|
||||
}
|
||||
},
|
||||
setSid: function (idx=null, tid=null) {
|
||||
this.sid = null
|
||||
if (tid === null) {
|
||||
this.sid = idx
|
||||
return
|
||||
}
|
||||
for (var i=0; i<this.frameData.slots.length; i++) {
|
||||
if (this.frameData.slots[i].tids.includes(tid)) {
|
||||
this.sid = i
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
hasHoverTid: function(idx=null, tid=null) {
|
||||
if (this.hid === null) {
|
||||
return false
|
||||
}
|
||||
if (tid === null) {
|
||||
if (idx == this.hid) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return this.frameData.slots[this.hid].tids.includes(tid)
|
||||
},
|
||||
hasSelTid: function (idx=null, tid=null) {
|
||||
if (this.sid === null) {
|
||||
return false
|
||||
}
|
||||
if (tid === null) {
|
||||
if (idx == this.sid) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return this.frameData.slots[this.sid].tids.includes(tid)
|
||||
},
|
||||
isHw: function (tid) {
|
||||
return this.frameData.tids.includes(tid)
|
||||
},
|
||||
getSense: function () {
|
||||
for (var i in this.sensData.senses) {
|
||||
if (this.sensData.senses[i].sense_id === this.frameData.sense_info.sense_id) {
|
||||
return this.sensData.senses[i]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
getAggrHws: function() {
|
||||
return (Object.keys(this.frameData.aggr_sent)).sort()
|
||||
},
|
||||
getAggrSent: function(hw) {
|
||||
var sentences = []
|
||||
for (var i=0; i<this.frameData.aggr_sent[hw].length; i++) {
|
||||
sentences.push(
|
||||
this.frameData.sentences[this.frameData.aggr_sent[hw][i]]
|
||||
)
|
||||
}
|
||||
return sentences
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.my-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.my-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.my-frames {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
blockquote {
|
||||
background: #ffffff;
|
||||
border-left: 4px solid #ccc;
|
||||
margin: 10px 0px 10px 10px;
|
||||
padding: 0px 0px 0px 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
blockquote span {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
50
src/frontend_vue/src/components/Home.vue
Normal file
50
src/frontend_vue/src/components/Home.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<div>
|
||||
<p
|
||||
v-if="this.$root.store.api_error !== null"
|
||||
class="text-warning"
|
||||
>
|
||||
api_error: {{ this.$root.store.api_error }}
|
||||
</p>
|
||||
<Nav></Nav>
|
||||
<div class="my-home container-fluid">
|
||||
<div class="row">
|
||||
<div id="serach" class="col-sm-2 border-right fill">
|
||||
<LWords v-if="navSS()"></LWords>
|
||||
<LFunctors v-else></LFunctors>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Nav from "./Nav"
|
||||
import LWords from "./LWords"
|
||||
import LFunctors from "./LFunctors"
|
||||
import MainDispl from "./MainDispl"
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
Nav: Nav,
|
||||
LWords: LWords,
|
||||
LFunctors: LFunctors,
|
||||
MainDispl: MainDispl,
|
||||
},
|
||||
methods: {
|
||||
navSS: function () {
|
||||
return this.$root.storeGet("navSS") === "words"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.my-home {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
51
src/frontend_vue/src/components/LFunctors.vue
Normal file
51
src/frontend_vue/src/components/LFunctors.vue
Normal file
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div>
|
||||
<table>
|
||||
<tr v-for="functor in functors">
|
||||
<td><a href="#" v-on:click="selectFunctor(functor)">{{ functor[0] }}</a></td>
|
||||
<td>({{ functor[1] }})</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LWords",
|
||||
props: ["appState"],
|
||||
data() {return {
|
||||
functors: []
|
||||
}},
|
||||
methods: {
|
||||
apiGetFunctors: function () {
|
||||
var component = this
|
||||
this.$http.get(this.$root.store.api_addr + "/api/functors")
|
||||
.then(function(response) {
|
||||
component.$root.store.api_error = null
|
||||
component.functors = response.data
|
||||
})
|
||||
.catch(function(error) {
|
||||
component.$root.store.api_error = error
|
||||
})
|
||||
},
|
||||
selectFunctor: function (functor) {
|
||||
this.$router.push({
|
||||
name: "MainDispl",
|
||||
params: {
|
||||
hw: functor[0],
|
||||
fmode: true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.apiGetFunctors()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
80
src/frontend_vue/src/components/LWords.vue
Normal file
80
src/frontend_vue/src/components/LWords.vue
Normal file
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<div>
|
||||
<select v-model="selectedLetter">
|
||||
<option v-for="letter in alphabet" :value="letter">
|
||||
{{ letter.toUpperCase() }} ({{ getNumWords(letter) }})
|
||||
</option>
|
||||
</select>
|
||||
<table>
|
||||
<tr v-for="word in getWords()">
|
||||
<td><a href="#" v-on:click="selectHw(word)">{{ word[0] }}
|
||||
<span v-if="$root.store.has_se.includes(word[0])">se</span>
|
||||
</a></td>
|
||||
<td>({{ word[1] }})</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LWords",
|
||||
data() {return {
|
||||
alphabet: "abcčdefghijklmnoprsštuvzž",
|
||||
letters: {},
|
||||
selectedLetter: "a"
|
||||
}},
|
||||
methods: {
|
||||
apiGetWords: function() {
|
||||
var component = this
|
||||
this.$http.get(this.$root.storeGet("api_addr") + "/api/words")
|
||||
.then(function(response) {
|
||||
component.$root.store.api_error = null
|
||||
component.$root.store.has_se = response.data["has_se"]
|
||||
component.letters = response.data["sorted_words"]
|
||||
})
|
||||
.catch(function(error) {
|
||||
component.$root.store.api_error = error
|
||||
})
|
||||
},
|
||||
getNumWords: function(letter) {
|
||||
var entry = this.letters[letter]
|
||||
if (entry) {
|
||||
return entry.length
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
getWords: function() {
|
||||
var entry = this.letters[this.selectedLetter]
|
||||
if (entry) {
|
||||
return entry
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
},
|
||||
selectHw: function(word) {
|
||||
this.$router.push({
|
||||
name: "MainDispl",
|
||||
params: {
|
||||
hw: word[0],
|
||||
fmode: false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.apiGetWords()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
120
src/frontend_vue/src/components/Login.vue
Normal file
120
src/frontend_vue/src/components/Login.vue
Normal file
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="col-sm-2">
|
||||
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
|
||||
</div>
|
||||
<div class="ev-login col-sm-4 offset-sm-4">
|
||||
<div class="alert alert-danger" v-if="error">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="text"
|
||||
data-id="login.username"
|
||||
class="form-control js-login__username"
|
||||
placeholder="Uporabnik"
|
||||
v-model="credentials.username"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control js-login__password "
|
||||
placeholder="Geslo"
|
||||
v-model="credentials.password"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
data-id="login.submit"
|
||||
class="btn btn-primary solid blank js-login__submit"
|
||||
@click="submit()"
|
||||
>
|
||||
Prijava<i class="fa fa-arrow-circle-o-right"></i>
|
||||
</button>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<router-link to="/new_pass">Ste pozabili geslo?</router-link>
|
||||
<br>
|
||||
<br>
|
||||
Nov uporabnik?
|
||||
<br>
|
||||
<router-link to="/register">Ustvarite nov račun.</router-link>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Login',
|
||||
data () {
|
||||
return {
|
||||
credentials: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
loggingIn: false,
|
||||
error: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit () {
|
||||
this.error = ""
|
||||
//this.loggingIn = true
|
||||
// Auth.login() returns a promise. A redirect will happen on success.
|
||||
// For errors, use .then() to capture the response to output
|
||||
// error_description (if exists) as shown below:
|
||||
/*
|
||||
this.$auth.login(credentials, 'dashboard').then((response) => {
|
||||
this.loggingIn = false
|
||||
this.error = utils.getError(response)
|
||||
})
|
||||
*/
|
||||
|
||||
if ( this.credentials.username === "" ||
|
||||
this.credentials.password === ""
|
||||
) {
|
||||
this.error = "Izpolnite vsa polja."
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var data = {
|
||||
username: this.credentials.username,
|
||||
password: this.credentials.password
|
||||
}
|
||||
|
||||
var component = this
|
||||
this.$http.post(this.$root.storeGet("api_addr") + "/api/login",
|
||||
data, // the data to post
|
||||
{ headers: {
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
component.$root.store.api_error = null
|
||||
var token = response.data.token
|
||||
if (token === null) {
|
||||
component.error = "Napačno uporabniško ime ali geslo."
|
||||
} else {
|
||||
// set cookies (if the page reloads)
|
||||
component.$root.store.username = component.credentials.username
|
||||
component.$root.store.token = token
|
||||
component.$router.go(-1)
|
||||
component.$cookies.set("valency_token", token, 60*60*48)
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
component.$root.store.api_error = err
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ev-login {
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
256
src/frontend_vue/src/components/MainDispl.vue
Normal file
256
src/frontend_vue/src/components/MainDispl.vue
Normal file
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<!--load mode-->
|
||||
<div v-if="show_loader">
|
||||
<pulse-loader :color="loader_color"></pulse-loader>
|
||||
</div>
|
||||
|
||||
<!--edit mode (button: razvrsti po pomenih)-->
|
||||
<div v-else-if="state === 'editing'" class="container-fluid">
|
||||
<EditSenses
|
||||
v-bind:hw="hw"
|
||||
v-bind:sentences="sentences"
|
||||
v-bind:sens="sens"
|
||||
></EditSenses>
|
||||
</div>
|
||||
|
||||
<!--normal mode-->
|
||||
<div v-else class="container-fluid" id="head">
|
||||
|
||||
<!--header (verb/adjective, radio buttons)-->
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<table>
|
||||
<tr><h4 id="main-displ-hw">{{ hw }}
|
||||
<span v-if="$root.store.has_se.includes(hw)">se</span>
|
||||
</h4></tr>
|
||||
<tr>{{ calcPos() }}</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<table>
|
||||
<tr>Združevanje vezljivostnih vzorcev:</tr>
|
||||
<tr>
|
||||
<label class="radio-inline"><input value="one" v-model="$root.store.radio" v-on:change="reload()" checked="" type="radio" name="optradio">posamezne povedi</label>
|
||||
<label class="radio-inline"><input value="two" v-model="$root.store.radio" v-on:change="reload()" type="radio" name="optradio">skupne udeleženske vloge</label>
|
||||
<label v-if="this.$root.store.navSS === 'words'" class="radio-inline"><input value="three" v-model="$root.store.radio" v-on:change="reload()" type="radio" name="optradio">po meri</label>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--frames-->
|
||||
<div v-if="$root.store.radio === 'three'" class="row">
|
||||
<div class="col-sm-4">
|
||||
<button v-on:click="userEdit">razvrsti po pomenih</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-for="frame in frames">
|
||||
<Frame
|
||||
v-bind:frameData="frame"
|
||||
v-bind:sensData="sens"
|
||||
v-bind:fmode="fmode">
|
||||
</Frame>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Frame from "./Frame"
|
||||
import EditSenses from "./EditSenses"
|
||||
import PulseLoader from 'vue-spinner/src/PulseLoader.vue'
|
||||
export default {
|
||||
name: "MainDispl",
|
||||
components: {
|
||||
Frame: Frame,
|
||||
EditSenses: EditSenses,
|
||||
PulseLoader: PulseLoader,
|
||||
},
|
||||
props: ["hw", "fmode"],
|
||||
data () { return {
|
||||
frames: [],
|
||||
sentences: {},
|
||||
sens: {
|
||||
senses: [],
|
||||
sense_map: {},
|
||||
},
|
||||
state: "loading", // editing, normal
|
||||
request_reload: false,
|
||||
loader_color: "#007bff",
|
||||
}},
|
||||
created: function () {
|
||||
this.reload()
|
||||
},
|
||||
computed: {
|
||||
show_loader: function () {
|
||||
return this.state === "loading" && this.$root.store.api_error !== null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
hw: function () {
|
||||
this.reload()
|
||||
},
|
||||
frames: function () {
|
||||
this.buildSentences()
|
||||
},
|
||||
request_reload: function () {
|
||||
if (this.request_reload) {
|
||||
this.request_reload = false
|
||||
this.reload()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getFFrames: function(functor, reduce_fun=null) {
|
||||
// get frames in functor mode
|
||||
if (functor === null || functor === undefined) return
|
||||
if (reduce_fun === null) {
|
||||
switch (this.$root.store.radio) {
|
||||
case "one":
|
||||
reduce_fun = "reduce_0"
|
||||
break
|
||||
case "two":
|
||||
reduce_fun = "reduce_1"
|
||||
break
|
||||
default:
|
||||
reduce_fun = "reduce_0"
|
||||
break
|
||||
}
|
||||
}
|
||||
var component = this
|
||||
this.$http.get(
|
||||
this.$root.storeGet("api_addr") + "/api/functor-frames" +
|
||||
"?functor=" + functor + "&rf=" + reduce_fun)
|
||||
.then(function (response) {
|
||||
component.$root.store.api_error = null
|
||||
component.frames = response.data.frames
|
||||
component.state = "normal"
|
||||
})
|
||||
.catch(function(error) {
|
||||
component.$root.store.api_error = error
|
||||
})
|
||||
},
|
||||
getFrames: function (hw, reduce_fun=null) {
|
||||
if (hw === null || hw === undefined) return
|
||||
if (reduce_fun === null) {
|
||||
switch (this.$root.store.radio) {
|
||||
case "one":
|
||||
reduce_fun = "reduce_0"
|
||||
break
|
||||
case "two":
|
||||
reduce_fun = "reduce_1"
|
||||
break
|
||||
case "three":
|
||||
reduce_fun = "reduce_5"
|
||||
break
|
||||
}
|
||||
}
|
||||
var component = this
|
||||
this.$http.get(
|
||||
this.$root.storeGet("api_addr") + "/api/frames" +
|
||||
"?hw=" + hw + "&rf=" + reduce_fun)
|
||||
.then(function (response) {
|
||||
component.$root.store.api_error = null
|
||||
component.frames = response.data.frames
|
||||
component.state = "normal"
|
||||
})
|
||||
.catch(function(error) {
|
||||
component.$root.store.api_error = error
|
||||
})
|
||||
},
|
||||
buildSentences: function () {
|
||||
if (this.frames.length == 0) {
|
||||
return
|
||||
}
|
||||
this.sentences = {}
|
||||
for (var fi in this.frames) {
|
||||
for (var si in this.frames[fi].sentences) {
|
||||
var sentence = this.frames[fi].sentences[si]
|
||||
// get ssj_id without .t123
|
||||
var ssj_id = sentence[0][0].split(".")
|
||||
ssj_id.splice(-1, 1) // removes last element
|
||||
ssj_id = ssj_id.join(".")
|
||||
var words = []
|
||||
var hw_idx = -1
|
||||
var tmp_hw = this.hw
|
||||
if (tmp_hw[tmp_hw.length - 1] === "_") {
|
||||
tmp_hw = tmp_hw.substr(0, tmp_hw.length - 1)
|
||||
}
|
||||
for (var i in sentence) {
|
||||
words.push(sentence[i][1].word)
|
||||
if (sentence[i][1].lemma === tmp_hw && hw_idx == -1) {
|
||||
hw_idx = i
|
||||
}
|
||||
}
|
||||
this.sentences[ssj_id] = {
|
||||
hw_idx: hw_idx,
|
||||
words: words
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getSenses: function (hw, callback) {
|
||||
if (hw === null || hw === undefined) {
|
||||
return
|
||||
}
|
||||
var component = this
|
||||
this.$http.get(
|
||||
this.$root.store.api_addr + "/api/senses/get" + "?hw=" + hw)
|
||||
.then(function(response) {
|
||||
// console.log(response.data)
|
||||
component.sens.senses = response.data.senses
|
||||
component.sens.sense_map = response.data.sense_map
|
||||
callback()
|
||||
})
|
||||
},
|
||||
reload: function () {
|
||||
this.state = "loading"
|
||||
this.sentences = {}
|
||||
if (this.$root.store.navSS === "functors") this.getFFrames(this.hw)
|
||||
else {
|
||||
this.getFrames(this.hw)
|
||||
if (this.$root.store.radio === "three") {
|
||||
this.getSenses(this.hw, this.sortBySense)
|
||||
}
|
||||
}
|
||||
this.calcPos()
|
||||
},
|
||||
userEdit: function () {
|
||||
// authenticate the user for this
|
||||
var tthis = this
|
||||
this.$root.checkToken()
|
||||
.then(function (response) {tthis.state = "editing"})
|
||||
.catch(function (err) {alert("Za urejanje je potrebna prijava.")}
|
||||
)
|
||||
},
|
||||
calcPos: function() {
|
||||
var bfmode = this.fmode
|
||||
if (typeof(bfmode) === "string") {
|
||||
bfmode = (bfmode === "true")
|
||||
}
|
||||
if (bfmode) return "udeleženska vloga"
|
||||
else if (this.hw.substr(this.hw.length-1) === "_") return "pridevnik"
|
||||
return "glagol"
|
||||
},
|
||||
sortBySense: function() {
|
||||
// frames with defined senses on top
|
||||
var undefFrames = []
|
||||
var defFrames = []
|
||||
//console.log(Object.keys(this.sens.sense_map))
|
||||
for (var i=0; i<this.frames.length; i++) {
|
||||
var sense_id = this.frames[i].sense_info.sense_id
|
||||
if (sense_id === "nedefinirano") undefFrames.push(this.frames[i])
|
||||
else defFrames.push(this.frames[i])
|
||||
}
|
||||
this.frames = defFrames.concat(undefFrames)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#main-displ-hw {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
</style>
|
74
src/frontend_vue/src/components/Nav.vue
Normal file
74
src/frontend_vue/src/components/Nav.vue
Normal file
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<nav>
|
||||
<b-navbar toggleable="md" type="light" variant="light">
|
||||
<b-navbar-toggle target="nav_collapse"></b-navbar-toggle>
|
||||
<b-navbar-brand>Vezljivostni vzorci slovenskih glagolov</b-navbar-brand>
|
||||
<b-collapse is-nav id="nav_collapse">
|
||||
|
||||
<b-navbar-nav>
|
||||
<b-nav-item-dropdown text="Prikaz" right>
|
||||
<b-dropdown-item v-for="option in search_options"
|
||||
:value="option.val"
|
||||
:key="option.val"
|
||||
v-on:click="setNavSS(option.val)">
|
||||
{{ option.key }}
|
||||
</b-dropdown-item>
|
||||
</b-nav-item-dropdown>
|
||||
</b-navbar-nav>
|
||||
|
||||
<!-- Right aligned nav items -->
|
||||
<b-navbar-nav class="ml-auto" right v-if="this.loggedIn()">
|
||||
<b-nav-item>
|
||||
Uporabnik: {{ this.$root.store.username }}
|
||||
<a href="#" v-on:click="logOut()">(odjava)</a>
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
<b-navbar-nav class="ml-auto" right v-else>
|
||||
<b-nav-item>
|
||||
<router-link to="/register">
|
||||
Registracija
|
||||
</router-link>
|
||||
</b-nav-item>
|
||||
<b-nav-item>
|
||||
<router-link to="/login">
|
||||
Prijava
|
||||
</router-link>
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Nav",
|
||||
props: ["appState"],
|
||||
data() {return {
|
||||
search_options: [
|
||||
{key: "besede", val: "words"},
|
||||
{key: "udeleženske vloge", val: "functors"},
|
||||
],
|
||||
}},
|
||||
methods: {
|
||||
setNavSS(val) {
|
||||
this.$root.store.radio = "one"
|
||||
this.$root.store.navSS = val
|
||||
this.$router.push({
|
||||
name: "Home"
|
||||
})
|
||||
},
|
||||
loggedIn() {
|
||||
return (this.$root.store.token !== null)
|
||||
},
|
||||
logOut() {
|
||||
this.$root.store.token = null
|
||||
this.$root.store.username = null
|
||||
this.$router.push({
|
||||
name: "Home"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
106
src/frontend_vue/src/components/NewPass.vue
Normal file
106
src/frontend_vue/src/components/NewPass.vue
Normal file
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="col-sm-2">
|
||||
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
|
||||
</div>
|
||||
<div class="ev-login col-sm-4 offset-sm-4">
|
||||
<div class="alert alert-danger" v-if="error">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="text"
|
||||
data-id="login.username"
|
||||
class="form-control js-login__username"
|
||||
placeholder="Uporabnik"
|
||||
v-model="credentials.username"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
placeholder="e-pošta"
|
||||
v-model="credentials.email"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>Novo geslo bo poslano na vaš e-poštni naslov.</p>
|
||||
</div>
|
||||
<button
|
||||
data-id="new_pass.submit"
|
||||
class="btn btn-primary solid blank js-login__submit"
|
||||
@click="submit()"
|
||||
>
|
||||
Novo geslo<i class="fa fa-arrow-circle-o-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NewPass',
|
||||
data () {
|
||||
return {
|
||||
credentials: {
|
||||
username: '',
|
||||
email: ''
|
||||
},
|
||||
error: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit () {
|
||||
this.error = ""
|
||||
//this.loggingIn = true
|
||||
// Auth.login() returns a promise. A redirect will happen on success.
|
||||
// For errors, use .then() to capture the response to output
|
||||
// error_description (if exists) as shown below:
|
||||
/*
|
||||
this.$auth.login(credentials, 'dashboard').then((response) => {
|
||||
this.loggingIn = false
|
||||
this.error = utils.getError(response)
|
||||
})
|
||||
*/
|
||||
|
||||
if ( this.credentials.username === "" ||
|
||||
this.credentials.email === ""
|
||||
) {
|
||||
this.error = "Izpolnite vsa polja."
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var data = {
|
||||
username: this.credentials.username,
|
||||
email: this.credentials.email
|
||||
}
|
||||
|
||||
var component = this
|
||||
this.$http.post(this.$root.storeGet("api_addr") + "/api/new_pass",
|
||||
data, // the data to post
|
||||
{ headers: {
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
component.$root.store.api_error = null
|
||||
var confirmation = response.data.confirmation
|
||||
component.$router.push({
|
||||
name: "Home"
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
component.$root.store.api_error = err
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ev-login {
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
138
src/frontend_vue/src/components/Register.vue
Normal file
138
src/frontend_vue/src/components/Register.vue
Normal file
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="col-sm-2">
|
||||
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
|
||||
</div>
|
||||
<div class="ev-login col-sm-4 offset-sm-4">
|
||||
<div class="alert alert-danger" v-if="error">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control js-login__username"
|
||||
placeholder="Uporabnik"
|
||||
v-model="credentials.username"
|
||||
autocomplete="off"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
placeholder="e-pošta"
|
||||
v-model="credentials.email"
|
||||
autocomplete="off"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control js-login__password "
|
||||
placeholder="Geslo"
|
||||
v-model="credentials.password"
|
||||
autocomplete="off"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control js-login__password "
|
||||
placeholder="Ponovite geslo."
|
||||
v-model="credentials.snd_password"
|
||||
autocomplete="off"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary solid blank js-login__submit"
|
||||
@click="submit()"
|
||||
>
|
||||
Registracija<i class="fa fa-arrow-circle-o-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Register',
|
||||
data () { return {
|
||||
credentials: {
|
||||
username: "",
|
||||
password: "",
|
||||
snd_password: "",
|
||||
email: ""
|
||||
},
|
||||
error: ""
|
||||
}},
|
||||
methods: {
|
||||
clearFields () {
|
||||
for (var key in this.credentials) {
|
||||
this.credentials[key] = ""
|
||||
}
|
||||
},
|
||||
checkEmail () {
|
||||
// check? ... todo
|
||||
return true
|
||||
},
|
||||
submit () {
|
||||
//console.log(this.credentials.password)
|
||||
//console.log(this.credentials.snd_password)
|
||||
const credentials = {
|
||||
username: this.credentials.username,
|
||||
password: this.credentials.password
|
||||
}
|
||||
|
||||
// check if fields are full
|
||||
for (var key in this.credentials) {
|
||||
if (credentials[key] === "") {
|
||||
this.error = "Izpolnite vsa polja."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check e-mail
|
||||
if (!this.checkEmail(this.credentials.email)) {
|
||||
this.error = "Preverite e-poštni naslov."
|
||||
return
|
||||
}
|
||||
|
||||
// check passwords
|
||||
if (this.credentials.password !== this.credentials.snd_password) {
|
||||
this.error = "Gesli se ne ujemata."
|
||||
this.credentials.password = ""
|
||||
this.credentials.snd_password = ""
|
||||
return
|
||||
}
|
||||
|
||||
var component = this
|
||||
const post_data = {
|
||||
username: this.credentials.username,
|
||||
password: this.credentials.password,
|
||||
email: this.credentials.email,
|
||||
}
|
||||
this.$http.post(this.$root.storeGet("api_addr") + "/api/register",
|
||||
post_data, // the data to post
|
||||
{ headers: {
|
||||
'Content-type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
component.$router.push({
|
||||
name: "Home"
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
component.$root.store.api_error = err
|
||||
component.error = "Registracija ni uspela."
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ev-login {
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
48
src/frontend_vue/src/components/Sense.vue
Normal file
48
src/frontend_vue/src/components/Sense.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="pb-3">
|
||||
<div v-if="sense === undefined">
|
||||
pomen ni definiran
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>{{ sense.desc }}</span>
|
||||
<br>
|
||||
|
||||
<small class="text-secondary">
|
||||
- {{ sense.author }}
|
||||
{{ gen_id() }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Sense",
|
||||
props: ["sense"],
|
||||
methods: {
|
||||
gen_id: function() {
|
||||
var id_arr = this.sense.sense_id.split("-")
|
||||
var ret = ""
|
||||
if (this.sense.author === "SSKJ") {
|
||||
if (id_arr[1] !== "0") {
|
||||
ret += ("[" + id_arr[1] + ["] "])
|
||||
}
|
||||
if (id_arr[2] !== "0") {
|
||||
ret += ("pomen " + id_arr[2])
|
||||
if (id_arr[3] !== "0") {
|
||||
ret += ("." + id_arr[3])
|
||||
}
|
||||
}
|
||||
if (id_arr[4] === "sopo") {
|
||||
ret += " (sopomenka)"
|
||||
}
|
||||
}
|
||||
// ret = this.sense.sense_id //debugging
|
||||
if (ret.length > 0) {
|
||||
ret = ": " + ret
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
140
src/frontend_vue/src/main.js
Normal file
140
src/frontend_vue/src/main.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
// The Vue build version to load with the `import` command
|
||||
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import VueCookies from "vue-cookies"
|
||||
|
||||
// bootstrap
|
||||
import BootstrapVue from "bootstrap-vue"
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
// ajax
|
||||
import axios from "axios"
|
||||
|
||||
// config
|
||||
import config_data from "../config/config.json"
|
||||
// console.log(config_data)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// cokies
|
||||
Vue.use(VueCookies)
|
||||
|
||||
// bootstrap
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
// CORS
|
||||
// Vue.$http.headers.common['Access-Control-Allow-Origin'] = true
|
||||
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
// hand-made global storage
|
||||
const store = {
|
||||
api_error: null,
|
||||
api_addr: config_data.api_addr,
|
||||
// api_addr: "http://localhost:5004", // development (webpack)
|
||||
// api_addr: "http://193.2.76.103:5004", // production
|
||||
token: null,
|
||||
username: null,
|
||||
navSS: "words",
|
||||
radio: "one",
|
||||
has_se: [], // used for appending (se) to certain verbs
|
||||
}
|
||||
|
||||
const store_methods = {
|
||||
storeSet: function(key, val) {
|
||||
store[key] = val
|
||||
},
|
||||
storeGet: function(key) {
|
||||
// returns undefined if not in dict.
|
||||
// check if (variable)
|
||||
return store[key]
|
||||
}
|
||||
}
|
||||
|
||||
const login_methods = {
|
||||
checkToken: function () {
|
||||
var tthis = this
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (tthis.store.token === null) {
|
||||
tthis.store.username = null
|
||||
reject(false)
|
||||
}
|
||||
var data = {
|
||||
token: tthis.store.token,
|
||||
user: tthis.store.username
|
||||
}
|
||||
tthis.$http.post(tthis.store.api_addr + "/api/token", data,
|
||||
{ headers: {
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
}}
|
||||
)
|
||||
.then(function (response) {
|
||||
tthis.store.api_error = null
|
||||
if (response.data.confirmation) {
|
||||
resolve(true)
|
||||
} else {
|
||||
tthis.store.username = null
|
||||
tthis.store.token = null
|
||||
reject(false)
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
tthis.store.api_error = err
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const other_methods = {
|
||||
routeBack: function() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
mkspace: function (idx, word) {
|
||||
var stopwords = [".", ",", ":", ";"]
|
||||
if (stopwords.includes(word)) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
components: { App },
|
||||
template: '<App/>',
|
||||
data() { return {
|
||||
store: store,
|
||||
}},
|
||||
methods: Object.assign(store_methods, login_methods, other_methods),
|
||||
beforeCreate: function() {
|
||||
document.title = "Vezljivostni vzorci"
|
||||
if (this.$cookies.isKey("valency_token")) {
|
||||
var cookie_token = this.$cookies.get("valency_token")
|
||||
var data = {
|
||||
token: cookie_token,
|
||||
}
|
||||
var component = this
|
||||
this.$http.post(store.api_addr + "/api/token",
|
||||
data, // the data to post
|
||||
{ headers: {
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.data.confirmation) {
|
||||
store.username = response.data.username
|
||||
store.token = cookie_token
|
||||
} else {
|
||||
this.$cookies.remove("valency_token")
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
store.api_error = err
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
48
src/frontend_vue/src/router/index.js
Normal file
48
src/frontend_vue/src/router/index.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Home from "@/components/Home"
|
||||
import Login from "@/components/Login"
|
||||
import Register from "@/components/Register"
|
||||
import NewPass from "@/components/NewPass"
|
||||
import MainDispl from "@/components/MainDispl"
|
||||
import EditSenses from "@/components/EditSenses"
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
mode: "history",
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: "/home"
|
||||
},
|
||||
{
|
||||
path: "/home",
|
||||
name: "Home",
|
||||
component: Home,
|
||||
children: [
|
||||
{
|
||||
path: "words/:hw",
|
||||
name: "MainDispl",
|
||||
component: MainDispl,
|
||||
props: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
name: 'Register',
|
||||
component: Register
|
||||
},
|
||||
{
|
||||
path: '/new_pass',
|
||||
name: 'NewPass',
|
||||
component: NewPass
|
||||
}
|
||||
]
|
||||
})
|
0
src/frontend_vue/static/.gitkeep
Normal file
0
src/frontend_vue/static/.gitkeep
Normal file
3
src/frontend_vue/webpack.config.js
Normal file
3
src/frontend_vue/webpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
proxy: {
|
||||
'*': 'http://localhost:8080'
|
||||
}
|
Loading…
Reference in New Issue
Block a user