create project

pull/35/head
sunface 6 years ago
parent 6ab91b7533
commit 123f177fb3

@ -1,6 +1,4 @@
<div align="center">
<br>
<img alt="DEV" src="https://thepracticaldev.s3.amazonaws.com/i/ro3538by3b2fupbs63sr.png" width="500px">
<h1>DEV Community 👩‍💻👨‍💻</h1>
</div>
@ -28,7 +26,6 @@ Like many open source projects, we require that contributors provide us with a C
Our version of the CLA was adapted from the Microsoft Contributor License Agreement, which they generously made available to the public domain under Creative Commons CC0 1.0 Universal.
Any questions, please refer to our [license FAQ](https://docs.dev.to/licensing/) doc or email yo@dev.to
<br>

@ -0,0 +1,62 @@
// Copyright © 2019 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/thinkindev/im.dev/internal"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "im.dev",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
Run: func(cmd *cobra.Command, args []string) {
internal.Start("conf.yaml")
},
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.im.dev1.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

@ -0,0 +1,12 @@
version: 0.1.0
log_level: debug
listen_addr: ":10086"
cql:
cluster:
- "10.77.64.46:9042"
- "10.77.64.47:9042"
- "10.77.64.48:9042"
connection_num: 10
keyspace: "im_dev"

@ -0,0 +1,12 @@
package internal
import (
"github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/session"
)
func apiHandler(e *echo.Echo) {
// sign-in apis
e.POST("/web/signIn", session.SignIn)
e.POST("/web/signOut", session.SignOut)
}

@ -0,0 +1,7 @@
package ecode
// account
const (
NotSignIn = 1001
NotSignInMsg = "user need sign in"
)

@ -0,0 +1,69 @@
package internal
import (
"time"
"go.uber.org/zap"
"github.com/gocql/gocql"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/thinkindev/im.dev/internal/misc"
)
// Start web server for im.dev ui
func Start(confPath string) {
// init config
misc.InitConf(confPath)
// init logger
misc.InitLog(misc.Conf.LogLevel)
misc.Log.Info("config init ok", zap.Any("config", misc.Conf))
err := connectDatabase()
if err != nil {
if err != nil {
misc.Log.Fatal("connect to cql cluster error", zap.String("error", err.Error()))
}
}
e := echo.New()
e.Pre(middleware.RemoveTrailingSlash())
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{Level: 5}))
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowHeaders: append([]string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, "token"}),
AllowCredentials: true,
}))
// route the request to corresponding api
apiHandler(e)
// start dashboard api server
e.Logger.Fatal(e.Start(misc.Conf.ListenAddr))
}
const (
// ConnectTimeout specify the timeout interval for connecting to cassandra
ConnectTimeout = 30
// ReconnectInterval specify the reconnect interval for connecting to cassandra
ReconnectInterval = 500
)
// connect to cassandra/scyllaDB cluster
func connectDatabase() error {
c := gocql.NewCluster(misc.Conf.CQL.Cluster...)
c.Timeout = ConnectTimeout * time.Second
c.ReconnectInterval = ReconnectInterval * time.Millisecond
c.Keyspace = misc.Conf.CQL.Keyspace
c.NumConns = misc.Conf.CQL.ConnectionNum
session, err := c.CreateSession()
if err != nil {
return err
}
misc.CQL = session
return nil
}

@ -0,0 +1,36 @@
package misc
import (
"io/ioutil"
"log"
"gopkg.in/yaml.v2"
)
// Config for dashboardå
type Config struct {
Version string `yaml:"version"`
LogLevel string `yaml:"log_level"`
ListenAddr string `yaml:"listen_addr"`
CQL struct {
Cluster []string `yaml:"cluster"`
ConnectionNum int `yaml:"connection_num"`
Keyspace string
}
}
// InitConf init the config for dashboard
func InitConf(p string) {
conf := &Config{}
data, err := ioutil.ReadFile(p)
if err != nil {
log.Fatal("load config from file error,", err)
}
err = yaml.Unmarshal(data, &conf)
if err != nil {
log.Fatal("decode config error,", err)
}
Conf = conf
}

@ -0,0 +1,10 @@
package misc
// HTTPResp is the structure for http response
type HTTPResp struct {
ErrCode int `json:"err_code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

@ -0,0 +1,40 @@
package misc
import (
"os"
"strings"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// InitLog the logger interface
func InitLog(level string) {
var lv zapcore.Level
switch strings.ToLower(level) {
case "debug":
lv = zap.DebugLevel
case "info":
lv = zap.InfoLevel
case "warn":
lv = zap.WarnLevel
case "error":
lv = zap.ErrorLevel
}
atom := zap.NewAtomicLevel()
// To keep the example deterministic, disable timestamps in the output.
encoderCfg := zap.NewProductionEncoderConfig()
encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
l := zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(encoderCfg),
zapcore.Lock(os.Stdout),
atom,
), zap.AddCaller())
atom.SetLevel(lv)
Log = l
}

@ -0,0 +1,20 @@
package misc
import (
"encoding/base64"
"github.com/gocql/gocql"
"go.uber.org/zap"
)
// Conf is the global var for config
var Conf *Config
// Log is the global var for log
var Log *zap.Logger
// Base64 is the base64 handler
var Base64 = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
// CQL is the cql session for access cassandra
var CQL *gocql.Session

@ -0,0 +1,55 @@
package session
import (
"net/http"
"strconv"
"sync"
"time"
"github.com/thinkindev/im.dev/internal/ecode"
"github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/misc"
)
// UserInfo contains user's info
type UserInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Token string `json:"token"`
}
var sessions = &sync.Map{}
// SignIn is user's sign-in action
func SignIn(c echo.Context) error {
ui := &UserInfo{"13269", "Sunface", "https://avatars2.githubusercontent.com/u/7036754?s=460&v=4", strconv.FormatInt(time.Now().UnixNano(), 10)}
sessions.Store(ui.Token, ui)
return c.JSON(http.StatusOK, misc.HTTPResp{
Data: ui,
})
}
// SignOut is user's sign-out action
func SignOut(c echo.Context) error {
token := c.Request().Header.Get("token")
sessions.Delete(token)
return c.JSON(http.StatusOK, misc.HTTPResp{})
}
// CheckSignIn checks whether user has signed in
func CheckSignIn(f echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
token := c.Request().Header.Get("token")
_, ok := sessions.Load(token)
if !ok {
return c.JSON(http.StatusUnauthorized, misc.HTTPResp{
ErrCode: ecode.NotSignIn,
Message: ecode.NotSignInMsg,
})
}
return f(c)
}
}

@ -0,0 +1,21 @@
// Copyright © 2019 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "github.com/thinkindev/im.dev/cmd"
func main() {
cmd.Execute()
}

@ -0,0 +1,15 @@
CREATE KEYSPACE im_dev WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'}
AND durable_writes = false;
USE im_dev;
CREATE TABLE IF NOT EXISTS user (
id text,
name text,
PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;
CREATE TABLE IF NOT EXISTS user_activity (
id text,
PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;

@ -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"]
}

@ -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
ui/.gitignore vendored

@ -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

@ -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": {}
}
}

@ -0,0 +1,21 @@
# vue-starter
> A vuejs starter project integrated with vuex, vue-router, less, iview
## 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).

@ -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'
))
})
})

@ -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)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

@ -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')
})
}
}

@ -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'
}
}

@ -0,0 +1,86 @@
'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: /\.less$/,
loader: "style-loader!css-loader!less-loader",
},
{
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'
}
}

@ -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)
}
})
})

@ -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

@ -0,0 +1,8 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
WEB_ADDR: '"http://localhost:10086"'
})

@ -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: 9530, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
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
}
}

@ -0,0 +1,4 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>mafanr.com</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

11708
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,71 @@
{
"name": "mafanr",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "sunface <cto@188.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.17.1",
"element-ui": "^2.11.1",
"js-cookie": "2.2.0",
"vue": "^2.6.10",
"vue-i18n": "^8.11.2",
"vue-router": "^3.0.3",
"vuex": "^3.1.1"
},
"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.23.1",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-loader": "^0.3.0",
"html-webpack-plugin": "^2.30.1",
"less": "^2.7.1",
"less-loader": "^2.2.3",
"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",
"style-loader": "^0.13.1",
"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.6.10",
"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"
]
}

@ -0,0 +1,21 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
}
</style>
<style lang="less" scoped>
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -0,0 +1,11 @@
export default {
nav: {
signIn : 'Sign in',
signInTitle: "Welcome to I'm Dev",
whySignIn: 'Sign in to get personalized story recommendations, follow authors and topics you love, and interact with stories.',
signInFooter: "I'm Dev is 100% open-source, if you are intrest in building developer commnity, please join us in github.",
navSearchHolder: 'Search in Dev',
setLang: 'Language',
setTheme: 'Theme',
}
}

@ -0,0 +1,33 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import eleEN from 'element-ui/lib/locale/lang/en'
import eleZh from 'element-ui/lib/locale/lang/zh-CN'
import enLocale from './en'
import zhLocale from './zh'
Vue.use(VueI18n)
Vue.locale = () => {}
const messages = {
en: {
...enLocale,
...eleEN
},
zh: {
...zhLocale,
...eleZh
}
}
const i18n = new VueI18n({
// set locale
// options: en or zh
locale: Cookies.get('imdev-lang') || 'en',
// set locale messages
messages
})
export default i18n

@ -0,0 +1,11 @@
export default {
nav: {
signIn : '登录',
signInTitle: "欢迎来到I'm Dev开发者社区",
whySignIn: '登录后你可以创建自己的资料、发表文章,接收个性化推荐收藏喜欢的文章、与其它成员互动等',
signInFooter: "I'm Dev的源码是100%开源的,如果你对参与创建个性化社区充满兴趣,欢迎加入我们的组织",
navSearchHolder: 'Search in Dev',
setLang: '选择语言',
setTheme: '选择主题',
}
}

@ -0,0 +1,43 @@
// 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 ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App'
Vue.use(ElementUI);
import router from './router'
// 全局范围加载通用样式每个vue page里无需重复引入
import '!style-loader!css-loader!less-loader!./theme/common_layout.less'
import '!style-loader!css-loader!less-loader!./theme/layout.less'
import '!style-loader!css-loader!less-loader!./theme/style.less'
Vue.config.productionTip = false
import i18n from './lang' // Internationalization
import store from './store'
router.beforeEach((to, _, next) => {
next()
})
router.afterEach(() => {
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
i18n,
components: { App },
template: '<App/>'
})

@ -0,0 +1,20 @@
import Vue from 'vue'
import Router from 'vue-router'
import Nav from '@/views/nav'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: Nav,
children: [
{ path: '/', component: () => import('@/views/home')},
]
},
{ path: '/404', component: () => import('@/views/errorPage/page404')},
{ path: '*', redirect: '/404'}
]
})

@ -0,0 +1,7 @@
/* eslint-disable */
const getters = {
//misc
service: state => state.misc.service
}
export default getters

@ -0,0 +1,18 @@
import Vue from 'vue'
import Vuex from 'vuex'
import misc from './modules/misc'
import user from './modules/user'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
misc,
user
},
getters
})
export default store

@ -0,0 +1,28 @@
import Cookies from 'js-cookie'
const misc = {
state: {
theme: Cookies.get('imdev-theme') || 'light',
lang: Cookies.get('imdev-lang') || 'en',
},
mutations: {
SET_THEME: (state, theme) => {
state.theme = theme
Cookies.set('imdev-theme', theme)
},
SET_LANG: (state, lang) => {
state.lang = lang
Cookies.set('imdev-lang', lang)
}
},
actions: {
setTheme({ commit }, theme) {
commit('SET_THEME', theme)
},
setLang({ commit }, val) {
commit('SET_LANG', val)
}
}
}
export default misc

@ -0,0 +1,62 @@
/* eslint-disable */
import { getToken, setToken, removeToken } from '@/utils/auth'
import request from '@/utils/request'
import Cookies from 'js-cookie'
const user = {
state: {
id: Cookies.get('imdev-userid') || '',
name: Cookies.get('imdev-name') || '',
avatar: Cookies.get('imdev-avatar') || '',
token: getToken() || ''
},
mutations: {
SET_USERID: (state,userID) => {
Cookies.set('imdev-userid', userID)
state.id = userID
},
SET_NAME: (state, name) => {
Cookies.set('imdev-name', name)
state.name = name
},
SET_AVATAR: (state, avatar) => {
Cookies.set('imdev-avatar', avatar)
state.avatar = avatar
},
SET_TOKEN: (state, token) => {
state.token = token
setToken(token)
}
},
actions: {
setToken({ commit }, token) {
commit('SET_TOKEN', token)
},
// SSO登陆成功保存信息
SetUserInfo({ commit,state},userInfo) {
return new Promise(resolve => {
setToken(userInfo.token)
commit('SET_TOKEN', userInfo.token)
commit('SET_USERID', userInfo.id)
commit('SET_NAME', userInfo.name)
commit('SET_AVATAR', userInfo.avatar)
resolve()
})
},
// 登出
SignOut({ commit, state }) {
return request({
url: '/web/signOut',
method: 'post',
params:{
}
}).then(res => {
commit('SET_TOKEN', '')
removeToken()
})
}
}
}
export default user

@ -0,0 +1,165 @@
// layout
.margin-left-5 {
margin-left: 5px
}
.margin-left-10 {
margin-left: 10px
}
.margin-left-15 {
margin-left: 15px
}
.margin-left-20 {
margin-left: 20px
}
.margin-right-5 {
margin-right: 5px
}
.margin-right-10 {
margin-right: 10px
}
.margin-right-15 {
margin-right: 15px
}
.margin-right-20 {
margin-right: 20px
}
.margin-right-30 {
margin-right: 30px
}
.margin-right-40 {
margin-right: 40px
}
.margin-top-5 {
margin-top: 5px
}
.margin-top-10 {
margin-top: 10px
}
.margin-top-15 {
margin-top: 15px
}
.margin-top-20 {
margin-top: 20px !important
}
.padding-5 {
padding: 5px 5px
}
.padding-10 {
padding: 10px 10px
}
.padding-20 {
padding: 20px 20px
}
.padding-left-5 {
padding-left: 5px
}
.padding-left-10 {
padding-left: 10px
}
.padding-left-15 {
padding-left: 15px
}
.padding-left-20 {
padding-left: 20px
}
.padding-right-5 {
padding-right: 5px
}
.padding-right-10 {
padding-right: 10px
}
.padding-right-15 {
padding-right: 15px
}
.padding-right-20 {
padding-right: 20px
}
.padding-top-5 {
padding-top: 5px
}
.padding-top-10 {
padding-top: 10px
}
.padding-top-15 {
padding-top: 15px
}
.padding-top-20 {
padding-top: 20px
}
.padding-top-40 {
padding-top: 40px
}
.padding-bottom-5 {
padding-bottom: 5px
}
.padding-bottom-10 {
padding-bottom: 10px
}
.padding-bottom-15 {
padding-bottom: 15px
}
.padding-bottom-20 {
padding-bottom: 20px
}
.padding-bottom-40 {
padding-bottom: 40px
}
// 鼠标悬浮
.hover-cursor:hover{
cursor: pointer
}
.float-right {
float: right
}
.text-align-center {
text-align:center
}
.middle-inline {
display: inline-block;
vertical-align: middle;
}
.font-size-18 {
font-size:18px;
}
.font-size-20 {
font-size:18px;
}
.font-size-22 {
font-size:18px;
}
.font-size-24 {
font-size:18px;
}

@ -0,0 +1,3 @@
.el-dialog__wrapper.white-bg-modal {
background-color: rgba(255,255,255,.9);
}

@ -0,0 +1,8 @@
@text-default-color: rgba(0, 0, 0, .84);
@text-second-color: rgba(0, 0, 0, .54);
@nav-color: rgba(0,0,0,.70);
@text-enter-more-color: rgb(150, 150, 150);
@text-annotation-color: rgb(180, 180, 180);
@topic-text-color: white;
@topic-annotation-color: rgba(255, 255, 255, .7);
@menu-item-active-color: #f0f0f0;

@ -0,0 +1,6 @@
// overide global focus style
:focus {
outline-color: transparent;
outline-width: 0;
outline-style: none;
}

@ -0,0 +1,15 @@
import Cookies from 'js-cookie'
const TokenKey = 'MF-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}

@ -0,0 +1,63 @@
/* eslint-disable */
// 该模块用来请求API网关
import axios from 'axios'
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth'
import Cookies from 'js-cookie'
// create an axios instance
const service = axios.create({
baseURL: process.env.WEB_ADDR, // api的base_url
timeout: 10000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// 设置token
config.headers['token'] = getToken()
return config
},
error => {
// Do something with request error
Promise.reject(error)
})
// respone interceptor
service.interceptors.response.use(
response => {
return response
},
error => {
var response = error.response
if (response.data.err_code == 1001) {
Message(
{
showClose: true,
message: response.data.message+' : '+ response.data.err_code,
type: 'error'
}
)
return Promise.reject(response.data.message+' : '+ response.data.err_code)
}
if (response.data.err_code != 0) {
Message(
{
showClose: true,
message: response.data.message+' : '+ response.data.err_code,
type: 'error'
}
)
return Promise.reject(response.data.message+' : '+ response.data.err_code)
}
Message.error({
content: error.message,
duration: 3
})
return Promise.reject(error)
})
export default service

@ -0,0 +1,68 @@
<template>
<div class="errPage-container">
404
</div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'page404',
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/' })
} else {
this.$router.go(-1)
}
}
}
}
</script>
<style lang="less" scoped>
.errPage-container {
width: 800px;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

@ -0,0 +1,103 @@
<template>
<div class="home">
<el-row>
<el-col :span="20" :offset="2">
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'Home',
data () {
return {
}
},
watch: {
},
computed: {
},
methods: {
},
mounted() {
}
}
</script>

@ -0,0 +1,253 @@
<template>
<div>
<transition name="fade" >
<el-row v-show="toTop" :class="{'nav':'true','inTop':inTop,'toTop': toTop}" type="flex" align="middle">
<el-col
:xs="{span:4}"
:sm="{span:7,offset:1}"
:md="{span: 2,offset:1}"
:lg="{ span: 2, offset: 2 }"
>
<img src="../assets/logo.png" class="hover-cursor" style="height:45px" />
</el-col>
<el-col
:xs="{span:10,offset:4}"
:sm="{span:8}"
:md="{span: 4}"
:lg="{ span: 6,offset:2}"
class
>
<el-input
v-model="searchContent"
prefix-icon="el-icon-search"
:placeholder="$t('nav.navSearchHolder')"
size="medium"
></el-input>
</el-col>
<el-col :span="10">
<span class="float-right">
<el-popover placement="bottom" trigger="hover" class="margin-right-20">
<el-form label-width="80px">
<el-form-item :label="$t('nav.setLang')">
<el-radio-group v-model="currentLang" size="medium" @change="changeLang">
<el-radio-button label="zh">中文</el-radio-button>
<el-radio-button label="en">English</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('nav.setTheme')">
<el-select
v-model="currentTheme"
placeholder="theme.."
size="medium"
style="width:150px"
@change="changeTheme"
>
<el-option label="Light" value="light"></el-option>
<el-option label="Dark" value="dark"></el-option>
</el-select>
</el-form-item>
</el-form>
<i class="el-icon-more-outline hover-cursor" slot="reference"></i>
</el-popover>
<el-button
v-if="this.$store.state.user.token==''"
type="primary"
@click="signInModalVisible=true"
>{{$t('nav.signIn')}}</el-button>
<!-- <img v-else :src="this.$store.state.user.avatar" style="height:45px;display:inline-block;vertical-align:middle" /> -->
<el-popover v-else placement="bottom" trigger="hover" >
<div class="user-panel font-size-18">
<div>@{{this.$store.state.user.name}}</div>
<el-divider></el-divider>
<div>Dashboard</div>
<div class="margin-top-5">Write a Post</div>
<div class="margin-top-5">Reading List</div>
<div class="margin-top-5">Settings</div>
<el-divider></el-divider>
<div>About im.dev</div>
<el-divider></el-divider>
<div>Sign Out</div>
</div>
<el-avatar
:src="this.$store.state.user.avatar"
slot="reference"
class="middle-inline hover-cursor"
></el-avatar>
</el-popover>
</span>
</el-col>
</el-row>
</transition>
<router-view class="main-view" style="padding-top:60px"></router-view>
<el-dialog class="white-bg-modal sign-in-modal" :visible.sync="signInModalVisible">
<el-row class="sign-in-panel text-align-center padding-top-20 padding-bottom-20">
<el-col :span="10" :offset="7">
<h2 class="padding-top-20">{{$t('nav.signInTitle')}}</h2>
<p class="padding-top-10">{{$t('nav.whySignIn')}}</p>
<div class="padding-top-20">
<el-button type="primary" icon="el-icon-github" @click="signIn">Sign in with Github</el-button>
</div>
<p class="padding-top-20">{{$t('nav.signInFooter')}}</p>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script>
import request from "@/utils/request";
export default {
name: "Nav",
data() {
return {
theme: this.$store.state.misc.theme,
searchContent: "",
signInModalVisible: false,
currentLang: this.$store.state.misc.lang,
currentTheme: this.$store.state.misc.theme,
// nav fixed to top
scrollTop: 0,
toTop : true,
topCount : 0,
inTop : true
};
},
watch: {},
computed: {},
methods: {
signIn() {
request({
url: "/web/signIn",
method: "POST",
params: {}
}).then(res => {
this.$store.dispatch("SetUserInfo", res.data.data).then(() => {
this.signInModalVisible = false;
});
});
},
changeLang(val) {
this.$store.dispatch("setLang", val);
this.$i18n.locale = val;
},
changeTheme(val) {
this.$store.dispatch("setTheme", val);
window.location.reload();
},
loadTheme() {
// vue page
if (this.theme == "light") {
require("!style-loader!css-loader!less-loader!../theme/light/layout.less");
require("!style-loader!css-loader!less-loader!../theme/light/style.less");
} else {
require("!style-loader!css-loader!less-loader!../theme/dark/layout.less");
require("!style-loader!css-loader!less-loader!../theme/dark/style.less");
}
},
handleScroll: function(e) {
var y = window.scrollY
if (y <1) {
if (!this.inTop){
this.inTop = true
}
} else {
if (this.inTop) {
this.inTop = false
}
}
if (y- this.scrollTop < 0) {
if (!this.toTop) {
// \
this.topCount = this.topCount + 1
if (this.topCount >= 9) {
this.toTop = true
this.topCount = 0
}
}
} else {
if (window.scrollY < 30) {
return
}
this.toTop = false
this.topCount = 0
}
this.scrollTop = y
}
},
mounted() {
this.loadTheme();
window.addEventListener('scroll', this.handleScroll);
}
};
</script>
<style lang="less">
@import "../theme/light/var.less";
.sign-in-modal {
.el-dialog__header {
display: none;
}
.el-dialog__body {
padding: 0 0;
padding-bottom: 40px;
}
.sign-in-panel {
height: 100%;
background: url(../assets/login.png) no-repeat;
background-size: 100%;
}
}
.user-panel {
.el-divider {
margin: 13px 0;
}
div {
cursor:pointer;
padding-left:10px
}
}
.fade-enter-active, .fade-leave-active {
transition: all .6s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
// opacity: 0;
transform: translateY(-50px);
}
.fade-leave-active ,.fade-enter-active{
transition: all .4s;
}
</style>
<style lang="less" scoped>
.nav {
top: 0;
width: 100%;
background-color: rgba(255, 255, 255, 0);
z-index: 999;
position: absolute;
// box-shadow: rgba(0, 0, 0, 0.0470588) 0px 4px 12px 0px;
padding-top:8px;
padding-bottom:4px;
}
.nav.toTop {
position: fixed;
box-shadow: rgba(0, 0, 0, 0.0470588) 0px 4px 12px 0px;
background-color: white;
// transition:transform 300ms ease;
// transform: translateY(100px)
}
.nav.inTop {
box-shadow: none;
}
</style>

@ -0,0 +1,68 @@
<template>
<div class="app-container">
<div style="margin-top:300px;" class="margin-left-20">{{hello}} {{world}}</div>
<div style="margin-left:45%">
{{world}}
<div class="margin-left-20">{{getAddr}}</div>
你好中国人
</div>
</div>
</template>
<script>
import request from '@/utils/request' 
export default {
name: 'tmpl',
data () {
return {
hello: '',
world: ''
}
},
watch: {
$route() {
//
},
world(val) {
// world
}
},
computed: {
getAddr() {
return process.env.API_ADDR
}
},
methods: {
},
mounted() {
this.$store.dispatch('setService', 'hello')
this.hello = this.$store.state.misc.service
this.world = this.$t('common.test')
setTimeout(() => {
this.world = "new world"
}, 3000)
request({
url: '/signIn',
method: 'POST',
params: {
user: '111'
}
}).then(res => {
})
}
}
</script>
<style lang="less" scoped>
// @import "../theme/light/var.less";
.margin-left-20 {
margin-left: 100px
}
</style>
Loading…
Cancel
Save