Commit edb9dc3c authored by liupengfei's avatar liupengfei

测试

parents
Pipeline #271068 failed with stages
in 0 seconds
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
# 本地打包输出目录
LOCAL_DIR=dist
# 应用名称
APP_NAME=tuiaApp
\ No newline at end of file
# 本地开发端口号
SERVER_PORT=2222
# 本地mock目录
MOCK_DIR=mock
# 代理ip
#PROXY_HOST=0.0.0.0
# 代理端口号
#PROXY_PORT=17435
# 上传CDN目录
CDN_DIR=/tuia-package/
# CDN地址
CDN_HOST=//yun.tuisnake.com
# 如果需要上传资源到cdn备用,可以指定图片是否启用hash,仅针对手动上传
ASSETS_NAME=[name].[hash:8]
\ No newline at end of file
node_modules
dist
app.js
\ No newline at end of file
/*
* @Author: flyharvest
* @Date: 2020-07-08 14:53:42
* @LastEditTime: 2020-07-08 15:31:58
* @LastEditors: flyharvest
*/
const config = {
root: true, // 作用的目录是根目录
extends: [
'standard',
"plugin:vue/essential"
], // 继承标准规则
// parser: 'babel-eslint',
parserOptions: {
sourceType: 'module',
},
globals: {
localStorage: true,
alert: true,
Image: true,
history: true,
location: true
},
rules: {
"no-console": process.env.NODE_ENV === 'production' ? "error" : "warn",
"no-debugger": process.env.NODE_ENV === 'production' ? "error" : "warn",
"no-unused-vars": process.env.NODE_ENV === 'production' ? "error" : "warn"
}
}
module.exports = config
.DS_Store
node_modules
*-lock.json
*.lock
dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"hooks": {
"pre-commit": "lint-staged"
}
}
\ No newline at end of file
{
"src/**/*.{js,vue}": [
"cross-env NODE_ENV=production eslint"
],
"src/**/*.{css,less,vue}": [
"stylelint"
]
}
\ No newline at end of file
registry=http://npm.dui88.com
\ No newline at end of file
module.exports = {
"ignoreFiles": ["**/*.js", "**/fonts/**", "dist/**"],
"extends": ["stylelint-config-standard", "stylelint-config-recess-order"],
"plugins": ["stylelint-order"],
"rules": {
"selector-pseudo-class-no-unknown": null,
"unit-case":null,
"no-descending-specificity":null
}
}
<!--
* @Author: flyharvest
* @Date: 2020-07-09 18:05:59
* @LastEditTime: 2020-07-09 19:14:12
* @LastEditors: flyharvest
-->
<template>
<div class="test-body">
<Kefu></Kefu>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less">
body {
position: relative;
width: 100vw;
height: 100vh;
}
.test-body {
position: absolute;
width: 100%;
height: 100%;
}
</style>
# tuia-package 示例工程
开发npm包
- 开发环境使用webpack启动,打包使用rollup
- babel配置为bundled,见cli/rollup/rollup.config.js和babel.config.js
- 打包会在dist目录下生成两个版本,es和umd,见package.json中main和module字段
- 发布只会有三个文件,dist、package.json、READEME.md,见package.json中的files字段
## 创建工程
```
yarn add @tuia/tuia-cli -g
tuia create tuia-xxxx
// 选择模版 tuia-package
```
## 修改工程
- 修改.env中的APP_NAME,最终导出的umd使用改命名
- 修改.env.production中的CDN_DIR,如果使用了CDN上传,该目录为项目上传目标
- 修改packae.json中的name为@tuia/xxxx,最终发布到npm仓库的包名
## 开发注意
- babel只会在最终打包的页面中导入一次帮助函数
- 建议尽量使用es5语法,过多的es6语法会导致更多的帮助函数被打入包内
- 打包不会导入新的API,比如Promise、Map等,这应该是使用该包的主应用要关心的兼容性问题
## 安装依赖
```
yarn install
```
## 本地开发
```
yarn serve
```
## 打包
```
yarn build
```
## 发布
```
yarn publish
```
/*
* @Author: flyharvest
* @Date: 2020-07-08 13:46:42
* @LastEditTime: 2020-07-09 20:10:44
* @LastEditors: flyharvest
*/
import './app.less';
import * as rem from '@tuia/flexible'
import * as services from '@tuia/services'
import { ajax } from '@tuia/xhr'
import kefu from './src'
import Vue from 'vue'
import App from './App.vue'
rem.setViewPort()
rem.init(750, 750)
console.log(ajax)
// use 比较好。表示 services 需要一个插件, 并且建议统一有返回值,返回真正可以用的服务
// 目前上传的services 包是不管用的。 可以看控制台。
services.install(ajax)
const myService = {
getKefu () {
return ajax.get('/getKefu')
}
}
// Kefu 是真正的组件
const Kefu = kefu.use(myService.getKefu)
Vue.component('Kefu', Kefu)
new Vue({
el: '#app',
components: {
App,
},
render: h => h('app')
})
* {
padding: 0;
margin: 0;
}
@width: 200px;
.test {
width: @width;
height: @width;
background: red;
}
module.exports = {
presets: [
['@babel/preset-env', {
modules: false
}]
],
plugins: ['@babel/plugin-transform-runtime']
}
const { dotenv } = require('./util')
dotenv(`.env.${process.env.NODE_ENV}`, true)
const rollup = require('rollup')
async function build () {
start(require('./rollup/rollup.config')('es'))
start(require('./rollup/rollup.config')('umd'))
}
async function start ({ inputOptions, outputOptions }) {
// create a bundle
const bundle = await rollup.rollup(inputOptions)
// generate code and a sourcemap
await bundle.generate(outputOptions)
// or write the bundle to disk
await bundle.write(outputOptions)
}
build()
const fs = require('fs')
const path = require('path')
const api = fs.readdirSync(path.join(__dirname, './util')).filter(value => value !== 'index.js')
let str = ''
api.forEach((value, i) => {
const name = /(.*).js/.exec(value)[1]
if (i === api.length - 1) {
str += `exports.${name} = require('./${value}')
`
} else {
str += `exports.${name} = require('./${value}')\n
`
}
})
fs.writeFileSync(path.join(__dirname, './util/index.js'), str)
const { execSync, log } = require('./util')
const chalk = require('chalk')
const { stderr, exit } = require('process')
execSync('git fetch origin master')
const result = execSync('git cherry -v HEAD origin/master').toString().split('\n')
result.pop()
if (result.length) {
stderr.write(chalk.red('\n'))
stderr.write(chalk.red('\n'))
stderr.write(chalk.red('【请更新最新代码】 \n\n'))
stderr.write(chalk.red(`与远程 master 分支存在以下 ${result.length} 个commit未更新:\n\n`))
result.forEach((i, index) => {
index < 10 && stderr.write(chalk.red(`${index + 1}.${i.replace('+', '')}\n`))
})
result.length > 10 && stderr.write(chalk.red('......\n'))
stderr.write(chalk.red('\n'))
stderr.write(chalk.red('【更新命令 git pull origin master】\n\n'))
exit(1)
} else {
stderr.write(chalk.red('\n'))
stderr.write(chalk.red('\n'))
log.succcess(chalk.green('baseline 校验通过'))
}
/*
* @Author: flyharvest
* @Date: 2020-07-08 13:46:42
* @LastEditTime: 2020-07-08 16:19:31
* @LastEditors: flyharvest
*/
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const commonjs = require('@rollup/plugin-commonjs')
const json = require('@rollup/plugin-json')
const buble = require('@rollup/plugin-buble')
const image = require('@rollup/plugin-image')
const alias = require('@rollup/plugin-alias')
const replace = require('@rollup/plugin-replace')
const VuePlugin = require('rollup-plugin-vue')
const postcss = require('rollup-plugin-postcss')
const del = require('rollup-plugin-delete')
const { env, git } = require('../util')
const path = require('path')
const { isProd, isDev } = env
const localDir = path.join(__dirname, '../../' + process.env.LOCAL_DIR)
const bannerInfo = `/*!\n * @bundleinfo-${git.name} * -${git.email} * -${(new Date()).toLocaleString()}\n */`
const inputOptions = {
input: path.join(__dirname, '../../src/index.js'),
plugins: [
del({ targets: localDir }),
replace({
'process.env.isProd': isProd,
'process.env.isDev': isDev
}),
nodeResolve(),
// babel({
// exclude: 'node_modules/**',
// babelHelpers: 'runtime'
// }),
commonjs(),
json(),
VuePlugin({
css: false
}),
postcss({
extract: 'index.css'
}),
buble(),
alias({
entries: {
src: path.join(__dirname, '../../src')
}
}),
image()
]
}
module.exports = function (format = 'es') {
const outputOptions = {
file: localDir + `/index.${format}.js`,
format,
name: process.env.APP_NAME,
banner: bannerInfo
}
return {
inputOptions,
outputOptions
}
}
const glob = require('glob')
const { requireNoCache, log, qs } = require('./util')
const fs = require('fs')
const MOCK = process.env.MOCK_DIR
const resolve = dir => require('path').resolve(__dirname, '../', MOCK + dir)
function handleJs (req, res, api) {
return requireNoCache(resolve(api))(req, res)
}
function handleJson (req, res, api) {
return requireNoCache(resolve(api))
}
function handleError () {
return {
code: 0,
success: true
}
}
function routerCurry (app, api) {
// 将req.headers.referer的参数解析出来,提供给mock使用
app.get('*', (req, res, next) => {
if (req.headers.referer) {
req.mock = qs.parse(req.headers.referer)
}
next()
})
return ({ type, handler, suffix }) => {
app[type](api.split(suffix)[0], (req, res) => {
// 设置响应头
res.setHeader('Content-Type', 'application/json; charset=utf-8')
// 延迟时间
const randomTime = Math.floor(Math.random() * 100) * 10
const result = handler(req, res, api)
// 模拟请求延时
setTimeout(() => {
res.json(result)
}, randomTime)
})
}
}
function router (routerApiCurry, handler, suffix) {
routerApiCurry({
type: 'get',
handler,
suffix
})
routerApiCurry({
type: 'post',
handler,
suffix
})
}
exports.routeAPi = app => {
if (fs.existsSync(MOCK)) {
glob.sync(MOCK + '/**/*.?(js|json)').forEach(api => {
const routerApiCurry = routerCurry(app, api.substring(MOCK.length))
if (api.endsWith('.js')) {
router(routerApiCurry, handleJs, '.js')
} else if (api.endsWith('.json')) {
router(routerApiCurry, handleJson, '.json')
} else if (api.endsWith('.json2')) {
router(routerApiCurry, handleJson, '.json2')
} else {
router(routerApiCurry, handleError, '')
}
})
log.succcess('mock is ready')
} else {
log.warn("'mock' floder is unExisted")
}
}
exports.routeProxy = () => {
let proxtOptions = {}
if (process.env.PROXY_HOST) {
const port = process.env.PROXY_PORT ? ':' + process.env.PROXY_PORT : ''
const target = 'http://' + process.env.PROXY_HOST + port
log.succcess(`mock api start with '/api' is proxy on ${target}`)
proxtOptions = {
proxy: {
'/api': {
target,
pathRewrite: {
'^/api': ''
}
}
}
}
}
return proxtOptions
}
const { execSync, dotenv } = require('./util')
dotenv(`.env.${process.env.NODE_ENV}`, true)
async function start () {
const env = process.env
execSync('cross-env BUILD_TYPE=page npx webpack-dev-server --config cli/webpack/index.js', { env, stdio: [0, 1, 2] })
}
start()
const fs = require('fs')
const path = require('path')
// 缓存的目录
const cacheDir = path.join(__dirname, '../../node_modules/.cache')
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir)
}
// 缓存的文件
const dir = path.join(cacheDir, 'cli-cache.json')
if (!fs.existsSync(dir)) {
fs.writeFileSync(dir, '{}')
}
// 缓存
const res = JSON.parse(fs.readFileSync(dir))
// 缓存用到的key
exports.keys = {
LAST_CHOICE_PAGE: 'last-choice-page'
}
// 获取缓存
exports.get = key => {
return res[key]
}
// 设置缓存
exports.set = (key, value) => {
res[key] = value
fs.writeFileSync(dir, JSON.stringify(res))
}
const path = require('path')
const fs = require('fs')
function copyFileSync (target, dest) {
fs.copyFileSync(target, dest)
}
function copydirSync (target, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest)
}
const dirs = fs.readdirSync(target)
dirs.forEach(dir => {
const filepath = path.join(target, dir)
const temp = fs.statSync(filepath)
if (temp.isFile()) {
copyFileSync(filepath, path.join(dest, dir))
} else if (temp.isDirectory()) {
copydirSync(filepath, path.join(dest, dir))
}
})
}
/**
* 移动目录以及内容
*/
module.exports = (target, dest) => {
copydirSync(target, dest)
}
const fs = require('fs')
function deletedirSync (path) {
let files = []
if (fs.existsSync(path)) {
files = fs.readdirSync(path)
files.forEach(function (file, index) {
const curPath = path + '/' + file
if (fs.statSync(curPath).isDirectory()) {
deletedirSync(curPath)
} else {
fs.unlinkSync(curPath)
}
})
fs.rmdirSync(path)
}
}
/**
* 删除文件夹及其内容
*/
module.exports = function (dest) {
deletedirSync(dest)
}
const fs = require('fs')
const path = require('path')
const dotenv = require('dotenv')
const baseEnv = path.join(__dirname, '../../.env')
module.exports = (name, isWriteToEnv = false) => {
const filepath = path.join(__dirname, '../../' + name)
if (isWriteToEnv) {
dotenv.config({
path: baseEnv
})
dotenv.config({
path: filepath
})
}
const baseConfig = dotenv.parse(fs.readFileSync(baseEnv))
const config = dotenv.parse(fs.readFileSync(filepath))
return {
...baseConfig,
...config
}
}
const { isDev } = require('./env')
const inquirer = require('inquirer')
const cache = require('./cache')
const fs = require('fs')
const path = require('path')
const glob = require('glob')
/**
* 所有的皮肤
*/
const pages = {}
glob.sync('./src/pages/**/main.js').forEach(fullpath => {
const shortPath = /.\/src\/pages\/(.*)\/main.js/.exec(fullpath)[1]
const key = shortPath.replace(/\//g, '_').toLowerCase()
pages[key] = {
shortPath,
longPath: path.join(__dirname, '../../src/pages/' + shortPath + '/main.js')
}
})
exports.pages = pages
/**
* 启动询问
*/
exports.inquirer = () => {
return inquirer.prompt({
type: 'list',
name: 'name',
message: 'Pick pages, default is last choice',
default: cache.get(cache.keys.LAST_CHOICE_PAGE) || Object.keys(pages)[0],
choices: Object.keys(pages)
})
}
/**
* 获取选中的页面
*/
const choicesPages = () => {
const name = process.env.page
if (!name) return pages
const _pages = {}
Object.keys(pages).forEach(key => {
if (name === key) {
_pages[key] = pages[key]
return _pages
}
})
return _pages
}
exports.choicesPages = choicesPages
/**
* 入口文件
*/
let cacheEntrys = null
const entrys = () => {
if (cacheEntrys) return cacheEntrys
const pages = choicesPages()
const isSinglePage = Object.keys(pages).length === 1
// 页面入口
Object.keys(pages).forEach(key => {
// 页面有自己的模版
const pageTemplate = path.join(__dirname, '../../src/pages/' + pages[key].shortPath + '/index.html')
const isPageTplexist = fs.existsSync(pageTemplate)
pages[key].template = isPageTplexist ? pageTemplate : path.join(__dirname, '../../public/index.html')
pages[key] = {
...pages[key],
title: key,
entry: pages[key].longPath,
filename: isSinglePage ? 'index.html' : `${key}.html`,
chunks: [key],
chunksSortMode: 'manual'
}
})
// 如果开发环境,需要注入config
if (isDev && process.env.CFG_INJECT) {
Object.keys(pages).forEach(key => {
// 页面有自己的配置项
const cfgChunkName = `${key}-config.dev`
const pageConfig = path.join(__dirname, '../../src/pages/' + pages[key].path + '/config.js')
const isPageConfigExist = fs.existsSync(pageConfig)
pages[cfgChunkName] = {
entry: isPageConfigExist ? pageConfig : './src/config/index.js'
}
// 页面级注入配置项entry
const chunk = pages[key].chunks
pages[key].chunks = [cfgChunkName, ...chunk]
})
}
cacheEntrys = pages
return pages
}
exports.entrys = entrys
/**
* 环境判断
*/
const isProd = process.env.NODE_ENV === 'production'
const isDev = process.env.NODE_ENV === 'development'
exports.isProd = isProd
exports.isDev = isDev
const { execSync } = require('child_process')
module.exports = execSync
const { execSync } = require('child_process')
/**
* git信息
*/
const email = execSync('git config user.email').toString()
const name = execSync('git config user.name').toString()
module.exports = {
email,
name
}
exports.cache = require('./cache.js')
exports.copydirSync = require('./copydirSync.js')
exports.deletedirSync = require('./deletedirSync.js')
exports.dotenv = require('./dotenv.js')
exports.entry = require('./entry.js')
exports.env = require('./env.js')
exports.execSync = require('./execSync.js')
exports.git = require('./git.js')
exports.log = require('./log.js')
exports.md5 = require('./md5.js')
exports.mkdirSync = require('./mkdirSync.js')
exports.qs = require('./qs.js')
exports.readdirSync = require('./readdirSync.js')
exports.requireNoCache = require('./requireNoCache.js')
const chalk = require('chalk')
/**
* 控制台打印
*/
module.exports = {
succcess: msg => {
console.log('\n')
console.log(chalk.bold.bgGreen(' SUCCESS ') + ' ' + chalk.green(msg))
console.log('\n')
},
warn: msg => {
console.log('\n')
console.log(chalk.bold.bgYellow(' WARN ') + ' ' + chalk.yellow(msg))
console.log('\n')
},
error: msg => {
console.log('\n')
console.log(chalk.bold.bgRed(' ERROR ') + ' ' + chalk.red(msg))
console.log('\n')
},
info: msg => {
console.log('\n')
console.log(chalk.bold.bgBlue(' INFO ') + ' ' + chalk.blue(msg))
console.log('\n')
}
}
const crypto = require('crypto')
const fs = require('fs')
module.exports = filePath => {
const buffer = fs.readFileSync(filePath)
const fsHash = crypto.createHash('md5')
fsHash.update(buffer)
const md5 = fsHash.digest('hex')
return md5
}
const fs = require('fs')
const path = require('path')
const deletedirSync = require('./deletedirSync')
const cacheDir = path.join(__dirname, '../../node_modules/.cache')
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir)
}
module.exports = (dir, isClear = false) => {
const dest = path.join(cacheDir, dir)
if (fs.existsSync(dest)) {
if (isClear) {
deletedirSync(dest)
}
} else {
fs.mkdirSync(dest)
}
return dest
}
/**
* 类似 String.prototype.trim (处于兼容考虑)
* @param { String } str
*/
function trim (str) {
return str.replace(/^\s+|\s+$/g, '')
}
/**
* 解码 (增加对异常的处理)
* @param { String } input 字符串
*/
function decode (input) {
try {
// decodeURIComponent('param=%A8')
return decodeURIComponent(input)
} catch (err) {
return ''
}
}
// 解析缓存
const qsCache = {}
module.exports = {
/**
* 从 url 上获取 query string, 返回对象
* 如: //xxx.com?a=1&b=2 => {a: 1, b: 2}
* @param { String } url 链接
*/
parse: function (url = window.location.href) {
if (!url) {
return {}
}
if (qsCache[url]) {
return qsCache[url]
}
const m = url.match(/\?([^#]*)/)
const search = m ? m[1] : '' // a=1&b=2
if (!search) {
return {}
}
const res = {}
const pairs = search.split('&')
for (let i = 0; i < pairs.length; i++) {
const kv = pairs[i] // a=1
if (!kv) {
continue
}
let [key, value] = kv.split('=')
key = trim(decode(key))
value = trim(decode(value))
// 空值
if (value === 'undefined' || value === 'null') {
value = ''
}
res[key] = value
}
qsCache[url] = res
return res
},
/**
* 将对象参数,转换 query string
* 如: { a: 1, b: 2} => a=1&b=2
* @param { Object } params 参数对象
*/
stringify: function (params = {}) {
return Object.keys(params).map(key => {
let value = params[key]
if (value == null) {
value = ''
}
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
}).join('&')
}
}
const path = require('path')
const fs = require('fs')
let fileList = []
function readdirSync (dest) {
const dirs = fs.readdirSync(dest)
dirs.forEach(dir => {
const filePath = path.join(dest, dir)
const temp = fs.statSync(filePath)
if (temp.isFile()) {
fileList.push({
name: dir,
filePath
})
} else if (temp.isDirectory()) {
readdirSync(filePath)
}
})
}
module.exports = dest => {
fileList = []
readdirSync(dest)
return fileList
}
/**
* 没有缓存的require
*/
module.exports = function (filePath) {
delete require.cache[filePath]
return require(filePath)
}
const path = require('path')
const webpack = require('webpack')
const resolve = dir => path.join(__dirname, dir)
const route = require('../route')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { env } = require('../util')
const { isProd, isDev } = env
const localDir = path.join(__dirname, '../../' + process.env.LOCAL_DIR)
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: process.env.NODE_ENV,
entry: {
main: resolve('../../app.js')
},
output: {
path: localDir,
filename: '[name].js',
publicPath: '/'
},
devtool: 'inline-source-map',
devServer: {
historyApiFallback: true,
port: process.env.SERVER_PORT,
contentBase: localDir,
hot: true,
inline: true,
stats: 'errors-only',
overlay: true,
compress: true,
before (app) {
route.routeAPi(app)
},
...route.routeProxy()
},
optimization: {
splitChunks: false // 禁止webpack自动分包优化
},
// 解析
resolve: {
// 路径优化
alias: {
src: resolve('../../src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(le|c)ss$/,
use: ['style-loader', 'css-loader', 'postcss-loader','less-loader', {
loader: 'style-resources-loader',
options: {
patterns: [
'./src/style/mixin/index.less'
]
}
}]
},
{
test: /\.(js|vue)$/,
enforce: 'pre', // 必须强制在代码babel之前处理
use: 'eslint-loader',
exclude: /node_modules/
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(json|json2)$/,
use: 'json-loader'
},
{
test: /\.(png|jpe?g|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1,
name: isProd ? '[name].[contenthash:8].[ext]' : '[path][name].[ext]'
}
}
]
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: resolve('../../public/index.html'),
title: process.env.APP_NAME,
entry: resolve('../../src/main.js'),
filename: 'index.html',
chunks: ['main'],
chunksSortMode: 'manual'
}),
new webpack.EnvironmentPlugin({
isProd,
isDev
}),
// 自动加载模块,而不必到处 import 或 require 。
new webpack.ProvidePlugin({
// $: 'jquery'
}),
// 当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境。
new webpack.NamedModulesPlugin(),
// 启用热替换模块(Hot Module Replacement),也被称为 HMR。
new webpack.HotModuleReplacementPlugin()
]
}
{
"exclude": [
"node_modules"
],
"include": [
"src/**/*", "app.js"
],
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
},
"allowSyntheticDefaultImports": true
}
}
{
"code": "0000000",
"desc": "成功",
"data": {
"id": 1,
"buttonCode": "<link rel=\"stylesheet\"href=\"//yun.tuisnake.com/h5-mami/kefuModal/1/index_201712260000.css\">\n<script type=\"text/javascript\">\nconsole.log('ss');var oHead=document.querySelector('.kefu-modal');\nvar oScript=document.createElement('script');\noScript.type=\"text/javascript\";\noScript.id=\"kefuScript\";\noScript.src=\"//yun.tuisnake.com/newactivity/assets/1.cbfc064e.js\";\noHead.appendChild(oScript);\n</script>",
"phone": "0571-22930370",
"speKeFuStyle": null,
"embed": {
"stKefuModalPhoneClick": "{\"dpm\":\"73735.3.10.3\",\"consumerId\":22396539718,\"appId\":73735,\"domain4Web\":\"////activity.tuia.cn\",\"url\":\"/statistics/click\",\"dcm\":\"1016.1.1.0\"}",
"stKefuClick": "{\"dpm\":\"73735.3.9.0\",\"consumerId\":22396539718,\"appId\":73735,\"domain4Web\":\"////activity.tuia.cn\",\"url\":\"/statistics/click\",\"dcm\":\"1016.1.1.0\"}",
"stKefuModalCancelClick": "{\"dpm\":\"73735.3.10.4\",\"consumerId\":22396539718,\"appId\":73735,\"domain4Web\":\"////activity.tuia.cn\",\"url\":\"/statistics/click\",\"dcm\":\"1016.1.1.0\"}",
"stKefuModalClick": "{\"dpm\":\"73735.3.10.2\",\"consumerId\":22396539718,\"appId\":73735,\"domain4Web\":\"////activity.tuia.cn\",\"url\":\"/statistics/click\",\"dcm\":\"1016.1.1.0\"}",
"stKefuModalInfo": "{\"dpm\":\"73735.3.10.0\",\"consumerId\":22396539718,\"domain\":\"//embedlog.duiba.com.cn\",\"appId\":73735,\"dcm\":\"1016.1.1.0\"}",
"stKefuModalGroundClick": "{\"dpm\":\"73735.3.10.1\",\"consumerId\":22396539718,\"appId\":73735,\"domain4Web\":\"////activity.tuia.cn\",\"url\":\"/statistics/click\",\"dcm\":\"1016.1.1.0\"}"
}
},
"success": true
}
\ No newline at end of file
{
"name": "@tuia/package",
"version": "0.1.0",
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"license": "MIT",
"scripts": {
"serve": "cross-env NODE_ENV=development node cli/serve",
"build": "npm run check && cross-env NODE_ENV=production node cli/build",
"check": "cross-env NODE_ENV=production node cli/check.branch",
"publish": "npm publish --access=public"
},
"files": [
"dist",
"package.json",
"README.md",
"types"
],
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-transform-runtime": "^7.10.4",
"@babel/preset-env": "^7.9.5",
"@rollup/plugin-alias": "^3.1.0",
"@rollup/plugin-babel": "^5.0.2",
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^12.0.0",
"@rollup/plugin-image": "^2.0.4",
"@rollup/plugin-json": "^4.0.3",
"@rollup/plugin-node-resolve": "^8.0.0",
"@rollup/plugin-replace": "^2.3.2",
"@vue/compiler-sfc": "^3.0.0-beta.19",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"chalk": "^4.0.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.2",
"css-loader": "^3.5.2",
"dotenv": "^8.2.0",
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.0",
"eslint-plugin-html": "^6.0.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"express": "^4.17.1",
"file-loader": "^6.0.0",
"glob": "^7.1.6",
"html-webpack-plugin": "^4.2.0",
"husky": "^4.2.5",
"inquirer": "^7.1.0",
"json-loader": "^0.5.7",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"lint-staged": "^10.1.7",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-px2rem": "^0.3.0",
"rollup": "^2.7.2",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-less": "^1.1.2",
"rollup-plugin-postcss": "^3.1.2",
"rollup-plugin-terser": "^6.1.0",
"rollup-plugin-vue": "5.1.6",
"style-loader": "^1.1.4",
"style-resources-loader": "^1.3.3",
"stylelint": "^13.3.3",
"stylelint-config-recess-order": "^2.0.4",
"stylelint-config-standard": "^20.0.0",
"stylelint-order": "^4.0.0",
"ts-loader": "^7.0.1",
"typescript": "^3.8.3",
"url-loader": "^4.1.0",
"vue": "^2.6.11",
"vue-loader": "15.9",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.10.3",
"webpack-hot-middleware": "^2.25.0"
},
"dependencies": {
"@tuia/flexible": "^1.0.3",
"@tuia/services": "^1.0.1",
"@tuia/xhr": "^1.0.2"
}
}
/*
* @Author: flyharvest
* @Date: 2020-07-08 14:10:50
* @LastEditTime: 2020-07-08 16:42:20
* @LastEditors: flyharvest
*/
module.exports = {
plugins: {
'postcss-px2rem': {
// 基准大小 baseSize,需要html中font-size换算比例倍数相同
remUnit: 200
}
}
}
<!--
* @Author: flyharvest
* @Date: 2020-07-08 13:46:42
* @LastEditTime: 2020-07-08 14:07:19
* @LastEditors: flyharvest
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
<link rel="icon" href="./favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<!--
* @Author: flyharvest
* @Date: 2020-07-08 14:07:59
* @LastEditTime: 2020-07-09 20:03:49
* @LastEditors: flyharvest
-->
<template>
<div class="kefu-modal" v-if="kefuShow">
</div>
</template>
<script>
export default {
name: 'kefu',
data () {
return {
kefuShow: true
}
}
}
</script>
<style lang="less">
.kefu-modal {
z-index: 50;
img {
width: 100%;
}
}
</style>
/*
* @Author: flyharvest
* @Date: 2020-07-08 13:46:42
* @LastEditTime: 2020-07-09 20:12:27
* @LastEditors: flyharvest
*/
import Kefu from './components/Kefu.vue'
const isThenAble = (fn) => {
if (fn.then) {
return true
}
}
// ui 和 业务 结合的组件, 可以这么做。 ui组件应该有一份纯ui的。
// use 未必只返回组件, 也可以返回一个组件的控制器之类的。
// {
// type: // 抛出的错误类型, 唯一标识,用来
// resource: // 组件或者 库名称
// reason: // 中文字符串,期望core中Toast可以统一处理
// }
const use = (getKefu) => {
if (isThenAble(getKefu)) {
console.warn('[Kefu] need a service plugin that thenAble')
return Kefu
} else {
return {
extends: Kefu,
created () {
this.startGetKefu()
},
methods: {
// 校验接口
validate (data) {
const keys = Object.keys(data)
console.log(keys)
if (this.validateTuiaService(keys)) {
if (data.data && this.validateKefuData(Object.keys(data.data))) {
return true
} else {
this.$emit('error', {
type: 'GetKefu',
resource: 'Kefu',
reason: '获取客服信息失败'
})
return false
}
} else {
this.$emit('error', {
type: 'notTuiaService', // 真正的原因标示
resource: 'Kefu',
reason: '客服请求失败' // 生产环境报这个
})
return false
}
},
// 校验是不是推啊服务
validateTuiaService (keys) {
const needKeys = ['data', 'code', 'desc', 'success']
return this.validateKeys(keys, needKeys)
},
// 校验是不是符合 kefu 接口返回的数据。
validateKefuData (keys) {
const needKeys = ['buttonCode']
return this.validateKeys(keys, needKeys)
},
validateKeys (keys, needKeys) {
for (let i = 0; i < needKeys.length; i++) {
if (keys.indexOf(needKeys[i]) === -1) {
return false
}
}
return true
},
startGetKefu () {
getKefu().then(res => {
if (this.validate(res)) {
console.log(res)
// 不知道为啥 <scirpt 的js没运行>, 先不管
this.$el.innerHTML = res.data.buttonCode
}
}).catch(err => {
console.log(err)
})
}
}
}
}
}
export default {
use
}
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