├── .gitignore
├── README.md
├── build_tools
├── build.js
├── check-versions.js
├── dev-client.js
├── dev-server.js
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
├── webpack.prod.conf.js
└── webpack.test.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── deploy
├── index.html
├── package.json
├── src
├── assets
│ ├── img
│ │ ├── add-icon.png
│ │ ├── back-icon.png
│ │ ├── choice-icon.png
│ │ ├── comment-icon.png
│ │ ├── edit-title.png
│ │ ├── email.png
│ │ ├── fine.png
│ │ ├── friendship1.png
│ │ ├── friendship2.png
│ │ ├── friendship3.png
│ │ ├── friendship4.png
│ │ ├── friendship5.png
│ │ ├── friendship6.png
│ │ ├── github.png
│ │ ├── kotlin_logo.png
│ │ ├── logo.png
│ │ ├── logo_big.png
│ │ ├── logo_k.png
│ │ ├── pulldown-icon.png
│ │ ├── register_success.png
│ │ ├── toolbar_Italic.png
│ │ ├── toolbar_a.png
│ │ ├── toolbar_back.png
│ │ ├── toolbar_blod.png
│ │ ├── toolbar_cut_off_rule.png
│ │ ├── toolbar_image.png
│ │ ├── toolbar_next.png
│ │ ├── toolbar_ordered_list.png
│ │ ├── toolbar_paragraph_reference.png
│ │ ├── toolbar_pre.png
│ │ ├── toolbar_title.png
│ │ ├── toolbar_unordered_list.png
│ │ └── ttf.png
│ ├── js
│ │ ├── Cache.js
│ │ ├── Config.js
│ │ ├── Event.js
│ │ ├── LoginMgr.js
│ │ ├── Net.js
│ │ ├── Parse.js
│ │ └── Util.js
│ └── logo.png
├── components
│ ├── ArticleList.vue
│ ├── ArticleMeta.vue
│ ├── ArticleMetaDialog.vue
│ ├── ArticleSideBar.vue
│ ├── Avatar.vue
│ ├── Comment.vue
│ ├── Dialog.vue
│ ├── DisplayPanels.vue
│ ├── Footers.vue
│ ├── Headers.vue
│ ├── HomeLinkTitle.vue
│ ├── Login.vue
│ ├── NameCard.vue
│ ├── Reply.vue
│ ├── ReplyList.vue
│ ├── SideBar.vue
│ ├── TopicBar.vue
│ └── UserBaseInfo.vue
├── componentsMobile
│ ├── ArticleList.vue
│ ├── Drawer.vue
│ └── Login.vue
├── layout
│ ├── AppMobile.vue
│ ├── AppMobileSun.vue
│ ├── AppWeb.vue
│ └── AppWebFixContent.vue
├── main.js
├── router
│ └── routes.js
├── views
│ ├── 404.vue
│ ├── Edit.vue
│ ├── Home.vue
│ ├── Post.vue
│ ├── Topic.vue
│ └── User.vue
└── viewsMobile
│ ├── Comments.vue
│ ├── Edit.vue
│ ├── Home.vue
│ └── Post.vue
├── static
├── .gitkeep
├── css
│ ├── atom-one-dark.min.css
│ ├── font-awesome.min.css
│ ├── github-markdown.css
│ └── reset.scss
├── favicon.ico
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── jquery-3.2.1.min.js
├── js
│ ├── highlight.min.js
│ └── rangeFn.js
├── layer.js
├── skin
│ └── default
│ │ ├── icon-ext.png
│ │ ├── icon.png
│ │ ├── layer.css
│ │ ├── loading-0.gif
│ │ ├── loading-1.gif
│ │ └── loading-2.gif
└── vConsole.js
└── test
└── unit
├── .eslintrc
├── index.js
├── karma.conf.js
└── specs
└── Hello.spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | build
4 | *.iml
5 | *.ipr
6 | *.iws
7 | *.ids
8 | .idea
9 | local.properties
10 | out
11 | !build.release
12 | node_modules
13 | /log
14 | default.etcd
15 | /env
16 | /dist/
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kotlin-cn frontend
2 |
3 | Hi,
4 |
5 | This is the website of kotlin-cn ,[VUEJS](http://vuejs.org/) Frameworks is used .
6 |
7 |
8 | ### Preview
9 |
10 | - Debug: [debug.kotlin-cn.org](https://debug.kotlin-cn.org/)
11 |
12 | - Release: [discuss.kotliner.cn](https://discuss.kotliner.cn/) , [kotlin-cn.org](https://kotlin-cn.org/)
13 |
14 |
15 | ### Getting Started
16 |
17 | - install [npm](https://www.npmjs.com/get-npm)
18 |
19 | - run in local
20 |
21 | ```shell
22 | npm install && npm run dev
23 | ```
24 |
25 | - Check this site in your browser
26 |
27 | > http://localhost:3000/
28 |
29 |
30 | ### Build release version
31 |
32 | Release in local
33 |
34 | ```shell
35 | npm run build
36 | ```
37 |
38 | more about npm and VueJs
39 |
40 | checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
41 |
42 | ``` bash
43 | # install dependencies
44 | npm install
45 |
46 | # serve with hot reload at localhost:8080
47 | npm run dev
48 |
49 | # build for production with minification
50 | npm run build
51 |
52 | # build for production and view the bundle analyzer report
53 | npm run build --report
54 |
55 | # run unit tests
56 | npm run unit
57 |
58 | # run all tests
59 | npm test
60 | ```
61 |
62 | ### Any suggestions are Welcome
63 |
64 | Create an [issue](https://github.com/Kotlin-lang-CN/Kotlin-CN-Frontend/issues) to help us improve .
65 |
66 | Contact Us:
67 |
68 | [chpengzh](mailto:chpengzh@foxmail.com) 、 [Yeshen](mailto:me@yeshen.org)
69 |
70 |
71 |
72 | # 中文版
73 |
74 | 你好,这是kotlin 论坛的前端部分,使用了VueJs的框架和webpack的脚手架
75 |
76 | ### 预览
77 |
78 | - 内测版 : [debug.kotlin-cn.org](https://debug.kotlin-cn.org/)
79 | - 正式版 : [discuss.kotliner.cn](https://discuss.kotliner.cn/) , [kotlin-cn.org](https://kotlin-cn.org/)
80 |
81 | ### 从零开始
82 |
83 | - 安装[npm](https://www.npmjs.com/get-npm),国内可用[cnpm](https://npm.taobao.org/) 替代
84 |
85 | - 本地测试(Terminal中执行)
86 |
87 | ```shell
88 | npm install && npm run dev
89 | ```
90 |
91 | - 用浏览器查看这个地址:`http://localhost:3000/`
92 |
93 | ### 输出正式版
94 |
95 | ```shell
96 | npm run build
97 | ```
98 |
99 | 更多细节看VueJs的文档: [guide](http://vuejs-templates.github.io/webpack/) 和 [docs for vue-loader](http://vuejs.github.io/vue-loader).
100 |
101 | ### 协助我们?遇到问题?
102 |
103 | QQ群:561490348
--------------------------------------------------------------------------------
/build_tools/build.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | var ora = require('ora')
6 | var rm = require('rimraf')
7 | var path = require('path')
8 | var chalk = require('chalk')
9 | var webpack = require('webpack')
10 | var config = require('../config')
11 | var webpackConfig = require('./webpack.prod.conf')
12 |
13 | var spinner = ora('building for production...')
14 | spinner.start()
15 |
16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
17 | if (err) throw err
18 | webpack(webpackConfig, function (err, stats) {
19 | spinner.stop()
20 | if (err) throw err
21 | process.stdout.write(stats.toString({
22 | colors: true,
23 | modules: false,
24 | children: false,
25 | chunks: false,
26 | chunkModules: false
27 | }) + '\n\n')
28 |
29 | console.log(chalk.cyan(' Build complete.\n'))
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ))
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/build_tools/check-versions.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk')
2 | var semver = require('semver')
3 | var packageConfig = require('../package.json')
4 | var shell = require('shelljs')
5 | function exec (cmd) {
6 | return require('child_process').execSync(cmd).toString().trim()
7 | }
8 |
9 | var versionRequirements = [
10 | {
11 | name: 'node',
12 | currentVersion: semver.clean(process.version),
13 | versionRequirement: packageConfig.engines.node
14 | },
15 | ]
16 |
17 | if (shell.which('npm')) {
18 | versionRequirements.push({
19 | name: 'npm',
20 | currentVersion: exec('npm --version'),
21 | versionRequirement: packageConfig.engines.npm
22 | })
23 | }
24 |
25 | module.exports = function () {
26 | var warnings = []
27 | for (var i = 0; i < versionRequirements.length; i++) {
28 | var mod = versionRequirements[i]
29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
30 | warnings.push(mod.name + ': ' +
31 | chalk.red(mod.currentVersion) + ' should be ' +
32 | chalk.green(mod.versionRequirement)
33 | )
34 | }
35 | }
36 |
37 | if (warnings.length) {
38 | console.log('')
39 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
40 | console.log()
41 | for (var i = 0; i < warnings.length; i++) {
42 | var warning = warnings[i]
43 | console.log(' ' + warning)
44 | }
45 | console.log()
46 | process.exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/build_tools/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('eventsource-polyfill')
3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
4 |
5 | hotClient.subscribe(function (event) {
6 | if (event.action === 'reload') {
7 | window.location.reload()
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/build_tools/dev-server.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | var config = require('../config')
4 | if (!process.env.NODE_ENV) {
5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
6 | }
7 |
8 | var opn = require('opn')
9 | var path = require('path')
10 | var express = require('express')
11 | var webpack = require('webpack')
12 | var proxyMiddleware = require('http-proxy-middleware')
13 | var webpackConfig = process.env.NODE_ENV === 'testing'
14 | ? require('./webpack.prod.conf')
15 | : require('./webpack.dev.conf')
16 |
17 | // default port where dev server listens for incoming traffic
18 | var port = process.env.PORT || config.dev.port
19 | // automatically open browser, if not set will be false
20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser
21 | // Define HTTP proxies to your custom API backend
22 | // https://github.com/chimurai/http-proxy-middleware
23 | var proxyTable = config.dev.proxyTable
24 |
25 | var app = express()
26 | var compiler = webpack(webpackConfig)
27 |
28 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
29 | publicPath: webpackConfig.output.publicPath,
30 | quiet: true
31 | })
32 |
33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
34 | log: () => {}
35 | })
36 | // force page reload when html-webpack-plugin template changes
37 | compiler.plugin('compilation', function (compilation) {
38 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
39 | hotMiddleware.publish({ action: 'reload' })
40 | cb()
41 | })
42 | })
43 |
44 | // proxy api requests
45 | Object.keys(proxyTable).forEach(function (context) {
46 | var options = proxyTable[context]
47 | if (typeof options === 'string') {
48 | options = { target: options }
49 | }
50 | app.use(proxyMiddleware(options.filter || context, options))
51 | })
52 |
53 | // handle fallback for HTML5 history API
54 | app.use(require('connect-history-api-fallback')())
55 |
56 | // serve webpack bundle output
57 | app.use(devMiddleware)
58 |
59 | // enable hot-reload and state-preserving
60 | // compilation error display
61 | app.use(hotMiddleware)
62 |
63 | // serve pure static assets
64 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
65 | app.use(staticPath, express.static('./static'))
66 |
67 | var uri = 'http://localhost:' + port
68 |
69 | var _resolve
70 | var readyPromise = new Promise(resolve => {
71 | _resolve = resolve
72 | })
73 |
74 | console.log('> Starting dev server...')
75 | devMiddleware.waitUntilValid(() => {
76 | console.log('> Listening at ' + uri + '\n')
77 | // when env is testing, don't need open it
78 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
79 | opn(uri)
80 | }
81 | _resolve()
82 | })
83 |
84 | var server = app.listen(port)
85 |
86 | module.exports = {
87 | ready: readyPromise,
88 | close: () => {
89 | server.close()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/build_tools/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
4 |
5 | exports.assetsPath = function (_path) {
6 | var assetsSubDirectory = process.env.NODE_ENV === 'production'
7 | ? config.build.assetsSubDirectory
8 | : config.dev.assetsSubDirectory
9 | return path.posix.join(assetsSubDirectory, _path)
10 | }
11 |
12 | exports.cssLoaders = function (options) {
13 | options = options || {}
14 |
15 | var cssLoader = {
16 | loader: 'css-loader',
17 | options: {
18 | minimize: process.env.NODE_ENV === 'production',
19 | sourceMap: options.sourceMap
20 | }
21 | }
22 |
23 | // generate loader string to be used with extract text plugin
24 | function generateLoaders (loader, loaderOptions) {
25 | var loaders = [cssLoader]
26 | if (loader) {
27 | loaders.push({
28 | loader: loader + '-loader',
29 | options: Object.assign({}, loaderOptions, {
30 | sourceMap: options.sourceMap
31 | })
32 | })
33 | }
34 |
35 | // Extract CSS when that option is specified
36 | // (which is the case during production build)
37 | if (options.extract) {
38 | return ExtractTextPlugin.extract({
39 | use: loaders,
40 | fallback: 'vue-style-loader'
41 | })
42 | } else {
43 | return ['vue-style-loader'].concat(loaders)
44 | }
45 | }
46 |
47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
48 | return {
49 | css: generateLoaders(),
50 | postcss: generateLoaders(),
51 | less: generateLoaders('less'),
52 | sass: generateLoaders('sass', { indentedSyntax: true }),
53 | scss: generateLoaders('sass'),
54 | stylus: generateLoaders('stylus'),
55 | styl: generateLoaders('stylus')
56 | }
57 | }
58 |
59 | // Generate loaders for standalone style files (outside of .vue)
60 | exports.styleLoaders = function (options) {
61 | var output = []
62 | var loaders = exports.cssLoaders(options)
63 | for (var extension in loaders) {
64 | var loader = loaders[extension]
65 | output.push({
66 | test: new RegExp('\\.' + extension + '$'),
67 | use: loader
68 | })
69 | }
70 | return output
71 | }
72 |
--------------------------------------------------------------------------------
/build_tools/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var config = require('../config')
3 | var isProduction = process.env.NODE_ENV === 'production'
4 |
5 | module.exports = {
6 | loaders: utils.cssLoaders({
7 | sourceMap: isProduction
8 | ? config.build.productionSourceMap
9 | : config.dev.cssSourceMap,
10 | extract: isProduction
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/build_tools/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var config = require('../config')
4 | var vueLoaderConfig = require('./vue-loader.conf')
5 |
6 | function resolve(dir) {
7 | return path.join(__dirname, '..', dir)
8 | }
9 |
10 | module.exports = {
11 | entry: {
12 | app: './src/main.js'
13 | },
14 | output: {
15 | path: config.build.assetsRoot,
16 | filename: '[name].js',
17 | publicPath: process.env.NODE_ENV === 'production'
18 | ? config.build.assetsPublicPath
19 | : config.dev.assetsPublicPath
20 | },
21 | resolve: {
22 | extensions: ['.js', '.vue', '.json'],
23 | alias: {
24 | '@': resolve('src')
25 | }
26 | },
27 | module: {
28 | rules: [
29 | {
30 | test: /\.vue$/,
31 | loader: 'vue-loader',
32 | options: vueLoaderConfig
33 | },
34 | {
35 | test: /\.js$/,
36 | loader: 'babel-loader?presets=es2015',
37 | include: [resolve('src'), resolve('test')]
38 | },
39 | {
40 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
41 | loader: 'url-loader',
42 | options: {
43 | limit: 10000,
44 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
45 | }
46 | },
47 | {
48 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
49 | loader: 'url-loader',
50 | options: {
51 | limit: 10000,
52 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
53 | }
54 | }
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/build_tools/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var webpack = require('webpack')
3 | var config = require('../config')
4 | var merge = require('webpack-merge')
5 | var baseWebpackConfig = require('./webpack.base.conf')
6 | var HtmlWebpackPlugin = require('html-webpack-plugin')
7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
8 |
9 | // add hot-reload related code to entry chunks
10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
11 | baseWebpackConfig.entry[name] = ['./build_tools/dev-client'].concat(baseWebpackConfig.entry[name])
12 | })
13 |
14 | module.exports = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: '#cheap-module-eval-source-map',
20 | plugins: [
21 | new webpack.DefinePlugin({
22 | 'process.env': config.dev.env
23 | }),
24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
25 | new webpack.HotModuleReplacementPlugin(),
26 | new webpack.NoEmitOnErrorsPlugin(),
27 | // https://github.com/ampedandwired/html-webpack-plugin
28 | new HtmlWebpackPlugin({
29 | filename: 'index.html',
30 | template: 'index.html',
31 | inject: true
32 | }),
33 | new FriendlyErrorsPlugin()
34 | ]
35 | })
36 |
--------------------------------------------------------------------------------
/build_tools/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var webpack = require('webpack')
4 | var config = require('../config')
5 | var merge = require('webpack-merge')
6 | var baseWebpackConfig = require('./webpack.base.conf')
7 | var CopyWebpackPlugin = require('copy-webpack-plugin')
8 | var HtmlWebpackPlugin = require('html-webpack-plugin')
9 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
11 |
12 | var env = process.env.NODE_ENV === 'testing'
13 | ? require('../config/test.env')
14 | : config.build.env
15 |
16 | var webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true
21 | })
22 | },
23 | devtool: config.build.productionSourceMap ? '#source-map' : false,
24 | output: {
25 | path: config.build.assetsRoot,
26 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
28 | },
29 | plugins: [
30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
31 | new webpack.DefinePlugin({
32 | 'process.env': env
33 | }),
34 | new webpack.optimize.UglifyJsPlugin({
35 | compress: {
36 | warnings: false
37 | },
38 | sourceMap: true
39 | }),
40 | // extract css into its own file
41 | new ExtractTextPlugin({
42 | filename: utils.assetsPath('css/[name].[contenthash].css')
43 | }),
44 | // Compress extracted CSS. We are using this plugin so that possible
45 | // duplicated CSS from different components can be deduped.
46 | new OptimizeCSSPlugin({
47 | cssProcessorOptions: {
48 | safe: true
49 | }
50 | }),
51 | // generate dist index.html with correct asset hash for caching.
52 | // you can customize output by editing /index.html
53 | // see https://github.com/ampedandwired/html-webpack-plugin
54 | new HtmlWebpackPlugin({
55 | filename: process.env.NODE_ENV === 'testing'
56 | ? 'index.html'
57 | : config.build.index,
58 | template: 'index.html',
59 | inject: true,
60 | minify: {
61 | removeComments: true,
62 | collapseWhitespace: true,
63 | removeAttributeQuotes: true
64 | // more options:
65 | // https://github.com/kangax/html-minifier#options-quick-reference
66 | },
67 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
68 | chunksSortMode: 'dependency'
69 | }),
70 | // split vendor js into its own file
71 | new webpack.optimize.CommonsChunkPlugin({
72 | name: 'vendor',
73 | minChunks: function (module, count) {
74 | // any required modules inside node_modules are extracted to vendor
75 | return (
76 | module.resource &&
77 | /\.js$/.test(module.resource) &&
78 | module.resource.indexOf(
79 | path.join(__dirname, '../node_modules')
80 | ) === 0
81 | )
82 | }
83 | }),
84 | // extract webpack runtime and module manifest to its own file in order to
85 | // prevent vendor hash from being updated whenever app bundle is updated
86 | new webpack.optimize.CommonsChunkPlugin({
87 | name: 'manifest',
88 | chunks: ['vendor']
89 | }),
90 | // copy custom static assets
91 | new CopyWebpackPlugin([
92 | {
93 | from: path.resolve(__dirname, '../static'),
94 | to: config.build.assetsSubDirectory,
95 | ignore: ['.*']
96 | }
97 | ])
98 | ]
99 | })
100 |
101 | if (config.build.productionGzip) {
102 | var CompressionWebpackPlugin = require('compression-webpack-plugin')
103 |
104 | webpackConfig.plugins.push(
105 | new CompressionWebpackPlugin({
106 | asset: '[path].gz[query]',
107 | algorithm: 'gzip',
108 | test: new RegExp(
109 | '\\.(' +
110 | config.build.productionGzipExtensions.join('|') +
111 | ')$'
112 | ),
113 | threshold: 10240,
114 | minRatio: 0.8
115 | })
116 | )
117 | }
118 |
119 | if (config.build.bundleAnalyzerReport) {
120 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
122 | }
123 |
124 | module.exports = webpackConfig
125 |
--------------------------------------------------------------------------------
/build_tools/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | // This is the webpack config used for unit tests.
2 |
3 | var utils = require('./utils')
4 | var webpack = require('webpack')
5 | var merge = require('webpack-merge')
6 | var baseConfig = require('./webpack.base.conf')
7 |
8 | var webpackConfig = merge(baseConfig, {
9 | // use inline sourcemap for karma-sourcemap-loader
10 | module: {
11 | rules: utils.styleLoaders()
12 | },
13 | devtool: '#inline-source-map',
14 | resolveLoader: {
15 | alias: {
16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
18 | 'scss-loader': 'sass-loader'
19 | }
20 | },
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': require('../config/test.env')
24 | })
25 | ]
26 | })
27 |
28 | // no need for app entry during tests
29 | delete webpackConfig.entry
30 |
31 | module.exports = webpackConfig
32 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var prodEnv = require('./prod.env')
3 |
4 | module.exports = merge(prodEnv, {
5 | NODE_ENV: '"development"'
6 | })
7 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: require('./prod.env'),
7 | index: path.resolve(__dirname, '../dist/index.html'),
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'static',
10 | assetsPublicPath: '/',
11 | productionSourceMap: true,
12 | // Gzip off by default as many popular static hosts such as
13 | // Surge or Netlify already gzip all static assets for you.
14 | // Before setting to `true`, make sure to:
15 | // npm install --save-dev compression-webpack-plugin
16 | productionGzip: false,
17 | productionGzipExtensions: ['js', 'css'],
18 | // Run the build command with an extra argument to
19 | // View the bundle analyzer report after build finishes:
20 | // `npm run build --report`
21 | // Set to `true` or `false` to always turn it on or off
22 | bundleAnalyzerReport: process.env.npm_config_report
23 | },
24 | dev: {
25 | env: require('./dev.env'),
26 | port: 3000,
27 | autoOpenBrowser: true,
28 | assetsSubDirectory: 'static',
29 | assetsPublicPath: '/',
30 | proxyTable: {},
31 | // CSS Sourcemaps off by default because relative paths are "buggy"
32 | // with this option, according to the CSS-Loader README
33 | // (https://github.com/webpack/css-loader#sourcemaps)
34 | // In our experience, they generally work as expected,
35 | // just be aware of this issue when enabling this option.
36 | cssSourceMap: false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var devEnv = require('./dev.env')
3 |
4 | module.exports = merge(devEnv, {
5 | NODE_ENV: '"testing"'
6 | })
7 |
--------------------------------------------------------------------------------
/deploy:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | mode=$1
4 |
5 | if [ 'release' = ${mode} ]; then
6 |
7 | cp src/assets/js/Config.js /tmp/Config.js
8 | cp ../env/Config.js src/assets/js/Config.js
9 | cnpm install && npm run build
10 | cp /tmp/Config.js src/assets/js/Config.js
11 |
12 | rsync -azP dist/ root@106.75.105.42:frontend/
13 |
14 | elif [ 'dev' = ${mode} ]; then
15 |
16 | cnpm install && npm run build
17 | rsync -azP dist/ root@debug.kotlin-cn.org:frontend/
18 |
19 | else
20 |
21 | echo 'Error: deploy mode should be one of `release` or `dev`'
22 | echo 'try `./deploy dev` or `./deploy release` instead'
23 | exit -1
24 |
25 | fi
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kotlin China
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "forum_ui",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "yisheng ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build_tools/dev-server.js",
9 | "start": "node build_tools/dev-server.js",
10 | "build": "node build_tools/build.js",
11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
12 | "test": "npm run unit",
13 | "lint": "eslint --ext .js,.vue src test/unit/specs"
14 | },
15 | "dependencies": {
16 | "js-cookie": "^2.1.4",
17 | "lodash": "latest",
18 | "marked": "^0.3.6",
19 | "md5": "^2.2.1",
20 | "moment": "^2.18.1",
21 | "page": "^1.7.1",
22 | "qiniu-js": "^1.0.19",
23 | "tagify": "^0.1.1",
24 | "vue": "^2.3.3",
25 | "vue-input-tag": "^0.0.11",
26 | "vue-loading-template": "^0.1.7",
27 | "vue-router": "^2.3.1",
28 | "vue-scroll": "^2.0.3"
29 | },
30 | "devDependencies": {
31 | "autoprefixer": "^6.7.2",
32 | "babel-core": "^6.22.1",
33 | "babel-eslint": "^7.1.1",
34 | "babel-loader": "^6.2.10",
35 | "babel-plugin-istanbul": "^4.1.1",
36 | "babel-plugin-transform-runtime": "^6.22.0",
37 | "babel-preset-env": "^1.3.2",
38 | "babel-preset-es2015": "^6.0.0",
39 | "babel-preset-stage-2": "^6.22.0",
40 | "babel-register": "^6.22.0",
41 | "chai": "^3.5.0",
42 | "chalk": "^1.1.3",
43 | "connect-history-api-fallback": "^1.3.0",
44 | "copy-webpack-plugin": "^4.0.1",
45 | "cross-env": "^4.0.0",
46 | "css-loader": "^0.28.0",
47 | "eslint": "^3.19.0",
48 | "eslint-config-standard": "^6.2.1",
49 | "eslint-friendly-formatter": "^2.0.7",
50 | "eslint-loader": "^1.7.1",
51 | "eslint-plugin-html": "^2.0.0",
52 | "eslint-plugin-promise": "^3.4.0",
53 | "eslint-plugin-standard": "^2.0.1",
54 | "eventsource-polyfill": "^0.9.6",
55 | "express": "^4.14.1",
56 | "extract-text-webpack-plugin": "^2.0.0",
57 | "file-loader": "^0.11.1",
58 | "friendly-errors-webpack-plugin": "^1.1.3",
59 | "html-webpack-plugin": "^2.28.0",
60 | "http-proxy-middleware": "^0.17.3",
61 | "inject-loader": "^3.0.0",
62 | "karma": "^1.4.1",
63 | "karma-coverage": "^1.1.1",
64 | "karma-mocha": "^1.3.0",
65 | "karma-phantomjs-launcher": "^1.0.2",
66 | "karma-phantomjs-shim": "^1.4.0",
67 | "karma-sinon-chai": "^1.3.1",
68 | "karma-sourcemap-loader": "^0.3.7",
69 | "karma-spec-reporter": "0.0.30",
70 | "karma-webpack": "^2.0.2",
71 | "less": "^2.7.2",
72 | "less-loader": "^4.0.3",
73 | "lolex": "^1.5.2",
74 | "mocha": "^3.2.0",
75 | "node-sass": "^4.5.3",
76 | "opn": "^4.0.2",
77 | "optimize-css-assets-webpack-plugin": "^1.3.0",
78 | "ora": "^1.2.0",
79 | "phantomjs-prebuilt": "^2.1.14",
80 | "rimraf": "^2.6.0",
81 | "sass-loader": "^6.0.5",
82 | "semver": "^5.3.0",
83 | "shelljs": "^0.7.6",
84 | "sinon": "^2.1.0",
85 | "sinon-chai": "^2.8.0",
86 | "url-loader": "^0.5.8",
87 | "vue-loader": "^12.1.0",
88 | "vue-style-loader": "^3.0.1",
89 | "vue-template-compiler": "^2.3.3",
90 | "webpack": "^2.6.1",
91 | "webpack-bundle-analyzer": "^2.2.1",
92 | "webpack-dev-middleware": "^1.10.0",
93 | "webpack-hot-middleware": "^2.18.0",
94 | "webpack-merge": "^4.1.0"
95 | },
96 | "engines": {
97 | "node": ">= 4.0.0",
98 | "npm": ">= 3.0.0"
99 | },
100 | "browserslist": [
101 | "> 1%",
102 | "last 2 versions",
103 | "not ie <= 8"
104 | ]
105 | }
106 |
--------------------------------------------------------------------------------
/src/assets/img/add-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/add-icon.png
--------------------------------------------------------------------------------
/src/assets/img/back-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/back-icon.png
--------------------------------------------------------------------------------
/src/assets/img/choice-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/choice-icon.png
--------------------------------------------------------------------------------
/src/assets/img/comment-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/comment-icon.png
--------------------------------------------------------------------------------
/src/assets/img/edit-title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/edit-title.png
--------------------------------------------------------------------------------
/src/assets/img/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/email.png
--------------------------------------------------------------------------------
/src/assets/img/fine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/fine.png
--------------------------------------------------------------------------------
/src/assets/img/friendship1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship1.png
--------------------------------------------------------------------------------
/src/assets/img/friendship2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship2.png
--------------------------------------------------------------------------------
/src/assets/img/friendship3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship3.png
--------------------------------------------------------------------------------
/src/assets/img/friendship4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship4.png
--------------------------------------------------------------------------------
/src/assets/img/friendship5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship5.png
--------------------------------------------------------------------------------
/src/assets/img/friendship6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/friendship6.png
--------------------------------------------------------------------------------
/src/assets/img/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/github.png
--------------------------------------------------------------------------------
/src/assets/img/kotlin_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/kotlin_logo.png
--------------------------------------------------------------------------------
/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/logo.png
--------------------------------------------------------------------------------
/src/assets/img/logo_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/logo_big.png
--------------------------------------------------------------------------------
/src/assets/img/logo_k.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/logo_k.png
--------------------------------------------------------------------------------
/src/assets/img/pulldown-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/pulldown-icon.png
--------------------------------------------------------------------------------
/src/assets/img/register_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/register_success.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_Italic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_Italic.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_a.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_back.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_blod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_blod.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_cut_off_rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_cut_off_rule.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_image.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_next.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_ordered_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_ordered_list.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_paragraph_reference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_paragraph_reference.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_pre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_pre.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_title.png
--------------------------------------------------------------------------------
/src/assets/img/toolbar_unordered_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/toolbar_unordered_list.png
--------------------------------------------------------------------------------
/src/assets/img/ttf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/img/ttf.png
--------------------------------------------------------------------------------
/src/assets/js/Cache.js:
--------------------------------------------------------------------------------
1 | class Cache {
2 | set(key, value) {
3 | localStorage.setItem(key, value);
4 | }
5 |
6 | get(key) {
7 | let value = localStorage.getItem(key);
8 | return value === null ? '' : value;
9 | }
10 |
11 | del(key) {
12 | localStorage.removeItem(key);
13 | }
14 | }
15 | export default new Cache();
16 |
--------------------------------------------------------------------------------
/src/assets/js/Config.js:
--------------------------------------------------------------------------------
1 | const CGI = {
2 | account: "https://debug.kotlin-cn.org/api/account",
3 | github: 'https://debug.kotlin-cn.org/api/github',
4 | admin: 'https://debug.kotlin-cn.org/api/admin',
5 | article: "https://debug.kotlin-cn.org/api/article",
6 | reply: 'https://debug.kotlin-cn.org/api/reply',
7 | rss: 'https://debug.kotlin-cn.org/api/rss',
8 | flower: 'https://debug.kotlin-cn.org/api/flower',
9 | misc: 'https://debug.kotlin-cn.org/api/misc',
10 | creator: 'https://debug.kotlin-cn.org/api/creator'
11 | };
12 |
13 | const Config = {
14 | URL: {
15 | account: {
16 | register: CGI.account + '/register',
17 | login: CGI.account + '/login',
18 | user: CGI.account + '/user/{0}',
19 | password: CGI.account + '/user/{0}/password',
20 | update: CGI.account + '/user/{0}/update',
21 | profile: CGI.account + '/profile',
22 | profile_update: CGI.account + '/profile/update'
23 | },
24 | github: {
25 | createState: CGI.github + '/state',
26 | auth: CGI.github + '/auth'
27 | },
28 | admin: {
29 | articleList: CGI.admin + '/article/list',//管理员视角查看所有文章内容
30 | updateArticleState: CGI.admin + '/article/{0}/state',//更新文章状态
31 | updateUserState: CGI.admin + '/user/{0}/state',//跟新用户状态
32 | updateReplyState: CGI.admin + '/reply/{0}/state'//更新回复状态
33 | },
34 | article: {
35 | post: CGI.article + '/post',
36 | detail: CGI.article + '/post/{0}',
37 | update: CGI.article + '/post/{0}/update',
38 | delete: CGI.article + '/post/{0}/delete',
39 |
40 | category: CGI.article + '/category',//获取文章类别列表
41 | getLatest: CGI.article + '/list',//获取最新文章列表
42 | getFine: CGI.article + '/fine', //获取精品文章列表
43 | getCategory: CGI.article + '/category/{0}'//获取特定类别最新文章列表
44 | },
45 | reply: {
46 | count: CGI.reply + '/count',
47 | article: CGI.reply + '/article/{0}',
48 | delete: CGI.reply + '/id/{0}/delete',
49 | user: CGI.reply + '/user/{0}'
50 | },
51 | flower: {
52 | article_star: CGI.flower + '/article/{0}/star',
53 | article_unstar: CGI.flower + '/article/{0}/unstar',
54 | article_count: CGI.flower + '/article/star/count',
55 | reply_star: CGI.flower + '/reply/{0}/star',
56 | reply_unstar: CGI.flower + '/reply/{0}/unstar',
57 | reply_count: CGI.flower + '/reply/star/count'
58 | },
59 | rss: {
60 | fine: CGI.rss + "/fine",//精品文章订阅
61 | latest: CGI.rss + '/latest'//最新文章订阅
62 | },
63 | misc: {
64 | dashboard: CGI.misc + '/dashboard',//网站公告栏
65 | homeLink: CGI.misc + '/home/link'//首页广告栏链接
66 | },
67 | creator: {
68 | article_count: CGI.creator + '/article/count',
69 | reply_count: CGI.creator + '/reply/count',
70 | article: CGI.creator + '/article',
71 | reply: CGI.creator + '/reply'
72 | }
73 | },
74 | OAuth: {
75 | github: {
76 | cgi: 'http://github.com/login/oauth/authorize',
77 | clientId: '4515da98f829c9feb99f',
78 | scope: 'user'
79 | }
80 | },
81 | UI: {
82 | root: '/',
83 | account: '/account',
84 | edit: '/edit',
85 | post: '/post',
86 | comments: '/comments',
87 | topic: '/topic',
88 | user: '/user'
89 | },
90 | Topic: {
91 | select: [
92 | 'p1', 'p2', 'p3', 'p4'
93 | ],
94 | def: "p1",
95 | data: {
96 | p1: {
97 | title: '【视频】Kotlin 入门到进阶',
98 | link: '/topic/p1/',
99 | logo: 'fa fa-puzzle-piece',
100 | color: '#ea4335',
101 | toc: '6284762828886261760',
102 | readme: '6284762828886261760'
103 | },
104 | p2: {
105 | title: 'Kotlin in Chinese',
106 | link: '/topic/p2/',
107 | logo: 'fa fa-send',
108 | color: '#4285f4',
109 | toc: '6287247888258359296',
110 | readme: '6284215952900517888'
111 | },
112 | p3: {
113 | title: '社区博客',
114 | link: '//www.kotliner.cn',
115 | logo: 'fa fa-comments-o',
116 | color: '#fbbc05',
117 | toc: '6287247888258359296',
118 | readme: '6284762828886261760'
119 | },
120 | p4: {
121 | title: '中文站',
122 | link: '//kotlincn.net',
123 | logo: 'fa fa-support',
124 | color: '#4285f4',
125 | toc: '6289370960008368128',
126 | readme: '6284215952900517888'
127 | }
128 | }
129 | }
130 | };
131 |
132 | export default Config;
133 |
--------------------------------------------------------------------------------
/src/assets/js/Event.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const bus = new Vue();
4 |
5 | const Event = {
6 | on: function (key, func) {
7 | bus.$on(key, func);
8 | },
9 | emit: function (key, value) {
10 | bus.$emit(key, value);
11 | }
12 | };
13 | export default Event;
14 |
--------------------------------------------------------------------------------
/src/assets/js/LoginMgr.js:
--------------------------------------------------------------------------------
1 | import Cookie from 'js-cookie';
2 | import Event from './Event.js';
3 |
4 | const LoginMgr = {
5 |
6 | info: function () {
7 | let uid = Cookie.get("X-App-UID");
8 | let username = Cookie.get("X-App-Name");
9 | let email = Cookie.get("X-App-Email");
10 | let token = Cookie.get("X-App-Token");
11 | let role = Cookie.get('X-App-Role');
12 | let logo = Cookie.get('X-App-Logo');
13 | if (uid && uid.length > 0
14 | && username && username.length > 0
15 | && email && email.length > 0
16 | && token && token.length > 0) {
17 | this.uid = uid;
18 | this.username = username;
19 | this.email = email;
20 | this.token = token;
21 | this.role = role;
22 | this.isLogin = true;
23 | this.isAdminRole = this.role === '1';
24 | this.logo = logo;
25 | return this
26 | } else {
27 | this.uid = undefined;
28 | this.username = undefined;
29 | this.email = undefined;
30 | this.token = undefined;
31 | this.role = undefined;
32 | this.logo = undefined;
33 | this.isLogin = false;
34 | this.isAdminRole = false;
35 | return this
36 | }
37 | },
38 |
39 | check: function (loginMode, guestMode) {
40 | let userInfo = this.info();
41 | if (userInfo.isLogin) {
42 | return loginMode ? loginMode(userInfo) : '';
43 | } else {
44 | return guestMode ? guestMode() : '';
45 | }
46 | },
47 |
48 | isAdmin: function () {
49 | const info = this.info();
50 | return info.isAdminRole
51 | },
52 |
53 | require: function (loginAlready, mode) {
54 | let info = this.info();
55 | if (info.isLogin) {
56 | loginAlready(info)
57 | } else {
58 | Event.emit(mode ? mode : 'request_login', loginAlready)
59 | }
60 | },
61 |
62 | login: function (data) {
63 | console.log(data);
64 | Cookie.set('X-App-Name', data.username);
65 | Cookie.set('X-App-Email', data.email);
66 | Cookie.set('X-App-UID', data.uid);
67 | Cookie.set('X-App-Token', data.token);
68 | Cookie.set('X-App-Role', data.role);
69 | Cookie.set('X-App-Logo', data.logo);
70 | Cookie.remove('X-App-Github');
71 | const info = this.info();
72 | Event.emit('login', info);
73 | },
74 |
75 | logout: function () {
76 | Cookie.remove('X-App-Email');
77 | Cookie.remove('X-App-Token');
78 | Cookie.remove('X-App-UID');
79 | Cookie.remove('X-App-Name');
80 | Cookie.remove('X-App-Role');
81 | Cookie.remove('X-App-Logo');
82 | Cookie.remove('X-App-Github');
83 | this.info();
84 | Event.emit('login', false);
85 | }
86 |
87 | };
88 |
89 | export default LoginMgr
90 |
--------------------------------------------------------------------------------
/src/assets/js/Net.js:
--------------------------------------------------------------------------------
1 | import bigJSON from './Parse.js';
2 | import Cookie from 'js-cookie';
3 | import Event from './Event.js';
4 |
5 | function generateHeaders() {
6 | let deviceId = Cookie.get("X-App-Device");
7 | if (deviceId === undefined) {
8 | deviceId = "WEB-" + Date.now().toString(36);
9 | deviceId += Math.random().toString(36).slice(2);
10 | deviceId += Math.random().toString(36).slice(3);
11 | deviceId = deviceId.toUpperCase();
12 | Cookie.set("X-App-Device", deviceId);
13 | }
14 | let platform = 0;
15 | let contentType = 'application/x-www-form-urlencoded';
16 | let system = navigator.platform;
17 | let info = navigator.userAgent.toLowerCase().match(/(msie|firefox|chrome|opera|version).*?([\d.]+)/);
18 | let vendor = (info && info.length >= 3) ? info[1].replace(/version/, "'safari") + info[2] : "null";
19 | let token = Cookie.get("X-App-Token");
20 | return {
21 | 'X-App-Device': deviceId,
22 | 'X-App-Platform': platform,
23 | 'X-App-Vendor': vendor,
24 | 'X-App-System': system,
25 | 'Content-Type': contentType,
26 | 'X-App-Token': token
27 | }
28 | }
29 |
30 | class Net {
31 | ajax(request, success, fail) {
32 | let errHandler = function (error) {
33 | const msg = (error.code < 0 ? '系统错误' : error.msg) + '(错误码 ' + error.code + ')';
34 | Event.emit('error', error.msg ? msg : error);
35 | console.log(error);
36 | if (fail) fail(error)
37 | };
38 | $.ajax({
39 | url: request.url,
40 | cache: !1,
41 | type: request.type,
42 | dataType: "text",
43 | headers: generateHeaders(),
44 | data: request.condition,
45 | success: (data) => {
46 | try {
47 | let resp = bigJSON.parse(data);
48 | if (0 !== resp.code) {
49 | errHandler(resp);
50 | } else {
51 | if (success) success(resp);
52 | }
53 | } catch (err) {
54 | errHandler(err)
55 | }
56 | },
57 | error: errHandler
58 | })
59 | }
60 |
61 | get(request, success, fail) {
62 | request.type = 'GET';
63 | this.ajax(request, success, fail)
64 | }
65 |
66 | post(request, success, fail) {
67 | request.type = 'POST';
68 | this.ajax(request, success, fail)
69 | }
70 |
71 | }
72 |
73 | export default new Net();
74 |
75 |
--------------------------------------------------------------------------------
/src/assets/js/Parse.js:
--------------------------------------------------------------------------------
1 | //var BigNumber = null;
2 | /*
3 | json_parse.js
4 | 2012-06-20
5 |
6 | Public Domain.
7 |
8 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
9 |
10 | This file creates a json_parse function.
11 | During create you can (optionally) specify some behavioural switches
12 |
13 | require('json-bigint')(options)
14 |
15 | The optional options parameter holds switches that drive certain
16 | aspects of the parsing process:
17 | * options.strict = true will warn about duplicate-key usage in the json.
18 | The default (strict = false) will silently ignore those and overwrite
19 | values for keys that are in duplicate use.
20 |
21 | The resulting function follows this signature:
22 | json_parse(text, reviver)
23 | This method parses a JSON text to produce an object or array.
24 | It can throw a SyntaxError exception.
25 |
26 | The optional reviver parameter is a function that can filter and
27 | transform the results. It receives each of the keys and values,
28 | and its return value is used instead of the original value.
29 | If it returns what it received, then the structure is not modified.
30 | If it returns undefined then the member is deleted.
31 |
32 | Example:
33 |
34 | // Parse the text. Values that look like ISO date strings will
35 | // be converted to Date objects.
36 |
37 | myData = json_parse(text, function (key, value) {
38 | var a;
39 | if (typeof value === 'string') {
40 | a =
41 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
42 | if (a) {
43 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
44 | +a[5], +a[6]));
45 | }
46 | }
47 | return value;
48 | });
49 |
50 | This is a reference implementation. You are free to copy, modify, or
51 | redistribute.
52 |
53 | This code should be minified before deployment.
54 | See http://javascript.crockford.com/jsmin.html
55 |
56 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
57 | NOT CONTROL.
58 | */
59 |
60 | /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode,
61 | hasOwnProperty, message, n, name, prototype, push, r, t, text
62 | */
63 |
64 | var json_parse = function (options) {
65 | "use strict";
66 |
67 | // This is a function that can parse a JSON text, producing a JavaScript
68 | // data structure. It is a simple, recursive descent parser. It does not use
69 | // eval or regular expressions, so it can be used as a model for implementing
70 | // a JSON parser in other languages.
71 |
72 | // We are defining the function inside of another function to avoid creating
73 | // global variables.
74 |
75 |
76 | // Default options one can override by passing options to the parse()
77 | var _options = {
78 | "strict": false, // not being strict means do not generate syntax errors for "duplicate key"
79 | "storeAsString": false // toggles whether the values should be stored as BigNumber (default) or a string
80 | };
81 |
82 |
83 | // If there are options, then use them to override the default _options
84 | if (options !== undefined && options !== null) {
85 | if (options.strict === true) {
86 | _options.strict = true;
87 | }
88 | if (options.storeAsString === true) {
89 | _options.storeAsString = true;
90 | }
91 | }
92 |
93 |
94 | var at, // The index of the current character
95 | ch, // The current character
96 | escapee = {
97 | '"': '"',
98 | '\\': '\\',
99 | '/': '/',
100 | b: '\b',
101 | f: '\f',
102 | n: '\n',
103 | r: '\r',
104 | t: '\t'
105 | },
106 | text,
107 |
108 | error = function (m) {
109 |
110 | // Call error when something is wrong.
111 |
112 | throw {
113 | name: 'SyntaxError',
114 | message: m,
115 | at: at,
116 | text: text
117 | };
118 | },
119 |
120 | next = function (c) {
121 |
122 | // If a c parameter is provided, verify that it matches the current character.
123 |
124 | if (c && c !== ch) {
125 | error("Expected '" + c + "' instead of '" + ch + "'");
126 | }
127 |
128 | // Get the next character. When there are no more characters,
129 | // return the empty string.
130 |
131 | ch = text.charAt(at);
132 | at += 1;
133 | return ch;
134 | },
135 |
136 | number = function () {
137 | // Parse a number value.
138 |
139 | var number,
140 | string = '';
141 |
142 | if (ch === '-') {
143 | string = '-';
144 | next('-');
145 | }
146 | while (ch >= '0' && ch <= '9') {
147 | string += ch;
148 | next();
149 | }
150 | if (ch === '.') {
151 | string += '.';
152 | while (next() && ch >= '0' && ch <= '9') {
153 | string += ch;
154 | }
155 | }
156 | if (ch === 'e' || ch === 'E') {
157 | string += ch;
158 | next();
159 | if (ch === '-' || ch === '+') {
160 | string += ch;
161 | next();
162 | }
163 | while (ch >= '0' && ch <= '9') {
164 | string += ch;
165 | next();
166 | }
167 | }
168 | number = +string;
169 | if (!isFinite(number)) {
170 | error("Bad number");
171 | } else {
172 | if (string.length > 15)
173 | return (_options.storeAsString === true) ? string : new BigNumber(string);
174 | return number;
175 | }
176 | },
177 |
178 | string = function () {
179 |
180 | // Parse a string value.
181 |
182 | var hex,
183 | i,
184 | string = '',
185 | uffff;
186 |
187 | // When parsing for string values, we must look for " and \ characters.
188 |
189 | if (ch === '"') {
190 | while (next()) {
191 | if (ch === '"') {
192 | next();
193 | return string;
194 | }
195 | if (ch === '\\') {
196 | next();
197 | if (ch === 'u') {
198 | uffff = 0;
199 | for (i = 0; i < 4; i += 1) {
200 | hex = parseInt(next(), 16);
201 | if (!isFinite(hex)) {
202 | break;
203 | }
204 | uffff = uffff * 16 + hex;
205 | }
206 | string += String.fromCharCode(uffff);
207 | } else if (typeof escapee[ch] === 'string') {
208 | string += escapee[ch];
209 | } else {
210 | break;
211 | }
212 | } else {
213 | string += ch;
214 | }
215 | }
216 | }
217 | error("Bad string");
218 | },
219 |
220 | white = function () {
221 |
222 | // Skip whitespace.
223 |
224 | while (ch && ch <= ' ') {
225 | next();
226 | }
227 | },
228 |
229 | word = function () {
230 |
231 | // true, false, or null.
232 |
233 | switch (ch) {
234 | case 't':
235 | next('t');
236 | next('r');
237 | next('u');
238 | next('e');
239 | return true;
240 | case 'f':
241 | next('f');
242 | next('a');
243 | next('l');
244 | next('s');
245 | next('e');
246 | return false;
247 | case 'n':
248 | next('n');
249 | next('u');
250 | next('l');
251 | next('l');
252 | return null;
253 | }
254 | error("Unexpected '" + ch + "'");
255 | },
256 |
257 | value, // Place holder for the value function.
258 |
259 | array = function () {
260 |
261 | // Parse an array value.
262 |
263 | var array = [];
264 |
265 | if (ch === '[') {
266 | next('[');
267 | white();
268 | if (ch === ']') {
269 | next(']');
270 | return array; // empty array
271 | }
272 | while (ch) {
273 | array.push(value());
274 | white();
275 | if (ch === ']') {
276 | next(']');
277 | return array;
278 | }
279 | next(',');
280 | white();
281 | }
282 | }
283 | error("Bad array");
284 | },
285 |
286 | object = function () {
287 |
288 | // Parse an object value.
289 |
290 | var key,
291 | object = {};
292 |
293 | if (ch === '{') {
294 | next('{');
295 | white();
296 | if (ch === '}') {
297 | next('}');
298 | return object; // empty object
299 | }
300 | while (ch) {
301 | key = string();
302 | white();
303 | next(':');
304 | if (_options.strict === true && Object.hasOwnProperty.call(object, key)) {
305 | error('Duplicate key "' + key + '"');
306 | }
307 | object[key] = value();
308 | white();
309 | if (ch === '}') {
310 | next('}');
311 | return object;
312 | }
313 | next(',');
314 | white();
315 | }
316 | }
317 | error("Bad object");
318 | };
319 |
320 | value = function () {
321 |
322 | // Parse a JSON value. It could be an object, an array, a string, a number,
323 | // or a word.
324 |
325 | white();
326 | switch (ch) {
327 | case '{':
328 | return object();
329 | case '[':
330 | return array();
331 | case '"':
332 | return string();
333 | case '-':
334 | return number();
335 | default:
336 | return ch >= '0' && ch <= '9' ? number() : word();
337 | }
338 | };
339 |
340 | // Return the json_parse function. It will have access to all of the above
341 | // functions and variables.
342 |
343 | return function (source, reviver) {
344 | var result;
345 | text = source + '';
346 | at = 0;
347 | ch = ' ';
348 | result = value();
349 | white();
350 | if (ch) {
351 | error("Syntax error");
352 | }
353 |
354 | // If there is a reviver function, we recursively walk the new structure,
355 | // passing each name/value pair to the reviver function for possible
356 | // transformation, starting with a temporary root object that holds the result
357 | // in an empty key. If there is not a reviver function, we simply return the
358 | // result.
359 |
360 | return typeof reviver === 'function'
361 | ? (function walk(holder, key) {
362 | var k, v, value = holder[key];
363 | if (value && typeof value === 'object') {
364 | Object.keys(value).forEach(function (k) {
365 | v = walk(value, k);
366 | if (v !== undefined) {
367 | value[k] = v;
368 | } else {
369 | delete value[k];
370 | }
371 | });
372 | }
373 | return reviver.call(holder, key, value);
374 | }({'': result}, ''))
375 | : result;
376 | };
377 | }
378 | let bigJSON = {};
379 | bigJSON.parse = json_parse({storeAsString: true});
380 | module.exports = bigJSON;
381 |
--------------------------------------------------------------------------------
/src/assets/js/Util.js:
--------------------------------------------------------------------------------
1 | const I64BIT_TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'.split('');
2 |
3 | function hash(input) {
4 | let hash = 5381;
5 | let i = input.length - 1;
6 |
7 | if (typeof input === 'string') {
8 | for (; i > -1; i--)
9 | hash += (hash << 5) + input.charCodeAt(i);
10 | }
11 | else {
12 | for (; i > -1; i--)
13 | hash += (hash << 5) + input[i];
14 | }
15 | let value = hash & 0x7FFFFFFF;
16 | let retValue = '';
17 | do {
18 | retValue += I64BIT_TABLE[value & 0x3F];
19 | }
20 | while (value >>= 6);
21 |
22 | return retValue;
23 | }
24 |
25 | const Util = {
26 |
27 | isValidEmail(s) {
28 | let reg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/;
29 | return reg.test(s);
30 | },
31 |
32 | isValidName(s) {
33 | let reg = /^[a-zA-Z]{2,20}$/;
34 | return reg.test(s);
35 | },
36 |
37 | isValidPass(s) {
38 | return s !== null && s.length > 5;
39 | },
40 |
41 | isMobile() {
42 | let ua = navigator.userAgent;
43 | return ua.match(/(Android)[\s\/]+([\d\.]+)/) !== null
44 | || ua.match(/(iPad|iPhone|iPod)\s+OS\s([\d_\.]+)/) !== null
45 | || ua.match(/(Windows\s+Phone)\s([\d\.]+)/) !== null;
46 | },
47 |
48 | //generate anchor from title text
49 | anchorHash(text) {
50 | return hash(text.replace('#', '_'))
51 | }
52 | };
53 | if (!String.prototype.format) {
54 | String.prototype.format = function () {
55 | let args = arguments;
56 | return this.replace(/{(\d+)}/g, function (match, number) {
57 | return typeof args[number] !== 'undefined'
58 | ? args[number]
59 | : match
60 | ;
61 | });
62 | };
63 | }
64 | export default Util;
65 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/ArticleList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
14 |
15 |
16 | {{ value.meta.title }}
17 |
27 |
28 |
{{ categories[value.meta.category - 1] }}
30 |
{{ '#' + tag + ' ' }}
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
149 |
256 |
--------------------------------------------------------------------------------
/src/components/ArticleMeta.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
50 |
106 |
--------------------------------------------------------------------------------
/src/components/ArticleMetaDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
74 |
88 |
160 |
--------------------------------------------------------------------------------
/src/components/ArticleSideBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
共 {{flowers}} 个赞
4 |
赞
5 |
取消赞
6 |
回到顶部
7 |
去评论
8 |
9 |
10 |
11 |
70 |
--------------------------------------------------------------------------------
/src/components/Avatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
44 |
81 |
--------------------------------------------------------------------------------
/src/components/Comment.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
107 |
185 |
186 |
--------------------------------------------------------------------------------
/src/components/Dialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ dialog.title }}
6 |
{{ dialog.text }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
50 |
51 |
56 |
57 |
88 |
--------------------------------------------------------------------------------
/src/components/DisplayPanels.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
47 |
48 |
61 |
--------------------------------------------------------------------------------
/src/components/Footers.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
34 |
35 |
81 |
--------------------------------------------------------------------------------
/src/components/HomeLinkTitle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
KOTLIN CHINA 上线了!
4 | 编辑
5 |
6 |
7 |
8 |
9 |
编辑首页链接
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
60 |
61 |
66 |
67 |
118 |
--------------------------------------------------------------------------------
/src/components/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
绑定GitHub账号到一个已创建的账号
10 |
11 |
{{ error.value }}
12 |
13 |
{{ error.value }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
首次使用GitHub账号登录,需要完善以下基本信息
23 |
24 |
{{ error.value }}
25 |
26 |
{{ error.value }}
27 |
28 |
{{ error.value }}
29 |
31 |
{{ error.value }}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
199 |
200 |
205 |
206 |
303 |
--------------------------------------------------------------------------------
/src/components/NameCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 | {{user.username}}
11 |
12 |
13 |
14 |
15 |
16 | 文章数:{{articleCount}}
17 | 回帖数:{{replyCount}}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
73 |
74 |
79 |
80 |
137 |
--------------------------------------------------------------------------------
/src/components/Reply.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
53 |
69 |
--------------------------------------------------------------------------------
/src/components/ReplyList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
8 |
9 |
10 | {{ value.user.username }}
11 | {{ value.meta.create_time | moment}}
12 |
14 | 删除
15 |
16 |
22 |
23 |
#该评论内容涉嫌违规已被冻结, 申诉请联系管理员#
24 |
#该评论已被删除#
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
135 |
136 |
--------------------------------------------------------------------------------
/src/components/SideBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
发布新话题
4 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
编辑首公告栏
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
77 |
78 |
83 |
84 |
205 |
--------------------------------------------------------------------------------
/src/components/TopicBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
17 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/UserBaseInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![]()
6 |
{{ me.info().username }}
7 |
文章数{{articleCount}},回帖数{{replyCount}}
8 |
9 |
53 |
54 |
55 |
56 |
157 |
158 |
186 |
--------------------------------------------------------------------------------
/src/componentsMobile/ArticleList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ categories[value.meta.category - 1] }}
8 |
9 | {{ value.meta.title }}
10 |
11 |
12 | {{value.author.username}}
13 | 发布于 {{ value.meta.create_time | moment}}, {{ value.replies }}评论
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
75 |
76 |
147 |
--------------------------------------------------------------------------------
/src/componentsMobile/Drawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kotlin China
6 | 使用 PC 登录, 体验完整功能
7 |
8 |
12 |
13 |
14 |
15 |
16 |
60 |
--------------------------------------------------------------------------------
/src/componentsMobile/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Kotlin CHINA
7 |
8 |
注册
10 |
11 |
登录
13 |
14 |
15 |
16 |
绑定GitHub账号到一个已创建的账号
17 |
18 |
{{ error.value }}
19 |
20 |
{{ error.value }}
21 |
22 |
23 |
24 |
25 |
使用GitHub账号首次登录需要完善以下基本信息
26 |
27 |
{{ error.value }}
28 |
29 |
{{ error.value }}
30 |
31 |
{{ error.value }}
32 |
34 |
{{ error.value }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
152 |
153 |
300 |
--------------------------------------------------------------------------------
/src/layout/AppMobile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
62 |
63 |
211 |
212 |
213 |
257 |
--------------------------------------------------------------------------------
/src/layout/AppMobileSun.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
11 |
12 |
13 |
14 |
15 |
28 |
29 |
103 |
--------------------------------------------------------------------------------
/src/layout/AppWeb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
61 |
62 |
63 |
77 |
78 |
127 |
--------------------------------------------------------------------------------
/src/layout/AppWebFixContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
49 |
50 |
82 |
83 |
84 |
133 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import page from 'page'
2 | import routes from './router/routes'
3 | import Event from './assets/js/Event.js'
4 | import moment from 'moment';
5 | import Vue from 'vue'
6 |
7 | Vue.config.productionTip = false;
8 |
9 | moment.locale('zh-cn');
10 | Vue.filter('moment', function (value) {
11 | return moment(value).fromNow();
12 | });
13 |
14 | function isMobile() {
15 | let ua = navigator.userAgent;
16 | return ua.match(/(Android)[\s\/]+([\d\.]+)/) !== null
17 | || ua.match(/(iPad|iPhone|iPod)\s+OS\s([\d_\.]+)/) !== null
18 | || ua.match(/(Windows\s+Phone)\s([\d\.]+)/) !== null;
19 | }
20 |
21 | Array.prototype.indexOf = function (val) {
22 | for (let i = 0; i < this.length; i++) {
23 | if (this[i] === val) return i;
24 | }
25 | return -1;
26 | };
27 |
28 | Array.prototype.remove = function (val) {
29 | const index = this.indexOf(val);
30 | if (index > -1) {
31 | this.splice(index, 1);
32 | }
33 | };
34 |
35 | const app = new Vue({
36 | el: '#app',
37 | data: {ViewComponent: {render: h => h('div', 'loading...')}},
38 | render (h) {
39 | return h(this.ViewComponent)
40 | }
41 | });
42 |
43 | Object.keys(routes).forEach(route => {
44 | let Component;
45 | if (isMobile() && !route.startsWith("/m")) {
46 | Component = require(routes["/m" + route] + '.vue');
47 | } else {
48 | Component = require(routes[route] + '.vue');
49 | }
50 | page(route, (ctx) => {
51 | app.$root.params = ctx.params;
52 | app.ViewComponent = Component;
53 | Event.emit('route-update', '');
54 | }
55 | )
56 | });
57 |
58 | page('*', () => app.ViewComponent = require('./views/404.vue'));
59 |
60 | page();
61 |
--------------------------------------------------------------------------------
/src/router/routes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | '/': './views/Home',
3 | '/edit': './views/Edit',
4 | '/post/:id': './views/Post',
5 | '/edit/:id': './views/Edit',
6 | '/topic': './views/Topic',
7 | '/topic/:key': './views/Topic',
8 | '/topic/:key/:id': './views/Topic',
9 | '/user/:uid':'./views/User',
10 |
11 | '/m/': './viewsMobile/Home',
12 | '/m/post/:id': './viewsMobile/Post',
13 | '/m/edit': './viewsMobile/Edit',
14 | '/m/edit/:id': './viewsMobile/Edit',
15 | '/m/comments/:id': './viewsMobile/Comments',
16 | '/comments/:id': './viewsMobile/Comments',
17 | '/m/topic': './views/404',
18 | '/m/topic/:key': './views/404',
19 | '/m/topic/:key/:id': './views/404',
20 | '/m/user/:uid':'./views/User'
21 | }
22 |
--------------------------------------------------------------------------------
/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
404
4 |
你来到什么都没有的处女地。
5 |
6 |
7 |
8 |
10 |
17 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
78 |
79 |
134 |
--------------------------------------------------------------------------------
/src/views/Post.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
15 |
编辑
16 |
17 |
18 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
125 |
126 |
195 |
--------------------------------------------------------------------------------
/src/views/Topic.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
12 |
13 |
14 |
120 |
121 |
156 |
--------------------------------------------------------------------------------
/src/views/User.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | - 热门文章
7 | - 最近回复
8 | - 新消息提醒
9 |
10 |
11 |
12 |
没有新消息提醒
13 |
14 |
15 |
16 |
17 |
59 |
60 |
--------------------------------------------------------------------------------
/src/viewsMobile/Comments.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
24 |
25 |
26 |
92 |
93 |
124 |
--------------------------------------------------------------------------------
/src/viewsMobile/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
70 |
71 |
109 |
--------------------------------------------------------------------------------
/src/viewsMobile/Post.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ categories[article.article.category - 1]}}
8 |
9 | {{ article.article.title }}
10 |
11 |
12 | {{article.author.username}} 发布于 {{article.article.create_time | moment}}
13 |
14 |
15 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
93 |
94 |
95 |
176 |
177 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/.gitkeep
--------------------------------------------------------------------------------
/static/css/atom-one-dark.min.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:0.5em;color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-formula{color:#c678dd}.hljs-section,.hljs-name,.hljs-selector-tag,.hljs-deletion,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta-string{color:#98c379}.hljs-built_in,.hljs-class .hljs-title{color:#e6c07b}.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-number{color:#d19a66}.hljs-symbol,.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#61aeee}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}.hljs-link{text-decoration:underline}
--------------------------------------------------------------------------------
/static/css/reset.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Set default font family to sans-serif.
5 | * 2. Prevent iOS and IE text size adjust after device orientation change,
6 | * without disabling user zoom.
7 | */
8 |
9 | *{
10 | margin: 0;
11 | padding: 0;
12 | border: 0;
13 | }
14 |
15 | html {
16 | font-family: sans-serif; /* 1 */
17 | -ms-text-size-adjust: 100%; /* 2 */
18 | -webkit-text-size-adjust: 100%; /* 2 */
19 | height: 100%;
20 | }
21 |
22 | /**
23 | * Remove default margin.
24 | */
25 |
26 | body {
27 | margin: 0;
28 | height: 100%;
29 | }
30 |
31 | /* HTML5 display definitions
32 | ========================================================================== */
33 |
34 | /**
35 | * Correct `block` display not defined for any HTML5 element in IE 8/9.
36 | * Correct `block` display not defined for `details` or `summary` in IE 10/11
37 | * and Firefox.
38 | * Correct `block` display not defined for `main` in IE 11.
39 | */
40 |
41 | article,
42 | aside,
43 | details,
44 | figcaption,
45 | figure,
46 | footer,
47 | header,
48 | main,
49 | menu,
50 | nav,
51 | section,
52 | summary {
53 | display: block;
54 | }
55 |
56 | /**
57 | * 1. Correct `inline-block` display not defined in IE 8/9.
58 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
59 | */
60 |
61 | audio,
62 | canvas,
63 | progress,
64 | video {
65 | display: inline-block; /* 1 */
66 | vertical-align: baseline; /* 2 */
67 | }
68 |
69 | /**
70 | * Prevent modern browsers from displaying `audio` without controls.
71 | * Remove excess height in iOS 5 devices.
72 | */
73 |
74 | audio:not([controls]) {
75 | display: none;
76 | height: 0;
77 | }
78 |
79 | /**
80 | * Address `[hidden]` styling not present in IE 8/9/10.
81 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
82 | */
83 |
84 | [hidden],
85 | template {
86 | display: none;
87 | }
88 |
89 | /* Links
90 | ========================================================================== */
91 |
92 | /**
93 | * Remove the gray background color from active links in IE 10.
94 | */
95 |
96 | a {
97 | background-color: transparent;
98 | }
99 |
100 | /**
101 | * Improve readability of focused elements when they are also in an
102 | * active/hover state.
103 | */
104 |
105 | a:active,
106 | a:hover {
107 | outline: 0;
108 | }
109 |
110 | /* Text-level semantics
111 | ========================================================================== */
112 |
113 | /**
114 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
115 | */
116 |
117 | abbr[title] {
118 | border-bottom: 1px dotted;
119 | }
120 |
121 | /**
122 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
123 | */
124 |
125 | b,
126 | strong {
127 | font-weight: bold;
128 | }
129 |
130 | /**
131 | * Address styling not present in Safari and Chrome.
132 | */
133 |
134 | dfn {
135 | font-style: italic;
136 | }
137 |
138 | /**
139 | * Address variable `h1` font-size and margin within `section` and `article`
140 | * contexts in Firefox 4+, Safari, and Chrome.
141 | */
142 |
143 | h1 {
144 | font-size: 2em;
145 | margin: 0.67em 0;
146 | }
147 |
148 | /**
149 | * Address styling not present in IE 8/9.
150 | */
151 |
152 | mark {
153 | background: #ff0;
154 | color: #000;
155 | }
156 |
157 | /**
158 | * Address inconsistent and variable font size in all browsers.
159 | */
160 |
161 | small {
162 | font-size: 80%;
163 | }
164 |
165 | /**
166 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
167 | */
168 |
169 | sub,
170 | sup {
171 | font-size: 75%;
172 | line-height: 0;
173 | position: relative;
174 | vertical-align: baseline;
175 | }
176 |
177 | sup {
178 | top: -0.5em;
179 | }
180 |
181 | sub {
182 | bottom: -0.25em;
183 | }
184 |
185 | /* Embedded content
186 | ========================================================================== */
187 |
188 | /**
189 | * Remove border when inside `a` element in IE 8/9/10.
190 | */
191 |
192 | img {
193 | border: 0;
194 | }
195 |
196 | /**
197 | * Correct overflow not hidden in IE 9/10/11.
198 | */
199 |
200 | svg:not(:root) {
201 | overflow: hidden;
202 | }
203 |
204 | /* Grouping content
205 | ========================================================================== */
206 |
207 | /**
208 | * Address margin not present in IE 8/9 and Safari.
209 | */
210 |
211 | figure {
212 | margin: 1em 40px;
213 | }
214 |
215 | /**
216 | * Address differences between Firefox and other browsers.
217 | */
218 |
219 | hr {
220 | box-sizing: content-box;
221 | height: 0;
222 | }
223 |
224 | /**
225 | * Contain overflow in all browsers.
226 | */
227 |
228 | pre {
229 | overflow: auto;
230 | }
231 |
232 | /**
233 | * Address odd `em`-unit font size rendering in all browsers.
234 | */
235 |
236 | code,
237 | kbd,
238 | pre,
239 | samp {
240 | font-family: monospace, monospace;
241 | font-size: 1em;
242 | }
243 |
244 | /* Forms
245 | ========================================================================== */
246 |
247 | /**
248 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
249 | * styling of `select`, unless a `border` property is set.
250 | */
251 |
252 | /**
253 | * 1. Correct color not being inherited.
254 | * Known issue: affects color of disabled elements.
255 | * 2. Correct font properties not being inherited.
256 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
257 | */
258 |
259 | button,
260 | input,
261 | optgroup,
262 | select,
263 | textarea {
264 | color: inherit; /* 1 */
265 | font: inherit; /* 2 */
266 | margin: 0; /* 3 */
267 | }
268 |
269 | /**
270 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
271 | */
272 |
273 | button {
274 | overflow: visible;
275 | }
276 |
277 | /**
278 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
279 | * All other form control elements do not inherit `text-transform` values.
280 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
281 | * Correct `select` style inheritance in Firefox.
282 | */
283 |
284 | button,
285 | select {
286 | text-transform: none;
287 | }
288 |
289 | /**
290 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
291 | * and `video` controls.
292 | * 2. Correct inability to style clickable `input` types in iOS.
293 | * 3. Improve usability and consistency of cursor style between image-type
294 | * `input` and others.
295 | */
296 |
297 | button,
298 | html input[type="button"], /* 1 */
299 | input[type="reset"],
300 | input[type="submit"] {
301 | -webkit-appearance: button; /* 2 */
302 | cursor: pointer; /* 3 */
303 | }
304 |
305 | /**
306 | * Re-set default cursor for disabled elements.
307 | */
308 |
309 | button[disabled],
310 | html input[disabled] {
311 | cursor: default;
312 | }
313 |
314 | /**
315 | * Remove inner padding and border in Firefox 4+.
316 | */
317 |
318 | button::-moz-focus-inner,
319 | input::-moz-focus-inner {
320 | border: 0;
321 | padding: 0;
322 | }
323 |
324 | /**
325 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
326 | * the UA stylesheet.
327 | */
328 |
329 | input {
330 | line-height: normal;
331 | }
332 |
333 | /**
334 | * It's recommended that you don't attempt to style these elements.
335 | * Firefox's implementation doesn't respect box-sizing, padding, or width.
336 | *
337 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
338 | * 2. Remove excess padding in IE 8/9/10.
339 | */
340 |
341 | input[type="checkbox"],
342 | input[type="radio"] {
343 | box-sizing: border-box; /* 1 */
344 | padding: 0; /* 2 */
345 | }
346 |
347 | /**
348 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain
349 | * `font-size` values of the `input`, it causes the cursor style of the
350 | * decrement button to change from `default` to `text`.
351 | */
352 |
353 | input[type="number"]::-webkit-inner-spin-button,
354 | input[type="number"]::-webkit-outer-spin-button {
355 | height: auto;
356 | }
357 |
358 | /**
359 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
360 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
361 | */
362 |
363 | input[type="search"] {
364 | -webkit-appearance: textfield; /* 1 */
365 | box-sizing: content-box; /* 2 */
366 | }
367 |
368 | /**
369 | * Remove inner padding and search cancel button in Safari and Chrome on OS X.
370 | * Safari (but not Chrome) clips the cancel button when the search input has
371 | * padding (and `textfield` appearance).
372 | */
373 |
374 | input[type="search"]::-webkit-search-cancel-button,
375 | input[type="search"]::-webkit-search-decoration {
376 | -webkit-appearance: none;
377 | }
378 |
379 | /**
380 | * Define consistent border, margin, and padding.
381 | */
382 |
383 | fieldset {
384 | border: 1px solid #c0c0c0;
385 | margin: 0 2px;
386 | padding: 0.35em 0.625em 0.75em;
387 | }
388 |
389 | /**
390 | * 1. Correct `color` not being inherited in IE 8/9/10/11.
391 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
392 | */
393 |
394 | legend {
395 | border: 0; /* 1 */
396 | padding: 0; /* 2 */
397 | }
398 |
399 | /**
400 | * Remove default vertical scrollbar in IE 8/9/10/11.
401 | */
402 |
403 | textarea {
404 | overflow: auto;
405 | }
406 |
407 | /**
408 | * Don't inherit the `font-weight` (applied by a rule above).
409 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
410 | */
411 |
412 | optgroup {
413 | font-weight: bold;
414 | }
415 |
416 | /* Tables
417 | ========================================================================== */
418 |
419 | /**
420 | * Remove most spacing between table cells.
421 | */
422 |
423 | table {
424 | border-collapse: collapse;
425 | border-spacing: 0;
426 | }
427 |
428 | td,
429 | th {
430 | padding: 0;
431 | }
432 | .clearfix {
433 | overflow: hidden;
434 | _zoom: 1;
435 | }
436 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/favicon.ico
--------------------------------------------------------------------------------
/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/static/js/rangeFn.js:
--------------------------------------------------------------------------------
1 | // 获取光标位置
2 | function getCursortPosition(textDom) {
3 | var cursorPos = 0;
4 | if (document.selection) {
5 | // IE Support
6 | textDom.focus();
7 | var selectRange = document.selection.createRange();
8 | selectRange.moveStart('character', -textDom.value.length);
9 | cursorPos = selectRange.text.length;
10 | } else if (textDom.selectionStart || textDom.selectionStart == '0') {
11 | // Firefox support
12 | cursorPos = textDom.selectionStart;
13 | }
14 | return cursorPos;
15 | }
16 |
17 | // 设置光标位置
18 | function setCaretPosition(textDom, pos) {
19 | if (textDom.setSelectionRange) {
20 | // IE Support
21 | textDom.focus();
22 | textDom.setSelectionRange(pos, pos);
23 | } else if (textDom.createTextRange) {
24 | // Firefox support
25 | var range = textDom.createTextRange();
26 | range.collapse(true);
27 | range.moveEnd('character', pos);
28 | range.moveStart('character', pos);
29 | range.select();
30 | }
31 | }
32 | // 获取选中文字
33 | function getSelectText() {
34 | var userSelection, text;
35 | if (window.getSelection) {
36 | // Firefox support
37 | userSelection = window.getSelection();
38 | } else if (document.selection) {
39 | // IE Support
40 | userSelection = document.selection.createRange();
41 | }
42 | if (!(text = userSelection.text)) {
43 | text = userSelection;
44 | }
45 | return text;
46 | }
47 |
48 | /**
49 | * 选中特定范围的文本
50 | * 参数:
51 | * textDom [JavaScript DOM String] 当前对象
52 | * startPos [Int] 起始位置
53 | * endPos [Int] 终点位置
54 | */
55 | function setSelectText(textDom, startPos, endPos) {
56 | var startPos = parseInt(startPos),
57 | endPos = parseInt(endPos),
58 | textLength = textDom.value.length;
59 | if (textLength) {
60 | if (!startPos) {
61 | startPos = 0;
62 | }
63 | if (!endPos) {
64 | endPos = textLength;
65 | }
66 | if (startPos > textLength) {
67 | startPos = textLength;
68 | }
69 | if (endPos > textLength) {
70 | endPos = textLength;
71 | }
72 | if (startPos < 0) {
73 | startPos = textLength + startPos;
74 | }
75 | if (endPos < 0) {
76 | endPos = textLength + endPos;
77 | }
78 | if (textDom.createTextRange) {
79 | // IE Support
80 | var range = textDom.createTextRange();
81 | range.moveStart("character", -textLength);
82 | range.moveEnd("character", -textLength);
83 | range.moveStart("character", startPos);
84 | range.moveEnd("character", endPos);
85 | range.select();
86 | } else {
87 | // Firefox support
88 | textDom.setSelectionRange(startPos, endPos);
89 | textDom.focus();
90 | }
91 | }
92 | }
93 | function insertAfterText(textDom, value) {
94 | var selectRange;
95 | if (document.selection) {
96 | // IE Support
97 | textDom.focus();
98 | selectRange = document.selection.createRange();
99 | selectRange.text = value;
100 | textDom.focus();
101 | } else if (textDom.selectionStart || textDom.selectionStart == '0') {
102 | // Firefox support
103 | var startPos = textDom.selectionStart;
104 | var endPos = textDom.selectionEnd;
105 | var scrollTop = textDom.scrollTop;
106 | textDom.value = textDom.value.substring(0, startPos) + value + textDom.value.substring(endPos, textDom.value.length);
107 | textDom.focus();
108 | textDom.selectionStart = startPos + value.length;
109 | textDom.selectionEnd = startPos + value.length;
110 | textDom.scrollTop = scrollTop;
111 | } else {
112 | textDom.value += value;
113 | textDom.focus();
114 | }
115 | }
116 |
117 | module.exports = {
118 | getCursortPosition:getCursortPosition,
119 | setCaretPosition:setCaretPosition,
120 | getSelectText:getSelectText,
121 | setSelectText:setSelectText,
122 | insertAfterText:insertAfterText
123 | };
124 |
--------------------------------------------------------------------------------
/static/skin/default/icon-ext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/skin/default/icon-ext.png
--------------------------------------------------------------------------------
/static/skin/default/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/skin/default/icon.png
--------------------------------------------------------------------------------
/static/skin/default/loading-0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/skin/default/loading-0.gif
--------------------------------------------------------------------------------
/static/skin/default/loading-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/skin/default/loading-1.gif
--------------------------------------------------------------------------------
/static/skin/default/loading-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kotlin-lang-CN/Kotlin-CN-Frontend/a57f84e5556697f304fb87ceb31c69b2566fcb11/static/skin/default/loading-2.gif
--------------------------------------------------------------------------------
/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/unit/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
5 | // require all test files (files that ends with .spec.js)
6 | const testsContext = require.context('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | // This is a karma config file. For more details see
2 | // http://karma-runner.github.io/0.13/config/configuration-file.html
3 | // we are also using it with karma-webpack
4 | // https://github.com/webpack/karma-webpack
5 |
6 | var webpackConfig = require('../../build_tools/webpack.test.conf')
7 |
8 | module.exports = function (config) {
9 | config.set({
10 | // to run in additional browsers:
11 | // 1. install corresponding karma launcher
12 | // http://karma-runner.github.io/0.13/config/browsers.html
13 | // 2. add it to the `browsers` array below.
14 | browsers: ['PhantomJS'],
15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
16 | reporters: ['spec', 'coverage'],
17 | webpack: webpackConfig,
18 | webpackMiddleware: {
19 | noInfo: true
20 | },
21 | coverageReporter: {
22 | dir: './coverage',
23 | reporters: [
24 | { type: 'lcov', subdir: '.' },
25 | { type: 'text-summary' }
26 | ]
27 | }
28 | })
29 | }
30 |
--------------------------------------------------------------------------------
/test/unit/specs/Hello.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Hello from '@/components/Hello'
3 |
4 | describe('Edit.vue', () => {
5 | it('should render correct contents', () => {
6 | const Constructor = Vue.extend(Hello)
7 | const vm = new Constructor().$mount()
8 | expect(vm.$el.querySelector('.hello h1').textContent)
9 | .to.equal('Welcome to Your Vue.js App')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
6 |-
7 |
8 |
9 |
10 |
11 | {{ value.user.username }}
12 | 评论于 {{ value.meta.create_time | moment}}
13 |
14 | #该评论内容涉嫌违规已被冻结, 申诉请联系管理员#
15 | #该评论已被删除#
16 |
18 |
19 |
20 |
21 | 22 |