Commit e6a2a88e authored by 高宇's avatar 高宇

新增工作日志项目;

parents
Pipeline #7 failed with stages
{
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"comments": false,
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": [ "istanbul" ]
}
}
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
build/*.js
config/*.js
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
test/unit/coverage
test/e2e/reports
selenium-debug.log
.idea
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
// to edit target browsers: use "browserlist" field in package.json
"autoprefixer": {}
}
}
# portal2.0-app-template
> vue2.0
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
# run unit tests
npm run unit
# run e2e tests
npm run e2e
# run all tests
npm test
```
For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
##未完成事项记录
```$xslt
列表中
留言
放弃
```
{"mock":"part"}
\ No newline at end of file
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
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'
))
})
})
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../package.json')
var shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var 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 () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var 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 (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
require('./check-versions')()
let proxyMock = require('express-proxy-mock')
var config = require('../config')
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
? require('./webpack.prod.conf')
: require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable
var app = express()
proxyMock(app)
var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
})
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
// app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)
// set a cookie
app.use(function(req, res, next) {
res.cookie('vup_token', '321421343124', {maxAge: 99999999999999999})
next()
})
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
var uri = 'http://localhost:' + port
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n')
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
_resolve()
})
var server = app.listen(port)
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass').concat(
{
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, '../node_modules/sass-bem/_bem.scss'),
path.resolve(__dirname, '../node_modules/compass-mixins/lib/_compass.scss'),
path.resolve(__dirname, '../src/theme/var.scss')
]
}
}
),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction
})
}
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
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')
}
},
externals: {
'vis-portal': 'VupPortalSdk.vueAppDevSdk',
'jquery': 'VupPortalSdk.vueAppDevSdk.jQuery',
'vue': 'VupPortalSdk.vueAppDevSdk.Vue',
'vue-bus': 'VupPortalSdk.vueAppDevSdk.vueBus',
'bowser': 'VupPortalSdk.vueAppDevSdk.bowser',
'browser-cookie': 'VupPortalSdk.vueAppDevSdk.cookie',
'vue-router': 'VupPortalSdk.vueAppDevSdk.vueRouter',
'element-ui': 'VupPortalSdk.vueAppDevSdk.elementui',
'vue-cache-data': 'VupPortalSdk.vueAppDevSdk.VueCacheData',
'create-requestor': 'VupPortalSdk.vueAppDevSdk.createRequestor'
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// cheap-module-eval-source-map is faster for development
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.ejs',
inject: true
}),
new FriendlyErrorsPlugin()
]
})
var prodWebpackConfig = require('./webpack.prod.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var merge = require('webpack-merge')
var utils = require('./utils')
var webpackConfig = merge(prodWebpackConfig, {
output: {
filename: utils.assetsPath('js/[name].js'),
chunkFilename: utils.assetsPath('js/[id].js')
}
})
webpackConfig.plugins.splice(2, 1, new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].css')
})
)
module.exports = webpackConfig
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
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: process.env.NODE_ENV === 'testing'
? 'index.html'
: 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'
}),
// split vendor js into its own file
// new webpack.optimize.CommonsChunkPlugin({
// name: 'vendor',
// minChunks: function (module, count) {
// // 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',
// chunks: ['vendor']
// }),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
var 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) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
// This is the webpack config used for unit tests.
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseConfig = require('./webpack.base.conf')
var webpackConfig = merge(baseConfig, {
// use inline sourcemap for karma-sourcemap-loader
module: {
rules: utils.styleLoaders()
},
devtool: '#inline-source-map',
resolveLoader: {
alias: {
// necessary to to make lang="scss" work in test when using vue-loader's ?inject option
// see discussion at https://github.com/vuejs/vue-loader/issues/724
'scss-loader': 'sass-loader'
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/test.env')
})
]
})
// no need for app entry during tests
delete webpackConfig.entry
module.exports = webpackConfig
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API_HOST: '"http://vue.jinchangxiao.com"',
API_PORT: '""',
JANUS_HOST: '""',
JANUS_PORT: '""'
})
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
productionSourceMap: true,
// 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
},
dev: {
env: require('./dev.env'),
port: 8887,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
}
module.exports = {
NODE_ENV: '"production"',
API_HOST: '""',
API_PORT: '""',
JANUS_HOST: '""',
JANUS_PORT: '""'
}
var merge = require('webpack-merge')
var devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})
const fs = require('fs')
if (!fs.existsSync('./mockData')) {
fs.mkdirSync('./mockData')
}
if (!fs.existsSync('./test/data')) {
console.log('没有可迁移数据')
return
}
console.log('数据迁移中..')
exchangeFiles('./test/data')
function parseUrlToName (url) {
return url.split('/').map(item => firstUpperCase(item)).join('')
}
function firstUpperCase (str) {
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
}
function exchangeFiles (path) {
const files = fs.readdirSync(path)
files.forEach(item => {
let newPath = path + '/' + item
let stat = fs.lstatSync(newPath)
if (stat.isDirectory()) {
exchangeFiles(newPath)
} else {
if (item.includes('json')) {
const file = fs.readFileSync(newPath)
const data = JSON.parse(file)
data.forEach(itemData => {
if (itemData.response && itemData.response['json']) {
let name = parseUrlToName(itemData.request.uri).replace('-', '')
let newData = {
url: itemData.request.uri,
data: itemData.response.json,
name: name
}
fs.writeFileSync(`mockData/${name}.json`, JSON.stringify(newData, null, 2))
console.log(`mockData/${name}.json,迁移完成`)
}
})
}
}
})
}
console.log(`全部迁移完成`)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>portal2.0-template</title>
<!-- font icon css -->
<link rel="stylesheet" href="./static/css/font-awesome.min.css">
<!-- Sdk -->
<script src="./static/vupVueSdk.js"></script>
<link rel="stylesheet" href="./static/vupVueSdk.css">
<script>
(function () {
if (window.VupPortalSdk.vueAppDevSdk) {
console.log(window.VupPortalSdk.vueAppDevSdk)
var portal = VupPortalSdk.vueAppDevSdk
var $ = portal.jQuery
var portalName = 'work-log'
var menuConfig = {
menus: [
{
"icon": "fa-book",
"isRouteShow": 1,
"title": "工作日志",
"appName": portalName,
"url": "/list"
},
{
"icon": "fa-book",
"isRouteShow": 1,
"title": "test",
"appName": portalName,
"url": "/test"
}
],
homePage: {
appName: portalName,
url: '/list'
}
}
$(document).ready(function () {
portal.setMenu(menuConfig.menus)
portal.initPortalForDev({
homePage: menuConfig.homePage
})
})
}
})()
</script>
</head>
<body>
<div id="portalApp"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>portal2.0-app-template</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
[{"url":"http://vue.jinchangxiao.com","target":"http://localhost:8886","hasProxy":true,"ignorePath":false}]
{
"data": [
{
"name": "请求参数1",
"requestData": {},
"responseData": {
"code": 200,
"msg": "获取成功",
"result": [
{
"name": "name"
}
]
}
}
],
"name": "获取待办事项",
"url": "/vue/schedule/list",
"duration": 1000
}
\ No newline at end of file
This diff is collapsed.
{
"name": "portal2.0-app-template",
"version": "1.0.0",
"description": "vue2.0",
"author": "Huai.Li <Huai.Li@vipshop.com>",
"private": true,
"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js",
"build-test": "rimraf dist && cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.prod-nohash.conf.js",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
"moco": "cd test/data && java -jar moco.jar start -g settings.json -p 13009 && cd -"
},
"dependencies": {
"express-proxy-mock": "^1.2.12",
"sass-bem": "^2.6.5",
"sass-resources-loader": "^2.0.0",
"viewerjs": "^1.3.2",
"vis-requestor": "^1.0.11"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.22.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.10",
"babel-plugin-istanbul": "^4.1.1",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^4.0.0",
"cross-spawn": "^5.0.1",
"css-loader": "^0.28.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^6.2.1",
"eslint-friendly-formatter": "^2.0.7",
"eslint-loader": "^1.7.1",
"eslint-plugin-html": "^2.0.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^2.0.1",
"eventsource-polyfill": "^0.9.6",
"express": "^4.14.1",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"jquery": "^2.2.4",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3",
"inject-loader": "^3.0.0",
"lolex": "^1.5.2",
"node-sass": "^4.5.3",
"opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0",
"phantomjs-prebuilt": "^2.1.14",
"rimraf": "^2.6.0",
"sass-loader": "^4.0.2",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"url-loader": "^0.5.8",
"vue-loader": "^12.1.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.3.3",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
[{"host":"http://vue.jinchangxiao.com/vue?duplextrans","port":"","name":"测试"}]
\ No newline at end of file
{"host":"","port":"","id":"","name":""}
\ No newline at end of file
import createRequestor from 'create-requestor'
import { default as API } from '@/api'
let config = {
ajaxOption: {
method: 'POST',
dataType: 'json',
contentType: 'application/json',
xhrFields: {
withCredentials: true
}
},
token: {
checkBeforeSend: true,
addToHeader: false,
cookieName: 'vup_token',
headerName: 'X-Token'
},
resultCode: {
SESSION_EXPIRED: 401,
SUCCEED: 200
},
urlOption: {
host: API.option.hostJanus,
port: API.option.portJanus,
base: ''
},
// alert: function (msg) {
// },
failedMsg: '请求失败,请稍后重试!'
}
let ajax = createRequestor(config)
export function requestAPI (option, data) {
let params = { data: JSON.stringify(data) }
return ajax.requestAPI(option, params)
}
export function requestWithJsonAPI (option, data) {
return ajax.requestAPI(option, data)
}
export function requestJanusAPI (option, data) {
return ajax.requestJanusAPI(option, data)
}
import createRequestor from 'create-requestor'
import {default as API} from 'src/api'
let config = {
ajaxOption: {
method: 'POST',
dataType: 'json',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
},
token: {
checkBeforeSend: true,
addToHeader: false,
cookieName: 'vup_token',
headerName: 'X-Token'
},
resultCode: {
SESSION_EXPIRED: 401,
SUCCEED: 200
},
urlOption: {
host: API.option.host,
port: API.option.port,
base: ''
},
// alert: function (msg) {
// bootbox.alert(msg)
// },
failedMsg: '请求失败,请稍后重试!'
}
let ajax = createRequestor(config)
export function requestAPIUpload (option, data) {
return ajax.requestAPI(option, data)
}
import createRequestor from 'create-requestor'
import { default as API } from '@/api'
const usingJSONP = process.env.NODE_ENV !== 'production'
let config = {
ajaxOption: {
method: usingJSONP ? 'GET' : 'POST',
dataType: 'json',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
},
token: {
checkBeforeSend: false,
addToHeader: false,
cookieName: 'vup_token',
headerName: 'X-Token'
},
resultCode: {
SESSION_EXPIRED: 401,
SUCCEED: 2000
},
urlOption: {
host: API.option.host,
port: API.option.port,
base: ''
},
// alert: function (msg) {
// },
failedMsg: '请求失败,请稍后重试!'
}
let ajax = createRequestor(config)
let setUrlK = (ojson) => {
let params = ''
Object.keys(ojson).forEach(item => {
params += '&' + encodeURIComponent(item) + '=' + ojson[item]
})
return params
}
export function requestAPI (option, data) {
let params = { data: JSON.stringify(data) }
return ajax.requestAPI(option, params)
}
export function requestWithJsonAPI (option, data) {
let url = option.url
if (usingJSONP) {
url += '?duplextrans'
if (data) {
url += setUrlK(data.data)
data = {}
}
}
return ajax.requestAPI(Object.assign({}, option, {url}), data)
}
export function requestJanusAPI (option, data) {
return ajax.requestJanusAPI(option, data)
}
import ElementApi from './apis/workLog'
const usingJSONP = process.env.NODE_ENV !== 'production'
const API_HOST = process.env.API_HOST
const API_PORT = process.env.API_PORT
const JANUS_HOST = process.env.JANUS_HOST
const JANUS_PORT = process.env.JANUS_PORT
const option = {
tokenKey: '__token__',
sessionExpiredCode: 401,
loginUrl: '/logout.php',
host: API_HOST,
port: API_PORT,
hostJanus: JANUS_HOST,
portJanus: JANUS_PORT,
baseUrl: '',
ajax: {
method: usingJSONP ? 'GET' : 'POST',
dataType: 'json',
getQuery: usingJSONP ? '?duplextrans' : '',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
}
}
let apis = {}
apis = Object.assign(apis, ElementApi)
export default {
option,
...apis
}
// const urlEnd = process.env.NODE_ENV !== 'production' ? '?duplextrans' : ''
export default {
// 获取列表筛选条件
getFilter: {
url: '/vue/work-log/get-filter'
},
// 获取列表
getWorkLogList: {
url: '/vue/work-log/list'
}
}
/*# sourceMappingURL=app.css.map */
{
"version": 3,
"mappings": "",
"sources": [],
"names": [],
"file": "app.css"
}
\ No newline at end of file
.element-app {
.mb20 {
margin-bottom:20px;
}
}
export default function MapLoader () { // <-- 原作者这里使用的是module.exports
return new Promise((resolve, reject) => {
if (window.AMap) {
resolve(window.AMap)
} else {
var script = document.createElement('script')
script.type = 'text/javascript'
script.async = true
script.src = 'https://webapi.amap.com/maps?v=1.4.5&callback=initAMap&key=bd93b8717b90a76200dac805a0cf7e53'
script.onerror = reject
document.head.appendChild(script)
}
window.initAMap = () => {
resolve(window.AMap)
}
})
}
import VueCacheData from 'vue-cache-data'
let AppCache = new VueCacheData()
import { requestAPI, api } from '@/lib/commonMixin'
AppCache.add('queryCount', {}, function (onSucc, onFail) {
requestAPI(api.queryContent, {}).then((result) => {
onSucc(result)
}, onFail)
})
AppCache.init()
export default AppCache
import {
requestAPI,
api
} from '@/lib/commonMixin'
import Breadcrumb from '../unit/Breadcrumb.vue'
import VTable from '../unit/VTable.vue'
function getDefault (vm, key, def) {
let value = def
if (vm.$route.query[key]) value = vm.$route.query[key]
if (vm.rule.pageSize) value = vm.rule.pageSize
return parseInt(value)
}
export default {
components: {
Breadcrumb,
VTable
},
data () {
return {
loading: false,
post: null,
error: null,
tableData: {},
formData: {}
}
},
created () {
// 组件创建完后获取数据,
// 此时 data 已经被 observed 了
this.formData = Object.assign(this.formData, this.$route.query)
this.fetchData()
},
mounted () {
},
watch: {
// 如果路由有变化,会再次执行该方法
'$route': function () {
this.fetchData()
}
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
let query = {
pageIndex: getDefault(this, 'pageIndex', 1),
pageSize: getDefault(this, 'pageSize', 20)
}
query = Object.assign({}, query, this.$route.query)
if (query.t) delete query.t
// replace getPost with your data fetching util / API wrapper
requestAPI(api[this.$options.options.api], query)
.then((res) => {
this.tableData = res
this.loading = false
})
.catch((res) => {
this.listLoading = false
})
},
query () {
let query = Object.assign({}, this.$route.query, this.formData, {t: +new Date()})
this.$router.push({query})
}
}
}
<template>
<el-breadcrumb style="padding-bottom: 0px; margin-bottom: 20px;" separator="/">
<el-breadcrumb-item v-for="t of titleAry">{{t}}</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
export default {
props: {
title: {
require: true,
type: String
}
},
computed: {
titleAry () {
return this.title.split('/')
}
}
}
</script>
<style>
</style>
<template>
<div>
<el-table :data="data.list" highlight-current-row v-loading="rule.loading" @selection-change="selectionChange"
style="width: 100%;">
<el-table-column v-if="this.rule.selectionChange" type="selection">
</el-table-column>
<template v-for="item in rule.list">
<el-table-column v-if="!item.slots" :label="item.label" :width="item.width" :formatter="formatter(item)"
:type="item.type">
</el-table-column>
<el-table-column v-if="item.slots" :label="item.label" :width="item.width">
<template scope="scope" :item="item" :handle="handle">
<template v-for="slot in item.slots">
<el-button v-if="slot.type=='btn' || !slot.type" :type="slot.btnType" size="small"
@click="handle(scope.row, scope.$index, slot)">{{slot.text}}
</el-button>
<a v-if="slot.type=='link'" :href="href(slot.href, scope.row)">{{slot.text}}</a>
</template>
</template>
</el-table-column>
</template>
</el-table>
<el-pagination
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageIndex"
:page-sizes="rule.pageSizes?rule.pageSizes:pageSizes"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="data.total"></el-pagination>
</div>
</template>
<script>
export default{
props: {
rule: {
require: true,
type: Object
},
data: {
require: true,
type: [Object]
}
},
data: function () {
function getDefault (vm, key, def) {
let value = def
if (vm.$route.query[key]) value = vm.$route.query[key]
if (vm.rule.pageSize) value = vm.rule.pageSize
return parseInt(value)
}
return {
pageSizes: [20, 50, 100, 200],
pageSize: getDefault(this, 'pageSize', 20),
pageIndex: getDefault(this, 'pageIndex', 1)
}
},
methods: {
selectionChange (sels) {
if (this.rule.selectionChange) this.rule.selectionChange(sels)
},
formatter: function (item) {
if (item.formatter) return item.formatter
return function (row, column) {
if (item.exchange) {
return item.exchange[row.sex]
}
return row[item.prop]
}
},
handle (row, index, item) {
if (item.click) item.click(row, index)
},
href (href, row) {
if (typeof href === 'string') return href
if (typeof href === 'function') return href(row)
},
handleCurrentChange (pageIndex) {
this.updateRoute({pageIndex})
},
handleSizeChange (pageSize) {
this.updateRoute({pageSize})
},
updateRoute (obj) {
let query = this.$route.query
query = Object.assign({}, query, obj)
this.$router.push({query})
}
}
}
</script>
<style scope>
.pagination {
margin-top: 10px;
}
</style>
<template>
<el-col :span="12" class="header-search">
<div class="pull-right search-form">
<el-button type="primary" size="mini" @click="targetFun">
<i class="fa fa-edit faa-pulse animated"></i> {{buttonTitle}}
</el-button>
</div>
</el-col>
</template>
<script>
export default {
name: '',
data () {
return {}
},
methods: {
targetFun () {
this.editClient()
}
},
props: ['editClient', 'buttonTitle'],
created () {
}
}
</script>
<style scoped>
</style>
<template>
<div>
<el-row :gutter="10" class="header-title">
<el-col :span="titleSpan">
<h2>{{title}}</h2>
</el-col>
<component :is="type + 'Header'"
:ref="type + 'Header'"
:add-new-user="addNewUser"
:search-keyword="searchKeyword"
:search-key="searchKey"
:edit-client="editClient"
:button-title="buttonTitle"
:key-code="keyCode"
:model="model" v-if="type !== ''"></component>
</el-row>
</div>
</template>
<script>
import {blockHeadObj} from './viewHelper'
export default {
name: 'client-header',
data () {
return {}
},
components: {
...blockHeadObj
},
methods: {},
props: [
'title',
'titleSpan',
'searchKey',
'type',
'addNewUser',
'searchKeyword',
'buttonTitle',
'editClient',
'model',
'keyCode'],
created () {
}
}
</script>
<style scoped>
</style>
<template>
<section class="client-nav">
<router-link v-for="(nav, key) in navs"
:key="key"
:to="{name: nav.routerName, params: {id: $route.params.id}}"
:class="['btn', 'btn-default', 'btn-sm', {'btn-info': routeName === nav.routerName}, {'disabled': routeName === nav.routerName}]">
<i :class="['fa', 'fa-fw',nav.icon]"></i>
{{nav.title}}
</router-link>
<a @click.prevent="triggerNav" class="btn btn-default btn-sm el-button--primary">
<i class="fa fa-fw fa-plus"></i> 新建工作日志
</a>
</section>
</template>
<script>
import { navs } from '../../lib/constant'
export default {
name: '',
data () {
return {
navs: navs(),
routeName: ''
}
},
methods: {
triggerNav () {
this.trigger()
}
},
props: ['trigger'],
created () {
},
mounted () {
this.routeName = this.$route.name.split('-')[1]
}
}
</script>
<style scoped>
.client-nav{
margin: 0px 0 15px 0;
}
.client-nav a{
margin-right: 4px;
padding: 3.5px 7px;
}
.client-nav a:nth-last-child(2){
margin-right: 0;
}
.client-nav a i{
font-size: 14px;
}
.btn-info.disabled{
color: #fff;
background-color: #17a2b8;
border-color: #17a2b8;
}
</style>
<template>
<el-col :span="18" class="header-search">
<el-form ref="form" :model="model" size="mini" label-width="0">
<div class="pull-right search-form">
<el-form-item v-if="searchKey">
<el-input size="mini" v-model="model[(searchKey + '[' + keyCode + ']')]" placeholder="关键词">
<el-button slot="append" type="primary" @click="searchKeyword">搜索</el-button>
</el-input>
</el-form-item>
<el-form-item v-if="searchKey">
<el-button type="primary" size="mini" @click="addNewUser">
<i class="fa fa-plus faa-pulse animated"></i> {{buttonTitle}}
</el-button>
</el-form-item>
<el-form-item v-if="searchKey">
<a class="btn">
<i class="fa fa-fw fa-refresh faa-spin animated-hover"></i>
</a>
</el-form-item>
</div>
</el-form>
</el-col>
</template>
<script>
export default {
name: '',
data () {
return {}
},
methods: {
// searchKeyword () {
// this.$emit('update:searchKeyword')
// },
// addNew () {
// this.$emit('update:addNew')
// }
},
props: ['model', 'searchKey', 'keyCode', 'buttonTitle', 'searchKeyword', 'addNewUser'],
created () {
}
}
</script>
<style scoped>
</style>
<template>
<transition name="move" v-on:after-enter="afterEnter" :duration="{ enter: 500, leave: 0 }">
<section class="lm-content" v-show="is">
<el-row class="lm-header">
<el-col :span="2">
<i class="fa fa-angle-double-right" @click.prevent="isClose"></i>
</el-col>
<el-col :span="22" class="lm-content-leaveMessage">
<span>{{title}}</span>
</el-col>
</el-row>
<div class="page-body-content leaveMessage">
<el-row>
<el-col :span="24">
<component :is="pageName" :ref="pageName" @update:close="isClose"></component>
</el-col>
</el-row>
</div>
</section>
</transition>
</template>
<script>
import {clientObj} from './viewHelper'
export default {
name: '',
data () {
return {
is: false,
pageName: '',
title: '',
propData: {}
}
},
components: {
...clientObj
},
methods: {
isClose () {
this.$el.querySelector('.page-body-content').scrollTop = 0
this.is = false
},
isShow (title, pageName, data) {
this.is = true
this.title = title
this.pageName = pageName
this.propData = data
},
afterEnter () {
this.$refs[this.pageName].init(this.propData)
}
},
created () {
}
}
</script>
<style scoped lang="scss">
.page-body-content {
& .actionBox {
padding: 10px 0;
& .commentList {
padding: 0;
list-style: none;
li {
margin: 0;
margin-top: 10px;
list-style: none;
}
& .commenterImage {
width: 48px;
margin-right: 5px;
height: 100%;
float: left;
img {
width: 100%;
border-radius: 50%;
}
}
& .commentText {
padding: 0 0 10px;
margin-left: 60px;
& .comment-title {
color: #fff;
padding: 10px 10px 8px;
margin: 0;
background-color: #5a6277;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
font-size:12px;
}
& .addcomment {
color: white;
}
& .removecomment {
color: white;
}
& a {
.fa-fw {
color: white;
font-size:12px;
text-align: center;
}
}
& .sub-text {
background-color: #5a6277;
color: #fff;
font-size: 12px;
padding: 10px;
}
}
}
}
& .detailBox {
margin: 0;
min-height: 100%;
& .titleBox {
padding: 10px;
background: #4e5365;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
& .commentText {
padding: 0 0 10px;
& .sub-text {
& .el-row {
font-size: 12px;
.el-col:first-child {
text-align: right;
font-weight:700;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 3px;
}
.el-col:last-child {
padding-left: 15px;
padding-right: 15px;
margin-bottom: 3px;
}
}
}
}
}
}
}
.lm-content {
position: fixed;
top: 100px;
right: 0;
bottom: 0;
// height: 100%;
padding-bottom: 30px;
width: 60%;
background-color: #333744;
color: white;
font-size: 14px;
z-index: 1;
.lm-header{
border-bottom: 1px solid #fff;
height: 32px;
}
.lm-content-leaveMessage{
/*line-height: 32px;*/
& > span {
vertical-align: middle;
display: inline-block;
border-left: 2px solid #fff;
padding: 0 0 0 8px;
margin-top: 6px;
}
}
& .content {
width: 100%;
}
& i[class~=fa-angle-double-right] {
cursor: pointer;
font-size: 20px;
margin: 5px 10px 6px;
}
& .form-inline {
display: -ms-flexbox;
display: flex;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
-ms-flex-align: center;
align-items: center;
& .input-group {
position: relative;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-align: stretch;
align-items: stretch;
width: 100%;
margin-top: 10px;
& .form-control {
position: relative;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
width: 1%;
margin-bottom: 0;
}
& .comment-input {
height: 28px;
}
}
}
}
.move-enter-active, .move-leave-active {
transition: all 0.5s ease;
transform: trandslate3d(0, 0, 0);
}
.move-enter, .move-leave {
transform: translate3d(100%, 0, 0);
}
</style>
<template>
<section class="multiple-input">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<span v-for="(i, key) in item" :key="key">
<el-input v-model="i[multipleKey]" :placeholder="label" size="mini">
<el-button slot="append" v-if="key === 0" icon="el-icon-plus" @click="addItem(item)"></el-button>
<el-button slot="append" v-else icon="el-icon-minus" @click="delItem(item, key)"></el-button>
</el-input>
</span>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'multiple-input',
mixins: [itemMixin],
data () {
return {}
}
}
</script>
<style scoped>
</style>
<template>
<section class="multiple-mobile-input">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-row :gutter="10">
<el-col :span="mobileConfig.children[chil].typeSpan" v-for="(chil, key) in Object.keys(mobileConfig.children)" :key="key">
<el-input v-model="mobileItemForm[chil]" :placeholder="mobileConfig.children[chil].title" size="mini">
</el-input>
</el-col>
</el-row>
<span class="tips"><em>{{tips}}</em></span>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'multiple-mobile-input',
mixins: [itemMixin],
methods: {},
props: {
mobileItemForm: {
type: Object
},
mobileConfig: {
type: Object
}
}
}
</script>
<style scoped>
</style>
<template>
<section class="client-schedule-item">
<el-row class="client-schedule-row">
<el-col :span="4" :xs="24" class="client-schedule-left" :style="{'background-color': item.bgcolor}">
<el-row :gutter="10">
<el-col :span="24" class="entity-name">
<i class="fa fa-star" aria-hidden="true"></i>
<router-link :to="toView(item)">{{ item.client.name }}</router-link>
</el-col>
<el-col :span="24" class="entity-name">
<i class="fa fa-th-list" aria-hidden="true"></i>
<span>{{ Object.keys(item.project).length > 0 ? item.project.name : '(未设置)' }}</span>
</el-col>
<el-col :span="24">
<img class="user-avatar rounded-circle" :src="item.createdBy.avatar.name">
<span>{{ item.createdBy.name }}</span>
</el-col>
<el-col :span="24">
<i class="fa fa-sitemap" aria-hidden="true"></i>
<span>{{ item.createdBy.department.name}}</span>
</el-col>
</el-row>
</el-col>
<el-col :span="20" :xs="24" class="client-schedule-right">
<el-row :gutter="10">
<el-col :span="6" :xs="24">
开始时间: {{item.start_at !== '' ? item.start_at : noneText}}
</el-col>
<el-col :span="6" :xs="24">
用时: {{item.hours_spent_display !== '' ? item.hours_spent_display : noneText}}
</el-col>
<el-col :span="6" :xs="24">
联系人: {{item.workLogContacts.length === 0 ? noneText : item.workLogContacts.map(i => i.name).join(',')}}
</el-col>
<el-col :span="6" :xs="24">
签到:
<el-tooltip class="item" effect="dark" placement="top" v-if="Object.keys(item.location).length > 0">
<i class="fa fa-fw fa-location-arrow"></i>
<template slot="content"><p>地址: {{item.location.address}}</p>
<p>距离: {{item.distance !== '' ? item.distance : noneText}}</p>
<p>时间: </p></template>
</el-tooltip>
<span v-else>(未设置)</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="6" :xs="24">
结束时间: <span class="date-time">{{item.end_at !== '' ? item.end_at : noneText}}</span>
</el-col>
<el-col :span="6" :xs="24">
<span class="border border-success text-success rounded px-1">日志类型</span> :
{{Object.keys(item.scenarios).length === 0 ? noneText : item.scenarios.name}}
</el-col>
<el-col :span="6" :xs="24">
工作内容: {{Object.keys(item.scenarioOption).length === 0 ? noneText : item.scenarioOption.name }}
</el-col>
<el-col :span="6" :xs="24" class="show-picture">
图片:
<i class="fa fa-fw fa-file-image-o show-picture"
v-if="item.attachmentRelationships.length > 0"
@click="showImageViewer(item.attachmentRelationships)"></i>
<span v-else>{{noneText}}</span>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="12" :xs="24">
<span class="border border-danger text-danger rounded px-1">备注</span> :
{{item.description !== '' ? item.description : noneText}}
</el-col>
<el-col :span="12" :xs="24">
<slot name="opearate">
</slot>
</el-col>
</el-row>
</el-col>
</el-row>
</section>
</template>
<script>
export default {
name: 'scheduleItem',
props: {
item: Object
},
data () {
return {
noneText: '(未设置)'
}
},
methods: {
toView (item) {
let obj = {
name: 'viewClient',
params: {
id: item.id
}
}
return obj
},
showImageViewer (images) {
this.$emit('update:image', images.map(i => 'https://beta.jinchangxiao.com' + i.attachment.name))
// this.$emit('update:image', ['https://beta.jinchangxiao.com/files/protected/a57be577deb434/2019/03/34cf2b53-3959-33fe-93af-98a50fd18c11.jpg'])
}
},
computed: {
isNullClient () {
return !this.item.client
},
isPublic () {
return !!this.item.is_public
}
},
mounted () {
}
}
</script>
<style scoped lang="scss">
.text-info {
color: #17a2b8 !important;
}
.border-info {
border-color: #17a2b8 !important;
}
.colRed {
color: red;
border: 1px solid red;
}
.colGreen {
color: green;
border: 1px solid green;
}
.colRed, .colGreen, .col {
border-radius: .25rem;
padding: 0 .25rem;
}
.rounded-circle {
border-radius: 50% !important;
}
.user-avatar {
width: 16px;
max-width: 16px;
height: 16px;
max-height: 16px;
}
@include c('schedule-item') {
margin-bottom: 10px;
font-size: 12px;
}
@include c('schedule-item:first-child') {
.client-schedule-left {
border-radius: 5px 0 0 0;
}
}
@include c('schedule-row') {
min-height: 100px;
background-color: white;
display: flex;
flex-wrap: wrap;
}
@include c('schedule-left') {
background-color: #FF6A6A;
min-height: 100%;
padding: 10px 15px 6px;
> .el-row {
> .el-col {
margin-bottom: 4px;
}
}
.el-row div:first-child, .el-row div:first-child a, .entity-name, .entity-name a {
color: white;
}
span {
display: inline-block;
padding-left: 5px;
}
@include e('private') {
display: inline-block;
// border: 1px solid black;
padding-right: 5px;
border-radius: 5px;
width: 40px;
height: 20px;
}
}
@include c('schedule-right') {
height: 100%;
padding: 10px 15px 6px;
.border {
border: 1px solid #dee2e6;
}
.border-success {
border-color: #28a745 !important;
}
.border-danger {
border-color: #dc3545;
}
.text-success {
color: #28a745 !important;
}
.text-danger {
color: #E45744;
}
.rounded {
border-radius: .25rem !important;
}
.px-1 {
padding: 0 .25rem;
}
> .el-row {
> .el-col {
margin-bottom: 4px;
.el-button {
margin: 0 2px;
padding: 7px;
}
.date-time, .show-picture {
color: #649FD7;
}
.show-picture {
cursor: pointer;
}
}
}
.badge {
border-radius: 2px;
color: #333744;
background: #ffffff;
vertical-align: baseline;
display: inline;
padding: 2px 6px;
}
span.badge-unread {
&:hover {
border-color: #de321d
}
color: #fff;
background-color: #e54c3a;
border-color: #a32516
}
@include e('item') {
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 5px 0 5px 5px;
flex: 1;
}
@include e('status') {
color: red;
}
@include e('content') {
display: inline-block;
border: 1px solid red;
border-radius: 5px;
height: 20px;
padding: 0 5px 0 5px;
color: red;
}
}
</style>
<template>
<section class="multiple-input">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-autocomplete
v-model="workingAddresses.address2"
:fetch-suggestions="remoteMethod"
placeholder="选择办公地址"
:hide-loading="true"
:trigger-on-focus="false"
@select="handleSelect"
prefix-icon="el-icon-search"
suffix-icon="el-icon-arrow-down"
></el-autocomplete>
<div id="container" class="h500 w500"></div>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
import MapLoader from '@/assets/js/AMap.js'
// import $ from 'jquery'
// import AMapUI from 'AMapUI'
export default {
name: 'single-a-map',
mixins: [itemMixin],
data () {
return {
map: Object,
currentCity: '',
marker: [],
mapSearch: '',
mapKeyWord: '',
loading: false,
searchList: []
}
},
mounted () {
// this.initAMap()
},
methods: {
initAMap () {
let that = this
this.mapKeyWord = this.workingAddresses.address2
this.currentCity = this.workingAddresses.city_id
MapLoader().then(AMap => {
that.map = new AMap.Map('container', {
viewMode: '3D',
zoom: 12,
expandZoomRange: true,
jogEnable: true
})
AMap.plugin(
['AMap.Scale', 'AMap.ControlBar'],
() => {
that.map.addControl(new AMap.Scale())
that.map.addControl(new AMap.ControlBar())
})
that.addsinglemarker()
}, e => {
console.log('地图加载失败', e)
})
},
handleSelect (val) {
this.workingAddresses.region_id = parseInt(val.adcode)
let map = this.searchList.find(i => i.adcode === val.adcode)
if (map) {
this.selectResult(map.adcode, map.district + map.name)
}
},
remoteMethod (query, cb) {
this.mapKeyWord = query
let that = this
if (query !== '') {
this.loading = true
window.AMap.service(['AMap.Autocomplete'], () => {
let autoOptions = {
city: this.currentCity // 城市,默认全国
}
let auto = new window.AMap.Autocomplete(autoOptions)
// 查询成功时返回查询结果
auto.search(query, (status, result) => {
if (result.tips) {
that.searchList = result.tips
cb(that.searchList.map(i => {
return {
adcode: i.adcode,
value: i.district + ':' + i.name
}
}))
this.loading = false
}
})
})
} else {
this.mapKeyWord = []
}
},
selectResult (cityCode, text) {
this.map.plugin(['AMap.PlaceSearch'], () => {
let msearch = new window.AMap.PlaceSearch({
city: this.currentCity // 范围,默认: 500
}) // 构造地点查询类
window.AMap.event.addListener(msearch, 'complete', this.placeSearchCallBack) // 查询成功时的回调函数
msearch.setCity(cityCode)
msearch.search(text) // 关键字查询查询
})
},
placeSearchCallBack (data) {
// 清空地图上的InfoWindow和Marker
// let marker = []
this.map.clearMap()
let poiArr = data.poiList.pois
let resultCount = poiArr.length
for (let i = 0; i < resultCount; i++) {
this.addmarker(i, poiArr[i])
break
}
this.map.setFitView()
},
addmarker (i, d) {
let lngX = d.location.getLng()
let latY = d.location.getLat()
let markerOption = {
draggable: true, // 点标记可拖拽
map: this.map,
icon: 'https://beta.jinchangxiao.com/img/position.png',
position: new window.AMap.LngLat(lngX, latY),
raiseOnDrag: true
}
let mar = new window.AMap.Marker(markerOption)
this.marker.push(new window.AMap.LngLat(lngX, latY))
this.workingAddresses.lng = lngX
this.workingAddresses.lat = latY
let that = this
window.AMap.event.addListener(mar, 'mousemove', () => {
let position = mar.getPosition()
that.workingAddresses.lat = position.lat
that.workingAddresses.lng = position.lng
})
},
geocoder (address) {
// 加载地理编码插件
window.AMap.service(['AMap.Geocoder'], function () {
let MGeocoder = new window.AMap.Geocoder({
radius: 1000 // 范围,默认: 500
})
// 返回地理编码结果
// 地理编码
MGeocoder.getLocation(address, function (status, result) {
if (status === 'complete' && result.info === 'OK') {
this.map.clearMap()
this.geocoderCallBack(result)
this.map.setZoom(8)
}
})
})
},
regeocoder (lng, lat) {
// 加载地理编码插件
let lnglatXY = new window.AMap.LngLat(lng, lat)
window.AMap.service(['AMap.Geocoder'], function () {
let MGeocoder = new window.AMap.Geocoder({
radius: 1000,
extensions: 'all'
})
// 逆地理编码
MGeocoder.getAddress(lnglatXY, function (status, result) {
if (status === 'complete' && result.info === 'OK') {
}
})
})
},
addsinglemarker () {
let lngX = this.workingAddresses.lng
let latY = this.workingAddresses.lat
if ((lngX === '' && latY === '') || (lngX === '0' && latY === '0')) {
this.resetCityCenter()
return false
}
let markerOption = {
draggable: true, // 点标记可拖拽
map: this.map,
icon: 'https://beta.jinchangxiao.com/img/position.png',
position: new window.AMap.LngLat(lngX, latY),
raiseOnDrag: true
}
let mar = new window.AMap.Marker(markerOption)
this.marker.push(new window.AMap.LngLat(lngX, latY))
let that = this
window.AMap.event.addListener(mar, 'mousemove', () => {
let postion = mar.getPosition()
that.workingAddresses.lng = postion.lat
that.workingAddresses.lng = postion.lng
})
this.map.setFitView()
},
resetCityCenter () {
let that = this
window.AMap.service(['AMap.CitySearch'], function () {
// 实例化城市查询类
let citySearch = new window.AMap.CitySearch()
// 自动获取用户IP,返回当前城市
citySearch.getLocalCity(function (status, result) {
if (status === 'complete' && result.info === 'OK') {
if (result && result.city && result.bounds) {
let cityBounds = result.bounds
that.currentCity = result.city
that.map.setBounds(cityBounds)
that.map.setZoom(12)
}
}
})
})
},
geocoderCallBack (data) {
// 地理编码结果数组
let geocode = []
geocode = data.geocodes
for (let i = 0; i < geocode.length; i++) {
// 拼接输出html
this.addmarker(i, geocode[i])
}
this.map.setFitView()
}
},
created () {
},
props: ['workingAddresses'],
watch: {
'workingAddresses' (val) {
console.log(val)
}
}
}
</script>
<style scoped>
.h500 {
height: 300px;
}
.chzn-done {
width: 100%
}
.el-select, .el-autocomplete {
width: 100%;
margin-bottom: 14px
}
</style>
<template>
<section>
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-checkbox-group v-model="items" size="mini">
<el-checkbox-button v-for="(check, key) in optionsList" :value="check.value" :label="check.name" :key="key">{{check.name}}</el-checkbox-button>
</el-checkbox-group>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
<span class="tips">{{tips}}</span>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-checkbox',
mixins: [itemMixin],
methods: {}
}
</script>
<style>
.el-checkbox-button__inner, .el-checkbox-button:last-child .el-checkbox-button__inner, .el-checkbox-button:first-child .el-checkbox-button__inner{
border-left: 1px solid #DCDFE6;
margin: 0 10px 5px 0;
border-radius: 6px!important;
}
.el-checkbox-button__inner:hover{
color: #333;
background-color: #e6e6e6;
border-color: #adadad;
}
.el-checkbox-button.is-checked .el-checkbox-button__inner{
color: #fff;
background-color: #3c3c3c;
border-color: #373737;
box-shadow: 0 0 0 0 #373737;
}
</style>
<template>
<section class="single-date-picker">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-date-picker
v-model="timeItem"
type="date"
placeholder="选择日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd">
</el-date-picker>
<span class="tips"><em>{{tips}}</em></span>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-date-picker',
mixins: [itemMixin]
}
</script>
<style scoped>
.el-date-editor.el-input, .el-date-editor.el-input__inner{
width: 100%;
}
</style>
<template>
<section class="single-input">
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-input v-model="item" :placeholder="label" :type="type" :rows="rows" size="mini">
<template slot="prepend">
<span v-if="prepend.text !== ''">{{prepend.text}}</span>
<i :class="['fa', prepend.icon]" v-if="prepend.icon !== ''"></i>
</template>
<template slot="append">
<span v-if="append.text !== ''">{{append.text}}</span>
<i :class="['fa', append.icon]" v-if="append.icon !== ''"></i>
</template>
<el-select v-model="inputSelectItem" slot="prepend" placeholder="请选择" v-if="formInputSelectOptions">
<el-option v-for="(option, key) in formInputSelectOptions" :key="key" :label="option.name" :value="option.key"></el-option>
</el-select>
</el-input>
<span class="tips"><em>{{tips}}</em></span>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-input',
mixins: [itemMixin],
methods: {}
}
</script>
<style scoped>
.tips{
display: block;
line-height: 1.5;
color: #6c757d;
}
</style>
<style>
.single-input .el-select .el-input {
width: 90px;
}
</style>
<template>
<section>
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-radio-group size="mini" v-model="item">
<el-radio-button v-for="(radio, key) in optionsList" :key="key" :label="radio.key">
{{radio.name}}
</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
<span class="tips">{{tips}}</span>
</el-col>
</el-row>
</section>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-radio',
mixins: [itemMixin]
}
</script>
<style>
.el-radio-button__inner, .el-radio-button:last-child .el-radio-button__inner, .el-radio-button:first-child .el-radio-button__inner{
border-left: 1px solid #DCDFE6;
margin: 0 10px 5px 0;
border-radius: 6px!important;
}
.el-radio-button__inner:hover{
color: #333;
background-color: #e6e6e6;
border-color: #adadad;
}
.el-radio-button__orig-radio:checked+.el-radio-button__inner{
color: #fff;
background-color: #3c3c3c;
border-color: #373737;
box-shadow: 0 0 0 0 #373737;
}
.el-radio-button__orig-radio:checked+.el-radio-button__inner:hover{
color: #333;
background-color: #d4d4d4;
border-color: #8c8c8c;
}
</style>
<template>
<div>
<el-row :gutter="10">
<el-col :span="4" :class="['client-label', 'text-right', {'required': required}]">
<span>{{label}}</span>
</el-col>
<el-col :span="14">
<el-select v-model="item" :placeholder="label" size="mini">
<el-option
v-for="item in optionsList"
:key="item.key"
:label="item.name"
:value="item.key">
</el-option>
</el-select>
</el-col>
<el-col :span="6">
<slot name="formError"></slot>
<span class="tips">{{tips}}</span>
</el-col>
</el-row>
</div>
</template>
<script>
import itemMixin from '../../lib/singleItemMixin'
export default {
name: 'single-select',
mixins: [itemMixin],
methods: {}
}
</script>
<style scoped>
.el-select{
width: 100%;
}
</style>
<template>
<el-col :span="1">
</el-col>
</template>
<script>
export default {
name: '',
data () {
return {}
},
methods: {},
created () {
}
}
</script>
<style scoped>
</style>
import buttonHeader from './buttonHeader'
import keywordHeader from './keywordHeader'
import textHeader from './textHeader'
export let blockHeadObj = {
buttonHeader,
keywordHeader,
textHeader
}
import singleInput from './singleInput'
import singleRadio from './singleRadio'
import singleDataPicker from './singleDatePicker'
import singleSelect from './singleSelect'
import singleCheckbox from './singleCheckbox'
import singleAMap from './singleAMap'
import multipleInput from './multipleInput'
import multipleMobileInput from './multipleMobileInput'
export let formObj = {
singleInput,
singleRadio,
singleDataPicker,
singleSelect,
singleCheckbox,
singleAMap,
multipleInput,
multipleMobileInput
}
<template>
<div>
<div class="images clearfix" style="display: none;">
<img v-for="(source, index) in images" :key="index" :src="source" class="image">
</div>
<button type="button" class="button" @click="show">Show</button>
</div>
</template>
<script>
const sourceImages = []
const base = parseInt((Math.random() * 60), 10) + 10
for (let i = 0; i < 10; i++) {
sourceImages.push('https://picsum.photos/1440/900/?image=' + (base + i))
}
import 'viewerjs/dist/viewer.css'
import Viewer from 'viewerjs'
export default {
name: '',
data () {
return {
options: {
toolbar: true,
url: 'data-source'
},
images: []
}
},
methods: {
show () {
this.images = [...sourceImages].splice(0, 5)
const viewer = new Viewer(this.$el.querySelector('.images'), {
hidden: function () {
viewer.destroy()
}
})
viewer.update()
this.$nextTick(() => {
viewer.show()
viewer.update()
})
}
},
mounted () {
},
created () {
}
}
</script>
<style scoped>
</style>
<template>
<section>
<div class="content">
<client-header ref="clientHeader"
title="工作日志"
:title-span="6"
:model="form"
:search-key="'WorkLogSearch'"
:key-code="'keyword'"
type="keyword"
button-title="新建工作日志"
:add-new-user="addNewUser"
:search-keyword="searchKeyword"></client-header>
<client-form ref="clientForm" :filter="filter" @update:clientList="form =>{ updateForm(form) }"></client-form>
<div class="page-body-content">
<div v-loading="loading">
<ScheduleItem
v-for="(item, key) in result.list"
:item="item"
:key="key"
@update:image="imgs => {setImage(imgs)}">
<span slot="opearate">
<el-button class="pull-right" type="primary" size="mini" :disabled="!item.can_give_up"
@click="giveUp(item.id)">
<i class="fa fa-trash-o faa-shake"></i> 删除
</el-button>
<el-button class="pull-right" type="primary" size="mini" :disabled="!item.can_update"
@click="edit(item.id)">
<i class="fa fa-edit"></i> 编辑
</el-button>
<el-button class="pull-right" type="primary" size="mini" @click="addMessage(item.id)">
<span :class="['badge', {'badge-unread': item.unread > 0}]" v-if="item.commentCount">{{item.commentCount.comment}}</span>
<i class="fa fa-commenting"></i> 留言
</el-button>
</span>
</ScheduleItem>
<div v-if="result.list.length === 0">
<el-card class="box-card">
未查询到数据!
</el-card>
</div>
</div>
<Pagenation
@update:pager="pager => {updatePage(pager)}"
:pager.sync="pagenation"
:total="totalcount">
</Pagenation>
<!--<el-button type="primary" @click="showLeave">测试</el-button>-->
</div>
</div>
<div class="images clearfix" style="display: none;">
<img v-for="(source, index) in images" :key="index" :src="source" class="image">
</div>
</section>
</template>
<script>
import ScheduleItem from '../common/scheduleItem'
import Pagenation from './schedulePagenation'
import clientHeader from '../common/clientHeader'
import clientForm from './workLogListForm'
import {
requestAPI,
api
} from '@/lib/commonMixin'
import 'viewerjs/dist/viewer.css'
import Viewer from 'viewerjs'
export default {
name: '',
components: {
clientHeader,
clientForm,
ScheduleItem,
Pagenation
},
data () {
return {
options: {
toolbar: true,
url: 'data-source'
},
result: {
list: []
},
form: {
'WorkLogSearch[keyword]': ''
},
filter: [],
pagenation: {
page: 1
},
totalcount: 0,
loading: false,
images: []
}
},
methods: {
setImage (imgs) {
this.images = imgs
const viewer = new Viewer(this.$el.querySelector('.images'), {
hidden: function () {
viewer.destroy()
}
})
this.$nextTick(() => {
viewer.show()
viewer.update()
})
},
addNewUser () {
this.$refs.leaveModule.isShow('新建客户', 'clientAdd')
},
searchKeyword (search) {
this.updateForm(search)
},
updateForm (form) {
console.log(form)
Object.assign(this.form, form)
this.getList()
},
getList () {
if (this.loading) {
return
}
this.loading = true
requestAPI(api.getWorkLogList, {
data: {
...this.form,
page: this.pagenation.page
}
}).then(res => {
this.result.list = res.list
// this.pagenation.page = res.pagenation.thispage
this.totalcount = res.pagenation.totalcount
// this.totalcount = 8
}).finally(_ => {
this.loading = false
})
},
updateOptions (res) {
res.forEach(item => {
if (item.itemKey) {
item.cascader = []
item.value.forEach(i => {
i.label = i.name
i.value = i.id
i.children = i.items
delete i.items
i.children.forEach(s => {
s.label = s.name
s.value = s.id
})
})
}
})
return res
},
getFilter () {
requestAPI(api.getFilter).then(res => {
this.filter = this.updateOptions(res)
})
}
},
created () {
this.getFilter()
}
}
</script>
<style scoped lang="scss">
.text-info {
color: #17a2b8 !important;
}
.border-info {
border-color: #17a2b8 !important;
}
.colRed {
color: red;
border: 1px solid red;
}
.colGreen {
color: green;
border: 1px solid green;
}
.colRed, .colGreen, .col {
border-radius: .25rem;
padding: 0 .25rem;
}
.rounded-circle {
border-radius: 50% !important;
}
@include c('schedule-right') {
> .el-row {
> .el-col {
margin-bottom: 4px;
.el-button {
margin: 0 2px;
padding: 7px;
}
.date-time {
color: #649FD7;
}
}
}
.badge {
border-radius: 2px;
color: #333744;
background: #ffffff;
vertical-align: baseline;
display: inline;
padding: 2px 6px;
}
span.badge-unread {
&:hover {
border-color: #de321d
}
color: #fff;
background-color: #e54c3a;
border-color: #a32516
}
}
</style>
<template>
<section class="pull-right">
<el-pagination v-if="total > 0" class=""
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pager.page"
:page-sizes="[10, 20, 40, 60, 80, 100]"
:page-size="pager.pagesize"
layout="total, prev, pager, next, jumper"
:total="total">
</el-pagination>
</section>
</template>
<script>
export default {
props: {
total: {
type: Number,
default: 0
}
},
data () {
return {
pager: {
page: 1,
pagesize: 20
},
totalcount: 0
}
},
created () {
// this.$emit('update:pager', this.pager)
},
methods: {
handleSizeChange (val) {
this.pager.page = 1
this.pager.pagesize = val
// this.totalcount = this.total
this.$emit('update:pager', this.pager)
// this.$parent.loadList()
},
handleCurrentChange (val) {
this.pager.page = val
// this.totalcount = this.total
this.$emit('update:pager', this.pager)
// this.$parent.loadList()
}
}
}
</script>
<style scoped>
</style>
<template>
<div>
<el-row class="form-content">
<el-form ref="clientForm" size="mini" :model="clientForm" label-width="0" label-position="top">
<el-col :span="4" v-for="(item, key) in filter" :key="key">
<el-form-item>
<span slot="label" v-if="item.key !== 'Filter[sort]'">
<a @click.prevent="timeSort" v-if="item.key === 'Filter[name]'">
{{item.name}}
<i :class="['fa', setSortIcon()]"></i>
</a>
<span v-else>
{{item.name}}
</span>
</span>
<el-select clearable
v-model="clientForm[item.key]"
placeholder="请选择"
v-if="!item.itemKey && item.key !== 'Filter[sort]'" size="mini">
<el-option v-for="(option, optKey) in item.value"
:key="optKey"
:label="option.name"
:value="option.key">
<span style="font-size: 12px;">{{option.name}}</span>
</el-option>
</el-select>
<el-cascader
v-model="item.cascader"
change-on-select
clearable
v-if="item.itemKey"
:options="item.value"
@change="setCascader(item)"
></el-cascader>
</el-form-item>
</el-col>
</el-form>
</el-row>
</div>
</template>
<script>
import {findWhere} from '../../lib/viewHelper'
export default {
name: 'client-form',
data () {
return {
clientForm: {}
}
},
props: ['filter'],
methods: {
setCascader (item) {
if (item.cascader.length === 1) {
this.clientForm[item.key] = item.cascader[0]
this.clientForm[item.itemKey] = ''
} else if (item.cascader.length === 2) {
this.clientForm[item.itemKey] = item.cascader[0]
this.clientForm[item.key] = item.cascader[1]
} else {
this.clientForm[item.itemKey] = ''
this.clientForm[item.key] = ''
}
},
setDefault (filter) {
filter.forEach(item => {
if (item.default) {
let ids = findWhere(item.value, item.default, 'id')
if (ids.node) {
if (ids.parentNode) {
this.clientForm[item.itemKey] = ids.parentNode.id
this.clientForm[item.key] = ids.node.id
item.cascader = [ids.parentNode.id, ids.node.id]
} else {
this.clientForm[item.itemKey] = ''
this.clientForm[item.key] = ids.node.id
item.cascader = [ids.node.id]
}
}
}
})
},
init () {
this.$watch('clientForm', (val) => {
this.$emit('update:clientList', val)
}, {deep: true})
},
setSortIcon () {
if (this.clientForm['Filter[sort]'] === '') {
return 'fa-sort-amount-desc'
} else if (this.clientForm['Filter[sort]'] === 'DESC') {
return 'fa-sort-amount-desc'
} else {
return 'fa-sort-amount-asc'
}
},
timeSort () {
if (this.clientForm['Filter[sort]'] === '') {
this.clientForm['Filter[sort]'] = 'ASC'
} else if (this.clientForm['Filter[sort]'] === 'ASC') {
this.clientForm['Filter[sort]'] = 'DESC'
} else {
this.clientForm['Filter[sort]'] = 'ASC'
}
}
},
created () {
},
watch: {
'filter' (val) {
val.forEach(item => {
if (item.itemKey) {
this.$set(this.clientForm, item.itemKey, '')
}
this.$set(this.clientForm, item.key, '')
})
this.$nextTick(() => {
this.init()
this.setDefault(val)
})
}
}
}
</script>
<style scoped>
</style>
import Vue from 'vue'
import VueBus from 'vue-bus'
import VueRouter from 'vue-router'
Vue.use(VueBus)
Vue.use(VueRouter)
import ElementUI from 'element-ui'
if (process.env.NODE_ENV !== 'production') {
Vue.use(ElementUI)
}
import './assets/css/app.scss'
import RouterInit from './route'
RouterInit()
// request
export { requestWithJsonAPI } from '../ajax'
export { requestAPI } from '../lib/request'
export { requestJanusAPI } from '../ajax-janus'
export { default as api } from '@/api'
export let detailKey = () => {
return [
{
title: '客户名称',
key: 'name',
value: ''
},
{
title: '英文名称',
key: 'en_name',
value: ''
},
{
title: '客户性质',
key: 'client_type',
value: ''
},
{
title: '客户级别',
key: 'client_class',
value: '',
styleKey: 'clientClass',
style: {}
},
{
title: '客户行业',
key: 'client_industry',
value: ''
},
{
title: '客户经理',
key: 'sales_rep',
value: ''
},
{
title: '公司性质',
key: 'company_nature',
value: ''
},
{
title: '公司网址',
key: 'website',
value: '',
link: ''
},
{
title: '注册资金',
key: 'capital',
value: ''
},
{
title: '成立日期',
key: 'established_at',
value: ''
},
{
title: '法人代表',
key: 'corp_rep',
value: ''
},
{
title: '员工人数',
key: 'headcount',
value: ''
},
{
title: '办公地址',
key: 'working_address',
value: ''
},
{
title: '座机',
key: 'telephone',
value: ''
},
{
title: '传真',
key: 'fax',
value: ''
},
{
title: '年营业额',
key: 'scale',
value: ''
},
{
title: '分支机构',
key: 'branch',
value: ''
},
{
title: '年采购规模',
key: 'purchase_scale_per_yr',
value: ''
},
{
title: '年采购频率',
key: 'purchase_freq',
value: ''
},
{
title: '备注',
key: 'description',
value: ''
},
{
title: '开票信息',
key: 'invoice_info',
value: ''
},
{
title: '录入人',
key: 'created_by',
value: ''
},
{
title: '创建时间',
key: 'created_at',
value: ''
}
]
}
export let navs = () => {
return [
{
title: '客户信息',
icon: 'fa-user',
routerName: 'viewClient'
},
{
title: '工作日志',
icon: 'fa-book',
routerName: ''
},
{
title: '商机信息',
icon: 'fa-info-circle',
routerName: ''
},
{
title: '待办事项',
icon: 'fa-align-justify',
routerName: ''
},
{
title: 'Case',
icon: 'fa-briefcase',
routerName: ''
},
{
title: '销售记录',
icon: 'fa-signal',
routerName: ''
},
{
title: '销售合同',
icon: 'fa-server',
routerName: ''
},
{
title: '采购合同',
icon: 'fa-shopping-bag',
routerName: ''
},
{
title: '备忘录',
icon: 'fa-list-alt',
routerName: ''
},
{
title: '项目列表',
icon: 'fa-tasks',
routerName: ''
},
{
title: '客户文档',
icon: 'fa-file-text-o',
routerName: ''
},
{
title: '费用报销',
icon: 'fa-credit-card-alt',
routerName: ''
}
]
}
export let clientUserForm = () => {
return {
id: {
type: Number,
showType: 'hidden'
},
name: {
title: '联系人姓名',
type: String,
showType: 'singleInput',
required: true
},
email: {
title: '电子邮箱',
type: String,
showType: 'singleInput'
},
sex: {
title: '性别',
type: String,
showType: 'singleRadio'
},
birth_date: {
title: '生日',
type: String,
showType: 'singleDataPicker'
},
client_id: {
title: '属于客户',
type: String,
showType: 'singleSelect',
routerParam: 'clientId',
routerType: 'int'
},
user_status: {
title: '联系人状态',
type: String,
showType: 'singleRadio',
defaultData: 2
},
contactKeyRoles: {
title: '关键角色',
type: Array,
showType: 'singleCheckbox'
},
contactAttitude: {
title: '客户关系',
type: Number,
showType: 'singleRadio'
},
department: {
title: '部门',
type: String,
showType: 'singleInput'
},
mobiles: {
title: '手机',
type: Array,
showType: 'multipleInput',
defaultData: {
key: 'mobile_display',
name: ''
},
multipleKey: 'mobile_display'
},
multipleMobile: {
title: '座机',
showType: 'multipleMobileInput',
children: {
telephone_area_code: {
title: '区号',
type: String,
typeSpan: 6
},
telephone: {
title: '座机',
type: String,
typeSpan: 12
},
telephone_extension_number: {
title: '分机号',
type: String,
typeSpan: 6
}
}
},
description: {
title: '备注',
type: String,
showType: 'singleInput'
}
}
}
import api from '../api'
import $ from 'jquery'
// import Cookie from 'browser-cookie'
let globalOpt = api.option
const SESSION_EXPIRED_CODE = globalOpt.sessionExpiredCode
const SUCCESS_CODE = 2000
const HTTP_PROTOCOL = /^http[s]?:\/\//
// const usingJSONP = false
const usingJSONP = process.env.NODE_ENV !== 'production'
function normalizeUrl (url, query, option) {
if (HTTP_PROTOCOL.test(url)) return url
let fullUrl = option.host || ''
if (option.port) {
fullUrl += option.port ? ':' + option.port : ''
}
if (option.baseUrl) {
fullUrl += option.baseUrl
}
fullUrl += url
if (Object.keys(query).length > 0) {
fullUrl += '?' + $.param(query)
}
return fullUrl
}
function mergeAjaxOption (...options) {
let option1 = options.shift()
let option2 = options.shift()
// console.log(option1, option2)
let query = {...option1.query, ...option2.query}
let data = {...option1.data, ...option2.data}
let headers = {...option1.headers, ...option2.headers}
let mergedResult = {
...option1,
...option2,
query,
data,
headers
}
if (options.length) {
options.unshift(mergedResult)
return mergeAjaxOption(...options)
} else {
return mergedResult
}
}
export function request (option) {
return new Promise(function (resolve, reject) {
$.ajax(option).then(function (res, status, xhr) {
let code = res.code
if (code === SESSION_EXPIRED_CODE) {
window.location.replace(globalOpt.loginUrl)
}
if (code === SUCCESS_CODE) {
resolve(res.result)
} else {
// 如果不执行 reject,可能加载中动画不被关闭
reject(res, xhr, null)
if (res.msg) {
// 先让界面变化
// setTimeout(function () {
// window.alert(res.msg)
// }, 300)
}
}
}, function (xhr, status, error) {
console.error('请求' + option.url + '失败:', xhr, status, error)
// 如果不执行 reject,可能加载中动画不被关闭
reject(null, xhr, error)
// 先让界面变化
setTimeout(function () {
if ('onLine' in window.navigator) {
if (!window.navigator.onLine) {
window.alert('网络连接已断开,请连接网络后重试!')
} else {
window.alert('数据请求失败,请稍后重试。\n如还是未能解决,请向系统客服咨询,谢谢。\n数据请求地址:' + option.url)
}
} else {
window.alert('数据请求失败!\n请确认您的网络联接正常,并稍后重试或向系统客服咨询,谢谢。\n数据请求地址:' + option.url)
}
}, 300)
})
})
}
// function getTokenFromCookie () {
// let cookie = new Cookie()
// return cookie.get('vup_token')
// }
let isSessionExpired = false
export function requestAPI (api, option = {}) {
// 第一个请求会放过期提示用户登录后,避免之后的其他请求再次弹消息
if (isSessionExpired) {
window.location.href = globalOpt.loginUrl
return new Promise(function (resolve, reject) {
resolve({})
})
}
let setUrlK = (ojson) => {
let params = ''
Object.keys(ojson).forEach(item => {
params += '&' + encodeURIComponent(item) + '=' + ojson[item]
})
return params
}
// merge option
let ajaxOption = mergeAjaxOption(
globalOpt.ajax, // global
api, // curr api option
option // curr request option
)
ajaxOption.url = normalizeUrl(ajaxOption.url, ajaxOption.query, globalOpt)
delete ajaxOption.query
if (usingJSONP) {
ajaxOption.url = ajaxOption.url + ajaxOption.getQuery
if (ajaxOption.data) {
ajaxOption.url += setUrlK(ajaxOption.data)
delete ajaxOption.data
}
}
// ajaxOption.contentType = 'application/json'
// ajaxOption.dataType = 'json'
// ajaxOption.data = JSON.stringify(ajaxOption.data)
// ajaxOption.data = {data: JSON.stringify(ajaxOption.data)}
// stringify object member of data
let data = ajaxOption.data
for (let p in data) {
let dp = data[p]
if (typeof dp === 'object' && dp !== null) {
data[p] = JSON.stringify(dp)
}
}
// check is session expired
// let token = getTokenFromCookie()
// if (!token && process.env.NODE_ENV === 'production') {
// console.info('token expired')
// isSessionExpired = true
// window.alert('登录超时,请重新登录!')
// window.location.href = globalOpt.loginUrl
// return new Promise(function (resolve, reject) {
// resolve({})
// })
// }
// append token to header
// let headers = ajaxOption.headers || (ajaxOption.headers = {})
// headers['X-Token'] = token
return request(ajaxOption)
}
export default {
data () {
return {
item: '',
items: [],
timeItem: '',
inputSelectItem: 'rmb'
}
},
props: {
formItem: [String, Number, Array],
formItems: {
type: Array,
default () {
return [{key: 'new', name: ''}]
}
},
formTimeItem: {
type: String
},
label: {
type: String
},
required: {
type: Boolean
},
optionsList: Array,
tips: {
type: String
},
formInputSelect: {
type: String
},
formInputSelectOptions: Array,
type: {
type: String,
default: 'text'
},
rows: {
type: Number
},
prepend: {
type: Object,
default () {
return {
icon: '',
text: ''
}
}
},
append: {
type: Object,
default () {
return {
icon: '',
text: ''
}
}
},
multipleKey: {
type: String,
default: 'name'
}
},
methods: {
addItem (items) {
items.push({
key: this.multipleKey,
name: ''
})
},
delItem (items, key) {
items.splice(key, 1)
}
},
watch: {
'formItem': {
handler (val) {
this.item = val
},
deep: true,
immediate: true
},
'formTimeItem' (val) {
this.timeItem = val
},
'formInputSelect' (val) {
this.inputSelectItem = val
},
'item' (val) {
this.$emit('update:item', val)
},
'items': {
handler (val) {
this.$emit('update:item', val)
},
deep: true
},
'timeItem' (val) {
this.$emit('update:item', val)
},
'inputSelectItem' (val) {
this.$emit('update:inputSelect', val)
}
}
}
import Cookie from 'browser-cookie'
export let getAuthInfoFromCookie = () => {
let cookie = new Cookie()
return cookie.get('vup_token')
}
export function removeEmptyMember (obj) {
for (let p in obj) {
if (obj[p] === '') {
delete obj[p]
}
}
}
export function removeEmptyMemberArray (arr) {
let newArr = []
arr.forEach((v, i) => {
if (v.telNumber !== '') {
newArr.push(v)
}
})
return newArr
}
export let setModule = (obj, key, options) => {
let ob = {}
Object.keys(obj).forEach((item) => {
if (Array.isArray(obj[item])) {
// let opt = options[key + '[' + item + ']']
// console.log(opt)
obj[item].forEach((arr, index) => {
if (arr.key) {
if (arr.key === 'new') {
ob[key + '[' + item + '][' + arr.key + '][]'] = arr.name
} else {
ob[key + '[' + item + '][' + arr.key + ']'] = arr.name
}
} else {
ob[key + '[' + item + '][' + index + ']'] = arr
}
})
} else {
ob[key + '[' + item + ']'] = obj[item]
}
})
return ob
}
let getNode = function (json, nodeId, type, nodeObj) {
// 1.第一层 root 深度遍历整个JSON
nodeObj = nodeObj || {node: null, parentNode: null}
for (var i = 0; i < json.length; i++) {
if (nodeObj.node) {
break
}
var obj = json[i]
// 没有就下一个
if (!obj || !obj[type]) {
continue
}
// 2.有节点就开始找,一直递归下去
if (obj[type] === nodeId) {
// 找到了与nodeId匹配的节点,结束递归
nodeObj.node = obj
break
} else {
// 3.如果有子节点就开始找
if (obj.children) {
// 4.递归前,记录当前节点,作为parent 父亲
nodeObj.parentNode = obj
// 递归往下找
getNode(obj.children, nodeId, type, nodeObj)
} else {
// 跳出当前递归,返回上层递归
continue
}
}
}
// 5.如果木有找到父节点,置为null,因为没有父亲
if (!nodeObj.node) {
nodeObj.parentNode = null
}
// 6.返回结果obj
return nodeObj
}
export const findWhere = (list, catId, type) => {
return getNode(list, catId, type)
}
import './index'
import portal from 'vis-portal'
import ElementRoute from './routes/workLog'
let routes = []
const appName = 'work-log'
routes = [].concat(ElementRoute)
console.log(routes)
let RouterInit = () => {
portal.createApp(appName, {}, app => {
app.mapRoute(routes)
})
}
export default RouterInit
import clientList from '../components/workLog/list'
import test from '../components/test'
const projectTitle = '金畅逍BMS - '
const routes = [
{
path: '/list',
name: 'workLogList',
component: clientList,
meta: {
title: projectTitle + '客户信息'
}
},
{
path: '/test',
name: 'test',
component: test
}
]
export default routes
$bem-component-namespace: 'client';
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
var map;
var marker = [];
var currentCity = "'.$workingAddress->city_id.'";
$(function () {
$("#workingaddresses-region_id").chosen();
map = new AMap.Map('map', {
viewMode: '3D',
zoom: 12,
expandZoomRange: true,
jogEnable: true
});
AMap.plugin(["AMap.Scale", "AMap.ControlBar"],
function () {
map.addControl(new AMap.Scale());
map.addControl(new AMap.ControlBar());
});
$("#workingaddresses_region_id_chzn").on("keyup", "input", function () {
autoSearch($(this).val())
})
$("#workingaddresses_region_id_chzn .chzn-results").on("click", "li", function () {
if ($(this).index() < 0) {
return false;
}
$("#workingAddresses_address2").val($("#workingaddresses-region_id option:selected").attr("keyword"));
selectResult($("#workingaddresses-region_id option:selected").val(), $("#workingaddresses-region_id option:selected").attr("keyword"));
})
})
;
function resetCityCenter () {
//加载城市查询插件
AMap.service(["AMap.CitySearch"], function () {
//实例化城市查询类
var citysearch = new AMap.CitySearch();
//自动获取用户IP,返回当前城市
citysearch.getLocalCity(function (status, result) {
if (status ===
'complete' && result.info === 'OK') {
if (result && result.city && result.bounds) {
//var cityinfo = result.city;
var citybounds = result.bounds;
currentCity = result.city;
map.setBounds(citybounds);
map.setZoom(12)
}
}
else {
}
})
}
)
}
function autoSearch (keywords) {
var auto;
//加载输入提示插件
AMap.service(["AMap.Autocomplete"], function () {
var autoOptions = {
city: currentCity //城市,默认全国
};
auto = new AMap.Autocomplete(autoOptions);
//查询成功时返回查询结果
if (keywords.length > 0) {
auto.search(keywords, function (status, result) {
autocomplete_CallBack(result);
});
}
else {
// document.getElementById("result1").style.display = "none";
}
});
}
//输出输入提示结果的回调函数
function autocomplete_CallBack (data) {
var resultStr = "";
var tipArr = data.tips;
$("#workingaddresses-region_id option").remove();
if (tipArr && tipArr.length > 0) {
for (var i = 0; i < tipArr.length; i++) {
if (typeof(tipArr[i].adcode) != "object" && tipArr[i].adcode != "") {
currentCity = tipArr[i].adcode;
$("#workingaddresses-region_id").append("<option value=\'" + tipArr[i].adcode + "\' keyword=" + tipArr[i].district + tipArr[i].name + ">" + tipArr[i].district +\':\'+tipArr[i].name+"</option>");
$("#workingaddresses-region_id").trigger("liszt:updated");
}
}
}
else {
$("#workingaddresses-region_id").append("<option value=\'-1\'>没有找到您输入的地址!</option>");
$("#workingaddresses-region_id").trigger("liszt:updated");
}
}
function geocoder (address) {
var MGeocoder;
//加载地理编码插件
AMap.service(["AMap.Geocoder"], function () {
MGeocoder = new AMap.Geocoder({
radius: 1000 //范围,默认: 500
});
//返回地理编码结果
//地理编码
MGeocoder.getLocation(address, function (status, result) {
if (status ===
'complete' && result.info === 'OK') {
map.clearMap();
geocoder_CallBack(result);
map.setZoom(8)
}
})
}
)
}
function geocoder_CallBack (data) {
var resultStr = "";
//地理编码结果数组
var geocode = new Array();
geocode = data.geocodes;
for (var i = 0; i < geocode.length; i++) {
//拼接输出html
addmarker(i, geocode[i]);
}
map.setFitView();
}
function addmarker (i, d) {
var lngX = d.location.getLng();
var latY = d.location.getLat();
var markerOption = {
draggable: true, //点标记可拖拽
map: map,
icon: "/img/position.png",
position: new AMap.LngLat(lngX, latY),
raiseOnDrag: true
};
var mar = new AMap.Marker(markerOption);
marker.push(new AMap.LngLat(lngX, latY));
$("#workingAddresses_lng").val(lngX);
$("#workingAddresses_lat").val(latY);
var aa = function (e) {
postion = mar.getPosition();
$("#workingAddresses_lat").val(postion.lat);
$("#workingAddresses_lng").val(postion.lng);
};
AMap.event.addListener(mar, "mousemove", aa);
}
function selectResult (cityCode, text) {
//截取输入提示的关键字部分
//根据选择的输入提示关键字查询
map.plugin(["AMap.PlaceSearch"], function () {
var msearch = new AMap.PlaceSearch({
city: currentCity //范围,默认: 500
}); //构造地点查询类
AMap.event.addListener(msearch, "complete", placeSearch_CallBack); //查询成功时的回调函数
msearch.setCity(cityCode);
msearch.search(text); //关键字查询查询
});
}
function placeSearch_CallBack (data) {
//清空地图上的InfoWindow和Marker
marker = [];
map.clearMap();
var resultStr1 = "";
var poiArr = data.poiList.pois;
var resultCount = poiArr.length;
for (var i = 0; i < resultCount; i++) {
addmarker(i, poiArr[i]);
break;
}
map.setFitView();
}
function regeocoder (lng, lat) {
var MGeocoder;
//加载地理编码插件
lnglatXY = new AMap.LngLat(lng, lat);
AMap.service(["AMap.Geocoder"], function () {
MGeocoder = new AMap.Geocoder({
radius: 1000,
extensions: "all"
});
//逆地理编码
MGeocoder.getAddress(lnglatXY, function (status, result) {
if (status ===
'complete' && result.info === 'OK') {
}
})
}
)
}
function addsinglemarker () {
var lngX = $("#workingAddresses_lng").val();
var latY = $("#workingAddresses_lat").val();
if ((lngX == "" && latY == "") || (lngX == "0" && latY == "0")) {
resetCityCenter();
return false;
}
var markerOption = {
draggable: true, //点标记可拖拽
map: map,
icon: "/img/position.png",
position: new AMap.LngLat(lngX, latY),
raiseOnDrag: true
};
var mar = new AMap.Marker(markerOption);
marker.push(new AMap.LngLat(lngX, latY));
var aa = function (e) {
postion = mar.getPosition();
$("#workingAddresses_lat").val(postion.lat);
$("#workingAddresses_lng").val(postion.lng);
};
AMap.event.addListener(mar, "mousemove", aa);
map.setFitView();
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
[
{
"request": {
"uri": "/dispatch-url/getExpensesDeptByName"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"total": 2,
"list": [
{
"deptNo": 56547,
"deptName": "商务中心_运营组"
},
{
"deptNo": 56809,
"deptName": "商务中心_鞋包业务部"
}
]
}
}
}
}
]
[
{
"request": {
"uri": "/dispatch-url/getPoSampleOutList"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"total": "200",
"list": [
{
"commandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "10",
"statName": "待提交",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "20",
"statName": "待出库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"commandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "40",
"statName": "已出库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "99",
"statName": "已取消",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
}
]
}
}
}
},
{
"request": {
"uri": "/dispatch-url/getPoSampleGiftList"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"total": "200",
"list": [
{
"commandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "10",
"statName": "待提交",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "20",
"statName": "待出库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
},
{
"commandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "40",
"statName": "已出库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "60",
"statName": "已取消",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "70",
"statName": "已取消",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
},
{
"commandId": "YC201706250002",
"type": "1",
"typeName": "样品借出",
"stat": "99",
"statName": "已取消",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
}
]
}
}
}
},
{
"request": {
"uri": "/dispatch-url/getPoSampleInList"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"total": "200",
"list": [
{
"commandId": "YC201705250001",
"preCommandId": "preCommandIdYC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "10",
"statName": "待提交",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00"
},
{
"commandId": "YC201706250002",
"preCommandId": "preCommandIdYC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "30",
"statName": "待入库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
},
{
"commandId": "YC201705250001",
"preCommandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "50",
"statName": "已入库",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-05-25 12:30:00"
},
{
"commandId": "YC201706250002",
"preCommandId": "YC201705250001",
"type": "1",
"typeName": "样品借出",
"stat": "99",
"statName": "已取消",
"applyByOa": "jingxin.li",
"applyDeptName": "供应链研发部",
"expensesDeptName": "商务中心_服装业务部",
"createBy": "pengcheng.wang",
"createByOa": "pengcheng.wang",
"createTime": "2017-06-25 12:30:00"
}
]
}
}
}
}
]
[
{
"request": {
"uri": "/dispatch-url/getUsers",
"forms": {
"data": "{\"name\":\"\"}"
}
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"users": [
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
},
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305",
"remark": "商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部商务中心_服装业务部"
}
],
"total": "80"
}
}
}
},
{
"request": {
"uri": "/dispatch-url/getUsers",
"forms": {
"data": "{\"name\":\"test\"}"
}
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"users": [
{
"name": "test",
"sex": 0,
"age": "24",
"birth": "1986-10-18",
"addr": "十里煲5号院2单元305"
}
]
}
}
}
}
]
[{
"request": {
"uri": "/getDownloadUrl"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {}
}
}
},{
"request": {
"uri": "/upload"
},
"response": {
"json": {
"code": 200,
"msg": "导入成功",
"result": {
"fid": "2222ded15e7e4488b433d6ac9a51f68a"
}
}
}
},{
"request": {
"uri": "/dispatch-url/getImportList"
},
"response": {
"json": {
"code": 201,
"msg": "导入失败!申请数量不符",
"result": {
"errorMsg": [
"第1行的申请数量只能是正整数。",
"第3行商品名称错误,该条形码对应商品名称为【湖蓝色单肩手挽旅行袋】。",
"第50行商品条码不能为空。",
"所有导入数据必须是同一个PO下的不同条码。",
"第1行的申请数量只能是正整数。",
"第3行商品名称错误,该条形码对应商品名称为【湖蓝色单肩手挽旅行袋】。",
"第50行商品条码不能为空。",
"所有导入数据必须是同一个PO下的不同条码。",
"第1行的申请数量只能是正整数。",
"第3行商品名称错误,该条形码对应商品名称为【湖蓝色单肩手挽旅行袋】。",
"第50行商品条码不能为空。",
"所有导入数据必须是同一个PO下的不同条码。",
"第1行的申请数量只能是正整数。",
"第3行商品名称错误,该条形码对应商品名称为【湖蓝色单肩手挽旅行袋】。",
"第50行商品条码不能为空。",
"所有导入数据必须是同一个PO下的不同条码。"
],
"totalNum": "20",
"totalPrice": "200",
"total": "100",
"detail": [
{
"goodSn": "SN1020232",
"goodName": "testtesttest夏季衣服",
"po": 21000742,
"unitType": "个",
"stockType": "SI",
"applyNum": 5,
"price": "10.00",
"budgetPrice": "50.00"
},
{
"goodSn": "SN1020232",
"goodName": "testtesttest电脑",
"po": 21000742,
"unitType": "个",
"stockType": "SI",
"applyNum": 5,
"price": "11.00",
"budgetPrice": "55.00"
}
],
"data": [
{
"detailNum": 1,
"id": "VIS_STO_201705230000013"
},
{
"detailNum": 2,
"id": "VIS_STO_201705230000013"
},
{
"detailNum": 10,
"id": "VIS_STO_201705230000013"
},
{
"detailNum": 200,
"id": "VIS_STO_201705230000013"
}
]
}
}
}
}]
\ No newline at end of file
[
{
"request": {
"uri": "/dispatch-url/getStatus"
},
"response": {
"json": {
"code": 200,
"msg": "成功",
"result": {
"10": "待提交",
"20": "待出库",
"30": "待入库",
"40": "已出库",
"50": "已入库",
"99": "已取消"
}
}
}
}
]
[
{
"include": "options-request.json"
},
{
"request": {
"method": "get"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/test.json"
},
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/getUsers.json"
},
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/queryContent.json"
},
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/import.json"
},
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/department.json"
},
{
"request": {
"method": "post"
},
"response": {
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"include": "element/status.json"
}
]
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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