Commit f77009d6 authored by fanxuehui's avatar fanxuehui

feat: clear

parent 79c591ea
...@@ -3,7 +3,6 @@ const webpack = require('webpack') ...@@ -3,7 +3,6 @@ const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware') const webpackDevMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware') const webpackHotMiddleware = require('webpack-hot-middleware')
const path = require('path') const path = require('path')
const proxy = require('http-proxy-middleware')
const fs = require('fs') const fs = require('fs')
const app = express() const app = express()
...@@ -26,113 +25,35 @@ app.set('views', path.join(__dirname, '../dist/html')) ...@@ -26,113 +25,35 @@ app.set('views', path.join(__dirname, '../dist/html'))
app.engine('.html', require('ejs').__express) app.engine('.html', require('ejs').__express)
app.set('view engine', 'html') app.set('view engine', 'html')
app.use('/basics', function(req, res) {
res.sendFile(path.resolve(__dirname, '../dist/html/index.html'))
})
app.use('/pro', function(req, res) { app.use('/pro', function(req, res) {
res.sendFile(path.resolve(__dirname, '../dist/html/pro.html')) res.sendFile(path.resolve(__dirname, '../dist/html/pro.html'))
}) })
app.use('/render', function(req, res) {
res.setHeader('X-Frame-Options', 'allow-from *')
res.render('render-v1', {
env: process.env.NODE_ENV,
isTestLog: true
})
})
app.use('/render-v1', function(req, res) {
res.setHeader('X-Frame-Options', 'allow-from *')
res.render('render-v1', {
env: process.env.NODE_ENV,
isTestLog: true
})
})
app.use('/layers', function(req, res) {
res.sendFile(path.resolve(__dirname, '../dist/html/layers.html'))
})
app.use( app.use(
express.static('./', { express.static('./', {
extensions: ['html'] extensions: ['html']
}) })
) )
if (process.env.proxy) { // 本地mock数据
app.use( app.use((req, res) => {
['/api', '/sso/ssoIndex'], const path = req.path
proxy({ res.set('Content-Type', 'application/json')
target: 'http://localhost:3000'
}) console.log(path)
) try {
/* eslint no-path-concat: "off" */
// 代理到广告后台开发人员机器 res.send(fs.readFileSync(__dirname + '/../mock' + path + '.json'))
// TODO换前缀名称 } catch (e) {
app.use( res.send(
'/java', JSON.stringify({
proxy({ code: '0',
target: 'http://172.16.47.169:1112', desc: '无此mock数据',
// target: 'http://yapi.dui88.com/mock/66', data: {}
pathRewrite: {
'^/java': ''
},
changeOrigin: true
})
)
// 代理到活动后端开发人员机器
app.use(
'/activity',
proxy({
target: 'http://172.16.47.145:17792',
pathRewrite: {
'^/activity': ''
},
changeOrigin: true
})
)
} else if (process.env.proxy_api) {
// 将接口代理到对应环境
// 例如 `proxy_api=http://jimu.tuiatest.cn npm run dev:client` 可以将接口代理到测试环境
const { proxyAPI } = require('./proxyAPI')
console.log(`开启接口代理,代理到 ${process.env.proxy_api}`)
app.use(proxyAPI())
} else {
// 本地调开发环境
if (process.env.NODE_ENV === 'dev') {
app.use(
proxy('/', {
target: 'http://localhost:3000'
}) })
) )
} else {
app.use(
proxy('/api/system/upload', {
target: 'http://localhost:3000'
})
)
// 本地mock数据
app.use((req, res) => {
const path = req.path
res.set('Content-Type', 'application/json')
console.log(path)
try {
/* eslint no-path-concat: "off" */
res.send(fs.readFileSync(__dirname + '/../mock' + path + '.json'))
} catch (e) {
res.send(
JSON.stringify({
code: '0',
desc: '无此mock数据',
data: {}
})
)
}
})
} }
} })
const port = 3010 const port = 3010
app.listen(port, function() { app.listen(port, function() {
......
...@@ -87,7 +87,6 @@ const getStyle = isDev => { ...@@ -87,7 +87,6 @@ const getStyle = isDev => {
const getEntry = isDev => { const getEntry = isDev => {
let entry = { let entry = {
basics: ['./client/pages/basics/index.js'],
// fixme // fixme
pro: ['./client/pages/pro/index.js'] pro: ['./client/pages/pro/index.js']
} }
...@@ -111,24 +110,11 @@ const getEntry = isDev => { ...@@ -111,24 +110,11 @@ const getEntry = isDev => {
} }
const HTML_WEBPACK_PLUGINS = { const HTML_WEBPACK_PLUGINS = {
basics: {
alwaysWriteToDisk: true,
chunks: ['basics'],
filename: 'html/index.html',
template: path.resolve(__dirname, '../client/pages/basics/index.html')
},
pro: { pro: {
alwaysWriteToDisk: true, alwaysWriteToDisk: true,
chunks: ['pro-render', 'pro'], chunks: ['pro-render', 'pro'],
filename: 'html/pro.html', filename: 'html/pro.html',
template: path.resolve(__dirname, '../client/pages/pro/index.html') template: path.resolve(__dirname, '../client/pages/pro/index.html')
},
render: {
JIMU_ENV: process.env.NODE_ENV,
alwaysWriteToDisk: true,
chunks: ['render'],
filename: 'html/render.html',
template: path.resolve(__dirname, '../client/pages/pro/render/entry.ejs')
} }
} }
......
import React from 'react'
import { Link } from 'react-router'
import { hot } from 'react-hot-loader'
import { Icon, Layout, Menu } from 'antd'
import ajax from 'libs/ajax'
const { Header, Content, Footer } = Layout
const SubMenu = Menu.SubMenu
const App = class App extends React.Component {
constructor (props) {
super(props)
this.state = {
user: {}
}
}
componentWillMount () {
ajax({
url: '/system/user'
}).then(data => {
this.setState(state => {
state.user = data
return state
})
}).catch(err => err)
}
goIndex () {
this.props.router.push('activity')
}
logOut () {
ajax({
url: 'system/logout',
method: 'post'
}).then(() => {
this.props.router.push('login')
}).catch(err => err)
}
render () {
let { user } = this.state
return (
<Layout className='layout'>
<Header>
<div className='logo' onClick={this.goIndex.bind(this)}><span></span></div>
<div className='user'>
<Link to=''><div className='headimg'>{user.username} | </div></Link>
<Link to='' onClick={this.logOut.bind(this)}><div className='logOut'><Icon type='logout' /></div></Link>
</div>
<Menu
theme='light'
mode='horizontal'
defaultSelectedKeys={[]}
style={{ lineHeight: '64px' }}
>
<Menu.Item key='0'><Link to='/activity'><Icon type='book' />活动市场</Link></Menu.Item>
{/* <Menu.Item key='1'><Link to='/plugin'><Icon type='cloud' />模版市场</Link></Menu.Item> */}
<SubMenu title={<span><Icon type='appstore' />插件市场</span>}>
<Menu.Item key='setting:1'><Link to='/plugin'>插件列表</Link></Menu.Item>
<Menu.Item key='setting:2'><Link to='/plugin/cate'>插件分类</Link></Menu.Item>
</SubMenu>
<Menu.Item key='2'><Link to='/layout'><Icon type='shop' />模板市场</Link> </Menu.Item>
</Menu>
</Header>
<Content style={{ padding: '64px 50px 75px 50px' }}>{this.props.children}</Content>
<Footer style={{ textAlign: 'center' }}>
Jimu ©2018 Created by Tuia Frontend
</Footer>
</Layout>
)
}
}
export default hot(module)(App)
import React from 'react'
import { Input, Switch, Upload, InputNumber, Button, Icon } from 'antd'
import Styles from './index.less'
import { ChromePicker } from 'react-color'
const pluginAttrItem = ({ item = {}, option = {}, onChange }) => {
let value = option[item.key]
switch (item.type.toLowerCase()) {
case 'number':
return (<InputNumber onChange={e => onChange(e, item)} value={value} />)
case 'boolean':
return (<Switch checked={value} onChange={e => onChange(e, item)} />)
case 'image':
return (<Upload name='file' listType='picture' showUploadList={false} action='/api/system/upload' onChange={e => onChange(e, item)}>
{value ? <img className={Styles.editImage} src={value} /> : <Button><Icon type='upload' /> 上传图片</Button>}
</Upload>)
case 'color':
return (<ChromePicker onChangeComplete={e => onChange(e, item)} color={value} />)
default:
return (<Input onChange={e => onChange(e, item)} value={value} />)
}
}
export default pluginAttrItem
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
.editImage {
max-width: 100%;
}
\ No newline at end of file
import React from 'react'
import styles from './index.less'
import { Button, Icon } from 'antd'
const pluginItem = props => {
const { item, remove } = props
const thumb = item.thumb ? item.thumb : '//yun.dui88.com/jimo-web/b47235c0-9c84-11e8-8beb-b77a9f575a78.jpeg'
return (
<div className={styles.plugin} onClick={props.onClick}>
<div className={styles.thumb} style={{ backgroundImage: 'url(' + thumb + ')' }} />
<div className={styles.title}>{item.title}</div>
<div className={styles.cate}>#{item.basics_cate.title}#</div>
<div className={styles.line} />
<div className={styles.revisor}>{item.revisor || '佚名'}</div>
{/* <div className={styles.avatar} style={{ backgroundImage: 'url(' + item.avatar + ')' }} /> */}
<div className={styles.action}>
<a href={`#/plugin/edit/${item.id}`}><Button type='primary' ghost><Icon type='edit' />编辑</Button></a>
<a href='javascript:;' onClick={remove.bind(this, item.id)}><Button type='danger' ghost><Icon type='delete' />删除</Button></a>
</div>
</div>
)
}
export default pluginItem
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
@h: 302px;
.plugin {
position: relative;
width: 200px;
max-width: 100%;
height: @h;
background-color: #fff;
display: inline-block;
margin-right: 40px;
margin-bottom: 40px;
border-radius: 4px;
border: 1px solid #e5e5e5;
cursor: pointer;
&:hover {
// box-shadow: 0 0 10px 4px #ddd;
.action {
display: block;
}
}
.thumb{
.size(120,160);
.pos(40,20);
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
.title{
.text(12,30);
.pos(13,198);
color: #333;
font-weight: bold;
}
.line{
.size(180,1);
background-color: #eaeaea;
.pos(10,257);
}
.cate{
.text(12,20);
.pos(10,227);
color: #333;
}
.revisor{
.text(12,20);
.pos(13,270);
color: #333;
}
.avatar{
.size(24,24);
background-color: @primary-color;
border-radius: 50%;
.pos(166,268);
}
.action{
background-color: #fff;
display: none;
z-index: 99;
width: 100%;
height: 250px;
position: absolute;
top: 0;
left: 0;
a{
display: block;
text-align: center;
}
button{
border-width: 2px;
.size(180,48);
margin: 50px 0 0 0;
}
}
}
import React from 'react'
import styles from './index.less'
const pluginItem = props => {
const item = props.item
const thumb = item.thumb ? item.thumb : '//yun.dui88.com/jimo-web/b47235c0-9c84-11e8-8beb-b77a9f575a78.jpeg'
return (
<div className={styles.plugin + ' plugin'} onClick={props.onClick}>
<div className={styles.thumb} style={{ backgroundImage: 'url(' + thumb + ')' }} />
<div className={styles.title}>{item.title}</div>
</div>
)
}
export default pluginItem
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
@h: 282px;
.plugin {
position: relative;
width: 80px;
height: 80px;
cursor: pointer;
border: 1px solid #e5e5e5;
border-left-width: 0;
// border-top-width: 0;
padding-top: 50px;
.thumb{
.size(28,28);
.pos(26,16);
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
.title{
.text(12,16);
width: 100%;
overflow: hidden;
text-align: center;
color: #333;
font-weight: bold;
text-overflow: ellipsis;
white-space: nowrap;
}
}
import React from 'react'
import { hot } from 'react-hot-loader'
const container = class container extends React.Component {
render () {
return (
this.props.children
)
}
}
export default hot(module)(container)
This diff is collapsed.
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
@leftWidth: 240px;
@rightWidth: 280px;
@headerHeight: 72;
.main {
position: relative;
margin-left: -50px;
margin-right: -50px;
}
.left {
position: absolute;
padding: @headerHeight *1px 0 15px 1px;
left: 0;
top: 0;
width: @leftWidth;
height: 100%;
overflow-x: visible;
overflow-y: scroll;
background-color: #fff;
}
.content {
padding: 30px;
width: 100%;
padding-left: @leftWidth;
padding-right: @rightWidth;
}
.right {
padding: @headerHeight *1px 0 0 0;
position: absolute;
right: 0;
top: 0;
width: @rightWidth;
height: 100%;
overflow-y: scroll;
background-color: #fff;
}
.header{
border-bottom: 1px solid @normal-color;
padding:0 0 0 @leftWidth;
height: @headerHeight *1px;
span {
display: inline-block;
color: #666;
cursor: pointer;
}
}
.headerRight{
.posr(192,0);
}
.back{
height: @headerHeight *1px;
line-height: @headerHeight *1px;
.pos(12,0);
i{
margin-right: 10px;
}
}
.action{
height: @headerHeight;
line-height: 2;
padding: 19px 10px 14px 10px;
user-select: none;
i{
display: block;
font-size: 18;
svg {
margin: 0 auto;
}
}
@disabled-color: #c2c2c2;
&[disabled]{
pointer-events: none;
color: @disabled-color;
}
}
.listTitle {
display: block;
font-size: bold;
color: #666;
.text(12,40);
padding-left: 20px;
width: 100%;
// border-bottom: 1px solid @normal-color;
}
.preview{
margin-top: 40px;
width: 395px;
padding: 10px 10px 10px 10px;
min-height: 643px;
.center;
background-color: #e0e0e0;
border:1px dashed rgba(224,224,224,1);
.previewContent{
width: 375px;
min-height: 603px;
background-color:#fff;
>div{
width: 375px;
min-height: 603px;
}
}
}
.rightTitle{
width: 100%;
padding-left: 28px;
color: @primary-color;
.text(12,40);
border-bottom: 1px solid @normal-color;
}
.comItem {
width: 100%;
height: 150px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
&:hover {
.comItemAction {
display: block;
}
}
}
.comItemAction {
width: 100%;
height: 100%;
position: relative;
// background-color: rgba(255, 255, 255, 1);
button {
.posr(10, 10);
}
}
.editItem {
width: 100%;
padding: 10px;
}
.editItemTitle {
.text(12,32);
display: block;
}
.pluginItemContainer{
float: left;
}
.cateListContainer{
width: @leftWidth * 1px;
.clearfix;
}
::-webkit-scrollbar {
width: 0;
height: 0;
}
\ No newline at end of file
import React from 'react'
import { Link } from 'react-router'
import { Select, Button, Modal, message, Icon, Pagination } from 'antd'
import styles from './index.less'
import { hot } from 'react-hot-loader'
import ajax from 'libs/ajax'
import util from 'libs/util'
const Option = Select.Option
class ActivityList extends React.Component {
constructor (props) {
super(props)
let self = this
this.state = {
actList: [
],
pagination: {
current: 0,
total: 0,
pageSize: 10,
onChange: this.handleChange.bind(self)
}
}
}
actClone (id) {
Modal.confirm({
title: '确定克隆该活动?',
confirmLoading: true,
onOk: () => {
return ajax({
url: `/basics/activity/clone/${id}`,
method: 'post'
}).then(data => {
message.success('克隆成功')
this.getActList()
}).catch(err => err)
}
})
}
actDelete (id) {
Modal.confirm({
title: '确定删除该活动?',
confirmLoading: true,
okType: 'danger',
onOk: () => {
return ajax({
url: `/basics/activity`,
params: {
id
},
method: 'delete'
}).then(data => {
message.success('删除成功')
this.getActList()
}).catch(err => err)
}
})
}
actPublish (id) {
Modal.confirm({
title: '确定发布该活动?',
confirmLoading: true,
okType: 'success',
onOk: () => {
return ajax({
url: `/basics/activity/publish`,
data: {
id
},
method: 'post'
}).then(data => {
message.success('发布成功')
this.getActList()
}).catch(err => err)
}
})
}
getActList (opts = {
pageIndex: 1
}) {
let { pageSize } = this.state.pagination
let { pageIndex, status } = opts
ajax({
url: 'basics/activity',
params: {
pageIndex,
pageSize: pageSize,
status
}
}).then(res => {
this.setState({
actList: res.lists
})
this.setState(function (prevState) {
prevState.pagination.current = res.pageIndex
prevState.pagination.total = res.count
return {
pagination: prevState.pagination
}
})
}).catch(err => err)
}
handleChange (page, pageSize) {
this.getActList({ pageIndex: page })
}
handleStatusChange (value) {
this.getActList({ pageIndex: this.state.pagination.pageIndex, status: value })
}
componentWillMount () {
this.getActList()
}
copyLink (str) {
util.copyToClipboard(str)
message.success('复制成功')
}
render () {
const { pagination } = this.state
return (
<div>
<div className={'jimu-tools ' + styles.tools}>
<Select
showSearch
style={{ width: 120 }}
placeholder='全部状态'
onChange={this.handleStatusChange.bind(this)}
>
<Option value={0}>全部状态</Option>
<Option value='1'>未发布</Option>
<Option value='2'>已上线</Option>
</Select>
<div className={styles.bars}>
<Button type='primary'><Link to='/activity/edit'>新建活动</Link></Button>
</div>
</div>
<div className='jimu-container'>
<div className={styles.list}>
{
this.state.actList.map(item => {
return (<div className={styles.item} key={item.id}>
<span className={styles.thumb} />
<span className={styles.id}>ID:{item.id}</span>
<span className={styles.revisor}><Icon type='user' />{item.revisor}</span>
<span className={styles.title}>{item.title}</span>
<span className={styles.summary}>#{item.summary}#</span>
<span className={styles.status}>{item.status === 2 ? <span><i className={styles.success} />已发布</span> : <span><i className={styles.failed} />未发布</span>}</span>
<div className={styles.right}>
<Link title='编辑' to={'/activity/edit?id=' + item.id} className={styles.link}>编辑</Link>
<a href={`/api/basics/activity/preview/${item.id}`} target='blank' className={styles.link}>预览</a>
<a href='javascript:;' className={styles.link} onClick={this.actClone.bind(this, item.id)}>克隆</a>
<a href='javascript:;' disabled={item.pageLink ? '' : 'disabled'} className={styles.link} onClick={this.copyLink.bind(this, item.pageLink)}>复制链接</a>
<Button type='primary' className={styles.publish} onClick={this.actPublish.bind(this, item.id)}>发布</Button>
<Button type='danger' className={styles.delete} onClick={this.actDelete.bind(this, item.id)}>删除</Button>
</div>
</div>
)
})
}
</div>
<Pagination className={styles.pagination} showTotal={(total, range) => `当前查看为 ${range[0]} - ${range[1]} , 共 ${total} 个`} onChange={pagination.onChange} current={pagination.current} pageSize={pagination.pageSize} total={pagination.total} />,
</div>
</div>
)
}
}
export default hot(module)(ActivityList)
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
.tools {
padding-top: 20px;
}
.bars {
float: right;
button {
margin-left: 8px;
}
}
.list{
}
.item{
height: 80px;
position: relative;
border-bottom: 1px solid #eaeaea;
span {
display: inline-block;
}
button{
margin-right: 20px;
font-size: 12px;
height: 24px;
}
&:hover{
background:rgba(87,140,254,0.08);
}
}
.thmub{
.size(45,60);
.pos(10,10);
}
.id{
color: #999;
.text(12,20);
.pos(75,5);
}
.revisor{
color: #999;
.text(12,20);
.pos(211,5);
i{
vertical-align: middle;
margin-right: 10px;
}
}
.title{
color: #333;
font-weight: bold;
.text(12,26);
.pos(75,25);
}
.summary{
color: #333;
.text(12,20);
.pos(75,56);
}
.status{
color: #666;
.text(12,20);
.pos(351,30);
i{
.size(8,8);
vertical-align: middle;
margin-right: 8px;
display: inline-block;
border-radius: 50%;
}
.success{
background-color: @success-color;
}
.failed{
background-color: @error-color;
}
}
.right{
position: relative;
.posr(0,0);
}
.link{
display: inline-block;
margin-right: 20px;
font-size: 12px;
line-height: 80px;
text-decoration: none;
}
.pagination{
text-align: center;
padding-top: 50px;
}
\ No newline at end of file
import React, { Component } from 'react'
import { hot } from 'react-hot-loader'
import styles from './index.less'
class guide extends Component {
constructor(props) {
super(props)
this.state = {
origin: window.location.origin
}
}
render() {
return (
<div className={styles.body}>
<div className={styles.content}>
<p className={styles.title}>选择下列功能版本</p>
<a href={`${this.state.origin}/layers#/template`}>创建弹层</a>
<a href={`${this.state.origin}/pro#/home`}>创建落地页</a>
</div>
</div>
)
}
}
export default hot(module)(guide)
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
.body {
width: 100%;
height: 100%;
background-color: @primary-color;
}
.title {
text-align: center;
font-size: 26px;
margin: 70px 0;
color: #fff;
}
.content {
.size(460, 400);
.middle;
text-align: center;
a {
@height: 50;
.size(130, @height);
background-color: #fff;
margin-right: 20px;
color: @primary-color;
display: inline-block;
border-radius: 5px;
line-height: @height*1px;
text-align: center;
font-size: 20px;
}
}
import { hot } from 'react-hot-loader'
import React from 'react'
import { Button, Form, Input, Tabs, message } from 'antd'
import { Controlled as CodeMirror } from 'react-codemirror2'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/material.css'
import 'codemirror/mode/htmlembedded/htmlembedded'
import 'codemirror/mode/css/css'
import 'codemirror/mode/javascript/javascript'
import ajax from 'libs/ajax'
const FormItem = Form.Item
const TextArea = Input.TextArea
const TabPane = Tabs.TabPane
// import Styles from './index.less'
class layoutEdit extends React.Component {
constructor (props) {
super(props)
this.state = {
formData: {
id: '',
title: '',
summary: '',
template: `<html>
<head>
<title>{{injecttitle}}</title>
{{injectcss}}
</head>
<body>
<div id="app">
{{injecthtml}}
</div>
{{injectjs}}
</body>
</html>`
}
}
}
componentWillMount () {
let { id } = this.props.router.location.query
id && ajax({
url: '/basics/layout',
params: {
id
}
}).then(res => {
let data = res.lists[0]
this.setState(state => {
state.formData = {
id: data.id,
title: data.title,
summary: data.summary,
template: data.template
}
return state
})
})
}
handleSubmit () {
this.props.form.validateFields(
(err) => {
if (!err) {
const formData = this.props.form.getFieldsValue()
const { id, template } = this.state.formData
formData.id = id
formData.template = template
this.saveData(formData)
}
}
)
}
saveData (data) {
ajax({
url: '/basics/layout',
method: data.id ? 'put' : 'post',
data
}).then(res => {
message.success('保存成功')
this.props.router.push('layout')
})
}
// 编辑器改变
handCodeChange (key, value) {
this.setFormData(key, value)
}
// 设置表单值
setFormData (key, value) {
this.setState(prevState => {
let formData = prevState.formData
formData[key] = value
return {
formData
}
})
}
render () {
const { formData } = this.state
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 14 }
}
const options = {
html: {
mode: 'xml',
htmlMode: true,
theme: 'material',
lineNumbers: true,
tabSize: 2,
autofocus: true
}
}
const { getFieldDecorator } = this.props.form
return (<div><div className='jimu-tools'>
<div className={'jimu-bars'}>
<Button type='primary' onClick={this.handleSubmit.bind(this)}>保存</Button>
</div>
</div><div className='jimu-container'>
<Form onSubmit={this.handleSubmit}>
<FormItem {...formItemLayout} label='标题'>
{getFieldDecorator('title', {
initialValue: formData.title,
rules: [{
required: true, message: '请输入标题'
}]
})(<Input />
)}
</FormItem>
<FormItem {...formItemLayout} label='简介'>
{getFieldDecorator('summary', {
initialValue: formData.summary,
rules: [{
required: true, message: '请输入简介'
}]
})(<TextArea rows='4' />
)}
</FormItem>
<FormItem {...formItemLayout} label='模板代码'>
<Tabs defaultActiveKey='0' tabBarStyle={{ height: '40px' }}>
<TabPane tab='html' key='1'>
<CodeMirror value={formData.template} onBeforeChange={(editor, data, value) => this.handCodeChange('template', value)} options={options.html} />
</TabPane>
</Tabs>
</FormItem>
</Form>
</div></div>
)
}
}
const WrappedApp = Form.create()(layoutEdit)
export default hot(module)(WrappedApp)
import React from 'react'
import { hot } from 'react-hot-loader'
import { Button, Icon, Modal, message } from 'antd'
import { Link } from 'react-router'
import ajax from 'libs/ajax'
import styles from './index.less'
class layoutList extends React.Component {
constructor (props) {
super(props)
this.state = {
list: []
}
}
componentWillMount () {
this.getList()
}
getList () {
ajax({
url: '/basics/layout',
params: {
pageSize: 100
}
}).then(res => {
this.setState(state => {
state.list = res.lists
return state
})
})
}
layoutDelete (id) {
Modal.confirm({
title: `确定删除该模板吗?`,
okType: 'danger',
onOk: () => {
return ajax({
url: '/basics/layout',
method: 'delete',
params: {
id
}
}).then(res => {
message.success('删除成功')
this.getList()
})
},
onCancel() {
}
})
}
render () {
const { list } = this.state
return (<div><div className='jimu-tools'>
<div className={'jimu-bars'}>
<Link to='/layout/edit'><Button type='primary'>新建模板</Button></Link>
</div>
</div><div className='jimu-container'>
{
list && list.map(item => {
return (<div className={styles.item} key={item.id}>
<span className={styles.thumb} />
<span className={styles.id}>ID:{item.id}</span>
<span className={styles.revisor}><Icon type='user' />{item.revisor}</span>
<span className={styles.title}>{item.title}</span>
<span className={styles.summary}>#{item.summary}#</span>
<div className={styles.right}>
<Link title='编辑' to={'/layout/edit?id=' + item.id} className={styles.link}>编辑</Link>
<Button type='danger' className={styles.delete} onClick={this.layoutDelete.bind(this, item.id)}>删除</Button>
</div>
</div>
)
})
}
</div></div>)
}
}
export default hot(module)(layoutList)
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
.item{
height: 80px;
position: relative;
border-bottom: 1px solid #eaeaea;
span {
display: inline-block;
}
button{
margin-right: 20px;
font-size: 12px;
height: 24px;
}
&:hover{
background:rgba(87,140,254,0.08);
}
}
.thmub{
.size(45,60);
.pos(10,10);
}
.id{
color: #999;
.text(12,20);
.pos(75,5);
}
.revisor{
color: #999;
.text(12,20);
.pos(211,5);
i{
vertical-align: middle;
margin-right: 10px;
}
}
.title{
color: #333;
font-weight: bold;
.text(12,26);
.pos(75,25);
}
.summary{
color: #333;
.text(12,20);
.pos(75,56);
}
.status{
color: #666;
.text(12,20);
.pos(351,30);
i{
.size(8,8);
vertical-align: middle;
margin-right: 8px;
display: inline-block;
border-radius: 50%;
}
.success{
background-color: @success-color;
}
.failed{
background-color: @error-color;
}
}
.right{
position: relative;
.posr(0,0);
}
.link{
display: inline-block;
margin-right: 20px;
font-size: 12px;
line-height: 80px;
text-decoration: none;
}
import React from 'react'
import { hot } from 'react-hot-loader'
import ajax from 'libs/ajax'
import { Form } from 'antd'
import styles from './index.less'
class NormalLoginForm extends React.Component {
constructor (props) {
super(props)
const href = window.location.href
this.state = {
origin: window.location.origin,
isTest: href.indexOf('test') > -1 || href.indexOf('dev') > -1 || href.indexOf('localhost') > -1 || href.indexOf('127.0.0.1') > -1
}
}
handleSubmit (e) {
e.preventDefault()
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values)
}
})
}
componentWillMount () {
ajax({
url: '/system/user'
}).then(user => {
this.props.router.push('/')
}).catch(err => err)
}
render () {
return (
<div className={styles.login}>
<div className={styles.loginBody}>
<p className={styles.title}>选择下列登录方式</p>
<a className={styles.item} href={`http://sso.duiba${this.state.isTest ? 'test' : ''}.com.cn/login?redirect=${this.state.origin}/basics&systemId=29`}>SSO</a>
</div>
</div>
)
}
}
const login = Form.create()(NormalLoginForm)
export default hot(module)(login)
@import '~assets/styles/mixins.less';
@import '~assets/styles/theme.less';
.login{
width: 100%;
height: 100%;
background-color: @primary-color;
.loginBody{
.size(460,400);
border-radius: 5px;
.middle;
text-align: center;
}
.title{
text-align: center;
font-size: 26px;
margin: 70px 0;
color: #fff;
}
.item{
display: inline-block;
.size(60,60);
line-height: 60px;
font-size: 20px;
display: inline-block;
text-align: center;
border-radius: 50%;
color: #999;
background-color: #fff;
}
}
\ No newline at end of file
import React from 'react'
import { Tag, Button, Input, Modal } from 'antd'
import { hot } from 'react-hot-loader'
import styles from './index.less'
import ajax from 'libs/ajax'
class PluginCate extends React.Component {
constructor (props) {
super(props)
this.state = {
showEdit: false,
data: {},
cateList: []
}
}
componentWillMount () {
this.getCate()
}
getCate () {
ajax({
url: 'basics/cate',
params: {
pageSize: 100
}
}).then(res => {
this.setState({
cateList: res.lists
})
}).catch(err => err)
}
getColor () {
const colors = ['magenta', 'red', 'volcano', 'orange', 'gold', 'lime', 'green', 'cyan', 'blue', 'geekblue', 'purple']
return colors[parseInt(Math.random() * colors.length)]
}
toggleEdit (show) {
this.setState(prevState => ({
showEdit: show
}))
}
handleChange = (e) => {
let value = e.target.value
this.setState(preState => {
preState.data.title = value
return {
data: preState.data
}
})
}
handleCancel = (e) => {
this.setState({
showEdit: false
})
}
handleEdit = (data) => {
this.toggleEdit(true)
this.setState({
data: Object.assign({}, data)
})
}
handleSave = () => {
const { data } = this.state
ajax({
url: 'basics/cate',
method: data.id ? 'put' : 'post',
data: data
}).then(res => {
this.setState({
showEdit: false,
data: {}
})
this.getCate()
}).catch(err => err)
}
render () {
const { cateList, showEdit, data } = this.state
return (
<div>
<div className='jimu-tools'>
<div className={styles.bars}>
<Button type='primary' onClick={this.handleEdit.bind(this, {})}>添加分类</Button>
</div>
</div>
<div className='jimu-container'>
{
cateList.length === 0 ? null : cateList.map(item => <Tag color={this.getColor()} key={item.id} onClick={e => this.handleEdit(item, e)}>{item.title}</Tag>)
}
</div>
<Modal
title='添加分类'
visible={showEdit}
onCancel={this.handleCancel}
onOk={this.handleSave}>
<Input value={data.title} onChange={e => this.handleChange(e)} />
</Modal>
</div>
)
}
}
export default hot(module)(PluginCate)
@import '~assets/styles/mixins.less';
.bars {
float: right;
button {
margin-left: 8px;
}
}
\ No newline at end of file
import React from 'react'
import { Form, Select, Button, Input, Tabs, Upload, Icon, message } from 'antd'
import styles from './index.less'
import { hot } from 'react-hot-loader'
import ajax from 'libs/ajax'
import { Controlled as CodeMirror } from 'react-codemirror2'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/material.css'
import 'codemirror/mode/htmlembedded/htmlembedded'
import 'codemirror/mode/css/css'
import 'codemirror/mode/javascript/javascript'
const Option = Select.Option
const FormItem = Form.Item
const TabPane = Tabs.TabPane
const TextArea = Input.TextArea
class PluginEdit extends React.Component {
constructor (props) {
super(props)
this.state = {
formData: {
id: 0,
cateId: 0,
title: '',
summary: '',
html: '<p>这是一个示例内容</p>',
css: 'body{background-color:#fff;}',
js: 'console.log(opts);',
config: `[{"title": "测试配置项","type": "string","key": "demo"}]`,
thumb: ''
},
cateList: [],
pluginId: props.params.id
}
}
// 初始化数据
componentWillMount () {
this.getCateList()
if (this.state.pluginId !== '0') {
this.getComponentInfo(this.state.pluginId)
}
}
beforeUpload (file) {
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
message.error('文件不能超过2mb')
}
return isLt2M
}
// 获取插件信息
getComponentInfo (pluginId) {
ajax({
url: 'basics/component',
params: {
id: pluginId
}
}).then(res => {
this.props.form.setFieldsValue({
cateId: res.lists[0].cateId
})
this.setState({
formData: res.lists[0]
})
}).catch(err => err)
}
// 保存插件信息
saveComponentInfo (data) {
ajax({
url: 'basics/component',
method: data.id ? 'put' : 'post',
data: data
}).then(res => {
message.info('保存成功')
this.props.router.push('plugin')
}).catch(err => err)
}
// 获取分类
getCateList () {
ajax({
url: 'basics/cate',
params: {
pageSize: 100
}
}).then(res => {
this.setState({
cateList: res.lists
})
}).catch(err => err)
}
// 获取分类名称
getCateName (cateList, id) {
let name = '请选择类别'
cateList.forEach(item => {
if (item.id === id) {
name = item.title
}
})
return name
}
// 保存
handleSubmit () {
this.props.form.validateFields(
(err) => {
if (!err) {
const formData = this.props.form.getFieldsValue()
formData.html = this.state.formData.html
formData.css = this.state.formData.css
formData.js = this.state.formData.js
formData.config = this.state.formData.config
formData.thumb = this.state.formData.thumb
if (this.state.pluginId !== '0') {
formData.id = parseInt(this.state.pluginId)
}
this.saveComponentInfo(formData)
}
}
)
}
// 编辑器改变
handCodeChange (key, value) {
this.setFormData(key, value)
}
// 设置表单值
setFormData (key, value) {
this.setState(prevState => {
let formData = prevState.formData
formData[key] = value
return {
formData
}
})
}
// 上传
handleUpload (res) {
if (res.file.response && res.file.response.code === 0) {
this.setFormData('thumb', res.file.response.data)
} else if (res.file.response && res.file.response.msg) {
message.error(res.file.response.msg)
}
}
render () {
const { getFieldDecorator } = this.props.form
const options = {
html: {
mode: 'xml',
htmlMode: true,
theme: 'material',
lineNumbers: true,
tabSize: 2,
autofocus: true
},
css: {
mode: 'css',
theme: 'material',
lineNumbers: true,
tabSize: 2
},
js: {
mode: 'javascript',
theme: 'material',
lineNumbers: true,
tabSize: 2
},
config: {
mode: 'javascript',
theme: 'material',
lineNumbers: true,
tabSize: 2
}
}
const formData = this.state.formData
const cateList = this.state.cateList
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 14 }
}
return (
<div>
<div className='jimu-tools'>
<div className={styles.bars}>
<Button type='primary' onClick={this.handleSubmit.bind(this)}>保存</Button>
</div>
</div>
<div className='jimu-container'>
<div>
<Form onSubmit={this.handleSubmit}>
<FormItem label='所属类别' {...formItemLayout}>
{getFieldDecorator('cateId', {
rules: [{ required: true, message: '请选择插件类别' }]
})(
<Select
placeholder='所属类别'
>
{
cateList.map(item => {
return (
<Option key={item.id} value={item.id}>{item.title}</Option>
)
})
}
</Select>
)}
</FormItem>
<FormItem label='插件名称' {...formItemLayout}>
{getFieldDecorator('title', {
initialValue: formData.title,
rules: [{ required: true, message: '请输入插件名称' }]
})(
<Input placeholder='请输入插件名称' />
)}
</FormItem>
<FormItem label='插件描述' {...formItemLayout}>
{getFieldDecorator('summary', {
initialValue: formData.summary,
rules: [{ required: true, message: '请输入插件描述' }]
})(
<TextArea rows={4} placeholder='请输入插件描述' />
)}
</FormItem>
<FormItem label='封面图' {...formItemLayout}>
<Upload name='file'
beforeUpload={this.beforeUpload}
onChange={this.handleUpload.bind(this)}
action='/api/system/upload'
showUploadList={false}
listType='picture'>
<Button>
<Icon type='upload' /> 上传缩略图
</Button>
</Upload>
{formData.thumb ? <div style={{ width: '300px', marginTop: '20px' }}><img style={{ width: '100%' }} alt='thumb' src={formData.thumb} /></div> : ''}
</FormItem>
<FormItem label='相关代码' {...formItemLayout}>
<Tabs defaultActiveKey='0' tabBarStyle={{ height: '40px' }}>
<TabPane tab='html' key='1'>
<CodeMirror value={formData.html} onBeforeChange={(editor, data, value) => this.handCodeChange('html', value)} options={options.html} />
</TabPane>
<TabPane tab='css' key='2'>
<CodeMirror value={formData.css} onBeforeChange={(editor, data, value) => this.handCodeChange('css', value)} options={options.css} />
</TabPane>
<TabPane tab='js' key='3'>
<CodeMirror value={formData.js} onBeforeChange={(editor, data, value) => this.handCodeChange('js', value)} options={options.js} />
</TabPane>
<TabPane tab='config' key='4'>
<CodeMirror value={formData.config} onBeforeChange={(editor, data, value) => this.handCodeChange('config', value)} options={options.config} />
</TabPane>
</Tabs>
</FormItem>
</Form>
</div>
</div>
</div>
)
}
}
const WrappedApp = Form.create()(PluginEdit)
export default hot(module)(WrappedApp)
@import '~assets/styles/mixins.less';
.bars {
float: right;
button {
margin-left: 8px;
}
}
\ No newline at end of file
import React from 'react'
import { Link } from 'react-router'
import { Button, Select, Row, Modal, message } from 'antd'
import styles from './index.less'
import { hot } from 'react-hot-loader'
import PluginItem from 'basics/components/pluginItem/index'
import ajax from 'libs/ajax'
const Option = Select.Option
class PluginList extends React.Component {
constructor (props) {
super(props)
this.state = {
cateList: [],
componentList: []
}
}
// 初始化数据
componentWillMount () {
this.getCateList()
this.getComponentList()
}
// 获取分类
getCateList () {
ajax({
url: 'basics/cate',
params: {
pageSize: 100
}
}).then(res => {
this.setState({
cateList: res.lists
})
}).catch(err => err)
}
// 获取列表
getComponentList (opts = {}) {
let { cateId } = opts
ajax({
url: 'basics/component',
params: {
pageSize: 100,
cateId: cateId || ''
}
}).then(res => {
this.setState({
componentList: res.lists
})
}).catch(err => err)
}
// 筛选
handleCateChange (value) {
this.getComponentList({
cateId: value
})
}
// 删除一个组件
pluginRemove (id) {
Modal.confirm({
title: '确定删除该插件?',
confirmLoading: true,
okType: 'danger',
onOk: () => {
return ajax({
url: `/basics/component`,
params: {
id
},
method: 'delete'
}).then(data => {
message.success('删除成功')
this.getComponentList()
}).catch(err => err)
}
})
}
render () {
const { cateList } = this.state
return (
<div>
<div className='jimu-tools'>
<Select
style={{ width: 120 }}
placeholder='全部类别'
onChange={this.handleCateChange.bind(this)}
>
<Option value={0} key={0}>全部类别</Option>
{cateList.map(item => <Option value={item.id} key={item.id}>{item.title}</Option>)}
</Select>
<div className={styles.bars}>
<Button type='primary'><Link to='/plugin/edit/0'>新建</Link></Button>
</div>
</div>
<div style={{ padding: '10px 0' }}>
<Row gutter={16}>
{
this.state.componentList.map((item) => {
return (
<PluginItem item={item} remove={this.pluginRemove} key={item.id} />
)
})
}
</Row>
</div>
</div>
)
}
}
export default hot(module)(PluginList)
@import '~assets/styles/mixins.less';
.bars {
float: right;
button {
margin-left: 8px;
}
}
.gutter {
margin-bottom: 30px;
& > div {
position: relative;
left: 50%;
transform: translate(-50%);
}
}
\ No newline at end of file
<html>
<head>
<title>积木平台</title>
</head>
<body>
<div id="app">
</div>
</body>
</html>
\ No newline at end of file
import React from 'react'
import {
render
} from 'react-dom'
import {
Router,
hashHistory
} from 'react-router'
import 'assets/styles/admin.less'
import routes from './routes'
import { LocaleProvider } from 'antd'
import zhCN from 'antd/lib/locale-provider/zh_CN'
render(<LocaleProvider locale={zhCN}><Router history={hashHistory} routes={routes} /></LocaleProvider>, document.getElementById('app'))
import app from './app'
import container from './container'
const routes = [
{
path: '/',
component: container,
getIndexRoute (partialNextState, callback) {
require.ensure([], function (require) {
callback(null, {
component: require('./containers/guide/index').default
})
})
},
childRoutes: [{
path: '/',
component: app,
childRoutes: [
{
path: 'activity',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/activity/list/index').default)
})
}
}, {
path: 'plugin',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/plugin/list/index').default)
})
}
}, {
path: 'plugin/edit/:id',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/plugin/edit/index').default)
})
}
}, {
path: 'plugin/cate',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/plugin/cate/index').default)
})
}
}, {
path: 'layout',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/layout/list/index').default)
})
}
}, {
path: 'layout/edit',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/layout/edit/index').default)
})
}
}
]
}, {
path: 'login',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/login/index').default)
})
}
}, {
path: 'activity/edit',
getComponents (nextState, callback) {
require.ensure([], function (require) {
callback(null, require('./containers/activity/edit/index').default)
})
}
}]
}
]
export default routes
import 'render/libs/animate'
import 'render/libs/loader'
import 'render/libs/checkwebp' // 判断是否支持webp
import './components/renderButtuon' // 按钮控件
import './components/renderImage' // 图片控件
import './components/renderTurnCircle' // 大转盘控件
import './components/renderPopup' // 弹层控件
import './components/renderWechat' // 微信控件
import './components/renderMobile' // 电话控件
import './components/renderCarousel' // 轮播控件
import './components/renderText' // 文字控件
import './components/renderAddress' // 地址控件
import './components/renderForm' // 表单控件
import './components/renderDownload' // 下载控件
import './components/renderCountdown' // 倒计时控件
import './components/renderCounter' // 计数器控件
import './components/renderQRCode'; // 二维码
\ No newline at end of file
import Util from 'render/libs/utils';
// import Loader from 'src/lib/loader';
(function($) {
class RenderAddress {
constructor(result, index, isProps) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.number = index
this.isProps = isProps
this.address = 'https://webapi.amap.com/maps?v=1.4.8&key=13b7051cc83cc78dcfa7bb094256139c'
this.style = ''
this.map = ''
this.init()
}
init() {
let addressElem = $(`<div class="address-wrap${this.number}"><div id="land-address${this.number}"></div><div class="address-title"></div></div>`)
$('.land_wrap').append(addressElem)
// this.renderStyle();
this.event()
}
renderStyle() {
let {
style
} = this.options;
let {
showType
} = this.options.address;
let styleCss = Object.assign({},
Util.transformCss(style, this.isProps), { 'position': 'absolute' })
$(`.address-wrap${this.number}`).css(styleCss)
$(`#land-address${this.number}`).css({
'position': 'absolute',
'top': 0,
'left': 0,
'width': Util.fitSize(style.width, this.isProps),
'height': Util.fitSize(style.height, this.isProps)
})
let styleAddress = Object.assign({},
Util.transformCss(this.options.address.style, this.isProps), { 'position': 'absolute' })
$(`.address-wrap${this.number} .address-title`).text(this.options.address.text)
$(`.address-wrap${this.number} .address-title`).css(styleAddress)
if (showType === 2) {
$(`.address-wrap${this.number} .address-title`).css('top', Util.fitSize('4px', this.isProps))
} else {
$(`.address-wrap${this.number} .address-title`).css('bottom', Util.fitSize('4px', this.isProps))
}
}
event() {
let self = this
let {
lnglat
} = this.options
window.Loader.async([self.address], function() {
self.map = new window.AMap.Map(`land-address${self.number}`, {
center: lnglat,
zoom: 11
})
self.marker = new window.AMap.Marker({
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
position: lnglat
})
self.marker.setMap(self.map)
self.map.setZoomAndCenter(14, lnglat)
self.renderStyle()
})
}
}
window.TA.RenderAddress = RenderAddress
})(Zepto)
\ No newline at end of file
import Util from 'render/libs/utils'
import { NETVeify, buryAOId } from 'render/libs/helper'
import { commonJumpLogic } from 'render/libs/common'
import pp from 'render/libs/popup'
;(function($) {
class RenderButton {
constructor(result, index, isProps, elementConfig, selector) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.isIphone = navigator.userAgent.indexOf('iPhone') !== -1
this.number = index
this.elementConfig = elementConfig
this.isProps = isProps
this.style = ''
this.yidun = '//cstaticdun.126.net/load.min.js'
this.$mountNode = $(selector || '.land_wrap')
this.init()
}
init() {
let buttonElem = $(`<div class="land-button${this.number}"></div>`)
this.$mountNode.append(buttonElem)
this.renderStyle()
this.event()
// this.initPopup()
}
initPopup() {
let { type, popupId } = this.options.jump
if (type === 'popup') {
this.elementConfig &&
this.elementConfig.forEach((item, index) => {
if (item.id === popupId) {
this.popId = index
new window.TA.RenderPopup(
item,
index,
this.isProps,
this.elementConfig
).showPopup()
$('.land_wrap').css('height', $(window).height())
}
})
}
}
clickjump() {
let { name, picValid } = this.options
let extraParam = {}
picValid && (extraParam.sliderVerify = 1)
commonJumpLogic({
componentData: this.options,
jump: this.options.jump,
options: { location: name, ...extraParam },
popupCallback: () => {
this.initPopup()
}
})
}
renderStyle() {
let { height } = this.options.style
let { fixed, animation } = this.options
let duration = animation && animation.duration
let styleCss = Object.assign(
{},
Util.transformCss(this.options.style, this.isProps),
{
position: fixed === 0 ? 'absolute' : 'fixed',
'background-repeat': 'no-repeat',
'line-height': Util.fitSize(height, this.isProps)
},
{
animation: `scaleAnim ${duration}ms linear infinite alternate`
}
)
let selector = `.land-button${this.number}`
$(selector).css(styleCss)
$(selector).text(this.options.text)
}
event() {
let { picValid, styleMode, multiStyle } = this.options
let self = this
const selector = `.land_wrap .land-button${this.number}`
// 如果是跳转下载链接则埋唯一id
if (self.options.jump.type === 'link') {
buryAOId(selector)
}
$(selector).on('click', function() {
if (styleMode === 3) {
let ClickBackgroundImage = multiStyle.click.backgroundImage
if (ClickBackgroundImage) {
$(this).css({
backgroundImage: ClickBackgroundImage
})
}
}
if (picValid === 0) {
self.clickjump()
} else if (picValid === 1) {
NETVeify().then(self.clickjump.bind(self), () => {
pp.note('校验失败', {
mask: false,
closeTime: 2000
})
})
}
})
}
}
window.TA.RenderButton = RenderButton
})(Zepto)
import Swiper from 'swiper'
import '../../../../../node_modules/swiper/dist/css/swiper.min.css'
import Util from 'render/libs/utils';
(function($) {
class RenderCarousel {
constructor(result, index, isProps) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.number = index
this.isProps = isProps
this.style = ''
this.init()
}
init() {
let {
sliders
} = this.options
let carouselEle = `<div class="swiper-container${this.number}">
<div class="swiper-wrapper"></div>
</div>`
$('.land_wrap').append(carouselEle)
sliders && sliders.forEach((item, index) => {
if (typeof ''.ossimg === 'function' && item.src.indexOf('.gif') === -1) {
item.src = item.src.ossimg()
}
$(`.swiper-container${this.number} .swiper-wrapper`).append(`<div class="swiper-slide" src=${item.src} id=${item.id}></div>`)
})
this.renderStyle()
this.event()
}
renderStyle() {
let {
style,
fixed,
wrapStrategy
} = this.options
let styleCss = Object.assign({},
Util.transformCss(style, this.isProps), { 'position': fixed === 0 ? 'absolute' : 'fixed' }, { 'overflow': 'hidden' })
$(`.swiper-container${this.number}`).css(styleCss)
$(`.swiper-container${this.number} .swiper-slide`).each(function(index, ele) {
$(this).css({ 'background-image': 'url(' + $(this).attr('src') + ')', 'background-repeat': 'no-repeat', 'background-size': wrapStrategy })
})
}
event() {
let {
direction,
autoplay
} = this.options
new Swiper(`.swiper-container${this.number}`, {
direction,
autoplay: autoplay.enable === 0 ? false : autoplay,
loop: true
})
}
}
window.TA.RenderCarousel = RenderCarousel
})(Zepto)
import Util from 'render/libs/utils'
import { getDayStr, getTimeStr } from 'render/libs/helper'
let dayStr = getDayStr(new Date()) + ' '
let curryChange = (originalStr = '00:00:00', dstStr = '00:00:50') => {
let originDate = new Date(dayStr + originalStr)
let dstDate = new Date(dayStr + dstStr)
return step => {
return () => {
originDate = new Date(originDate.getTime() + 1000 * step)
if (step > 0) {
// 增加逻辑
return getTimeStr(originDate <= dstDate ? originDate : dstDate)
} else {
return getTimeStr(originDate >= dstDate ? originDate : dstDate)
}
}
}
}
(function($) {
class RenderCountdown {
constructor(result, index, isProps, elementConfig) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.isIphone = navigator.userAgent.indexOf('iPhone')
this.number = index
this.elementConfig = elementConfig
this.isProps = isProps
this.style = ''
// curry方式定义plus和subtract操作
let { start, end } = this.options
this.add = curryChange(start, end)(1)
this.remove = curryChange(start, end)(-1)
this.init()
}
init() {
let countdownEle = $(`<div class="land-countdown${this.number}"><div class="land-countdown-wrap"></div></div>`)
$('.land_wrap').append(countdownEle)
this.renderStyle()
this.event()
}
renderStyle() {
let {
start,
style,
fixed
} = this.options
let {
verticalAlign
} = this.options.style
let styleCss = Object.assign({},
Util.transformCss(style, this.isProps), { 'position': !fixed ? 'absolute' : 'fixed' }, { 'display': 'table' })
$(`.land-countdown${this.number}`).css(styleCss)
$(`.land-countdown${this.number} .land-countdown-wrap`).text(start)
$(`.land-countdown${this.number} .land-countdown-wrap`).css({
'display': 'table-cell',
'vertical-align': verticalAlign
})
}
event() {
// 绑定事件
// 判断是增加还是减少
let {
start,
end
} = this.options
let value = null
let timer = setInterval(() => {
let next
// 这里巧借字符串逐位比对ASCII码的方式判断大小
if (start <= end) {
next = this.add()
} else {
next = this.remove()
}
if (value === next) {
clearInterval(timer)
} else {
value = next
$(`.land-countdown${this.number} .land-countdown-wrap`).text(next)
}
}, 999)
}
}
window.TA.RenderCountdown = RenderCountdown
})(Zepto)
\ No newline at end of file
import Util from 'render/libs/utils'
(function($) {
class RenderCounter {
constructor(result, index, isProps, elementConfig) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.isIphone = navigator.userAgent.indexOf('iPhone')
this.number = index
this.elementConfig = elementConfig
this.isProps = isProps
this.style = ''
let { start, end } = this.options
this.add = () => start + 1 <= end ? ++start : end
this.remove = () => start - 1 >= end ? --start : end
this.init()
}
init() {
let counterEle = $(`<div class="land-counter${this.number}"><div class="land-counter-wrap"></div></div>`)
$('.land_wrap').append(counterEle)
this.renderStyle()
this.event()
}
renderStyle() {
let {
start,
style,
fixed
} = this.options
let {
verticalAlign
} = this.options.style
let styleCss = Object.assign({},
Util.transformCss(style, this.isProps), { 'position': !fixed ? 'absolute' : 'fixed' }, { 'display': 'table' })
$(`.land-counter${this.number}`).css(styleCss)
$(`.land-counter${this.number} .land-counter-wrap`).text(start)
$(`.land-counter${this.number} .land-counter-wrap`).css({
'display': 'table-cell',
'vertical-align': verticalAlign
})
}
event() {
// 判断是增加还是减少
let {
start,
end
} = this.options
let value = null
let timer = setInterval(() => {
let next
// 这里巧借字符串逐位比对ASCII码的方式判断大小
if (start <= end) {
next = this.add()
} else {
next = this.remove()
}
if (value === next) {
clearInterval(timer)
} else {
value = next
$(`.land-counter${this.number} .land-counter-wrap`).text(next)
}
}, 999)
}
}
window.TA.RenderCounter = RenderCounter
})(Zepto)
\ No newline at end of file
import Util from 'render/libs/utils';
import { NETVeify, buryAOId } from 'render/libs/helper'
import pp from 'render/libs/popup'
import { commonJumpLogic } from 'render/libs/common'
(function($) {
class RenderDownload {
constructor(result, index, isProps, elementConfig) {
let defaultValue = {
style: {}
}
this.style = '';
this.options = $.extend(true, defaultValue, result);
this.number = index;
this.isProps = isProps;
this.elementConfig = elementConfig;
this.init()
}
init() {
let downloadElem = $(`<div class="download${this.number}">
</div>`)
$('.land_wrap').append(downloadElem)
this.renderStyle()
this.event()
}
renderStyle() {
// let isFixed = this.options.fixed
let {
type
} = this.options.fixed
let styleCss = Object.assign({},
Util.transformCss(this.options.style, this.isProps), {
'position': 'fixed'
}
)
$(`.download${this.number}`).css(styleCss)
$('head').append('<style>' + this.style + '</style>')
}
initPopup() {
let {
type,
popupId
} = this.options.jump
if (type === 'popup') {
this.elementConfig && this.elementConfig.forEach((item, index) => {
if (item.id === popupId) {
this.popId = index
new window.TA.RenderPopup(item, index, this.isProps, this.elementConfig).showPopup();
$('.land_wrap').css('height', $(window).height());
}
})
}
}
clickjump() {
let { name } = this.options
commonJumpLogic({
componentData: this.options,
jump: this.options.jump,
options: { location: name },
popupCallback: () => { this.initPopup() }
})
}
event() {
let { type } = this.options.jump
let self = this
let { picValid } = this.options
const selector = `.land_wrap .download${this.number}`
// 如果是跳转下载链接则埋唯一id
if (type === 'link') {
buryAOId(selector)
}
$(selector).on('click', function() {
if (picValid) {
NETVeify().then(self.clickjump.bind(self), () => {
pp.note('校验失败', {
mask: false,
closeTime: 2000
})
})
} else {
self.clickjump()
}
})
}
}
window.TA.RenderDownload = RenderDownload
})(Zepto)
\ No newline at end of file
This diff is collapsed.
import Util from 'render/libs/utils';
import { buryAOId } from 'render/libs/helper'
import { commonJumpLogic } from 'render/libs/common'
(function($) {
class RenderImage {
constructor(result, index, isProps, elementConfig,selector) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.elementConfig = elementConfig
this.isProps = isProps
this.style = ''
this.number = index
this.$mountNode = $(selector || '.land_wrap')
this.init()
}
init() {
let imageElem = $(`<div class="land-image${this.number}"></div>`)
this.$mountNode.append(imageElem)
this.event()
this.renderStyle()
//this.initPopup()
}
initPopup() {
let {
type,
popupId
} = this.options.jump
if (type === 'popup') {
this.elementConfig && this.elementConfig.forEach((item, index) => {
if (item.id === popupId) {
this.popId = index
new window.TA.RenderPopup(item, index, this.isProps, this.elementConfig).showPopup();
$('.land_wrap').css('height', $(window).height());
}
})
}
}
renderStyle() {
let {
fixed
} = this.options
let styleCss = Object.assign({},
Util.transformCss(this.options.style, this.isProps), { 'position': fixed === 0 ? 'absolute' : 'fixed' }, { 'background-repeat': 'no-repeat' })
$(`.land_wrap .land-image${this.number}`).css(styleCss)
}
event() {
let {
type
} = this.options.jump
let {
name
} = this.options
let self = this
const selector = `.land_wrap .land-image${this.number}`
// 如果是跳转下载链接则埋唯一id
if (type === 'link') {
buryAOId(selector)
}
if (type !== 'none') {
$(selector).on('click', function() {
commonJumpLogic({
componentData: self.options,
jump: self.options.jump,
options: { location: name },
popupCallback: () => { self.initPopup() }
})
})
}
}
}
window.TA.RenderImage = RenderImage
})(Zepto)
\ No newline at end of file
import Util from 'render/libs/utils'
import { commonJumpLogic } from 'render/libs/common';
(function($) {
class RenderMobile {
constructor(result, index, isProps) {
let defaultValue = {
style: {}
}
this.style = ''
this.options = $.extend(true, defaultValue, result)
this.number = index
this.isProps = isProps
this.init()
}
init() {
console.log(this.options)
let {
mobile
} = this.options
let mobileElem = $(`<a class="mobile${this.number}" href="tel:${mobile}">
<div class="mobile-jump">
<i class="iconfont icon-control_phone"></i>
<span class="mobile-num"></span>
</div>
</a>`)
$('.land_wrap').append(mobileElem)
this.event()
this.renderStyle()
}
renderStyle() {
let {
text
} = this.options
let {
type
} = this.options.fixed
// let iconClass = this.options.icon.className || '';
let styleCss = Object.assign({},
Util.transformCss(this.options.style, this.isProps), {
'position': type === 'self' || type === 'bottom' ? 'fixed' : 'absolute'
}
)
$(`.mobile${this.number}`).css(styleCss)
$(`.mobile${this.number} .mobile-num`).text(text)
let iconCss = Util.transformCss(this.options.icon.style, this.isProps)
$(`.mobile${this.number} i`).css(iconCss)
$(`.mobile${this.number} .mobile-jump`).css({
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
})
$('head').append('<style>' + this.style + '</style>')
}
event() {
let {
name,
jump
} = this.options
$(document).on('click', `.mobile${this.number}`, function() {
commonJumpLogic({
componentData: self.options,
jump,
options: { location: name }
})
})
}
}
window.TA.RenderMobile = RenderMobile
})(Zepto)
This diff is collapsed.
import Util from 'render/libs/utils'
import { commonJumpLogic } from 'render/libs/common'
;(function($) {
class RenderQRCode {
constructor(result, index, isProps, elementConfig,selector) {
let defaultValue = {
style: {}
}
this.number = index
this.options = $.extend(true, defaultValue, result)
this.elementConfig = elementConfig
this.isProps = isProps
this.$mountNode = $(selector || '.land_wrap')
this.$el = ''
this.init()
}
init() {
this.$el = $(
`<div class="land-QRCode${this.number}"><div class="land-QRCode-wrap">
<img src="${this.options.src}" alt="">
</div></div>`
)
this.$mountNode.append(this.$el)
this.renderStyle()
this.event()
}
renderStyle() {
let { style, fixed } = this.options
let styleCss = Object.assign(
{},
Util.transformCss(style, this.isProps),
{ position: !fixed ? 'absolute' : 'fixed' },
{ display: 'table' }
)
$(`.land-QRCode${this.number}`).css(styleCss)
$(`.land-QRCode${this.number} .land-QRCode-wrap`).css({})
}
event() {
this.$el.longPress(() => {
commonJumpLogic({
componentData: this.options,
jump: this.options.jump,
options: { location: name }
})
})
}
}
window.TA.RenderQRCode = RenderQRCode
})(Zepto)
import Util from 'render/libs/utils';
import { buryAOId } from 'render/libs/helper'
import { commonJumpLogic } from 'render/libs/common'
(function($) {
class RenderText {
constructor(result, index, isProps, elementConfig,selector) {
let defaultValue = {
style: {}
}
this.options = $.extend(true, defaultValue, result)
this.isIpone = navigator.userAgent.indexOf('iPhone')
this.number = index
this.elementConfig = elementConfig
this.isProps = isProps
this.style = ''
this.$mountNode = $(selector || '.land_wrap')
this.init()
}
init() {
let textElem = $(`<div class="land-text${this.number}"><div class="land-text-wrap"></div></div>`)
this.$mountNode.append(textElem)
this.renderStyle()
this.event()
// this.initPopup()
}
initPopup() {
let {
type,
popupId
} = this.options.jump
if (type === 'popup') {
this.elementConfig && this.elementConfig.forEach((item, index) => {
if (item.id === popupId) {
this.popId = index
new window.TA.RenderPopup(item, index, this.isProps, this.elementConfig).showPopup();
$('.land_wrap').css('height', $(window).height());
}
})
}
}
renderStyle() {
let {
text,
style,
fixed
} = this.options
let {
verticalAlign
} = this.options.style
let styleCss = Object.assign({},
Util.transformCss(style, this.isProps), { 'position': fixed === 0 ? 'absolute' : 'fixed' }, { 'display': 'table' })
$(`.land-text${this.number}`).css(styleCss)
$(`.land-text${this.number} .land-text-wrap`).html(text)
$(`.land-text${this.number} .land-text-wrap`).css({
'display': 'table-cell',
'vertical-align': verticalAlign
})
}
event() {
let {
type
} = this.options.jump
let {
name
} = this.options
let self = this
const selector = `.land_wrap .land-text${this.number} .land-text-wrap`
if (type === 'link') {
buryAOId(selector)
}
if (type !== 'none') {
$(selector).on('click', function() {
commonJumpLogic({
componentData: self.options,
jump: self.options.jump,
options: { location: name },
popupCallback: () => { self.initPopup() }
})
})
}
}
}
window.TA.RenderText = RenderText
})(Zepto)
\ No newline at end of file
This diff is collapsed.
import Util from 'render/libs/utils';
import pp from 'render/libs/popup';
import { commonJumpLogic } from 'render/libs/common';
(function($) {
class RenderWechat {
constructor(result, index, isProps) {
let defaultValue = {
style: {}
}
this.style = ''
this.options = $.extend(true, defaultValue, result)
this.number = index
this.isProps = isProps
this.init()
}
init() {
let {
username
} = this.options.wechat;
let wechatElem = $(`<div class="wechat${this.number}">
<div class="wechat-user">
<img class="wechat-user-img"/>
<span class="wechat-user-name"></span>
</div>
<div class="wechat-btn" data-clipboard-action="copy" propy="${username}"></div>
</div>`)
$('.land_wrap').append(wechatElem)
this.renderStyle()
this.event()
}
renderStyle() {
let {
type
} = this.options.fixed;
let {
height
} = this.options.style
let styleCss = Object.assign({},
Util.transformCss(this.options.style, this.isProps), {
'position': type === 'bottom' || type === 'top' ? 'fixed' : 'absolute',
'line-height': Util.fitSize(height, this.isProps)
}
)
$(`.wechat${this.number}`).css(styleCss)
let imgCss = Util.transformCss(this.options.wechat.avatar.style, this.isProps)
$(`.wechat${this.number} .wechat-user .wechat-user-img`).css(imgCss)
let nameCss = Object.assign({},
Util.transformCss(this.options.wechat.name.style, this.isProps), {
'margin-left': Util.fitSize('10px', this.isProps)
}
)
$(`.wechat${this.number} .wechat-user .wechat-user-name`).css(nameCss)
let btnCss = Object.assign({},
Util.transformCss(this.options.btn.style, this.isProps), {
'position': 'absolute',
'right': Util.fitSize('10px', this.isProps),
'top': Util.fitSize('14px', this.isProps)
}
)
$(`.wechat${this.number} .wechat-btn`).css(btnCss)
$(`.wechat${this.number} .wechat-user .wechat-user-img`).attr('src', this.options.wechat.avatar.src)
this.style += `.wechat${this.number} .wechat-user` + `{
display: inline-block;
margin-left: ${Util.fitSize('10px', this.isProps)};
}`
$(`.wechat${this.number} .wechat-user .wechat-user-name`).text(this.options.wechat.name.text)
$(`.wechat${this.number} .wechat-btn`).text(this.options.btn.text)
$('head').append('<style>' + this.style + '</style>')
}
event() {
let {
name
} = this.options;
let self = this;
window.Loader.async(["//yun.tuisnake.com/h5-mami/insurance/taobao/clipboard.min.js"], function() {
var clipboard = new Clipboard(`.land_wrap .wechat${self.number} .wechat-btn`, {
text: function(trigger) {
return $(`.land_wrap .wechat${self.number} .wechat-btn`).attr('propy')
}
});
clipboard.on("success", function(element) { //复制成功的回调
setTimeout(function() {
pp.note('复制成功', {
mask: false,
closeTime: 1000
})
}, 0)
});
clipboard.on('error', function(e) {
console.info("复制失败");
});
})
$(`.land_wrap .wechat${this.number} .wechat-btn`).on('click', function() {
commonJumpLogic({
componentData: self.options,
jump: self.options.jump,
options: { location: name }
})
})
}
}
window.TA.RenderWechat = RenderWechat
})(Zepto)
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0,user-scalable=no,viewport-fit=cover" />
<link rel="dns-prefetch" href="//yun.tuia.cn" />
<link rel="dns-prefetch" href="//yun.tuisnake.com" />
<link rel="dns-prefetch" href="//yun.pansyta.com" />
<link rel="dns-prefetch" href="//yun.dahliata.com" />
<link rel="dns-prefetch" href="//yun.iristta.com" />
<link rel="dns-prefetch" href="//yun.freesiata.com" />
<link rel="dns-prefetch" href="//yun.shisrd.com" />
<link rel="dns-prefetch" href="//yun.heisldg.com" />
<script>
!(function (e, i) {
var t = e.documentElement,
n = navigator.userAgent.match(/iphone|ipod|ipad/gi),
a = n ? Math.min(i.devicePixelRatio, 3) : 1,
m = 'orientationchange' in window ? 'orientationchange' : 'resize'
t.dataset.dpr = a
for (
var d, l, c = !1, o = e.getElementsByTagName('meta'), r = 0; r < o.length; r++
)
(l = o[r]), 'viewport' == l.name && ((c = !0), (d = l))
if (c)
d.content =
'width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0,user-scalable=no,viewport-fit=cover'
else {
var o = e.createElement('meta');
(o.name = 'viewport'),
(o.content =
'width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0,user-scalable=no,viewport-fit=cover'
),
t.firstElementChild.appendChild(o)
}
var s = function () {
var e = t.clientWidth
e / a > 640 && (e = 640 * a),
(window.remScale = e / 640),
(t.style.fontSize = 200 * (e / 640) + 'px')
}
s(), e.addEventListener && i.addEventListener(m, s, !1)
})(document, window)
</script>
<% if (htmlWebpackPlugin.options.JIMU_ENV !== 'prod') { %>
<script type="text/javascript">
;
(function () {
if (!/eruda=true/.test(window.location.href)) return
var oHead = document.head
var oScript = document.createElement('script')
oScript.type = 'text/javascript'
oScript.src = '//yun.tuia.cn/h5-mami/optimize/eruda.min.js'
oHead.appendChild(oScript)
oScript.onload = function () {
eruda.init()
}
})()
</script>
<% } %> <% if (htmlWebpackPlugin.options.JIMU_ENV === 'prod') { %>
<script type="text/javascript">
!function () {
var host = "//activity.tuisnake.com/statistics/activityPagePerf";
var data = {
type: "landpage",
id: -1,
cid: -1,
oId: -1,
s: 1,
_t: +new Date()
};
function send() {
var m1 = /[\?&]id=([^#&]*)/.exec(location.href);
if (m1) {
data.id = m1[1];
}
var m2 = /[\?&]a_cid=([^#&]*)/.exec(location.href);
if (m2) {
data.cid = m2[1];
}
var m4 = /[\?&]a_oId=([^#&]*)/.exec(location.href);
if (m4) {
data.oId = m4[1];
}
new Image().src = "".concat(host, "?id=").concat(data.id, "&cid=").concat(data.cid, "&oid=").concat(data.oId, "&s=").concat(data.s, "&_t=").concat(data._t);
}
try {
send();
} catch (e) {
data.s = 0;
new Image().src = "".concat(host, "?id=").concat(data.id, "&cid=").concat(data.cid, "&oid=").concat(data.oId, "&s=").concat(data.s, "&_t=").concat(data._t);
}
}();
</script>
<script type="text/javascript" src="//yun.tuisnake.com/h5-mami/log.js?t=1547205079675" id="send_log"></script>
<% } else { %>
<script type="text/javascript" src="//yun.dui88.com/h5-mami/log-test/v1/log-test.js" id="send_log"></script>
<% } %>
<title></title>
<link href="//yun.dui88.com/jimu/swiper.min.css" rel="stylesheet" />
<script src="//yun.dui88.com/jimu/swiper.min.js"></script>
<style>
#__loading {
position: fixed;
z-index: 1000;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #fff;
}
#__loading img {
width: 100px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin-left: -50px;
margin-top: -50px;
}
</style>
<style>
.reload_tips {
position: fixed;
z-index: 2000;
display: none;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin: auto;
background: rgba(40, 40, 40, 0.85);
min-width: 124px;
max-width: 80%;
background: rgba(40, 40, 40, 0.85);
text-align: center;
border-radius: 3px;
color: #fff;
font-size: 16px;
margin: 0;
word-break: break-all;
line-height: 1.5;
padding: 10px 5px;
}
.show_reload_tips {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
body {
transition: transform 0.5s;
min-height: 100vh;
}
</style>
<script>
//超时toast
;
(function () {
var tipsTimer = setTimeout(function () {
var el = document.querySelector('#reload-tips')
el.classList.add('show_reload_tips')
//下拉刷新
var start = 0
var end = 0
var offset = 0
window.addEventListener('touchstart', function (e) {
var scrollTop = document.body.scrollTop
if (scrollTop > 10) return
start = e.touches[0].screenY
})
window.addEventListener('touchmove', function (e) {
var scrollTop = document.body.scrollTop
if (scrollTop > 10) return
end = e.touches[0].screenY
offset = end - start
document.querySelector('body').style.transform =
'translate(0,' + (offset > 50 ? 50 : offset) + 'px)'
})
window.addEventListener('touchend', function (e) {
document.querySelector('body').style.transform = 'translate(0,0)'
if (offset > 50) {
window.location.reload()
}
})
setTimeout(function () {
el.classList.remove('show_reload_tips')
}, 3000)
}, 5000)
window.onload = function () {
clearTimeout(tipsTimer)
console.log('load')
}
})()
</script>
</head>
<body>
<div class="reload_tips" id="reload-tips">
当前页面加载慢<br />下拉可重新加载页面
</div>
<div id="__loading">
<img src="//yun.tuisnake.com/jimu/balls.svg" />
</div>
</body>
</html>
\ No newline at end of file
import 'render/libs/base'
import './entry.less'
import './actBase.js'
import Util from 'render/libs/utils'
import { commonJumpLogic } from './libs/common'
window.FastClick = require('render/libs/fastclick')
;(function() {
FastClick.attach(document.body)
var defaultConfig, defaultWrapConfig, StyleConfig
let Template = {
init() {
let _self = this
// 获取皮肤数据
_self.getSkinInfo(result => {
// 初始化dom结构
_self.initElement(result)
})
},
getSkinInfo(callBack) {
let params = {
id: Util.getUrlParameter('id')
}
// 判断是本地开发环境
$.ajax({
url: '/activity/land/landPageDetail',
data: params,
success: result => {
try {
// 兼容后端返回text/html的情况
if (typeof result !== 'object') {
result = JSON.parse(result)
}
if (result.success === true) {
callBack &&
callBack({
data: {
pageJson: result.data.html
}
})
} else {
throw new Error('请求数据失败')
}
} catch (e) {
console.error('解析响应数据失败')
}
}
})
},
initElement(result) {
let _self = this
try {
// fixme
const data = JSON.parse(result.data.pageJson)
// const data = result.data.pageJson
if (result) {
defaultConfig = data.elements
defaultWrapConfig = data.page
}
StyleConfig = $.extend(true, [], defaultConfig)
_self.createElement(defaultWrapConfig, StyleConfig)
// 执行自定义代码
const code = data.page.code
code && setTimeout(() => eval(code), 100)
} catch (e) {
console.log(e)
}
},
createElement(wrapElementConfig, elementConfig) {
let _self = this
let isProps = wrapElementConfig.width
if (wrapElementConfig) {
let wrap = $('<div class="land_wrap"></div>')
$('body').prepend(wrap)
_self.initPage(wrapElementConfig, isProps, elementConfig)
}
elementConfig &&
elementConfig.forEach((item, index) => {
switch (item.type) {
case 'button':
new window.TA.RenderButton(item, index, isProps, elementConfig)
break
case 'image':
new window.TA.RenderImage(item, index, isProps, elementConfig)
break
case 'turntable':
new window.TA.RenderTurnCircle(
item,
index,
isProps,
elementConfig
)
break
case 'wechat':
new window.TA.RenderWechat(item, index, isProps)
break
// case 'popup':
// new window.TA.RenderPopup(item, index, isProps);
// break;
case 'mobile':
new window.TA.RenderMobile(item, index, isProps)
break
case 'carousel':
new window.TA.RenderCarousel(item, index, isProps)
break
case 'text':
new window.TA.RenderText(item, index, isProps, elementConfig)
break
case 'address':
new window.TA.RenderAddress(item, index, isProps)
break
case 'download':
new window.TA.RenderDownload(item, index, isProps, elementConfig)
break
case 'form':
new window.TA.RenderForm(item, index, isProps, elementConfig)
break
case 'countdown':
new window.TA.RenderCountdown(item, index, isProps, elementConfig)
break
case 'counter':
new window.TA.RenderCounter(item, index, isProps, elementConfig)
break
case 'QRCode':
new window.TA.RenderQRCode(item, index, isProps, elementConfig)
}
})
},
initPopup(data, isProps, elementConfig) {
let { popupId } = data.autoJump
elementConfig &&
elementConfig.forEach((item, index) => {
if (item.id === popupId) {
this.popId = index
new window.TA.RenderPopup(
item,
index,
isProps,
elementConfig
).showPopup()
$('.land_wrap').css('height', $(window).height())
}
})
},
// 初始化整个背景页面
initPage(data, isProps, elementConfig) {
let self = this
this.style = ''
this.isIpone = navigator.userAgent.indexOf('iPhone')
if (data.title) {
document.title = data.title
}
let {
width,
height,
backgroundColor,
backgroundImage,
backgroundRepeat,
backgroundSize,
autoJump,
name
} = data
this.style += `.land_wrap {
width: ${Util.fitSize(width, isProps)};
height: ${Util.fitSize(height, isProps)};
overflow: hidden;
}
@keyframes scaleAnim {
from {
transform: scale(1);
-webkit-tranform: scale(1);
}
to {
transform: scale(0.9);
-webkit-transform: scale(0.9);
}
}`
$('head').append('<style>' + this.style + '</style>')
// 背景色和背景图铺满屏幕
$('html,body').css({
backgroundColor,
backgroundImage,
backgroundRepeat,
backgroundSize: '100% auto'
})
if (autoJump.enable === 1) {
setTimeout(function() {
commonJumpLogic({
jump: autoJump,
options: { location: '自动跳转', _id: 'page$1234567890' },
popupCallback: () => {
self.initPopup(data, isProps, elementConfig)
}
})
}, autoJump.delay * 1000)
}
// 首页loading逻辑
let imgUrl = backgroundImage.replace(
/url\("([\s\S]*)"\)/,
(_, matches) => matches
)
let removeLoading = () =>
document.getElementById('__loading') &&
document.getElementById('__loading').remove()
if (imgUrl) {
let img = document.createElement('img')
img.src = imgUrl
img.onload = removeLoading
img.onerror = removeLoading
if (img.complete) removeLoading()
} else {
removeLoading()
}
// 兜底删除loading
setTimeout(removeLoading, 2000)
}
}
Template.init()
})()
@import './styles/common.less';
@import '../styles/font.less';
.land_wrap {
position: relative;
.land-circle {
.prize {
position: absolute;
width: 50%;
height: 50%;
transform-origin: right bottom;
.prize-dialog {
position: absolute;
text-align: center;
transform: skewY(-30deg) rotate(-30deg);
}
}
.prize:nth-child(1){
transform:rotate(60deg) skewY(30deg)
}
.prize:nth-child(2){
transform:rotate(120deg) skewY(30deg)
}
.prize:nth-child(3){
transform:rotate(180deg) skewY(30deg)
}
.prize:nth-child(4){
transform:rotate(240deg) skewY(30deg)
}
.prize:nth-child(5){
transform:rotate(300deg) skewY(30deg)
}
.prize:nth-child(6){
transform:rotate(360deg) skewY(30deg)
}
}
.land-form-input {
border: none !important;
}
.land-form-select {
appearance:none;
-moz-appearance:none;
-webkit-appearance:none;
}
}
.clearfix {
zoom: 1;
}
.clearfix:after {
display: table;
clear: both;
content: '';
}
// .land-form-input::-webkit-input-placeholder { /* WebKit browsers */ text-align: right;}
// .land-form-input:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ text-align: right;}
// .land-form-input::-moz-placeholder { /* Mozilla Firefox 19+ but I'm not sure about working */ text-align: right;}
// .land-form-input:-ms-input-placeholder { /* Internet Explorer 10+ */ text-align: right;}
This diff is collapsed.
/* eslint-disable */
// zepto.animate
(function($) {
var prefix = '',
eventPrefix,
vendors = {
Webkit: 'webkit',
Moz: '',
O: 'o'
},
testEl = document.createElement('div'),
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
transform,
transitionProperty,
transitionDuration,
transitionTiming,
transitionDelay,
animationName,
animationDuration,
animationTiming,
animationDelay,
cssReset = {};
function dasherize(str) {
return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase();
}
function normalizeEvent(name) {
return eventPrefix ? eventPrefix + name : name.toLowerCase();
}
$.each(vendors, function(vendor, event) {
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + vendor.toLowerCase() + '-';
eventPrefix = event;
return false;
}
});
transform = prefix + 'transform';
cssReset[(transitionProperty = prefix + 'transition-property')] = cssReset[
(transitionDuration = prefix + 'transition-duration')
] = cssReset[(transitionDelay = prefix + 'transition-delay')] = cssReset[
(transitionTiming = prefix + 'transition-timing-function')
] = cssReset[(animationName = prefix + 'animation-name')] = cssReset[
(animationDuration = prefix + 'animation-duration')
] = cssReset[(animationDelay = prefix + 'animation-delay')] = cssReset[
(animationTiming = prefix + 'animation-timing-function')
] =
'';
$.fx = {
off:
eventPrefix === undefined &&
testEl.style.transitionProperty === undefined,
speeds: {
_default: 400,
fast: 200,
slow: 600
},
cssPrefix: prefix,
transitionEnd: normalizeEvent('TransitionEnd'),
animationEnd: normalizeEvent('AnimationEnd')
};
$.fn.animate = function(properties, duration, ease, callback, delay) {
if ($.isFunction(duration))
(callback = duration), (ease = undefined), (duration = undefined);
if ($.isFunction(ease)) (callback = ease), (ease = undefined);
if ($.isPlainObject(duration))
(ease = duration.easing),
(callback = duration.complete),
(delay = duration.delay),
(duration = duration.duration);
if (duration)
duration =
(typeof duration == 'number'
? duration
: $.fx.speeds[duration] || $.fx.speeds._default) / 1000;
if (delay) delay = parseFloat(delay) / 1000;
return this.anim(properties, duration, ease, callback, delay);
};
$.fn.anim = function(properties, duration, ease, callback, delay) {
var key,
cssValues = {},
cssProperties,
transforms = '',
that = this,
wrappedCallback,
endEvent = $.fx.transitionEnd,
fired = false;
if (duration === undefined) duration = $.fx.speeds._default / 1000;
if (delay === undefined) delay = 0;
if ($.fx.off) duration = 0;
if (typeof properties == 'string') {
// keyframe animation
cssValues[animationName] = properties;
cssValues[animationDuration] = duration + 's';
cssValues[animationDelay] = delay + 's';
cssValues[animationTiming] = ease || 'linear';
endEvent = $.fx.animationEnd;
} else {
cssProperties = [];
// CSS transitions
for (key in properties)
if (supportedTransforms.test(key))
transforms += key + '(' + properties[key] + ') ';
else
(cssValues[key] = properties[key]),
cssProperties.push(dasherize(key));
if (transforms)
(cssValues[transform] = transforms), cssProperties.push(transform);
if (duration > 0 && typeof properties === 'object') {
cssValues[transitionProperty] = cssProperties.join(', ');
cssValues[transitionDuration] = duration + 's';
cssValues[transitionDelay] = delay + 's';
cssValues[transitionTiming] = ease || 'linear';
}
}
wrappedCallback = function(event) {
if (typeof event !== 'undefined') {
if (event.target !== event.currentTarget) return; // makes sure the event didn't bubble from "below"
$(event.target).unbind(endEvent, wrappedCallback);
} else $(this).unbind(endEvent, wrappedCallback); // triggered by setTimeout
fired = true;
!$(this).attr('stop') && $(this).css(cssReset);
callback && callback.call(this);
};
if (duration > 0) {
this.bind(endEvent, wrappedCallback);
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired
setTimeout(function() {
if (fired) return;
wrappedCallback.call(that);
}, (duration + delay) * 1000 + 25);
}
// trigger page reflow so new elements can animate
this.size() && this.get(0).clientLeft;
this.css(cssValues);
if (duration <= 0)
setTimeout(function() {
that.each(function() {
wrappedCallback.call(this);
});
}, 0);
return this;
};
testEl = null;
})(Zepto);
This diff is collapsed.
/* eslint-disable */
/*
* @Author: 周成
* @Date: 2018-03-22 20:11:20
* @Last Modified by: Dec-F (fanxuehui@duiba.com.cn)
* @Last Modified time: 2019-09-02 18:39:24
*/
require('./polyfill');
/**
* Zepto is a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API.
* If you use jQuery, you already know how to use Zepto.
* https://www.npmjs.com/package/zepto
*/
require('./zepto');
/**
* Polyfill to remove click delays on browsers with touch UIs.
* https://www.npmjs.com/package/fastclick
*/
if ('addEventListener' in document) {
document.addEventListener(
'DOMContentLoaded',
function() {
require.ensure([], () => {
const FastClick = require('./fastclick');
FastClick.attach(document.body);
}, 'fastclick');
},
false
);
}
require('render/libs/statistics');
$.fn.longPress = function(fn) {
var timeout = undefined
var $this = this
for (var i = 0; i < $this.length; i++) {
$this[i].addEventListener(
'touchstart',
function(event) {
timeout = setTimeout(fn, 800) // 长按时间超过800ms,则执行传入的方法
},
false
)
$this[i].addEventListener(
'touchend',
function(event) {
clearTimeout(timeout) // 长按时间少于800ms,不会执行传入的方法
},
false
)
}
}
/**
* 推啊所有的window变量都挂载在window.TA下面
*/
window.TA = window.TA || {};
\ No newline at end of file
/* eslint-disable */
/**
* 检查是否支持.webp 格式图片
*
* 支持 webp CDN ?x-oss-process=image/format,webp
* 不支持 CDN ?x-oss-process=image/quality,Q_80
*/
;
(function() {
var urlarr = [];
// localStorage存在且之前没设置过iswebp
if (localStorage && !localStorage.iswebp) {
var cs = document.createElement('canvas');
// 如果不支持canvas则退出
if (cs.getContext && cs.getContext('2d')) {
try {
localStorage.iswebp = (0 === cs.toDataURL('image/webp').indexOf('data:image/webp'));
} catch (e) {
// safari 浏览器无恒模式在低于11版本会有bug
// https://stackoverflow.com/questions/21159301/quotaexceedederror-dom-exception-22-an-attempt-was-made-to-add-something-to-st
console.error(e);
}
}
}
function getOssImg(url) {
if (!url) {
return url;
}
// 不支持localStorage或者没有设置过iswebp或者isweb是false
if (!localStorage || !localStorage.iswebp || localStorage.iswebp === 'false') {
url = url + '?x-oss-process=image/quality,Q_80';
return url;
} else {
// gif 的图片不做处理
urlarr = url.split('.');
if (urlarr.length > 0 && urlarr[urlarr.length - 1] === 'gif') {
return url;
}
url = url + '?x-oss-process=image/format,webp';
return url;
}
}
String.prototype.ossimg = function() {
return getOssImg(this);
};
})();
This diff is collapsed.
This diff is collapsed.
import Util from './utils'
import '../polyfills/string-padStart'
// 未完成
export const NETVeify = ({ triggerDom } = {}) => {
return new Promise((resolve, reject) => {
let yidunWrapper = $(`<div id="captcha"></div>`)
$('.land_wrap').append(yidunWrapper)
let ins = null
window.Loader.async(['//cstaticdun.126.net/load.min.js'], function() {
window.initNECaptcha({
captchaId: '4fd034beb0f0429f856ccc1b0cd71004',
element: `#captcha`,
mode: 'popup',
width: $(window).width() - 50,
onVerify: function(err, data) {
if (err) {
ins.destroy()
$(`#captcha`).remove()
reject(err)
return
}
ins.destroy()
resolve(data)
}
},
function onload(instance) {
ins = instance
// 初始化成功后,用户输入对应用户名和密码,以及完成验证后,直接点击登录按钮即可
instance.refresh()
instance.popUp()
},
function onerror(err) {
ins.destroy()
$(`#captcha`).remove()
reject(err)
// 验证码初始化失败处理逻辑,例如:提示用户点击按钮重新初始化
}
)
})
})
}
export const buryAOId = (selector) => {
window.Loader.async(["//yun.tuisnake.com/h5-mami/insurance/taobao/clipboard.min.js"], function() {
var clipboard = new Clipboard(selector, {
text: function(trigger) {
return `tuia=${Util.getUrlParameter('a_oId')}`
}
});
clipboard.on("success", function(element) { //复制成功的回调
});
clipboard.on('error', function(e) {
});
})
}
export const padStr = any => ''.padStart.call(any, 2, '0')
export const getDayStr = date => `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
export const getTimeStr = date => `${padStr(date.getHours())}:${padStr(date.getMinutes())}:${padStr(date.getSeconds())}`
export const isIphone = navigator.userAgent.indexOf('iPhone') !== -1
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.
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