Commit 84cd71f2 by member

提交member代码到本地分支

parent 4a8f82f1
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
publicPath: '../../'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'components': resolve('src/components'),
'view': resolve('src/view'),
'common': resolve('src/common')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin');
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new CleanWebpackPlugin(['dist']),
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true,
drop_console: true
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
const proxyConfig = require('./proxyConfig')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: proxyConfig.proxyConfig,
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8002, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
module.exports = {
proxyConfig: {
'/api-auth/': {
target: 'http://gicdev.demogic.com/api-auth/',
changeOrigin: true,
pathRewrite: {
'^/api-auth': ''
}
},
'/api-member/': {
target: 'http://gicdev.demogic.com/api-member/',
changeOrigin: true,
pathRewrite: {
'^/api-member': ''
}
},
'/api-plug/': {
target: 'http://gicdev.demogic.com/api-plug/',
changeOrigin: true,
pathRewrite: {
'^/api-plug': ''
}
},
'/api-admin/': {
target: 'http://gicdev.demogic.com/api-admin/',
changeOrigin: true,
pathRewrite: {
'^/api-admin': ''
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="./static/img/favicon.ico">
<script src="./static/js/loginanimate.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<title>memberproject</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
{
"name": "memberproject",
"version": "1.0.0",
"description": "membermanager",
"author": "smartxiaoxia <hexiaoxia1992@163.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"@antv/g2": "^3.2.6",
"@gic-test/gic-input": "^1.0.8",
"@gic-test/vue-area-ab": "^1.4.3",
"@gic-test/vue-gic-aside-menu": "^1.1.43",
"@gic-test/vue-gic-card": "^1.0.35",
"@gic-test/vue-gic-footer": "^1.0.9",
"@gic-test/vue-gic-header": "^1.3.29",
"@gic-test/vue-gic-people": "^1.1.42",
"@gic-test/vue-gic-store": "^1.0.38",
"@gic-test/vue-gic-store-group": "^1.0.4",
"@gic-test/vue-gic-store-linkage": "^1.0.2",
"packele": "^1.0.2",
"viser-vue": "^2.3.0",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"@gic-test/gic-input": "^1.0.8",
"@gic-test/vue-gic-card": "^1.0.35",
"@gic-test/vue-gic-header": "^1.2.40",
"@gic-test/vue-gic-store": "^1.0.38",
"autoprefixer": "^7.1.2",
"axios": "^0.18.0",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-component": "^1.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"element-ui": "^2.4.3",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-axios": "^2.1.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
created() {
console.log(this);
}
}
</script>
<style>
</style>
let formatTime = val => {
if(val) {
let date = new Date(val);
let Y = date.getFullYear() + '-';
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
let D = (date.getDate() < 10 ? '0'+ (date.getDate()): date.getDate()) + ' ';
let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
let s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
return Y+M+D+h+m+s;
}else {
return '--'
}
}
let formatYMD = val => {
if(val) {
let date = new Date(val);
let Y = date.getFullYear() + '-';
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
let D = (date.getDate() < 10 ? '0'+ (date.getDate()): date.getDate());
return Y+M+D;
}else {
return '--'
}
}
let fomatNumber = val => {
if(val) {
return val
}else {
return 0;
}
}
let fomatText = val => {
if(val) {
return val
}else {
return '--';
}
}
let fomatFloat = val => {
return parseFloat(val).toFixed(2)
}
let defaultImg = val => {
if(!val) {
return './static/img/default.jpg'
}
}
export { formatTime,fomatNumber,fomatText,formatYMD,fomatFloat,defaultImg }
<template>
<div>
<div class="gic-form-item" :class="{'curfocus': curfocus, 'gic-input': changeBColor}">
<div class="gic-form-wrap">
<input ref="input" class="gic-form-input" type="text" :max="max" :value="value" @keyup="handleKeyup($event.target.value)"
v-on:input="update($event.target.value)" @focus="handleFocus(true)" @blur="handleFocus(false)" :placeholder="placeholder">
</div>
<div class="gic-form-count"> {{ curcount }}/{{ max }}</div>
</div>
</div>
</template>
<script>
export default {
name: "gicinput",
data() {
return {
curcount: 0,
curfocus: false
}
},
props: [ 'value','max','placeholder','rules', 'changeBColor'],
watch: {
value(newVal,oldVal) {
var byteValLen = 0;
var max = this.max;
if(newVal) {
for (var i = 0; i < newVal.length; i++) {
if (newVal[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
this.curcount = Math.floor(byteValLen);
}
}else {
this.curcount = 0;
}
}
},
mounted() {
var byteValLen = 0;
var max = this.max;
if(this.value) {
for (var i = 0; i < this.value.length; i++) {
if (this.value[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
this.curcount = Math.floor(byteValLen);
}
}else {
this.curcount = 0;
}
},
methods: {
handleFocus(boolean) {
this.curfocus = boolean;
},
update: function(value) {
this.$emit('input', value)
},
handleKeyup: _.debounce(
function(val) {
var returnValue = '';
var byteValLen = 0;
var max = this.max;
if(val) {
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
returnValue += val[i];
this.curcount = Math.floor(byteValLen);
this.$emit('input', returnValue);
}
}else {
this.curcount = 0;
this.$emit('input', returnValue)
}
}
, 20)
},
}
</script>
<style scoped>
.gic-form-item{
width: 100%;
height: 40px;
line-height: 40px;
overflow: hidden;
display: flex;
border: 1px solid #dcdfe6;
box-sizing: border-box;
border-radius: 4px;
}
.gic-form-wrap{
flex: 1;
margin-left: 10px;
}
.gic-form-count{
flex: 0 0 40px;
width: 40px;
text-align: center;
color: #c0c4cc;
font-size: 12px;
}
.gic-form-input{
width: 100%;
height: 100%;
border: 0;
outline: none;
font-size: 14px;
color: #606266;
}
.gic-input {
border-color: #f00;
}
.curfocus{
border: 1px solid #409eff;
}
.requirevalue{
line-height: 0;
color: red;
}
</style>
<template>
<div>
<div class="gic-formarea-item" :class="{'curfocus': curfocus}">
<div class="gic-formarea-wrap">
<textarea @keyup="handleKeyup($event.target.value)" ref="input" class="gic-form-area" :max="max" :value="value"
v-on:input="update($event.target.value)" @focus="handleFocus(true)" @blur="handleFocus(false)" :placeholder="placeholder">
</textarea>
</div>
</div>
<div class="gic-formarea-count"> {{ curcount }}/{{ max }}</div>
</div>
</template>
<script>
export default {
name: "gicinput",
data() {
return {
curcount: 0,
curfocus: false,
curValue: '',
avalue: ''
}
},
props: [ 'value','max','placeholder'],
watch: {
value(newVal,oldVal) {
var byteValLen = 0;
var max = this.max;
if(newVal) {
for (var i = 0; i < newVal.length; i++) {
if (newVal[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
this.curcount = Math.floor(byteValLen);
}
}else {
this.curcount = 0;
}
}
},
mounted() {
var byteValLen = 0;
var max = this.max;
if(this.value) {
for (var i = 0; i < this.value.length; i++) {
if (this.value[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
this.curcount = Math.floor(byteValLen);
}
}else {
this.curcount = 0;
}
},
methods: {
handleFocus(boolean) {
this.curfocus = boolean;
},
update: function(value) {
this.$emit('input', value)
},
handleKeyup: _.debounce(
function(val) {
var returnValue = '';
var byteValLen = 0;
var max = this.max;
if(val) {
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
returnValue += val[i];
this.curcount = Math.floor(byteValLen);
this.$emit('input', returnValue);
}
}else {
this.curcount = 0;
this.$emit('input', returnValue)
}
}
,800)
}
}
</script>
<style scoped>
.gic-formarea-item{
width: 100%;
height: 116px;
padding: 5px;
border: 1px solid #dcdfe6;
/*line-height: 38px;*/
display: flex;
box-sizing: border-box;
border-radius: 4px;
}
.gic-formarea-wrap{
flex: 1;
/*margin-left: 10px;*/
}
.gic-form-area{
width: 98%;
height: 100px;
border: 0;
line-height: 20px;
font-size: 12px;
color: #444;
outline: none;
}
.gic-formarea-count{
line-height: 20px;
text-align: right;
color: #c0c4cc;
font-size: 12px;
}
.curfocus{
border: 1px solid #409eff;
}
</style>
<template>
<div class="navbarwrap">
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in navpath" :key="index">
<a :href="homeurl" v-if="index === 0">{{item.name}}</a>
<router-link tag="span" class="member-bread-link" :to="item.path" :class="{ 'member-nav-link' : item.path !== '' }" v-if="index !== 0">
{{item.name}}
</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
<div class="navtitle">
{{ navpath[navpath.length - 1].name }}
<slot name="member"></slot>
</div>
</div>
</template>
<script>
export default {
name: "navpath",
data() {
return {
curitem: 0,
homeurl: `${window.origin}/report/#/memberSummary`,
}
},
props: {
navpath: {
type: Array,
default: []
}
},
methods: {
}
}
</script>
<style lang="stylus" scoped>
.navbarwrap
padding 20px 25px
background-color #fff
border-radius 2px
.navtitle
margin 24px 0 0 0
font-size 20px
color #303133
font-weight 700
.wechat-setting
float right
padding 8px 20px
text-align right
line-height 1
border 1px solid #dcdfe6
color #606266
box-sizing border-box
font-size 14px
border-radius 4px
cursor pointer
.boxshow
border-bottom 1px solid #e4e7ed
box-shadow 5px -1px 5px #dfdfdf
.member-bread-link
cursor pointer
.member-nav-link
font-weight 700
color #303133
</style>
<template>
<div class="navbarwrap boxshow">
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item,index) in navpath" :to="{ path: item.path}" :key="index">{{ item.name }}</el-breadcrumb-item>
</el-breadcrumb>
<h1 class="navtitle">{{ navpath[navpath.length - 1].name }}</h1>
<div class="navbar">
<a class="navbar-item" :class="curitem === item.tab ? 'curnavbar' : ''" v-for="(item,index) in navtab" :key="index" @click="changeTab(item)">{{ item.name }}</a>
</div>
</div>
</template>
<script>
export default {
name: "navpath",
data() {
return {
curitem: 1
}
},
props: {
activeitem: {
type: Number,
default: 1
},
navpath: {
type: Array,
default: []
},
navtab: {
type: Array,
default: []
}
},
methods: {
changeTab(item) {
this.curitem = item.tab;
this.$emit('getCurTab',item.tab);
}
},
watch: {
activeitem(newVal,oldVal) {
this.curitem = newVal;
}
}
}
</script>
<style lang="stylus" scoped>
@import "../../../static/css/variables.styl";
.boxshow
border-bottom: 1px solid #e4e7ed;
box-shadow: 5px -1px 5px #dfdfdf;
.navbarwrap
padding: 25px 25px 0 25px;
background-color: #fff;
border-radius: 2px;
.navtitle{
margin: 30px 0 10px 0;
font-size: 20px;
font-weight: 700;
color: #303133;
}
.navbar
margin-top: 10px;
height: 40px;
line-height: 40px;
&-item
display: inline-block;
margin-right: 38px;
font-size: 14px;
color: #303133;
cursor: pointer;
&:last-child
margin-right: 0;
.curnavbar
position: relative;
color: $main-color;
&:after
position: absolute;
content: '';
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 2px;
background-color: $main-color;
</style>
<template>
<el-input ref="input" placeholder="输入会员姓名/昵称/手机号/卡号" clearable prefix-icon="el-icon-search" :style="stylelink"
:value='value' @keyup.enter.native="handleSearch($event.target.value)"></el-input>
</template>
<script>
export default {
name: "searchinput",
props: {
value: {
type: String
},
stylelink: {
type: String,
default: 'width: 200px'
}
},
methods: {
handleSearch(value) {
this.$emit('handleSearch',value)
}
}
}
</script>
<style scoped>
</style>
<template>
<!-- 所有门店分组 -->
<div class="choose-group">
<el-dialog
title="选择门店分组"
width="50%"
@close="closeGroup"
:close-on-click-modal="false"
:visible.sync="show">
<div class="all-store">
<el-tree
:data="storeData"
:props="defaultProps"
node-key="storeGroupId"
:default-expanded-keys="[storeGroupId]"
@node-click="handleNodeClick">
</el-tree>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="closeGroup">取 消</el-button>
<el-button type="primary" @click="confirm">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'gic-group',
props: {
groupVisible: {
type: Boolean,
default: false
}
},
data() {
return{
show: false,
storeGroupId: null,
storeData: [],
defaultProps: {
children: "children",
label: "storeGroupName"
}
};
},
created() {
this.baseURL = window.location.origin.includes('host') ? window.location.origin : 'http://gicdev.demogic.com';
this.getStoreId();
},
methods: {
confirm() {
if (!this.setGroupObj) {
this.$alert("请选择分组", '提示', {
confirmButtonText: "确定"
});
return;
}
this.show = false;
this.$emit('passGroupName', this.setGroupObj);
},
getStoreId() {
this.axios.get(`${this.baseURL}/api-admin/store-group-list`, {
params: {
requestProject: "member"
}
})
.then(res => {
if (res.data.errorCode === 0) {
this.storeData = res.data.result;
this.storeGroupId = this.storeData[0].storeGroupId;
this.dialogVisible = true;
}
});
},
closeGroup() {
this.show = false;
this.$emit('closeGroup', this.show);
},
handleNodeClick(val) {
// 第一次出现
this.setGroupObj = val;
}
},
watch: {
groupVisible(newVal) {
// 借用中间变量修改弹框
this.show = newVal;
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div>ddd</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js Apps'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
<template>
<div class="achievementwrap">
<!-- <div class="achievementwrap-left" :style="{height: (bodyHeight - 64) + 'px'}">
<vue-gic-aside-menu :projectName="projectName" :leftModulesName="leftModulesName" :collapseFlag="$store.state.show"></vue-gic-aside-menu>
</div> -->
<div class="achievementwrap-right">
<div :style="{height: (bodyHeight - 190) + 'px'}">
<!-- <router-view></router-view> -->
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
defaultActive: this.$route.path,
collapseFlag: false,
leftModulesName: '小程序配置',
projectName: 'member'
}
},
mounted() {
window.onresize = () => {
return (() => {
this.bodyHeight = document.body.clientHeight || document.documentElement.clientHeight;
})()
}
}
}
</script>
<style lang="stylus" scoped>
.left-aside-contain {
.el-submenu__title:hover {
background-color: #020b21;
}
}
// 使用页如果有加 scoped ,需要到公共样式中添加
.el-menu.el-menu--popup {
background: #020b21;
border-radius: 4px;
}
.achievementwrap{
display: flex;
min-width: 1200px;
overflow hidden
&-left{
flex: 0 0 200px;
width: 200px;
/*height: 100%;*/
/*overflow-y: auto;*/
background-color: #020b21;
}
&-right{
flex: 1;
overflow: auto;
}
}
.rewardname
margin-left 24px
line-height 0
.footer-box
background-color #f5f7fa
</style>
import { fetch,fetchqs } from "./fetch";
export function doFetch(url,option) {
return fetch(url+'?requestProject=member',option);
}
export function doFetchqs(url,option) {
return fetchqs(url+'?requestProject=member',option);
}
import axios from 'axios'
let qs = require('qs');
export function fetch(url,options) {
return new Promise((resolve, reject) => {
axios.post(url,options).then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
})
}
export function fetchqs(url,options) {
return new Promise((resolve, reject) => {
axios.post(url,qs.stringify(options)).then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
})
}
export default {
baseUrl: 'http://gicdev.demogic.com',
doLogin: '/api-auth/do-login',//登录
enterprise: '/api-auth/list-login-enterprise',//企业列表
clerkMenu: '/api-auth/login-clerk-menu',
tagManageList: '/api-member/tag-manage-list',//标签列表展示
insertParentTag: '/api-member/insert-parent-tag',//新建父级标签
insertSonTag: '/api-member/insert-son-tag',//新建子级标签
deleteParentTag: '/api-member/delete-parent-tag',//删除父级标签
deleteSonTag: '/api-member/delete-son-tag',////删除子级标签
memberMissionList: '/api-member/member-mission-list',//会员任务列表展示-商户
memberMissionUpdate: '/api-member/member-mission-update',//会员任务获取积分设置-商户
memberMissionOpen: '/api-member/member-mission-open',//会员任务开启关闭-商户
achievementList: '/api-member/achievement-list',//消费类/互动类 成就列表展示_商户
achievementUpdate: '/api-member/achievement-update',//成就额度、奖励积分修改_商户
achievementOpen: '/api-member/achievement-open',//成就开启关闭_商户
findMemberGroups: '/api-member/find-member-groups-page',//会员分组列表展示-分页
insertMemberGroup: '/api-member/insert-member-group',//新建会员分组
updateMemberGroup: '/api-member/update-member-group',//修改会员分组
checkCard:'/api-member/member-card-write-off-order-page', // 卡券手动核销_核销订单列表_new
chooseDestory: '/api-member/member-card-destroy', // 手动选取销毁
ajaxMembers: '/api-member/ajax-members',// 会员分组的会员列表
ajaxDealMembers: '/api-member/query-members-page', // 待处理会员列表,
deteleMemberGroup: '/api-member/detele-member-group',//删除会员分组
findMemberGroupInfo: '/api-member/find-member-group-info',//查询单个会员分组信息
findMemberFields: '/api-member/find-member-fields',//列表自定义字段
updateFields: '/api-member/update-fields',//列表自定义字段选择保存
memberCount: '/api-member/ecommerce-member-count',//微信会员、POS会员、电商会员和电商买家 人数统计
updateIntegrals: '/api-member/members-batch-update-integrals',//批量修改会员积分
storeCodeName: '/api-plug/query-store-by-code-name',//查询门店(code和name)
gradeList: '/api-admin/grade-list',//获取会员等级列表
updateGrade: '/api-member/members-batch-update-grade',//批量修改会员等级
updateStore: '/api-member/members-batch-update-store',//批量修改会员主门店、协管门店
batchProcess: '/api-member/get-members-batch-process',//查看批量处理进度
membersSubStoreList: '/api-member/members-sub-store-list',//获取选中会员的协管门店
singleInfo: '/api-member/load-member-single-info',//会员简易信息(会员头像浮层显示)
memberCardsPage: '/api-member/member-cards-page',// 会员卡券列表
writeOff: '/api-member/member-card-write-off',//卡券手动核销
cardDestroy: '/api-member/member-card-destroy',//卡券手动销毁
cardChannelType: '/api-member/card-channel-type',//发卡券渠道(字典列表)
memberDetail: '/api-member/member-load-member-detail',//微信 - 会员详情(头部+会员卡号+9宫格)
baseDetail: '/api-member/member-load-base-detail',//微信- 会员基本信息(可编辑修改)
addressPage: '/api-member/member-load-receiving-address-page',//收货地址
extendInfo: '/api-member/member-load-extend-info',//标准+扩展+类目
logPage: '/api-member/member-load-operate-log-page',//操作日志
memberGrade: '/api-member/member-load-update-member-grade',//修改会员等级
updateBaseDetail: '/api-member/member-load-update-base-detail',//修改微信会员基本信息
updateImgUrl: '/api-member/member-load-update-imgUrl',//修改头像
ajaxMembersInfo: '/api-member/ajax-members-info',//会员消费统计信息
memberOrderPage: '/api-member/member-order-page',//会员order列表
orderDetail: '/api-member/member-order-detail',//会员order-查看详情(订单详情+评价+回复)
infoMall: '/api-member/ajax-members-info-mall',//微信商城 - 会员消费统计信息
ajaxWechatMembers: '/api-member/ajaxMembers',//会员微信商城order列表
talkLogPage: '/api-member/member-talk-log-page',//会员通话记录列表
clerkTags: '/api-member/member-load-find-clerk-tags',//获取导购标签列表
updateClerkTags: '/api-member/member-load-update-clerk-tags',// 修改导购标签
codeList: '/api-member/get-naction-code-list',//手机号段 列表
updateRemark: '/api-member/member-load-update-remark',//修改导购备注信息
integralDataPage: '/api-member/member-integral-data-page',//积分列表(积分明细列表)
integralOperateType: '/api-member/member-integral-operate-type',//积分操作类型(字典列表)
memberIntegralUpdate: '/api-member/member-integral-update',// 调整积分进行保存
enterpriseInfo: '/api-member/enterprise-info',//获取企业配置信息
posInfo: '/api-member/member-load-member-pos-info',//pos会员详情 会员信息
posBaseDetail: '/api-member/member-load-update-pos-base-detail',//修改pos会员基本信息
batchUpdateStore: '/api-member/batch-update-members-store' // 回收站修改主门店
}
<template>
<div class="buyer-part">
<Navbar></Navbar>
电商买家
</div>
</template>
<script>
import Navbar from '@/view/layout/components/Navbar.vue';
export default {
name: "buyermembers",
components: {
Navbar
}
}
</script>
<style lang="stylus" scoped>
.buyer-part
padding 0 20px
</style>
\ No newline at end of file
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
import { setTimeout } from 'timers';
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
},
total: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 100);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
let that = this;
// 更新的时候要移除先destroy
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
新增会员: {
min: 1
},
会员率: {
min: 0,
formatter: function formatter(val) {
return ((Number(val) / that.total) * 100).toFixed(2) + '%';
}
}
});
this.chart.legend({
items: [{
value: '会员率',
marker: {
symbol: 'square',
fill: '#3182bd',
radius: 5
}
}, {
value: '新增会员',
marker: {
symbol: 'hyphen',
stroke: '#fdae6b',
radius: 5,
lineWidth: 4
}
}],
onClick: function onClick(ev) {
var item = ev.item;
var value = item.value;
var checked = ev.checked;
var geoms = chart.getAllGeoms();
for (var i = 0; i < geoms.length; i++) {
var geom = geoms[i];
if (geom.getYScale().field === value) {
if (checked) {
geom.show();
} else {
geom.hide();
}
}
}
}
});
this.chart.axis('新增会员', {
grid: null,
label: {
textStyle: {
fill: '#fdae6b',
width: 5
}
}
});
this.chart.interval().position('keyword*会员率').color('#3182bd').size(40);
this.chart.line().position('keyword*新增会员').color('#fdae6b').size(3).shape('smooth');
this.chart.point().position('keyword*新增会员').color('#fdae6b').size(3).shape('circle');
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
},
height: {
type: Number,
default: null
},
value: {
type: String,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart(); // 第一步是创建的时候更新图表,但是这个不适用于异步请求接口获取相关数据,采用下面的监听的方式
}, 100);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
let data = {
container: this.id,
width: this.width
};
if (!!this.height) {
data = {...data, height: this.height};
}
this.chart = new G2.Chart(data);
this.chart.source(this.charData);
// this.chart.source(this.charData, {
// 成交人数: {
// formatter: function formatter(val) {
// return val;
// }
// }
// });
this.chart.area().position(`keyword*${this.value}`).color('white').shape('smooth');
this.chart.line().position(`keyword*${this.value}`).color('white').shape('smooth');
this.chart.guide().regionFilter({
top: true,
start: ['min', 0],
end: ['max', 'min'],
color: '#18a1cd'
});
this.chart.guide().regionFilter({
top: true,
start: ['min', 'max'],
end: ['max', 0],
color: '#FF4D4F'
});
this.chart.guide().region({
top: false,
start: [2000, 'max'],
end: [2016, 'min']
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
import { setTimeout } from 'timers';
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 30);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
新增会员: {
formatter: function formatter(val) {
return val + '%';
}
}
});
this.chart.scale('sales',{
tickInterval: 20
});
this.chart.interval().position('keyword*新增会员').size(40);
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id">
</div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart(); // 第一步是创建的时候更新图表,但是这个不适用于异步请求接口获取相关数据,采用下面的监听的方式
}, 30);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width,
height: 300,
});
this.chart.source(this.charData);
this.chart.scale('value', {
min: 0
});
this.chart.scale('year', {
range: [0, 1]
});
this.chart.tooltip({
crosshairs: {
type: 'line'
}
});
this.chart.line().position('year*value');
this.chart.point().position('year*value').size(4).shape('circle').style({
stroke: '#fff',
lineWidth: 1
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id">
</div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 200);
},
beforeUpdate() {
console.log(this.charData);
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
value: {
formatter: function formatter(val) {
return val;
}
}
});
this.chart.coord('theta', {
radius: 0.75
});
this.chart.tooltip({
showTitle: false,
itemTpl: '<li><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</li>'
});
this.chart.intervalStack().position('value').color('keyword').label('keyword', {
formatter: function formatter(val, keyword) {
return keyword.point.keyword + ':' + keyword.point.value;
}
}).tooltip('keyword*value', function(item, value) {
value = value;
return {
name: keyword,
value: value
};
}).style({
lineWidth: 1,
stroke: '#fff'
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 30);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width,
height: 200
});
this.chart.source(this.charData, {
value: {
formatter: function formatter(val) {
return val.toFixed(2);
}
}
});
// this.chart.scale('key', {
// range: [0, 1]
// });
this.chart.line().position('keyword*value');
this.chart.render();
}
}
};
</script>
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
this.drawChart();
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
value: {
formatter: function formatter(val) {
return val;
}
}
});
this.chart.coord('theta', {
radius: 0.75
});
this.chart.tooltip({
showTitle: false,
itemTpl: '<li><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</li>'
});
this.chart.intervalStack().position('value').color('keyword').label('keyword', {
formatter: function formatter(val, keyword) {
return keyword.point.keyword + ':' + keyword.point.value;
}
}).tooltip('keyword*value', function(item, value) {
value = value;
return {
name: keyword,
value: value
};
}).style({
lineWidth: 1,
stroke: '#fff'
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
export default {
name: 'memberAgeChart',
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart(); // 第一步是创建的时候更新图表,但是这个不适用于异步请求接口获取相关数据,采用下面的监听的方式
}, 30);
},
beforeUpdate() {
this.drawChart();
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width,
forceFit: true
});
this.chart.source(this.charData);
this.chart.scale('value', {
min: 0
});
this.chart.scale('keyword', {
range: [0, 1]
});
this.chart.tooltip({
crosshairs: {
type: 'line'
}
});
this.chart.line().position('keyword*value');
this.chart.point().position('keyword*value').size(4).shape('circle').style({
stroke: '#fff',
lineWidth: 1
});
this.chart.render();
}
}
};
</script>
<template>
<div :id="id">
</div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
},
height: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart(); // 第一步是创建的时候更新图表,但是这个不适用于异步请求接口获取相关数据,采用下面的监听的方式
}, 100);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
let data = {
container: this.id,
width: this.width
};
if (!!this.height) {
data = {...data, height: this.height};
}
this.chart = new G2.Chart(data);
this.chart.source(this.charData);
this.chart.area().position('keyword*销售额').color('white').shape('smooth');
this.chart.line().position('keyword*销售额').color('white').shape('smooth');
this.chart.guide().regionFilter({
top: true,
start: ['min', 0],
end: ['max', 'min'],
color: '#18a1cd'
});
this.chart.guide().regionFilter({
top: true,
start: ['min', 'max'],
end: ['max', 0],
color: '#FF4D4F'
});
this.chart.guide().region({
top: false,
start: [2000, 'max'],
end: [2016, 'min']
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<!-- 销售分析 -->
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
import { setTimeout } from 'timers';
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 200);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
year: {
range: [1, 8]
},
// 操作数据
// value: {
// formatter: function formatter(val) {
// return val + '%';
// }
// }
});
this.chart.tooltip({
crosshairs: {
type: 'line'
}
});
this.chart.areaStack().position('date*value').color('name');
this.chart.lineStack().position('date*value').color('name').size(4);
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<!-- 销售分析 -->
<div :id="id"></div>
</template>
<script>
import G2 from "@antv/g2";
import { setTimeout } from 'timers';
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
},
type: {
type: String,
default: '0'
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 20);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
if (this.type === '0') {
this.chart.source(this.charData, {
year: {
range: [1, 8]
},
// 操作数据
value: {
formatter: function formatter(val) {
return val + '%';
}
}
});
} else {
this.chart.source(this.charData, {
year: {
range: [1, 8]
}
});
}
this.chart.tooltip({
crosshairs: {
type: 'line'
}
});
this.chart.areaStack().position('date*value').color('name');
this.chart.lineStack().position('date*value').color('name').size(4);
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div :id="id">
</div>
</template>
<script>
import G2 from "@antv/g2";
export default {
data() {
return {
chart: null
};
},
props: {
charData: {
type: Array,
default() {
return []
}
},
id: String,
width: {
type: Number,
default: null
}
},
mounted() {
setTimeout(() => {
this.drawChart();
}, 30);
},
beforeUpdate() {
this.drawChart();
},
watch: {
charData() {
this.drawChart();
}
},
methods: {
drawChart() {
// 更新的时候要移除先
this.chart && this.chart.destroy();
this.chart = new G2.Chart({
container: this.id,
width: this.width
});
this.chart.source(this.charData, {
value: {
formatter: function formatter(val) {
return val;
}
}
});
this.chart.coord('theta', {
radius: 0.75
});
this.chart.tooltip({
showTitle: false,
itemTpl: '<li><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</li>'
});
this.chart.intervalStack().position('value').color('keyword').label('keyword', {
formatter: function formatter(val, keyword) {
return keyword.point.keyword + ':' + keyword.point.value;
}
}).tooltip('keyword*value', function(item, value) {
value = value;
return {
name: keyword,
value: value
};
}).style({
lineWidth: 1,
stroke: '#fff'
});
this.chart.render();
}
}
};
</script>
<style lang="stylus" scoped>
</style>
<template>
<div class="member-group" :style="{height: (bodyHeight - 64) + 'px'}">
<div class="container">
<v-nav :navpath="navpath"></v-nav>
<div class="group-wrap">
<div class="group" style="">
<div class="add-wrap">
<el-button type="primary" @click="addMemberGroup" class="add-btn">新增分组</el-button>
</div>
<div class="groupwrap clear">
<div class="groupwrap-item" v-for="(item,index) in memberGroupList" :key="index">
<div class="groupwrap-content">
<div class="groupwrap-title">
<el-popover placement="top" width="200" trigger="hover" :content="'备注: ' + item.memberGroupDescribe">
<div class="member-group-name" slot="reference">{{ item.memberGroupName }}</div>
</el-popover>
</div>
<div class="groupwrap-total borderbmdashed"> <p class="groupwrap-text">{{ item.memberCount }}</p> </div>
<div class="groupwrap-condition">筛选条件: {{ item.memberSearchCount }}
<!--<p class="groupwrap-conditiontext" style="display:none;">-->
<!--{{ item.memberSearchStr | formatCondition }}-->
<!--</p>-->
</div>
</div>
<div class="groupwrap-handler">
<router-link :to="{path: '/membergroupAdd',query: {'memberGroupId': item.memberGroupId}}" class="mr6 pointer ml15 opt-icon" title="编辑"><i class="el-icon-edit"></i></router-link>
<span class="mr6 pointer opt-icon" title="详情" @click="handleDetailGrop(item.memberGroupId)"><i class="el-icon-document"></i></span>
<span class="pointer opt-icon" title="删除" @click="handleDeleteGroup(item.memberGroupId, index)"><i class="el-icon-delete"></i></span>
</div>
</div>
<div class="page mTop20" v-if="memberGroupList.length > 0">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.currentPage"
:page-sizes="[20, 40, 60, 80]"
:page-size="page.pageSize"
layout="total, sizes, prev, pager, next"
:total="page.totalCount">
</el-pagination>
</div>
<!-- 空图标 -->
<div class="el-table__body-wrapper is-scrolling-none" v-if="memberGroupList.length == 0">
<table cellspacing="0" cellpadding="0" border="0" class="el-table__body" style="width: 1077px;">
<colgroup>
<col name="el-table_3_column_9" width="359">
<col name="el-table_3_column_10" width="359">
<col name="el-table_3_column_11" width="359">
</colgroup>
<tbody><!----></tbody></table>
<div class="el-table__empty-block" style="width: 100%; height: 256px;">
<span class="el-table__empty-text">暂无数据</span>
</div><!---->
</div>
</div>
</div>
</div>
</div>
<div class="foot-add">
<vue-gic-footer></vue-gic-footer>
</div>
</div>
</template>
<script>
import nav from '../../common/navbar/navbar.vue'
import { doFetch } from "../../components/axios/api";
import url from '../../components/axios/url'
import { normalizePeople } from '@/utils/utils';
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
export default {
name: "membergroup",
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
navpath: [
{
name: '首页',
path: ''
},
{
name: '会员分组',
path: ''
}
],
page: {
pageSize: 20,
currentPage: 1,
totalCount: 0
},
memberGroupList: []
}
},
filters: {
formatCondition: function (val) {
let tempObj =JSON.parse(val);
return val
}
},
methods: {
handleCurrentChange(val) {
this.page.currentPage = val;
this.getFindMemberGroups({ pageSize: 20, currentPage: val });
},
handleSizeChange(val){
this.page.pageSize = val;
this.getFindMemberGroups({ pageSize: val, currentPage: 1 });
},
addMemberGroup() {
this.$router.push({ path:'/membergroupAdd'});
},
handleDetailGrop(memberGroupId) {
this.$router.push({ path:'/membergroupDetail', query: { memberGroupId: memberGroupId}});
},
getFindMemberGroups(opt) {
doFetch(url.findMemberGroups,{
pageSize: opt.pageSize || 20,
currentPage: opt.currentPage || 1,
shareStatus: 0
}).then(res => {
if(res.data.errorCode === 0) {
// 返回筛选条件的数据
this.memberGroupList = res.data.result.page.result.map((ele, index) => ({
...ele, memberSearchCount: normalizePeople(ele.memberSearchStr)
}));
this.page.currentPage = res.data.result.page.currentPage;
this.page.totalCount = res.data.result.page.totalCount;
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
},
handleDeleteGroup(memberGroupId, i) {
this.$confirm(`确定删除该会员分组`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
doFetch(url.deteleMemberGroup,{
memberGroupId: memberGroupId,
}).then(res => {
if(res.data.errorCode === 0) {
this.page.currentPage = 1;
this.getFindMemberGroups({});
// this.memberGroupList.splice(i, 1);
this.$message({
message: '删除成功',
type: 'success'
});
}else {
checkFalse(res.data.message);
return false;
}
}).catch(err => {
checkStatus(err);
})
}).catch((err) => {
checkStatus(err);
});
}
},
created() {
this.getFindMemberGroups({});
this.$store.commit('mutations-slide',false);
// this.$nextTick(_ => {
// this.$store.commit('mutations-slide',true);
// })
},
components: {
'v-nav': nav
}
}
</script>
<style lang="stylus" scoped>
.member-group
background #f0f2f5
overflow-y auto
.group-wrap
background-color: #f0f2f5
padding 24px
min-height: 560px
.group
padding 24px;
background: #fff
.add-wrap
margin-bottom 23px
padding-right 14px
overflow hidden
.groupwrap-title
border-color #dcdfe6
border-radius 4px 4px 0 0
background-color #f5f7fa
padding: 10px 15px
.groupwrap-handler
border-radius 0 0 4px 4px
background-color #f5f7fa
.member-group-name
max-width: 100%
white-space: nowrap
font-size 16px
overflow hidden
.groupwrap
.borderbmdashed
border-color #dcdfe6
.opt-icon
color: #909399;
font-size: 16px;
&:hover
.el-icon-edit
color: #409eff
.el-icon-document
color: #409eff
.el-icon-delete
color: #F56C6C
.groupwrap-item
border: 1px solid #dcdfe6
border-radius 4px
max-width 24%
min-width 290px
flex: 0 0 24%
.add-btn
float right
.member-group
.container
min-height 100%
box-sizing border-box
padding-bottom 100px
.foot-add
margin-top -100px
</style>
<template>
<div class="member-add" :style="{ height: bodyHeight - 64 + 'px'}">
<div class="container">
<v-nav :navpath="navpath"></v-nav>
<div class="wrap">
<div class="wrap-cell">
<h1 class="wrap-title">分组内容设置</h1>
<el-form class="groupform ml32 " ref="groupForm" :rules="rules" :model="groupForm" label-position="right" label-width="80px">
<el-form-item label="分组名称" prop="memberGroupName">
<gic-input v-model="groupForm.memberGroupName" :max="20" :changeBColor="changeBColor"></gic-input>
</el-form-item>
<el-form-item label="分组描述">
<gic-textarea v-model="groupForm.memberGroupDescribe" :max="20"></gic-textarea>
</el-form-item>
</el-form>
</div>
<div class="wrap-cell mTop20">
<h1 class="wrap-title">人群筛选器</h1>
<div class="gic-people">
<vue-gic-people
:projectName="projectName"
:sceneValue="sceneValue"
:useId="userId"
:hasSearchData="hasSearchData"
ref="peopleFilter"
@findFilter="findFilter"
@getBackData="getBackData"
@editHide= "editHide"
@editShow="editShow"
@hideBtn="hideBtn">
</vue-gic-people>
<!-- <div class="btn-filter"> -->
<!-- <el-button type="primary" @click="getData">确认</el-button> -->
<!-- </div> -->
<div class="gic-people-button">
<div v-if="toggleTag">
<el-button size="medium" type="primary" @click="getData">确定</el-button>
<el-button size="medium" @click="cancelFilter">取消</el-button>
</div>
<div v-if="saveTag">
<el-button size="medium" @click="saveTempData">保存</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
<vue-gic-footer></vue-gic-footer>
<div class="footwrap">
<el-button type="primary" size="small" @click="submitMemberGroup('groupForm')">保存</el-button>
<el-button plain size="small" @click="goback">返回</el-button>
</div>
</div>
</template>
<script>
import nav from '../../common/navbar/navbar.vue'
import { doFetch } from "../../components/axios/api";
import url from '../../components/axios/url'
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
import { setTimeout } from 'timers';
export default {
name: "membergrop-detail",
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
hasSearchData: '',
userId: this.$route.query.memberGroupId ? 'memberGroup' + this.$route.query.memberGroupId : '',
memberGroupId: this.$route.query.memberGroupId,
isAddtext: '',
navpath: [
{
name: '首页',
path: ''
},
{
name: '会员分组',
path: '/membergroup'
},
{
name: '',
path: ''
}
],
sceneValue: 'member',
projectName: '',
groupForm: {
memberGroupName: '',
memberGroupDescribe: '',
memberSearchStr: '',
indexSearchStr: '' // 所有数据
},
changeBColor: false,
page: {
currentPage: 1,
pageSize: 20,
totalPage: 0
},
memberData: [],
sortColName: '',
sortType: 'desc',
rules: {
memberGroupName: [
{ required: true, message: '分组名不能为空', trigger: 'blur' },
]
},
ruleForm: [
{
required: true,
message: '分组名称不能为空'
}
],
toggleTag: false,
saveTag: false
}
},
methods: {
// 父组件调用子组件方法,触发父组件事件
async getData() {
this.$refs.peopleFilter.confirmSet()
},
// 子组件触发父组件事件,返回过滤条件数据
findFilter(value){
this.groupForm.memberSearchStr = value;
},
// 取消
cancelFilter(){
this.$refs.peopleFilter.cancelSet()
},
// 获取需要回显的数据, 供保存时候使用
getBackData(val) {
this.groupForm.indexSearchStr = val;
},
// 编辑时候显示确定 取消按钮
editShow() {
this.toggleTag = true;
},
// 确认好选择条件之后 隐藏按钮
editHide() {
this.toggleTag = false;
},
// 隐藏保存按钮和确认按钮 (子组件会调用)
hideBtn() {
this.toggleTag = false;
},
// 保存当前模板,对接保存接口
saveTempData() {
},
submitMemberGroup(groupForm) {
this.$refs[groupForm].validate((valid) => {
if (valid) {
this.changeBColor = false;
this.getData();
let curUrl = null;
let params = null;
setTimeout(() => {
if(this.memberGroupId) {
curUrl = url.updateMemberGroup;
params = {
memberGroupName: this.groupForm.memberGroupName,
memberGroupDescribe: this.groupForm.memberGroupDescribe,
memberSearchStr: this.groupForm.memberSearchStr || '-1',
indexSearchStr: this.groupForm.indexSearchStr,
memberGroupId: this.memberGroupId
};
}else {
curUrl = url.insertMemberGroup;
params = {
memberGroupName: this.groupForm.memberGroupName,
memberGroupDescribe: this.groupForm.memberGroupDescribe,
indexSearchStr: this.groupForm.indexSearchStr,
memberSearchStr: this.groupForm.memberSearchStr || '-1',
};
}
doFetch(curUrl, params).then(res => {
if(res.data.errorCode === 0) {
checkSuccess();
setTimeout(() => {
this.goback();
},1000);
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
}, 200);
} else {
this.changeBColor = true ;
return false;
}
});
// if(!this.groupForm.memberGroupName.trim()) {
// checkFalse('请填写分组名称');
// return false;
// }
},
getDetail() {
doFetch(url.findMemberGroupInfo,{
memberGroupId: this.memberGroupId,
}).then(res => {
if(res.data.errorCode === 0) {
this.groupForm.memberGroupName = res.data.result.memberGroup.memberGroupName;
this.groupForm.memberGroupDescribe = res.data.result.memberGroup.memberGroupDescribe;
this.groupForm.memberSearchStr = this.hasSearchData = res.data.result.memberGroup.memberSearchStr;
}else {
checkFalse(res.data.message);
return false;
}
}).catch(err => {
checkStatus(err);
})
},
goback() {
this.$router.push({ path:'/membergroup'});
}
},
created() {
if(this.memberGroupId) {
this.getDetail(this.memberGroupId);
this.isAddtext = '编辑';
}else {
this.isAddtext = '新增';
}
this.navpath[this.navpath.length - 1].name = this.isAddtext;
},
components: {
'v-nav': nav
}
}
</script>
<style lang="stylus" scoped>
.member-add
background #f0f2f5
overflow auto
.wrap
padding 24px
background #f0f2f5
.wrap-cell
background #fff
.wrap-title
margin 0
padding 0 32px
line-height 56px
border-bottom 1px solid #dcdfe6
.groupform
padding 20px 32px
.gic-form-area
resize: none
.gic-people
position relative
padding 20px 32px
z-index 0
min-height 100px
background-color #fff
.btn-filter
padding-left 150px
margin-top 10px
background-color #fff
.gic-form-item
border-color #f00
.el-form-item
.gic-form-area
resize: none
.footwrap
.el-button+.el-button
margin-left 5px
.gic-people-button
background-color #f2f3f4
</style>
<template>
<div class="achievementwrap" :style="{height: (bodyHeight - 64) + 'px'}">
<div class="achievementwrap-right">
<div :style="{height: '100%'}">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</div>
</div>
</template>
<script>
import Navbar from '../../view/layout/components/Navbar.vue'
export default {
name: 'memberlist',
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
defaultActive: this.$route.path
}
},
mounted() {
window.onresize = () => {
return (() => {
this.bodyHeight = document.body.clientHeight || document.documentElement.clientHeight;
})()
}
},
components: {
Navbar
}
}
</script>
<style lang="stylus" scoped>
.achievementwrap{
display flex;
flex 1
overflow hidden
&-left{
flex: 0 0 200px;
width: 200px;
background-color: #020b21;
}
&-right{
flex: 1;
overflow: auto;
}
}
.rewardname
margin-left 24px
line-height 0
.footlogo
padding: 0 !important;
</style>
<template>
<div class="member-task" :style="{ height: bodyHeight - 64 + 'px'}">
<div class="min100">
<v-nav :navpath="navpath"></v-nav>
<div class="achievement">
<div class="achievement-item" v-for="(missitem, index) in missionList" :key="index">
<div class="achievement-top">
<span class="achievement-title">{{ missitem.missionName }}</span>
</div>
<div class="achievement-editwrap">
<el-table :data="missitem.item" tooltip-effect="dark" header-row-class-name="curheader">
<el-table-column prop="missionItemName" label="字段名称"></el-table-column>
<el-table-column prop="rewardValue" label="奖励积分额">
<template slot-scope="scope">
<p>奖励
<el-input-number v-show="scope.row.isEdit == true" maxlength="6" v-model="scope.row.rewardValue" controls-position="right" :min="0" :max="100000"></el-input-number>
<span v-show="scope.row.isEdit !== true"> {{ scope.row.rewardValue }} </span> 积分
<i v-show="isEdit == 1 && scope.row.isEdit !== true" @click="editRewardValue(scope.row)" class="el-icon-edit pointer"></i>
<el-button v-show="isEdit == 1 && scope.row.isEdit == true" @click="submitRewardValue(scope.row)" type="text">
保存
</el-button>
<el-button v-show="isEdit == 1 && scope.row.isEdit == true" @click="cancleRewardValue(scope.row)" type="text">
取消
</el-button>
<!-- <i v-show="isEdit == 0 && scope.row.isEdit == true" @click="submitRewardValue(scope.row)" class="el-icon-check pointer"></i>
<i v-show="isEdit == 0 && scope.row.isEdit == true" @click="cancleRewardValue(scope.row)" class="el-icon-close pointer"></i> -->
</p>
</template>
</el-table-column>
<el-table-column prop="status" label="开关" v-if="isEdit == 1" width="300">
<template slot-scope="scope">
<div class="achievement-handler">
<el-switch
v-model="scope.row.statusEdit"
@change="changeValue(scope.row)"
active-text="启用"
inactive-text="停用">
</el-switch>
<!-- <a class="achievement-btn pointer" :class="{activebtn: scope.row.status == 1}" @click="handlerStatus(scope.row,1)" >启用</a><a class="achievement-btn pointer" :class="{activebtn: scope.row.status == 0}" @click="handlerStatus(scope.row,0)" >停用</a> -->
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
<div class="footer-box foot-add">
<vue-gic-footer></vue-gic-footer>
</div>
</div>
</template>
<script>
import nav from '../../common/navbar/navbar.vue'
import { doFetch } from "../../components/axios/api";
import url from '../../components/axios/url'
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
import Vue from 'vue'
export default {
name: "membertask",
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
navpath: [
{
name: '首页',
path: ''
},
{
name: '会员',
path: '/wechatmembers'
},
{
name: '会员任务',
path: ''
}
],
isEdit: null,
missionList: []
}
},
methods: {
changeVal(i) {
// val = val.length > 6 ? val.substr(0, 6) : val;
},
getMemberMissionList() {
doFetch(url.memberMissionList).then(res => {
if(res.data.errorCode === 0) {
this.isEdit = res.data.result.isEdit;
this.missionList = res.data.result.missionList.map(ele => ({
...ele,
item: ele.item.map(item => ({
...item,
statusEdit: item.status == 1 ? true : false
}))
}));
console.log(this.missionList);
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
},
editRewardValue(row) {
if(!row.isEdit) {
Vue.set(row,'isEdit',true);
}else{
row.isEdit = true;
}
if(!row.isCurValue) {
Vue.set(row,'isCurValue',row.rewardValue);
}else {
row.isCurValue = row.rewardValue;
}
},
submitRewardValue(row) {
doFetch(url.memberMissionUpdate,{
missionItemEnterpriseId: row.missionItemEnterpriseId,
usedRule: row.usedRule,
rewardRule: row.rewardRule,
rewardValue: row.rewardValue
}).then(res => {
if(res.data.errorCode === 0) {
row.isEdit = false;
checkSuccess();
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
},
cancleRewardValue(row) {
row.rewardValue = row.isCurValue;
row.isEdit = false;
},
changeValue(row) {
const _item = row;
const type = row.statusEdit ? 1 : 0;
if(type == 1) {
if(row.rewardValue === 0) {
checkFalse('请完善积分奖励');
row.statusEdit = false;
return false;
}
}
if(row.status === type) {
row.statusEdit = false;
return false;
}
doFetch(url.memberMissionOpen,{
missionItemEnterpriseId: row.missionItemEnterpriseId,
status: type
}).then(res => {
if(res.data.errorCode === 0) {
row.status = type;
checkSuccess();
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
}
},
created() {
this.getMemberMissionList();
this.$store.commit('mutations-slide',false);
},
components: {
'v-nav': nav
}
}
</script>
<style lang="stylus">
.member-task
overflow auto
// 会员成就
.achievement
display block
padding: 20px 25px
&-item
padding 15px 10px
margin-bottom 16px
background-color #fff
border-radius 5px
&:last-child
margin-bottom 0
&-top
display flex
&-title
font-size 16px
font-weight bold
&-handler
flex 1
&-btn
display inline-block
border 1px solid #ddd
font-size 14px
padding 6px 20px
box-sizing border-box
&:last-child
border-left 0
.activebtn
border 1px solid $btncolor
color #fff
background-color $btncolor
&-editwrap
margin 20px 0
</style>
<template>
<div class="online-part">
<Navbar></Navbar>
电商会员
</div>
</template>
<script>
import Navbar from '@/view/layout/components/Navbar.vue';
export default {
name: "onlinemembers",
components: {
Navbar
}
}
</script>
<style lang="stylus" scoped>
.online-part
padding 0 20px
</style>
<template>
<div class="member-setting">
<div class="minheight">
<v-nav :navpath="navpath"></v-nav>
<div class="setting-con">
<div class="setting-container">
<el-switch v-model="value"></el-switch> 是否允许好办通过会员姓名、手机号和卡号搜索查询全品牌会员
<div class="save-btn">
<el-button type="primary" @click="saveSet(2)">保存</el-button>
</div>
</div>
</div>
</div>
<div class="footer-box">
<vue-gic-footer></vue-gic-footer>
</div>
</div>
</template>
<script>
import nav from '@/common/navbar/navbar.vue';
import { doFetch,doFetchqs } from "../../components/axios/api";
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
import { setTimeout } from 'timers';
export default {
components: {
'v-nav': nav
},
data() {
return {
navpath: [
{ name: '首页', path: '' },
{ name: '微信会员', path: '/wechatmembers' },
{ name: '会员设置', path: '' }
],
value: false
};
},
methods: {
saveSet(types) {
doFetch('/api-member/members-setting',{
optype: types,
appStatus: !!this.value ? 1 : 0
}).then(res => {
if (res.data.errorCode === 0) {
if (res.data.result.appStatus == 1) {
this.value = true;
}
this.$message({
message: '设置成功!',
type: 'success'
});
if (types == 2) {
setTimeout(() => {
this.$router.push({path: '/wechatmembers'});
}, 1000);
}
} else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
});
}
},
created() {
this.saveSet(1);
}
};
</script>
<style lang="stylus" scoped>
.setting-container
padding 20px 20px 100px 20px
margin 24px
color #606266
font-size 14px
background-color #fff
.save-btn
margin-top 20px
.member-setting
height 100%
background-color #f5f7fa
.minheight
min-height 100%
.footer-box
margin-top -116px
</style>
<template>
<div class="talk-container">
<div class="min100">
<v-nav :navpath="navpath"></v-nav>
<div class="padding-container">
<div class="mBottom20">
<el-row>
<el-col :span="19">
<el-select v-model="telType" placeholder="所有类型" @change="handleSearch">
<el-option :key=-1 label="所有类型" :value=-1></el-option>
<el-option :key=1 label="不良评价回访" :value=1></el-option>
<el-option :key=2 label="ECM计划" :value=2></el-option>
</el-select>
<el-select v-model="tekStatys" style="margin-left: 5px" placeholder="所有状态" @change="handleSearch">
<el-option :key=-1 label="所有状态" :value=-1></el-option>
<el-option :key=1 label="未接通" :value=1></el-option>
<el-option :key=2 label="接通" :value=2></el-option>
</el-select>
</el-col>
<el-col :span='5' class="tr">
<searchinput v-model="searchStr" stylelink="width: 280px" @handleSearch="handleSearch"></searchinput>
</el-col>
</el-row>
</div>
<div class="mBottom40">
<div class="table-content">
<el-table :data="memberData" tooltip-effect="dark" ref="multipleTable" >
<el-table-column prop="callBeginTime" label="呼叫时间">
<template slot-scope="scope">
{{ scope.row.callBeginTime | formatTime }}
</template>
</el-table-column>
<el-table-column prop="clerkName" label="主叫信息">
<template slot-scope="scope">
<div class="clerk">
<span class="clerkimg"><img style="vertical-align: middle" width="60" height="60" :src="scope.row.clerkImage" alt=""></span>
<span class="clerkname">{{ scope.row.clerkName }}<br>{{ scope.row.clerkType == 1 ? '店长': scope.row.clerkType == 0 ? '店员': '' }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="typeName" label="类型">
<template slot-scope="scope">
<span v-if="scope.row.typeName == -1">所有类型</span>
<span v-if="scope.row.typeName == 1">不良评价回访</span>
<span v-if="scope.row.typeName == 2">ECM计划</span>
</template>
</el-table-column>
<el-table-column prop="callStatus" label="通话状态">
<template slot-scope="scope">
<span v-if="scope.row.callStatus == 1">未接通</span>
<span v-if="scope.row.callStatus == 2">接通</span>
</template>
</el-table-column>
<el-table-column prop="callTime" label="通话时长(秒)">
<template slot-scope="scope">
<span v-if="scope.row.callTime">{{ scope.row.callTime }}</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column prop="recordUrl" label="录音">
<template slot-scope="scope">
<span class="pointer" v-if="scope.row.recordUrl" @click="playRecord(scope.row.recordUrl)">播放</span>
</template>
</el-table-column>
</el-table>
<div class="page mTop20" v-if="page.totalCount > 0">
<el-pagination background @size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.currentPage"
:page-sizes="[20, 40, 60, 80]"
:page-size="page.pageSize"
layout="total, sizes, prev, pager, next"
:total="page.totalCount">
</el-pagination>
</div>
</div>
</div>
</div>
</div>
<div class="foot-add">
<vue-gic-footer></vue-gic-footer>
</div>
<!-- <div class="footwrap">
<el-button plain @click="goback">返回</el-button>
</div> -->
<el-dialog title="播放" :visible.sync="dialogPlayVisible" width="500px" @close="closePlayer">
<div>
<audio controls id="audioplayer">
<source :src="curRecordUrl" type="audio/mpeg">
<source :src="curRecordUrl" type="audio/ogg">
Your browser does not support this audio format.
</audio>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogPlayVisible = false">关 闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import nav from '../../common/navbar/navbar.vue'
import { doFetch } from "../../components/axios/api";
import url from '../../components/axios/url'
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
import searchinput from 'common/searchinput'
export default {
name: "talkLogPage",
data() {
return {
memberId: this.$route.query.memberId || '',
navpath: [
{
name: '首页',
path: ''
},
{
name: '微信会员',
path: '/wechatmembers'
},
{
name: '会员详情',
path: '/wechatmemberDetail',
query: {memberId: this.$route.query.memberId || ''}
},
{
name: '通话记录',
path: ''
}
],
page: {
currentPage: 1,
pageSize: 20,
totalCount: 0
},
memberData: [],
telType: -1,
tekStatys: -1,
searchStr: '',
dialogPlayVisible: false,
curRecordUrl: ''
}
},
methods: {
closePlayer() {
var myAuto = document.getElementById('audioplayer');
myAuto.pause();
myAuto.load();
},
playRecord(recordUrl) {
this.dialogPlayVisible = true;
this.curRecordUrl = recordUrl;
},
handleSearch() {
this.page.currentPage = 1;
this.getMemberCardsPage();
},
handleCurrentChange(val) {
this.page.currentPage = val;
this.getMemberCardsPage();
},
handleSizeChange(val){
this.page.pageSize = val;
this.getMemberCardsPage();
},
goback() {
this.$router.push({ path:'wechatmemberDetail', query: { memberId: this.memberId}})
},
getMemberCardsPage() {
doFetch(url.talkLogPage,{
memberId: this.memberId,
pageSize: this.page.pageSize,
currentPage: this.page.currentPage,
telType: this.telType,
tekStatys: this.tekStatys,
searchStr: this.searchStr
}).then(res => {
if(res.data.errorCode === 0) {
this.memberData = res.data.result.page.result;
this.page.currentPage = res.data.result.page.currentPage;
this.page.totalCount = res.data.result.page.totalCount;
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
}
},
created() {
if(this.memberId) {
this.getMemberCardsPage();
}
this.$store.commit('mutations-slide', false);
},
components: {
'v-nav': nav,
searchinput
}
}
</script>
<style lang="stylus">
.clerk
display flex
align-items center
justify-content center
.clerkimg
flex 0 0 60px
width 60px
.clerkname
flex 1
margin-left 5px
.talk-container
height 100%
</style>
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import VueAxios from 'vue-axios'
import axios from 'axios'
import store from './store/store'
import 'element-ui/lib/theme-chalk/index.css'
import '../static/css/index.styl'
import '../static/font/iconfont.css'
import gicinput from 'common/gicinput'
import gictextarea from 'common/gictextarea'
import vueGicHeader from '@gic-test/vue-gic-header'
import vueGicCard from '@gic-test/vue-gic-card'
import vueGicStore from '@gic-test/vue-gic-store'
import vueGicPeople from '@gic-test/vue-gic-people'
import * as custom from 'common/filters/custom'
Vue.config.productionTip = false
import G2 from '@antv/g2';
Vue.use(G2);
Vue.use(ElementUI);
Vue.use(VueAxios,axios);
Vue.axios.defaults.withCredentials = true;
Vue.use(vueGicHeader);
Vue.use(vueGicPeople);
Vue.use(vueGicStore);
Vue.use(vueGicCard);
Vue.component('gic-textarea',gictextarea);
Vue.component('gic-input', gicinput);
import vueGicAsideMenu from '@gic-test/vue-gic-aside-menu'
import vueGicFooter from '@gic-test/vue-gic-footer'
Vue.use(vueGicAsideMenu)
Vue.use(vueGicFooter)
Vue.axios.interceptors.request.use(
config => {
return config;
},
err => {
return Promise.reject(err);
});
Vue.axios.interceptors.response.use(
response => {
const CODE = response.data.errorCode;
switch(CODE) {
case 401:
window.location.href = `${window.origin}/gic-web`;
break;
}
return response;
},
error => {
return Promise.reject(error)
});
Object.keys(custom).forEach(key => {
Vue.filter(key,custom[key]);
});
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
import { getFetch, postFetch } from './http';
// 会员总览数据
export const memberCount = (params) => postFetch('/api-report/data-main-member-count?requestProject=report', params);
// 昨日 今日 本月 上月会员率
export const memberPercent = (params) => postFetch('http://gicdev.demogic.com/api-report/data-statistics-percent?requestProject=report', params);
// 日/月 销售额 指标
export const dataOrMonth = (params) => getFetch('http://gicdev.demogic.com/api-report/date-or-month-sales?requestProject=report', params);
// 日达成图表
export const daySale = () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis-day-sale?requestProject=report');
// 月达成图表
export const monthSale = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 销售额 今日单效数据
export const todaySales = () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis?requestProject=report');
// 当日 隔日退货率
export const todayreFunds= () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis-refunds?requestProject=report');
// 销售分析
export const memberSalesAnalysis = (params) => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis?requestProject=report', params);
// 销售分析 销售占比 成交人数占比
export const memeberSalesMemberRation = (params) => getFetch('http://gicdev.demogic.com/api-report/member-ratio?requestProject=report', params);
// 总览 营销数据 积分商城 门店活动 微信图文数据 卡券数据
// 积分商城
export const mallData = () => postFetch('/api-report/data-get-integral-mall-data-for-index?requestProject=report');
// 门店活动
export const activityData = () => postFetch('http://gicdev.demogic.com/api-report/data-main-activity?requestProject=report');
// 微信图文数据
export const wechatData = () => postFetch('http://gicdev.demogic.com/api-report/data-main-wechat-report?requestProject=report');
// 卡券数据
export const carduseData = () => postFetch('http://gicdev.demogic.com/api-report/data-main-cardused?requestProject=report');
// 卡券领取
export const cardGet = () => postFetch('http://gicdev.demogic.com/api-report/data-main-card-get?requestProject=report');
// 门店概况
export const clerkCount = () => postFetch('http://gicdev.demogic.com/api-report/data-main-store-clerk-count?requestProject=report');
// 成员管理
export const storeClerk = () => postFetch('http://gicdev.demogic.com/api-report/data-main-store-clerk?requestProject=report');
// 门店评价
export const evaluate = () => postFetch('http://gicdev.demogic.com/api-report/data-main-evaluate?requestProject=report');
// 会员生动化管理
export const vividSearch = () => postFetch('http://gicdev.demogic.com/api-report/data-member-vivid-search-for-index?requestProject=report');
// 任务管理
export const telTask = () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis-tel-task?requestProject=report');
// 指标管理
export const perf = () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis-perf?requestProject=report');
// 投诉与建议
export const problem = () => postFetch('http://gicdev.demogic.com/api-report/data-sales-analysis-problem?requestProject=report');
// 会员总览数据 新增会员 会员率图表
export const memberAllChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 会员分析图表接口
// 会员画像年龄图表
export const memberAgeChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 会员画像星座图表
export const memberStarChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 会员画像出生年月图表
export const memberBorthChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 会员画像性别图表
export const memberSexChart = () => getFetch('http://gicdev.demogic.com/api-report/statistics-gender?requestProject=report');
// 会员画像会员等级图表
export const memberGradeChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
// 销售分析图表
export const salesAsChart = (params) => getFetch('http://gicdev.demogic.com/api-report/data-statistics-echarts?requestProject=report', params);
\ No newline at end of file
import axios from 'axios';
import QS from 'qs';
import { Message } from 'element-ui';
axios.defaults.baseURL = `${window.location.origin}`;
axios.defaults.timeout = 10000;
// axios.defaults.headers.post['Content-Type'] = 'appliaction/x-www-form-urlencoded;charset=UTF-8';
// Vue 导入登录token 判断状态
axios.interceptors.request.use(
config => {
// 判断token
return config;
}, error => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
error => {
if (error && error.response && error.response.status) {
switch(error.response.status) {
case 404:
Message({
message: '请求不存在',
type: 'error'
})
}
return Promise.reject(error);
}
}
)
/**
* getFetch
*/
export function getFetch(url, params) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
}).then(res => {
resolve(res.data);
}).catch(err => {
reject(err);
});
});
}
/**
* postFetch
*/
export function postFetch(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, QS.stringify(params)).then(res => {
resolve(res.data);
}).then(err => {
reject(err);
});
});
}
module.exports = (parantfile,file) => (r) => {
import('components/'+ parantfile + '/' + file + '.vue').then((module) => {
r(module)
})
}
import Vue from 'vue'
import Router from 'vue-router'
import _import from './_import.js'
import Layout from 'view/layout/Layout.vue'
// import AppMain from 'view/layout/components/AppMain.vue';
Vue.use(Router);
export const constantRouterMap = [
{ path: '/login', component: () => import('view/login/login'), hidden: true },
{ path: '/404', component: () => import('view/errorPage/404'), hidden: true },
{ path: '/401', component: () => import('view/errorPage/401'), hidden: true },
{
path: '/',
name: 'Layout',
redirect: 'wechatmembers',
component: Layout,
children: [
{
path: '/wechatmembers',
component: _import('wechatmembers', 'wechatmembers'),
meta: {
title: '微信会员',
keepAlive: true
}
},
{
path: '/talkLogPage',
component: _import('wechatmembers', 'talkLogPage'),
meta: {
title: '微信会员-通话记录'
}
},
{
path: '/infoMall',
component: _import('wechatmembers', 'infoMall'),
meta: {
title: '微信会员-微信商城消费'
}
},
{
path: '/posmembers',
component: _import('posmembers', 'posmembers'),
meta: {
title: 'pos会员',
keepAlive: true
}
},
{
path: '/onlinemembers',
component: _import('onlinemembers', 'onlinemembers'),
meta: {
title: '电商会员'
}
},
{
path: '/buyermembers',
component: _import('buyermembers', 'buyermembers'),
meta: {
title: '电商买家'
}
},
{
path: '/frozenList',
component: _import('wechatmembers', 'frozenList'),
meta: {
title: '冻结会员列表'
}
},
{
path: '/frozenList/frozenMember',
component: _import('wechatmembers', 'frozenMember'),
meta: {
title: '冻结会员'
}
},
{
path: '/memberSetting',
component: _import('wechatmembers', 'memberSetting'),
meta: {
title: '微信会员-设置'
}
},
{
path: '/labelmanager',
component: _import('labelmanager', 'labelmanager'),
meta: {
title: '会员标签'
}
},
{
path: '/achievement',
component: _import('achievement', 'achievement_sale'),
meta: {
title: '会员成就-消费类'
}
},
{
path: '/achievement_interaction',
component: _import('achievement', 'achievement_interaction'),
meta: {
title: '会员成就-互动类'
}
},
{
// 会员回收站数据页面
path: '/memberReturn',
component: _import('memberlist', 'memberReturn'),
meta: {
title: '会员回收'
}
},
{
path: '/membergroupAdd',
component: _import('membergroup', 'membergroupAdd'),
meta: {
title: '会员分组-新增'
}
},
{
path: '/membergroup',
component: _import('membergroup', 'membergroup'),
meta: {
title: '会员分组'
}
},
{
path: '/cardvoucher',
component: _import('wechatmembers', 'cardvoucher'),
meta: {
title: '微信会员-卡券'
}
},
{
path: '/membergroupDetail',
component: _import('membergroup', 'membergroupDetail'),
meta: {
title: '会员分组-详情'
}
},
{
path: '/membertask',
component: _import('membertask', 'membertask'),
meta: {
title: '会员任务'
}
},
{
path: '/posmemberDetail',
component: _import('posmembers', 'posmemberDetail'),
meta: {
title: 'pos会员-详情'
}
},
{
path: '/wechatmemberDetail',
component: _import('wechatmembers', 'wechatmemberDetail'),
meta: {
title: '微信会员-详情'
}
},
{
path: '/integralDataPage',
component: _import('wechatmembers', 'integralDataPage'),
meta: {
title: '微信会员-积分明细'
}
},
{
path: '/modifyintegral',
component: _import('wechatmembers', 'modifyintegral'),
meta: {
title: '微信会员-积分调整'
}
},
{
path: '/ajaxmembersinfo',
component: _import('wechatmembers', 'ajaxmembersinfo'),
meta: {
title: '微信会员-会员门店消费'
}
},
]
},
]
const router = new Router({
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
});
export default router;
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './types'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
title: '',
isShowFoot: true,
slideShow: true
},
mutations: {
[types.TITLE]: (state, data) => {
state.title = data;
},
isShowFoot(state,data) {
state.showfoot = data;
},
['mutations-slide'](state,data) {
state.slideShow = data;
},
}
})
export const LOGIN = 'login';
export const LOGOUT = 'logout';
export const TITLE = 'title';
export const SHOW = 'show';
export function fomatBirthday(val) {
if(val) {
let date = new Date(val);
let Y = date.getFullYear() + '-';
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
let D = (date.getDate() < 10 ? '0'+ (date.getDate()): date.getDate());
return Y+M+D;
}else {
return ''
}
}
// 解析时间
export function dateformat(time, fmt) {
let o = {
"M+": time.getMonth() + 1, //月份
"d+": time.getDate(), //日
"h+": time.getHours(), //小时
"m+": time.getMinutes(), //分
"s+": time.getSeconds(), //秒
"q+": Math.floor((time.getMonth() + 3) / 3), //季度
"S": time.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
}
export const __VERSION = '1.1';
/*获取url参数*/
/**
* ?name=zhangsan&age=23
* @param {} name url 参数名
* getParamsByName(age) 23
*/
export function getParamsByName(attr) {
let ret = RegExp(`[?&]${attr}=([^&]*)`).exec(window.location.href);
return ret && decodeURIComponent(ret[1].replace(/\+/g, ' '));
}
/**
* 人群筛选器 返回具体筛选条件的个数
* @param {}
* return 筛选条件的个数
*/
export function normalizePeople(jsonStr) {
if (!jsonStr) {
return 0;
}
let ret = 0;
const jsonObj = JSON.parse(jsonStr);
if (jsonObj.list && jsonObj.list.length) {
// 如果是一个对象
jsonObj.list.forEach(ele => {
// 如果list在ele里面 表示里面包含list数组
if ('list' in ele) {
if (ele.list && ele.list.length) {
ret += ele.list.length;
}
} else {
ret += 1;
}
});
}
return ret;
}
/**
* 时间格式转换
* 20181010101010 转换成2018-10-10 10:10:10
* @param { timeStr } 要转换的时间
* @param { type } 要转换的格式
*/
export function formatLongTime(timeStr, type) {
if (!timeStr) {
return '--';
}
timeStr = '' + timeStr;
let regTest = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/g;
if (type == 1) {
return timeStr.replace(regTest, function(match, p, p1, p2, p3, p4, p5){
return [p, p1, p2].join('-') + ' ' + [p3, p4, p5].join(':');
});
} else {
return timeStr.replace(/^(\d{4})(\d{2})(\d{2})$/g, function(match, p, p1, p2){
return [p, p1, p2].join('-');
});
}
}
/**
* 生日时间转换
* @param {}
*/
export function paddingBorth(str) {
if (!str) {
return '--';
}
str = '' + str;
// 如果是10月之前补位
if (str.length == 3) {
str = 0 + str;
}
return str.replace(/^(\d{2})(\d{2})$/g, function(match, p , p1) {
return [p, p1].join('-');
});
}
\ No newline at end of file
<template>
<div class="errPage-container">
<el-button @click="back" icon='arrow-left' class="pan-back-btn">返回</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">Oops!</h1>
页面
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li class="link-type">
<router-link to="/index">回首页</router-link>
</li>
<li class="link-type"><router-link to="/index">回首页</router-link></li>
<li><a @click.prevent="dialogVisible=true" href="#">点我看图</a></li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
<el-dialog title="随便看" :visible.sync="dialogVisible">
<img class="pan-img" :src="ewizardClap">
</el-dialog>
</div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'page401',
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/' })
} else {
this.$router.go(-1)
}
}
}
}
</script>
<style lang="stylus" scoped>
.errPage-container {
width: 800px;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
<template>
<div style="background:#f0f2f5;margin-top: -20px;height:100%;">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" :src="img_404" alt="404">
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有
<a class='link-type' href='https://wallstreetcn.com' target='_blank'>华尔街见闻</a>
</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
import img_404 from '@/assets/404_images/404.png'
import img_404_cloud from '@/assets/404_images/404_cloud.png'
export default {
name: 'page404',
data() {
return {
img_404,
img_404_cloud
}
},
computed: {
message() {
return '特朗普说这个页面你不能进......'
}
}
}
</script>
<style lang="stylus" scoped>
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
<template>
<div>
<vue-gic-header :projectName="projectName" @collapseTag="collapseTag" @toRouterView="toRouterView"></vue-gic-header>
<div class="layout" :style="{ height: bodyHeight - 64 + 'px'}">
<div class="app-main">
<div class="rou-container">
<div class="achievementwrap-left content-wrap" v-if="slideShow" :style="{height: (bodyHeight - 64) + 'px'}">
<div clas="">
</div>
<vue-gic-aside-menu
:projectName="projectName"
:leftModulesName="leftModulesName"
:collapseFlag="collapseFlag">
</vue-gic-aside-menu>
</div>
<div class="right-maincontainer">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</div>
</div>
<!-- <router-view></router-view> -->
</div>
</div>
</template>
<script>
import { doFetch } from "../../components/axios/api";
import url from '../../components/axios/url'
import {checkFalse, checkStatus, checkSuccess} from "../../../static/js/checkStatus";
import AppMain from './components/AppMain'
export default {
name: 'layout',
data() {
return {
collapseFlag: false,
menuRouter: [],
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
leftModulesName: '会员',
projectName: 'member'
}
},
computed:{
slideShow() {
return this.$store.state.slideShow;
}
},
methods: {
toRouterView(val) {
this.$router.push({
path: val.path
})
},
getClerkMenu() {
// doFetch(url.clerkMenu,{
// project: 'gic-web'
// }).then(res => {
// this.menuRouter = res.data.result;
// }).catch(err => {
// checkStatus(err);
// })
},
collapseTag(val){
this.collapseFlag = !this.collapseFlag;
},
},
created() {
this.getClerkMenu();
window.onresize = () => {
return (() => {
this.bodyHeight = document.body.clientHeight || document.documentElement.clientHeight;
})()
}
},
components: {
AppMain
}
}
</script>
<style lang="stylus">
.layout
overflow hidden
padding-top 64px
background-color #f0f2f5
.app-main
height 100%
.user-header-pop
min-width 95px
.el-popover.user-header-pop
min-width 95px
.rou-container
display flex
height 100%
.right-maincontainer
flex 1
overflow auto
</style>
<template>
<div>
<div class="app-main">
<div class="achievementwrap-left content-wrap" :style="{height: (bodyHeight - 64) + 'px'}">
<vue-gic-aside-menu
:projectName="projectName"
:leftModulesName="leftModulesName"
:collapseFlag="collapseFlag"
>
</vue-gic-aside-menu>
</div>
<router-view ></router-view>
</div>
</div>
</template>
<script>
import Navbar from './Navbar.vue'
export default {
name: 'AppMain',
props: {
collapseFlag: {
type: Boolean,
default: false
}
},
data() {
return {
bodyHeight: document.body.clientHeight || document.documentElement.clientHeight,
leftModulesName: '会员标签',
projectName: 'member'
};
},
computed: {
key() {
return this.$route.fullPath
}
},
mounted() {
window.onresize = () => {
return (() => {
this.bodyHeight = document.body.clientHeight || document.documentElement.clientHeight;
})()
}
},
components: {
Navbar
}
}
</script>
<style lang="stylus" scoped>
.app-main
position relative
display flex
.achievementwrap-left
display inline-block
/*padding 0 25px*/
</style>
<template>
<div class="memebr-count">
<span v-if="memberType == 1">会员共{{wxMember}}</span>
<span v-if="memberType == 2">会员共{{posMember}}</span>
<span v-if="memberType == 3">会员共{{ecommerceMember}}</span>
<span v-if="memberType == 4">会员共{{ecommerceBuyer}}</span>
<!-- <div class="maintop">
<h3> {{ $route.meta.title }} <span class="pointer"><i class="el-icon-setting fr"></i></span></h3>
</div> -->
<!-- <div class="type-member">
<router-link :to="{path: '/wechatmembers'}" class="type-item">
<div class="type-icon bgcolor-green"><i class="iconfont icon-weixin iconfontsize"></i></div>
<div class="member-detail">
<p class="member-num">{{ wxMember }}</p>
<p class="member-unit">微信会员(人)</p>
</div>
</router-link>
<router-link :to="{path: '/posmembers'}" class="type-item ">
<div class="type-icon bgcolor-red"><i class="iconfont icon-yinxingqia iconfontsize"></i></div>
<div class="member-detail">
<p class="member-num">{{ posMember }}</p>
<p class="member-unit">POS会员(人)</p>
</div>
</router-link> -->
<!-- <router-link :to="{path: '/onlinemembers'}" class="type-item">
<div class="type-icon bgcolor-purple"><i class="iconfont icon-touxiang iconfontsize"></i></div>
<div class="member-detail">
<p class="member-num">{{ ecommerceMember }}</p>
<p class="member-unit">电商会员(人)</p>
</div>
</router-link>
<router-link :to="{path: '/buyermembers'}" class="type-item ">
<div class="type-icon bgcolor-blue"><i class="iconfont icon-gouwu iconfontsize"></i></div>
<div class="member-detail">
<p class="member-num">{{ ecommerceBuyer }}</p>
<p class="member-unit">电商买家(人)</p>
</div>
</router-link> -->
</div>
</div>
</template>
<script>
import { doFetch } from "../../../components/axios/api";
import url from '../../../components/axios/url'
import {checkFalse, checkStatus, checkSuccess} from "../../../../static/js/checkStatus";
export default {
props: {
memberType: {
type: Number,
required: true
}
},
data() {
return {
wxMember: 0,
posMember: 0,
ecommerceMember: 0,
ecommerceBuyer: 0
}
},
methods: {
getMemberCount() {
doFetch(url.memberCount).then(res => {
if(res.data.errorCode === 0) {
let temp = res.data.result;
this.wxMember = temp.wxMember; // 微信会员
this.posMember = temp.posMember; // pos会员
this.ecommerceMember = temp.ecommerceMember; // 电商会员
this.ecommerceBuyer = temp.ecommerceBuyer; // 电商买家
}else {
checkFalse(res.data.message);
}
}).catch(err => {
checkStatus(err);
})
}
},
created() {
this.getMemberCount();
}
}
</script>
<style lang="stylus">
.memebr-count
display inline-block
height 40px
line-height 40px
span
margin-right 10px
</style>
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment