Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
biz.qingxiao.com
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wangxuelai
biz.qingxiao.com
Commits
f4b452b7
Commit
f4b452b7
authored
Mar 26, 2020
by
baixian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp营销页
parent
38a99787
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1983 additions
and
136 deletions
+1983
-136
check.png
image/theme/check.png
+0
-0
check_active.png
image/theme/check_active.png
+0
-0
api.js
src/common/api.js
+4
-4
createmarke.js
src/models/createmarke.js
+608
-0
createtheme.js
src/models/createtheme.js
+93
-19
index.js
src/models/index.js
+2
-0
AddMarkeModal.js
src/pages/newtheme/components/AddMarkeModal.js
+279
-22
AddMarkeModal.less
src/pages/newtheme/components/AddMarkeModal.less
+199
-0
ChooseClass.js
src/pages/newtheme/components/ChooseClass.js
+84
-60
ChooseClass.less
src/pages/newtheme/components/ChooseClass.less
+82
-0
MarkeEditor.js
src/pages/newtheme/components/MarkeEditor.js
+162
-0
MarkeEditor.less
src/pages/newtheme/components/MarkeEditor.less
+230
-0
MarketingListModal.js
src/pages/newtheme/components/MarketingListModal.js
+29
-5
MarketingListModal.less
src/pages/newtheme/components/MarketingListModal.less
+11
-18
index.js
src/pages/newtheme/jobclock/index.js
+166
-5
createtheme.js
src/services/createtheme.js
+34
-3
No files found.
image/theme/check.png
0 → 100644
View file @
f4b452b7
500 Bytes
image/theme/check_active.png
0 → 100644
View file @
f4b452b7
692 Bytes
src/common/api.js
View file @
f4b452b7
...
...
@@ -11,8 +11,8 @@ if (ENVIRONMENT == 'pro') {
}
else
if
(
ENVIRONMENT
==
'dev'
)
{
// api = 'https://wx.m.shangjiadao.cn/v2/api/';
// dakaapi = 'https://qxapi.qingxiao.online/daka/v3/';
api
=
'http://test.wp53.cn/v2/api/'
;
dakaapi
=
'http://clock.wp53.cn/v3/'
;
api
=
'http
s
://test.wp53.cn/v2/api/'
;
dakaapi
=
'http
s
://clock.wp53.cn/v3/'
;
}
else
if
(
ENVIRONMENT
==
'testenv'
)
{
api
=
'https://test.wp53.cn/v2/api/'
;
dakaapi
=
'https:clock.wp53.cn/v3/'
;
...
...
@@ -182,8 +182,8 @@ export default {
unlockSubjectDetail
:
`
${
dakaapi
}
member/unlock_subject/detail`
,
unlockAddSubjectCount
:
`
${
dakaapi
}
member/unlock_subject/add_subject_count`
,
unlock_student_export
:
`
${
dakaapi
}
member/erp/student/unlock_student_export`
,
classList
:
`
${
dakaapi
}
member/erp/courses`
,
landing_pages
:
`
${
dakaapi
}
member/
erp/
landing_pages`
,
class
Course
List
:
`
${
dakaapi
}
member/erp/courses`
,
landing_pages
:
`
${
dakaapi
}
member/landing_pages`
,
},
getschooluuid
:
`
${
dakaapi
}
member/school_uuid`
,
analyzeCenter
:
{
...
...
src/models/createmarke.js
0 → 100644
View file @
f4b452b7
import
{
routerRedux
}
from
'dva/router'
;
import
{
message
}
from
'antd'
;
import
{
delay
}
from
'redux-saga'
;
import
moment
from
'moment'
;
import
{
LocalStorage
,
SessionStorage
,
isExpired
,
getRandomFilename
,
getAudioDuration
,
getVideoDuration
,
}
from
'../utils/index'
;
import
errorcode
from
'../common/errorcode'
;
import
*
as
uploader
from
'../services/uploader'
;
import
*
as
themeAjax
from
'../services/createtheme'
;
export
default
{
namespace
:
'createmarke'
,
state
:
{
markeParams
:
{
title
:
''
,
name
:
''
,
mobile
:
''
,
img
:
''
,
cover
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2c/sharemoretheme/newunlockbg.png'
,
content
:
[
{
title
:
''
,
content
:
[{
type
:
'text'
,
value
:
'1.全程陪伴练字教学,我们通过听课+作业+点评+群内解答等互动形式,提高学习效果'
+
'
\
n'
+
'2.国家级书法名师亲自授课,指导你快速学习正确的握笔姿势和练字方法'
+
'
\
n'
+
'3.从零基础讲起,适合小白快速入门,课程系统全面,有经验的学员也能得到进一步提高;'
+
'
\
n'
+
'4.我们的每一位同学都将得到一对一教师指导,每天督促学习,陪伴式成长'
+
'适合人群
\
n'
+
'(1)零基础练字学员
\
n'
+
'(2)有一点基础,想提升的学员
\
n'
+
'(3)节假日期间想快速练好字的学员'
,
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/title-1.png'
},
{
type
:
'text'
,
value
:
'洛子帅, 书法练字训练营创始人,杭州阳关灿烂教育
\
r
\
n 教学主管,专业书法教师,已帮助3000多人写好字。'
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/shoubiao.png'
},
],
},
],
},
progressRate
:
0
,
themeAdInfo
:
{
id
:
0
,
title
:
''
,
},
has_subject
:
false
,
// 营销页是否在使用
},
subscriptions
:
{
setup
({
dispatch
,
history
})
{
// eslint-disable-line
},
},
effects
:
{
*
deleteMarke
({
payload
},
{
call
,
put
,
select
})
{
const
{
item
}
=
payload
;
const
{
has_subject
}
=
yield
select
(
state
=>
state
.
createmarke
);
if
(
has_subject
)
{
message
.
error
(
'这个营销页已经有主题使用,不能删除'
,
0.5
);
return
;
}
const
data
=
yield
call
(
themeAjax
.
deleteMarke
,
{
id
:
item
.
id
,
});
if
(
data
.
code
==
200
)
{
message
.
success
(
'删除成功'
,
0.5
);
yield
put
({
type
:
'createtheme/queryLandingList'
,
payload
:
{
params
:
{
},
},
});
}
else
{
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
,
},
});
}
},
*
saveMarke
({
payload
},
{
call
,
put
,
select
})
{
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
markeParams
,
markeLoading
,
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
{
title
,
name
,
mobile
,
callBack
,
}
=
payload
;
if
(
name
.
trim
()
==
''
)
{
message
.
error
(
'请填写老师姓名'
,
0.5
);
return
;
}
if
(
mobile
==
''
&&
markeParams
.
img
==
''
)
{
message
.
error
(
'请添加手机号码或者上传微信二维码'
,
0.5
);
return
;
}
if
(
markeLoading
)
{
return
;
}
yield
put
({
type
:
'updateState'
,
payload
:
{
markeLoading
:
true
,
},
});
const
newParams
=
{
...
markeParams
};
const
loadmessage
=
message
.
loading
(
'保存中...'
,
0
);
const
postFunction
=
(
newParams
.
id
!=
undefined
)
&&
newParams
.
id
!==
0
?
themeAjax
.
editMarke
:
themeAjax
.
addMarke
;
const
data
=
yield
call
(
postFunction
,
Object
.
assign
(
newParams
,
{
school_id
:
sid
,
title
,
name
,
mobile
,
content
:
JSON
.
stringify
(
newParams
.
content
),
},
));
yield
put
({
type
:
'updateState'
,
payload
:
{
markeLoading
:
false
,
},
});
setTimeout
(
loadmessage
);
if
(
data
.
code
===
200
)
{
message
.
success
(
'保存成功'
,
0.5
);
if
(
callBack
&&
(
typeof
callBack
==
'function'
))
{
callBack
();
}
yield
put
({
type
:
'createtheme/queryLandingList'
,
payload
:
{
markeLoading
:
false
,
},
});
yield
put
({
type
:
'updateState'
,
payload
:
{
addMarkeVisible
:
false
,
markeParams
:
{
title
:
''
,
name
:
''
,
mobile
:
''
,
img
:
''
,
cover
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2c/sharemoretheme/newunlockbg.png'
,
content
:
[
{
title
:
''
,
content
:
[{
type
:
'text'
,
value
:
'1.全程陪伴练字教学,我们通过听课+作业+点评+群内解答等互动形式,提高学习效果'
+
'
\
n'
+
'2.国家级书法名师亲自授课,指导你快速学习正确的握笔姿势和练字方法'
+
'
\
n'
+
'3.从零基础讲起,适合小白快速入门,课程系统全面,有经验的学员也能得到进一步提高;'
+
'
\
n'
+
'4.我们的每一位同学都将得到一对一教师指导,每天督促学习,陪伴式成长'
+
'适合人群
\
n'
+
'(1)零基础练字学员
\
n'
+
'(2)有一点基础,想提升的学员
\
n'
+
'(3)节假日期间想快速练好字的学员'
,
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/title-1.png'
},
{
type
:
'text'
,
value
:
'洛子帅, 书法练字训练营创始人,杭州阳关灿烂教育
\
r
\
n 教学主管,专业书法教师,已帮助3000多人写好字。'
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/shoubiao.png'
},
],
},
],
},
},
});
}
else
{
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
,
},
});
}
},
*
editMarke
({
payload
},
{
call
,
put
,
select
})
{
const
{
item
}
=
payload
;
const
data
=
yield
call
(
themeAjax
.
markeDetail
,
{
id
:
item
.
id
,
});
if
(
data
.
code
==
200
)
{
yield
put
({
type
:
'updateState'
,
payload
:
{
has_subject
:
data
.
data
.
has_subject
,
markeParams
:
{
id
:
data
.
data
.
id
,
title
:
data
.
data
.
title
,
name
:
data
.
data
.
name
,
mobile
:
data
.
data
.
mobile
,
img
:
data
.
data
.
img
,
cover
:
data
.
data
.
cover
,
content
:
JSON
.
parse
(
data
.
data
.
content
),
},
addMarkeVisible
:
true
,
},
});
}
},
*
markeAddPlate
({
payload
},
{
call
,
put
,
select
})
{
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
markeParams
.
content
.
push
({
title
:
''
,
content
:
[
],
});
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
markeAddText
({
payload
},
{
call
,
put
,
select
})
{
const
{
index
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
markeParams
.
content
[
index
].
content
.
push
({
type
:
'text'
,
value
:
''
,
});
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
markeChangeSize
({
payload
},
{
call
,
put
,
select
})
{
const
{
textValue
,
index
,
sort
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
markeParams
.
content
[
sort
].
content
[
index
]
=
{
type
:
'text'
,
value
:
textValue
,
};
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
markeChangeTitle
({
payload
},
{
call
,
put
,
select
})
{
const
{
textValue
,
sort
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
markeParams
.
content
[
sort
].
title
=
textValue
;
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
markeMoveContent
({
payload
},
{
call
,
put
,
select
})
{
const
{
index
,
direction
,
sort
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
let
newContent
=
[];
newContent
=
markeParams
.
content
[
sort
].
content
;
const
currentImg
=
newContent
[
index
];
const
preImg
=
newContent
[
index
-
1
];
const
afterImg
=
newContent
[
index
+
1
];
if
(
direction
==
'up'
)
{
newContent
.
splice
(
index
-
1
,
2
,
currentImg
,
preImg
);
}
else
if
(
direction
==
'down'
)
{
newContent
.
splice
(
index
,
2
,
afterImg
,
currentImg
);
}
markeParams
.
content
[
sort
].
content
=
newContent
;
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
deleteMarkeImg
({
payload
},
{
call
,
put
,
select
})
{
const
{
index
,
sort
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
newContent
=
markeParams
.
content
[
sort
].
content
;
newContent
.
splice
(
index
,
1
);
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
markeDeletePlate
({
payload
},
{
call
,
put
,
select
})
{
const
{
sort
}
=
payload
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
newContent
=
markeParams
.
content
;
newContent
.
splice
(
sort
,
1
);
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
},
*
queryimagesignature
({
payload
},
{
call
,
put
,
select
})
{
const
{
userInfo
,
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
files
,
uploadtype
,
contentSort
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
const
REGEXP_VIDEO
=
/^image
\/\w
+/
;
const
params
=
{
type
:
1
,
token
:
userInfo
.
token
,
schoolId
:
sid
};
let
signature
=
{};
if
(
file
&&
(
REGEXP_VIDEO
.
test
(
file
.
type
)
||
file
.
type
===
''
))
{
const
uploaderLoading
=
message
.
loading
(
'正在上传图片'
);
const
uploadSignature
=
yield
call
(
uploader
.
uploadImageSignature
,
params
);
if
(
uploadSignature
.
code
==
200
)
{
signature
=
uploadSignature
.
data
;
yield
put
({
type
:
'uploadimage'
,
payload
:
{
signature
,
files
,
uploaderLoading
,
uploadtype
,
contentSort
,
},
});
}
else
{
setTimeout
(
uploaderLoading
);
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
code
:
uploadSignature
,
},
});
}
}
},
*
uploadimage
({
payload
},
{
call
,
put
,
select
})
{
const
{
signature
,
files
,
uploaderLoading
,
uploadtype
,
contentSort
,
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
filename
=
`
${
signature
.
dir
}${
getRandomFilename
(
file
.
name
)}
`
;
const
params
=
{
key
:
filename
,
policy
:
signature
.
policy
,
OSSAccessKeyId
:
signature
.
accessid
,
signature
:
signature
.
signature
,
file
,
url
:
signature
.
host
,
};
const
uploadImg
=
yield
call
(
uploader
.
uploadImg
,
params
);
const
imageUrl
=
filename
;
if
(
uploadtype
==
'uploadImgMarke'
)
{
markeParams
.
content
[
contentSort
].
content
.
push
({
type
:
'img'
,
value
:
imageUrl
,
});
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
}
else
if
(
uploadtype
==
'quickCover'
)
{
markeParams
.
cover
=
imageUrl
;
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
}
else
if
(
uploadtype
==
'uploadWxImg'
)
{
markeParams
.
img
=
imageUrl
;
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
}
setTimeout
(
uploaderLoading
);
},
*
queryvoicesignature
({
payload
},
{
call
,
put
,
select
})
{
const
{
avatorUploader
}
=
yield
select
(
state
=>
state
.
uploader
);
const
{
userInfo
,
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
files
,
uploadtype
,
orgIndex
,
contentSort
,
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
const
REGEXP_VIDEO
=
/^audio
\/\w
+/
;
const
params
=
{
type
:
2
,
token
:
userInfo
.
token
,
schoolId
:
sid
};
let
signature
=
{};
if
(
file
&&
(
REGEXP_VIDEO
.
test
(
file
.
type
)
||
file
.
type
===
''
))
{
const
uploadSignature
=
yield
call
(
uploader
.
uploadVideoSignature
,
params
);
signature
=
uploadSignature
.
data
;
yield
put
({
type
:
'uploadvoice'
,
payload
:
{
signature
,
avatorUploader
,
files
,
uploadtype
,
orgIndex
,
contentSort
,
},
});
}
},
*
uploadvoice
({
payload
},
{
call
,
put
,
select
})
{
const
{
signature
,
files
,
uploadtype
,
orgIndex
,
contentSort
,
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
const
uploaderLoading
=
message
.
loading
(
'正在上传录音'
,
0
);
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
filename
=
`
${
signature
.
dir
}${
getRandomFilename
(
file
.
name
)}
`
;
const
params
=
{
key
:
filename
,
policy
:
signature
.
policy
,
OSSAccessKeyId
:
signature
.
accessid
,
signature
:
signature
.
signature
,
file
,
url
:
signature
.
host
,
};
const
uploadImg
=
yield
call
(
uploader
.
uploadVideo
,
params
);
const
videoUrl
=
filename
;
document
.
getElementById
(
'uploadVoice'
).
value
=
''
;
if
(
uploadtype
==
'uploadVoiceMarke'
)
{
const
duration
=
yield
call
(
getAudioDuration
,
file
);
markeParams
.
content
[
contentSort
].
content
.
push
({
type
:
'voice'
,
value
:
videoUrl
,
duration
,
});
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
}
setTimeout
(
uploaderLoading
);
},
*
queryvideosignature
({
payload
},
{
call
,
put
,
select
})
{
const
{
avatorUploader
}
=
yield
select
(
state
=>
state
.
uploader
);
const
{
userInfo
,
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
files
,
uploadtype
,
orgIndex
,
contentSort
,
progressCallBack
,
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
if
(
file
&&
file
.
size
>
2000
*
1024
*
1024
)
{
message
.
error
(
'视频的大小不能超过2GB,请重新上传'
,
1
);
return
;
}
const
REGEXP_VIDEO
=
/^video
\/\w
+/
;
const
params
=
{
type
:
1
,
token
:
userInfo
.
token
,
schoolId
:
sid
};
let
signature
=
{};
if
(
file
&&
(
REGEXP_VIDEO
.
test
(
file
.
type
)
||
file
.
type
===
''
))
{
const
uploadSignature
=
yield
call
(
uploader
.
uploadVideoSignature
,
params
);
signature
=
uploadSignature
.
data
;
if
(
uploadSignature
.
code
==
200
)
{
yield
put
({
type
:
'uploadvideo'
,
payload
:
{
signature
,
avatorUploader
,
files
,
uploadtype
,
orgIndex
,
contentSort
,
progressCallBack
,
},
});
}
else
{
yield
put
({
type
:
'updateState'
,
payload
:
{
progressRate
:
0
,
},
});
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
:
uploadSignature
,
},
});
}
}
},
*
uploadvideo
({
payload
},
{
call
,
put
,
select
})
{
const
{
signature
,
files
,
uploadtype
,
orgIndex
,
contentSort
,
progressCallBack
,
}
=
payload
;
const
file
=
files
.
files
?
files
.
files
[
0
]
:
null
;
const
{
markeParams
}
=
yield
select
(
state
=>
state
.
createmarke
);
const
filename
=
`
${
signature
.
dir
}${
getRandomFilename
(
file
.
name
)}
`
;
const
params
=
{
key
:
filename
,
policy
:
signature
.
policy
,
OSSAccessKeyId
:
signature
.
accessid
,
signature
:
signature
.
signature
,
file
,
url
:
signature
.
host
,
callback
:
signature
.
callback
,
};
document
.
getElementById
(
'uploadVideo'
).
value
=
''
;
const
uploadImg
=
yield
call
(
uploader
.
uploadVideo
,
params
,
(
res
)
=>
{
progressCallBack
(
res
);
});
const
videoUrl
=
filename
;
if
(
JSON
.
stringify
(
uploadImg
)
==
'{"status":"ok"}'
)
{
yield
put
({
type
:
'updateState'
,
payload
:
{
progressRate
:
0
,
},
});
if
(
uploadtype
==
'uploadVideoMarke'
)
{
const
duration
=
yield
call
(
getVideoDuration
,
file
);
markeParams
.
content
[
contentSort
].
content
.
push
({
type
:
'video'
,
value
:
videoUrl
,
duration
,
});
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
...
markeParams
},
},
});
}
}
else
{
yield
put
({
type
:
'updateState'
,
payload
:
{
progressRate
:
0
,
},
});
message
.
error
(
'上传失败'
,
1
);
}
},
*
pageInit
({
payload
},
{
call
,
put
,
select
})
{
yield
put
({
type
:
'updateState'
,
payload
:
{
markeParams
:
{
title
:
''
,
name
:
''
,
mobile
:
''
,
img
:
''
,
cover
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2c/sharemoretheme/newunlockbg.png'
,
content
:
[
{
title
:
''
,
content
:
[{
type
:
'text'
,
value
:
'1.全程陪伴练字教学,我们通过听课+作业+点评+群内解答等互动形式,提高学习效果'
+
'
\
n'
+
'2.国家级书法名师亲自授课,指导你快速学习正确的握笔姿势和练字方法'
+
'
\
n'
+
'3.从零基础讲起,适合小白快速入门,课程系统全面,有经验的学员也能得到进一步提高;'
+
'
\
n'
+
'4.我们的每一位同学都将得到一对一教师指导,每天督促学习,陪伴式成长'
+
'适合人群
\
n'
+
'(1)零基础练字学员
\
n'
+
'(2)有一点基础,想提升的学员
\
n'
+
'(3)节假日期间想快速练好字的学员'
,
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/title-1.png'
},
{
type
:
'text'
,
value
:
'洛子帅, 书法练字训练营创始人,杭州阳关灿烂教育
\
r
\
n 教学主管,专业书法教师,已帮助3000多人写好字。'
},
{
type
:
'img'
,
value
:
'https://cdn.img.shangjiadao.cn/qingxiao/daka/images/2b/themeeditorlock/shoubiao.png'
},
],
},
],
},
progressRate
:
0
,
markeLoading
:
false
,
has_subject
:
false
,
},
});
},
},
reducers
:
{
save
(
state
,
action
)
{
return
{
...
state
,
...
action
.
payload
};
},
updateState
(
state
,
action
)
{
return
{
...
state
,
...
action
.
payload
};
},
},
};
src/models/createtheme.js
View file @
f4b452b7
...
...
@@ -19,7 +19,6 @@ import * as themeAjax from '../services/createtheme';
import
*
as
classMgtAjax
from
'../services/classmgt'
;
import
*
as
uploader
from
'../services/uploader'
;
import
{
calendar
}
from
'../utils/calendar'
;
import
*
as
goodsAjax
from
'../services/integral'
;
import
*
as
commonAjax
from
'../services/common'
;
import
exportExcel
from
'../utils/exportexcel'
;
export
default
{
...
...
@@ -175,7 +174,21 @@ export default {
themeAdInfo
:
{
id
:
0
,
title
:
''
,
},
// 打卡营销页
},
landingParams
:
{
page
:
1
,
perPage
:
5
,
},
landingTotal
:
0
,
clockClassVisible
:
false
,
clockClassList
:
[],
clockClassParams
:
{
page
:
1
,
perPage
:
299
,
extra
:
'class'
,
},
classes
:
[],
chooseClasses
:
[],
},
subscriptions
:
{
setup
({
dispatch
,
history
})
{
// eslint-disable-line
...
...
@@ -183,10 +196,38 @@ export default {
},
effects
:
{
*
queryInfo
({
payload
},
{
call
,
put
,
select
})
{
yield
put
({
type
:
'querymemberinfo'
,
});
yield
delay
(
500
);
yield
put
({
type
:
'queryLandingList'
,
payload
:
{
params
:
{},
},
});
yield
put
({
type
:
'queryCourseClassList'
,
payload
:
{
params
:
{},
},
});
},
*
querymemberinfo
({
payload
},
{
call
,
put
,
select
})
{
// 释放该页面存储的所有状态
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
nickname
=
yield
call
(
commonAjax
.
myNickname
,
{
school_id
:
sid
,
});
if
(
nickname
.
code
==
200
)
{
console
.
log
(
nickname
.
data
,
'nickname.data'
);
yield
put
({
type
:
'updateState'
,
payload
:
{
schoolUserInfo
:
nickname
.
data
,
},
});
}
},
*
tabChange
({
payload
},
{
call
,
put
,
select
})
{
const
{
tabIndex
}
=
payload
;
yield
put
({
...
...
@@ -2258,20 +2299,31 @@ export default {
}
},
// 查询班级列表
*
query
My
ClassList
({
payload
},
{
call
,
put
,
select
})
{
*
query
Course
ClassList
({
payload
},
{
call
,
put
,
select
})
{
const
{
params
}
=
payload
;
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
classListData
=
yield
call
(
themeAjax
.
getClassList
,
Object
.
assign
({
const
{
clockClassParams
,
schoolUserInfo
}
=
yield
select
(
state
=>
state
.
createtheme
);
const
newParams
=
Object
.
assign
({
school_id
:
sid
,
// page: 1,
// perPage: 1000,
extra
:
'class'
,
},
params
));
},
clockClassParams
,
params
);
const
classListData
=
yield
call
(
themeAjax
.
getCourseClassList
,
newParams
);
if
(
classListData
.
code
==
200
)
{
classListData
.
data
.
list
.
forEach
((
item
)
=>
{
const
teachers
=
[];
item
.
classes
.
forEach
((
ele
)
=>
{
const
name
=
ele
.
school_teachers
.
find
(
e
=>
e
.
id
==
schoolUserInfo
.
schoolTeacher
.
id
);
if
(
name
)
{
ele
.
school_teachers
.
unshift
(
name
);
}
// eslint-disable-next-line no-param-reassign
ele
.
school_teachers
=
[...
new
Set
(
ele
.
school_teachers
)];
});
});
yield
put
({
type
:
'updateState'
,
payload
:
{
classList
:
(
classListData
.
data
&&
classListData
.
data
.
list
)
||
[],
clockClassList
:
(
classListData
.
data
&&
classListData
.
data
.
list
)
||
[],
clockClassParams
:
{
...
newParams
},
},
});
}
else
{
...
...
@@ -2285,19 +2337,25 @@ export default {
},
// 获取营销页列表
*
queryLandingList
({
payload
},
{
call
,
put
,
select
})
{
const
{
params
}
=
payload
;
const
{
landingParams
}
=
yield
select
(
state
=>
state
.
createtheme
);
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
data
=
yield
call
(
themeAjax
.
getlandingList
,
{
const
newParams
=
Object
.
assign
(
{
school_id
:
sid
,
});
},
landingParams
,
params
);
const
data
=
yield
call
(
themeAjax
.
getlandingList
,
newParams
);
if
(
data
.
code
==
200
)
{
data
.
data
.
list
.
forEach
((
ele
)
=>
{
let
content
=
[];
let
_
content
=
[];
try
{
content
=
JSON
.
parse
(
ele
.
content
);
_
content
=
JSON
.
parse
(
ele
.
content
);
}
catch
(
e
)
{
content
=
[];
_
content
=
[];
}
const
textobj
=
content
.
find
(
ele
=>
ele
.
type
==
'text'
&&
ele
.
value
!=
''
);
let
textobj
=
''
;
_content
.
forEach
((
item
,
index
)
=>
{
textobj
=
item
.
content
&&
item
.
content
.
find
(
item
=>
item
.
type
==
'text'
&&
item
.
value
!=
''
);
});
// eslint-disable-next-line no-param-reassign
ele
.
desc
=
textobj
?
textobj
.
value
:
''
;
});
...
...
@@ -2305,6 +2363,8 @@ export default {
type
:
'updateState'
,
payload
:
{
landingList
:
(
data
.
data
&&
data
.
data
.
list
)
||
[],
landingParams
:
{
...
newParams
},
landingTotal
:
(
data
.
data
&&
data
.
data
.
total
)
||
0
,
},
});
}
else
{
...
...
@@ -2467,10 +2527,24 @@ export default {
subjectList
:
[],
storeImg
:
''
,
progressRate
:
0
,
},
themeAdInfo
:
{
id
:
0
,
title
:
''
,
themeAdInfo
:
{
id
:
0
,
title
:
''
,
},
landingParams
:
{
page
:
1
,
perPage
:
5
,
},
landingTotal
:
0
,
clockClassVisible
:
false
,
clockClassList
:
[],
clockClassParams
:
{
page
:
1
,
perPage
:
299
,
extra
:
'class'
,
},
classes
:
[],
chooseClasses
:
[],
},
});
},
...
...
src/models/index.js
View file @
f4b452b7
...
...
@@ -57,6 +57,7 @@ import crmdetail from './crmdetail';
import
holidays
from
'./holidays'
;
import
officialtheme
from
'./officialtheme'
;
import
liveclass
from
'./liveclass'
;
import
createmarke
from
'./createmarke'
;
export
default
{
loginModel
,
indexstaicModel
,
...
...
@@ -108,4 +109,5 @@ export default {
holidays
,
officialtheme
,
liveclass
,
createmarke
,
};
src/pages/newtheme/components/AddMarkeModal.js
View file @
f4b452b7
...
...
@@ -4,10 +4,12 @@ import {
Form
,
Input
,
Modal
,
Checkbox
,
Checkbox
,
message
,
Icon
,
}
from
'antd'
;
import
pageStyle
from
'./
ChooseClass
.less'
;
import
pageStyle
from
'./
AddMarkeModal
.less'
;
import
{
pageIn
,
hasBtnPower
,
imagify
,
ossVideofy
}
from
'../../../utils/index'
;
import
MarkeEditor
from
'../components/MarkeEditor'
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
class
AddMarkeModal
extends
React
.
Component
{
...
...
@@ -22,39 +24,190 @@ class AddMarkeModal extends React.Component {
}
componentWillUnmount
()
{
// 卸载
}
close
=
()
=>
{
const
{
close
,
form
}
=
this
.
props
;
close
();
form
.
resetFields
();
markeUploadImg
=
(
e
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/queryimagesignature'
,
payload
:
{
files
:
e
.
target
,
uploadtype
:
'uploadImgMarke'
,
contentSort
:
sort
,
},
});
}
markeUploadVideo
=
(
e
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/queryvideosignature'
,
payload
:
{
files
:
e
.
target
,
uploadtype
:
'uploadVideoMarke'
,
contentSort
:
sort
,
progressCallBack
(
res
)
{
dispatch
({
type
:
'createmarke/updateState'
,
payload
:
{
progressRate
:
((
res
.
loaded
/
res
.
total
)
*
100
).
toFixed
(
0
),
},
});
},
},
});
}
markeUploadVoice
=
(
e
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/queryvoicesignature'
,
payload
:
{
files
:
e
.
target
,
uploadtype
:
'uploadVoiceMarke'
,
contentSort
:
sort
,
},
});
}
markeChangeSize
=
(
e
,
index
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
const
textValue
=
e
.
target
.
value
;
if
(
textValue
.
length
>
500
)
{
message
.
error
(
'最多500字'
,
0.5
);
return
;
}
dispatch
({
type
:
'createmarke/markeChangeSize'
,
payload
:
{
textValue
,
index
,
sort
,
},
});
}
markeChangeTitle
=
(
e
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
const
textValue
=
e
.
target
.
value
;
if
(
textValue
.
length
>
20
)
{
message
.
warning
(
'最多20字'
,
0.5
);
return
;
}
dispatch
({
type
:
'createmarke/markeChangeTitle'
,
payload
:
{
textValue
,
sort
,
},
});
}
deleteMarkeImg
=
(
index
,
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/deleteMarkeImg'
,
payload
:
{
index
,
sort
,
},
});
}
markeAddText
=
(
index
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/markeAddText'
,
payload
:
{
index
,
},
});
}
markeAddPlate
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/markeAddPlate'
,
payload
:
{
},
});
}
markeMoveContent
=
(
index
,
sort
,
direction
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/markeMoveContent'
,
payload
:
{
index
,
sort
,
direction
,
},
});
}
markeDeletePlate
=
(
sort
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/markeDeletePlate'
,
payload
:
{
sort
,
},
});
}
uploadCover
=
(
e
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/queryimagesignature'
,
payload
:
{
files
:
e
.
target
,
uploadtype
:
'quickCover'
,
},
});
}
uploadWxImg
=
(
e
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/queryimagesignature'
,
payload
:
{
files
:
e
.
target
,
uploadtype
:
'uploadWxImg'
,
},
});
}
save
=
(
e
)
=>
{
const
{
save
,
form
}
=
this
.
props
;
const
{
dispatch
,
form
}
=
this
.
props
;
e
.
preventDefault
();
this
.
props
.
form
.
validateFields
((
err
,
values
)
=>
{
if
(
!
err
)
{
const
{
is_encrypt
,
password
,
name
,
mobile
,
title
,
}
=
values
;
save
({
is_encrypt
,
password
,
},
()
=>
{
form
.
resetFields
();
dispatch
({
type
:
'createmarke/saveMarke'
,
payload
:
{
title
,
name
,
mobile
,
callBack
:
()
=>
{
form
.
resetFields
();
},
},
});
}
});
}
close
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/updateState'
,
payload
:
{
addMarkeVisible
:
false
,
},
});
}
render
()
{
const
{
visible
,
form
:
{
getFieldDecorator
,
getFieldValue
},
replyLoading
,
markeLoading
,
markeParams
,
progressRate
,
}
=
this
.
props
;
const
formItemModalLineLayout
=
{
labelCol
:
{
xs
:
{
span
:
24
},
sm
:
{
span
:
3
},
sm
:
{
span
:
4
},
},
wrapperCol
:
{
xs
:
{
span
:
24
},
...
...
@@ -66,26 +219,26 @@ class AddMarkeModal extends React.Component {
visible
=
{
visible
}
maskClosable
=
{
false
}
zIndex
=
{
110
}
width
=
{
6
00
}
width
=
{
9
00
}
bodyStyle
=
{{
padding
:
'10px 20px'
,
}}
title
=
"打卡营销页设置"
closable
=
{
false
}
onCancel
=
{
this
.
close
}
okText
=
"保存"
cancelText
=
"取消"
confirmLoading
=
{
reply
Loading
}
confirmLoading
=
{
marke
Loading
}
onOk
=
{
this
.
save
}
>
<
Form
className
=
{
pageStyle
.
modalform
}
labelAlign
=
"left"
onSubmit
=
{
this
.
save
}
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"模板名称"
>
{
getFieldDecorator
(
'title'
,
{
initialValue
:
''
,
initialValue
:
markeParams
.
title
,
rules
:
[
{
required
:
true
,
message
:
'请输入模板名称'
,
whitespace
:
true
,
},
],
})(
...
...
@@ -93,6 +246,97 @@ class AddMarkeModal extends React.Component {
)}
<
span
className
=
"ant-form-text"
>
模板名称不会在打卡营销页展示
<
/span
>
<
/FormItem
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"模板海报"
>
<
div
className
=
{
pageStyle
.
photoWrap
}
>
<
div
className
=
{
pageStyle
.
defaultImgBox
}
>
<
img
className
=
{
pageStyle
.
defaultImg
}
src
=
{
imagify
(
markeParams
.
cover
)}
alt
=
""
/>
<
div
className
=
{
pageStyle
.
chooseType
}
>
<
div
className
=
{
pageStyle
.
material
}
>
<
img
src
=
{
`
${
__IMGCDN__
}
course/upload_icon1.png`
}
alt
=
"上传图片"
/>
<
div
className
=
{
pageStyle
.
tip
}
>
上传图片
<
/div
>
<
input
type
=
"file"
className
=
{
pageStyle
.
uploadInput
}
id
=
"upload1"
accept
=
"image/png, image/jpeg"
onChange
=
{(
e
)
=>
{
this
.
uploadCover
(
e
);
}}
/
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
span
className
=
"ant-form-text"
>
图片建议格式位
JPG
/
PNG
,尺寸
750
*
422
<
/span
>
<
/FormItem
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"打卡概述"
>
<
div
className
=
{
pageStyle
.
ediorWrap
}
>
<
MarkeEditor
commentParams
=
{
markeParams
}
editorUploadImg
=
{
this
.
markeUploadImg
}
editorUploadAudio
=
{
this
.
markeUploadVideo
}
editorUploadVoice
=
{
this
.
markeUploadVoice
}
editorChange
=
{
this
.
markeChangeSize
}
deleteThemeImg
=
{
this
.
deleteMarkeImg
}
moveContent
=
{
this
.
markeMoveContent
}
editorAddText
=
{
this
.
markeAddText
}
editorAddPlate
=
{
this
.
markeAddPlate
}
editorChangeTitle
=
{
this
.
markeChangeTitle
}
editorDeletePlate
=
{
this
.
markeDeletePlate
}
progressRate
=
{
progressRate
}
/
>
<
/div
>
<
/FormItem
>
<
div
className
=
{
pageStyle
.
teahcerTip
}
>
老师联系方式
<
span
>
让意向家长方便添加老师微信
<
/span></
div
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"老师姓名"
>
{
getFieldDecorator
(
'name'
,
{
initialValue
:
markeParams
.
name
,
})(
<
Input
maxLength
=
{
50
}
placeholder
=
"请输入老师姓名 例如: 方老师"
style
=
{{
width
:
435
}}
/>
,
)}
<
/FormItem
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"手机号码"
>
{
getFieldDecorator
(
'mobile'
,
{
rules
:
[
{
pattern
:
/^1
[
3456789
]{1}[
0-9
]{9}
$/
,
message
:
'请输入正确的手机号码!'
},
{
max
:
11
,
message
:
'手机号长度为11位!'
},
],
initialValue
:
markeParams
.
mobile
,
})(
<
Input
maxLength
=
{
50
}
placeholder
=
"请输入手机号码(可选)"
style
=
{{
width
:
435
}}
/>
,
)}
<
/FormItem
>
<
FormItem
{...
formItemModalLineLayout
}
label
=
"微信号"
>
<
div
className
=
{
pageStyle
.
uploadFlex
}
>
{
markeParams
.
img
?
<
div
className
=
{
pageStyle
.
wxImg
}
>
<
img
src
=
{
imagify
(
markeParams
.
img
)}
alt
=
""
/>
<
/div>
:
<
div
className
=
{
pageStyle
.
wxWrap
}
>
<
Icon
type
=
"plus"
style
=
{{
fontSize
:
32
,
color
:
'#999'
}}
/
>
<
input
type
=
"file"
className
=
{
pageStyle
.
uploadInput
}
id
=
"upload1"
accept
=
"image/png, image/jpeg"
onChange
=
{(
e
)
=>
{
this
.
uploadWxImg
(
e
);
}}
/
>
<
/div
>
}
{
markeParams
.
img
&&
<
div
className
=
{
pageStyle
.
uploadText
}
>
<
input
type
=
"file"
className
=
{
pageStyle
.
uploadInput
}
id
=
"upload1"
accept
=
"image/png, image/jpeg"
onChange
=
{(
e
)
=>
{
this
.
uploadWxImg
(
e
);
}}
/>更换二维
码
<
/div
>
}
<
/div
>
<
span
className
=
"ant-form-text"
>
上传老师微信二维码
<
/span
>
<
/FormItem
>
<
/Form
>
<
/Modal
>
);
...
...
@@ -103,6 +347,19 @@ AddMarkeModal.propTypes = {
};
const
AddMarkeModalForm
=
Form
.
create
()(
AddMarkeModal
);
export
default
connect
()(
AddMarkeModalForm
);
function
mapStateToProps
(
state
)
{
const
{
markeParams
,
themeAdInfo
,
progressRate
,
markeLoading
,
}
=
state
.
createmarke
;
return
{
themeAdInfo
,
markeParams
,
progressRate
,
markeLoading
,
};
}
export
default
connect
(
mapStateToProps
)(
AddMarkeModalForm
);
src/pages/newtheme/components/AddMarkeModal.less
0 → 100644
View file @
f4b452b7
.photoWrap {
display: inline-flex;
width: 375px;
height: 211px;
position: relative;
margin-right: 30px;
.defaultImgBox {
width: 375px;
height: 211px;
cursor: pointer;
.defaultImg {
width: 100%;
height: 100%;
border-radius: 6px;
}
&:hover .chooseType {
opacity: 1;
z-index: 0;
}
}
.chooseType {
position: absolute;
width: 100%;
height: 100%;
bottom: 0;
left: 0;
opacity: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: -10;
background: rgba(0,0,0,0.7);
transition: opacity .3s linear;
border-radius: 6px;
}
.photoCover {
width: 375px;
height: 211px;
position: relative;
&:hover .chooseType {
opacity: 1;
z-index: 0;
}
&>img {
width: 424px;
height: 230px;
border-radius: 6px;
}
.coverDelete {
font-size: 22px;
color: #d5d5d5;
cursor: pointer;
position: absolute;
right: -25px;
top: 0;
}
.coverDelete:hover {
color: #fa4f53;
}
}
.material {
width: 50%;
height: 211px;
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-right: 11px;
cursor: pointer;
position: relative;
.uploadInput {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
cursor: pointer;
z-index: 1;
}
.tip {
font-size:14px;
font-weight:400;
color:#fff;
}
&>img {
width: 32px;
height: 32px;
margin-top: 10px;
}
}
}
.ediorWrap {
width: 650px;
min-height: 319px;
padding: 20px;
background-color: #fafafa;
-webkit-border-radius: 4px;
border-radius: 4px;
border: 1px solid #eee;
max-height: 350px;
overflow-y: scroll;
}
::-webkit-scrollbar {
width: 8px;
height: 10px;
background-color: rgba(0,0,0,0);
}
::-webkit-scrollbar-thumb {
border-radius: 6px;
background-color: rgba(0,0,0,.2);
transition: all .4s ease;
-moz-transition: all .4s ease;
-webkit-transition: all .4s ease;
-o-transition: all .4s ease;
}
.modalform {
:global {
.ant-form-item {
margin-bottom: 10px;
}
}
}
.teahcerTip {
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 600;
line-height: 24px;
color: rgba(0, 0, 0, 0.85);
margin-bottom: 20px;
}
.uploadFlex {
display: flex;
align-items: center;
.wxImg {
width: 100px;
height: 100px;
margin-right: 20px;
position: relative;
&>img {
width: 100%;
height: 100%;
}
.uploadInput {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
cursor: pointer;
z-index: 1;
}
}
.wxWrap {
width: 100px;
height: 100px;
position: relative;
cursor: pointer;
background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 4px;
cursor: pointer;
-webkit-transition: border-color 0.3s ease;
transition: border-color 0.3s ease;
line-height: 100px;
text-align: center;
&:hover {
border-color: #1890ff;
}
.uploadInput {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
cursor: pointer;
z-index: 1;
}
}
.uploadText {
position: relative;
color: #16b0fd;
font-size: 16px;
.uploadInput {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
cursor: pointer;
z-index: 1;
}
}
}
src/pages/newtheme/components/ChooseClass.js
View file @
f4b452b7
...
...
@@ -5,6 +5,7 @@ import {
Input
,
Modal
,
Checkbox
,
Button
,
}
from
'antd'
;
import
pageStyle
from
'./ChooseClass.less'
;
import
{
pageIn
,
hasBtnPower
,
imagify
,
ossVideofy
}
from
'../../../utils/index'
;
...
...
@@ -22,89 +23,112 @@ class ChooseClassModal extends React.Component {
}
componentWillUnmount
()
{
// 卸载
}
indexOf
=
(
arr
,
item
)
=>
{
return
arr
.
indexOf
(
item
);
}
close
=
()
=>
{
const
{
close
,
form
}
=
this
.
props
;
close
();
form
.
resetFields
();
}
save
=
(
e
)
=>
{
const
{
save
,
form
}
=
this
.
props
;
e
.
preventDefault
();
this
.
props
.
form
.
validateFields
((
err
,
values
)
=>
{
if
(
!
err
)
{
const
{
is_encrypt
,
password
,
}
=
values
;
save
({
is_encrypt
,
password
,
},
()
=>
{
form
.
resetFields
();
});
}
});
changeCourseClass
=
(
type
)
=>
{
const
{
changeCourseClass
}
=
this
.
props
;
changeCourseClass
(
type
);
}
chooseClass
=
(
item
)
=>
{
const
{
classes
,
chooseClasses
,
choose
}
=
this
.
props
;
const
classesIndex
=
classes
.
indexOf
(
item
.
id
);
const
chooseClassesIndex
=
chooseClasses
.
findIndex
(
ele
=>
ele
.
id
==
item
.
id
);
if
(
classesIndex
==
-
1
)
{
classes
.
push
(
item
.
id
);
}
else
{
classes
.
splice
(
classesIndex
,
1
);
}
if
(
chooseClassesIndex
==
-
1
)
{
chooseClasses
.
push
({
title
:
item
.
title
,
id
:
item
.
id
,
});
}
else
{
chooseClasses
.
splice
(
classesIndex
,
1
);
}
choose
({
classes
,
chooseClasses
});
}
saveClasses
=
()
=>
{
const
{
classes
,
saveClasses
}
=
this
.
props
;
saveClasses
(
classes
);
}
render
()
{
const
{
visible
,
form
:
{
getFieldDecorator
,
getFieldValue
},
record
,
replyLoading
,
info
,
list
,
clockClassParams
,
classes
,
chooseClasses
,
}
=
this
.
props
;
const
formItemModalLineLayout
=
{
labelCol
:
{
xs
:
{
span
:
24
},
sm
:
{
span
:
3
},
},
wrapperCol
:
{
xs
:
{
span
:
24
},
sm
:
{
span
:
20
},
},
};
return
(
<
Modal
visible
=
{
visible
}
maskClosable
=
{
false
}
zIndex
=
{
110
}
width
=
{
600
}
bodyStyle
=
{{
padding
:
'
4
0px 20px'
,
padding
:
'
1
0px 20px'
,
}}
title
=
"修改密码"
closable
=
{
false
}
title
=
"选择参与打卡的班级"
onCancel
=
{
this
.
close
}
okText
=
"保存"
cancelText
=
"取消"
confirmLoading
=
{
replyLoading
}
onOk
=
{
this
.
save
}
footer
=
{
null
}
>
<
Form
className
=
{
pageStyle
.
modalform
}
labelAlign
=
"left"
onSubmit
=
{
this
.
save
}
>
<
FormItem
>
{
getFieldDecorator
(
'is_encrypt'
,
{
valuePropName
:
'checked'
,
initialValue
:
info
.
is_encrypt
==
1
,
})(
<
Checkbox
><
span
className
=
{
pageStyle
.
tip
}
>
设置密码
<
/span></
Checkbox
>
)}
<
/FormItem
>
{
Number
(
getFieldValue
(
'is_encrypt'
))
===
1
?
<
FormItem
{...
formItemModalLineLayout
}
>
{
getFieldDecorator
(
'password'
,
{
initialValue
:
info
.
password
,
rules
:
[
<
div
>
<
div
className
=
{
pageStyle
.
topHead
}
>
<
Button
type
=
{
clockClassParams
.
extra
==
'class'
?
'primary'
:
''
}
className
=
{
pageStyle
.
btn
}
onClick
=
{()
=>
this
.
changeCourseClass
(
'class'
)}
>
全部班级
<
/Button
>
<
Button
type
=
{
clockClassParams
.
extra
==
'my_class'
?
'primary'
:
''
}
className
=
{
pageStyle
.
btn
}
onClick
=
{()
=>
this
.
changeCourseClass
(
'my_class'
)}
>
只看我的
<
/Button
>
<
/div
>
<
div
className
=
{
pageStyle
.
classList
}
>
{
list
.
length
>
0
&&
list
.
map
((
item
,
index
)
=>
{
return
(
<
div
className
=
{
pageStyle
.
classItem
}
>
<
div
className
=
{
pageStyle
.
classTitle
}
>
{
item
.
title
}
<
/div
>
{
required
:
true
,
message
:
'请输入4-8位数字密码'
,
pattern
:
new
RegExp
(
/^
\d{4,8}
$/
,
'g'
),
},
],
})(
<
Input
style
=
{{
width
:
200
}}
maxLength
=
{
8
}
placeholder
=
"请输入4-8位数字密码"
/>
,
)}
<
/FormItem> : '
'
}
<
/Form
>
item
.
classes
.
map
((
ele
,
i
)
=>
{
return
(
<
div
className
=
{
pageStyle
.
classFlex
}
onClick
=
{()
=>
this
.
chooseClass
(
ele
)}
>
<
div
className
=
{
pageStyle
.
classLeft
}
>
{
ele
.
title
}
<
/div
>
<
div
className
=
{
pageStyle
.
classRight
}
>
<
div
className
=
{
pageStyle
.
teacherList
}
>
{
ele
.
school_teachers
.
map
((
ll
,
i
)
=>
{
return
(
<
span
className
=
{
pageStyle
.
teacherName
}
>
{
ll
.
nickname
}
<
/span
>
);
})
}
<
/div
>
{
this
.
indexOf
(
classes
,
ele
.
id
)
==
-
1
?
<
img
className
=
{
pageStyle
.
check
}
src
=
{
`
${
__IMGCDN__
}
theme/check.png`
}
alt
=
""
/>
:
<
img
className
=
{
pageStyle
.
check
}
src
=
{
`
${
__IMGCDN__
}
theme/check_active.png`
}
alt
=
""
/>
}
<
/div
>
<
/div
>
);
})
}
<
/div
>
);
})
}
<
/div
>
<
div
className
=
{
pageStyle
.
save
}
>
<
Button
type
=
"primary"
onClick
=
{
this
.
saveClasses
}
>
保存
<
/Button
>
<
/div
>
<
/div
>
<
/Modal
>
);
}
...
...
src/pages/newtheme/components/ChooseClass.less
View file @
f4b452b7
.topHead {
text-align: center;
.btn {
border-radius: 0;
}
}
.classList {
margin-top: 20px;
max-height: 400px;
min-height: 350px;
overflow-y: scroll;
.classItem {
margin-bottom: 20px;
.classTitle {
font-size:13px;
font-family:PingFangSC-Medium,PingFang SC;
font-weight:600;
color:rgba(34,34,34,1);
line-height:35px;
margin-bottom: 5px;
}
.classFlex {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #F1F2F3;
padding: 5px 0;
.classLeft {
font-size:13px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(34,34,34,1);
}
.classRight {
display: flex;
align-items: center;
.teacherList {
overflow: hidden;
white-space: nowrap;
width: 150px;
text-overflow: ellipsis;
margin-right: 15px;
text-align: right;
}
.teacherName {
font-size: 12px;
color: #666666;
}
.teacherName:not(:last-child) {
&:after{
content: '、';
}
}
.teacherName:nth-child(1){
color: #FEA915;
}
}
.check {
width: 19px;
height: 19px;
cursor: pointer;
}
}
}
}
.save {
text-align: center;
margin-top: 15px;
}
::-webkit-scrollbar {
width: 8px;
height: 10px;
background-color: rgba(0,0,0,0);
}
::-webkit-scrollbar-thumb {
border-radius: 6px;
background-color: rgba(0,0,0,.2);
transition: all .4s ease;
-moz-transition: all .4s ease;
-webkit-transition: all .4s ease;
-o-transition: all .4s ease;
}
src/pages/newtheme/components/MarkeEditor.js
0 → 100644
View file @
f4b452b7
import
{
connect
}
from
'dva'
;
import
React
from
'react'
;
import
{
Icon
,
Divider
,
Tabs
,
Select
,
Form
,
Upload
,
Row
,
Col
,
Input
,
Radio
,
Modal
,
message
,
Progress
}
from
'antd'
;
import
pageStyle
from
'./MarkeEditor.less'
;
import
{
pageIn
,
hasBtnPower
,
imagify
,
ossVideofy
,
audioorigin
}
from
'../../../utils/index'
;
const
upImg
=
`
${
__IMGCDN__
}
subjectUp.png`
;
const
{
TabPane
}
=
Tabs
;
const
{
TextArea
}
=
Input
;
class
ThemeEditor
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
previewVisible
:
false
,
previewImage
:
''
,
};
}
componentDidMount
()
{
// 挂载
pageIn
(
'轻校-发布主题'
);
}
componentDidUpdate
()
{
}
componentWillUnmount
()
{
// 卸载
}
priviewImg
=
(
img
)
=>
{
this
.
setState
({
previewVisible
:
true
,
previewImage
:
img
,
});
}
handleCancel
=
()
=>
{
this
.
setState
({
previewVisible
:
false
,
});
}
render
()
{
const
{
previewVisible
,
previewImage
}
=
this
.
state
;
const
{
editorUploadImg
,
commentParams
,
editorUploadAudio
,
editorChange
,
deleteThemeImg
,
deleteThemeVideo
,
editorText
,
editorAddText
,
moveContent
,
editorUploadVoice
,
editorAddPlate
,
editorChangeTitle
,
editorDeletePlate
,
progressRate
,
}
=
this
.
props
;
return
(
<
div
className
=
{
pageStyle
.
container
}
>
{
progressRate
&&
progressRate
>
0
?
<
div
className
=
{
pageStyle
.
progressWrap
}
>
<
Progress
width
=
{
150
}
type
=
"circle"
percent
=
{
progressRate
}
/
>
<
p
>
{
progressRate
==
100
?
'上传成功'
:
'上传中。。。'
}
<
/p
>
<
/div> : '
'
}
<
div
className
=
{
pageStyle
.
editorwrap
}
>
{
commentParams
.
content
&&
commentParams
.
content
.
length
>
0
&&
commentParams
.
content
.
map
((
ele
,
sort
)
=>
{
return
(
<
div
className
=
{
pageStyle
.
plateWrap
}
>
{
/* <div className={pageStyle.plateDelete} onClick={() => editorDeletePlate(sort)}><Icon type="close-circle" theme="filled" /></div> */
}
{
/* <Input style={{ width: 560 }} value={ele.title} maxLength={15} placeholder="请输入标题,最多15个字" onChange={e => editorChangeTitle(e, sort)} /> */
}
{
ele
.
content
&&
ele
.
content
.
length
>
0
&&
ele
.
content
.
map
((
item
,
index
)
=>
{
switch
(
item
.
type
)
{
case
'text'
:
return
(
<
div
key
=
{
index
}
className
=
{
pageStyle
.
textWrap
}
>
<
TextArea
autoSize
=
{{
minRows
:
4
,
maxRows
:
10
}}
value
=
{
item
.
value
}
onChange
=
{
e
=>
editorChange
(
e
,
index
,
sort
)}
placeholder
=
"请输入打卡主题的具体要求,如:演唱歌曲《小小少年》,具体要求1.跟随伴奏唱一段副歌 2.视频录下唱歌过程 3.上传歌唱视频打卡成功 如果有该歌曲示例视频,你可以点击上传视频,供学员参考。"
style
=
{{
width
:
560
}}
maxLength
=
{
500
}
/
>
<
div
className
=
{
pageStyle
.
sizeNumber
}
>
{
item
.
value
.
length
}
/500</
div
>
<
div
className
=
{
pageStyle
.
toolList
}
>
{
index
===
0
&&
<
div
aria
-
disabled
=
"true"
className
=
{
pageStyle
.
noup
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
>
0
&&
<
div
className
=
{
pageStyle
.
up
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'up'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
===
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
nodown
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
<
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
down
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'down'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
<
div
className
=
{
pageStyle
.
delete
}
onClick
=
{()
=>
deleteThemeImg
(
index
,
sort
)}
><
Icon
type
=
"delete"
theme
=
"filled"
/><
/div
>
<
/div
>
<
/div
>
);
case
'img'
:
return
(
<
div
key
=
{
index
}
className
=
{
pageStyle
.
uploadimgbox
}
>
<
div
className
=
{
pageStyle
.
imgwrap
}
>
<
div
className
=
{
pageStyle
.
uploadimgwrap
}
>
<
img
alt
=
{
item
.
type
}
className
=
{
pageStyle
.
teacheruploadimg
}
src
=
{
imagify
(
item
.
value
)}
/></
div
>
<
div
className
=
{
pageStyle
.
imghide
}
>
<
span
title
=
"预览文件"
onClick
=
{()
=>
this
.
priviewImg
(
item
.
value
)}
><
Icon
style
=
{{
color
:
'#fff'
}}
type
=
"eye"
/>
预览
<
/span
>
<
/div
>
<
div
className
=
{
pageStyle
.
toolList
}
>
{
index
===
0
&&
<
div
aria
-
disabled
=
"true"
className
=
{
pageStyle
.
noup
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
>
0
&&
<
div
className
=
{
pageStyle
.
up
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'up'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
===
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
nodown
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
<
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
down
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'down'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
<
div
className
=
{
pageStyle
.
delete
}
onClick
=
{()
=>
deleteThemeImg
(
index
,
sort
)}
><
Icon
type
=
"delete"
theme
=
"filled"
/><
/div
>
<
/div
>
<
/div
>
<
/div
>
);
case
'voice'
:
return
(
<
div
key
=
{
index
}
className
=
{
pageStyle
.
uploadimgbox
}
>
<
div
className
=
{
pageStyle
.
imgwrap
}
>
<
audio
controls
=
"controls"
style
=
{{
width
:
'100%'
}}
src
=
{
audioorigin
(
item
.
value
)}
><
/audio
>
<
div
className
=
{
pageStyle
.
toolList
}
>
{
index
===
0
&&
<
div
aria
-
disabled
=
"true"
className
=
{
pageStyle
.
noup
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
>
0
&&
<
div
className
=
{
pageStyle
.
up
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'up'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
===
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
nodown
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
<
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
down
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'down'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
<
div
className
=
{
pageStyle
.
delete
}
onClick
=
{()
=>
deleteThemeImg
(
index
,
sort
)}
><
Icon
type
=
"delete"
theme
=
"filled"
/><
/div
>
<
/div
>
<
/div
>
<
/div
>
);
case
'video'
:
return
(
<
div
key
=
{
index
}
className
=
{
pageStyle
.
videowrap
}
>
<
video
controls
=
"controls"
className
=
{
pageStyle
.
videoPoster
}
src
=
{
ossVideofy
(
item
.
value
)}
/
>
<
div
className
=
{
pageStyle
.
toolList
}
>
{
index
===
0
&&
<
div
aria
-
disabled
=
"true"
className
=
{
pageStyle
.
noup
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
>
0
&&
<
div
className
=
{
pageStyle
.
up
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'up'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
===
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
nodown
}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
{
index
<
(
ele
.
content
.
length
-
1
)
&&
<
div
className
=
{
pageStyle
.
down
}
onClick
=
{()
=>
moveContent
(
index
,
sort
,
'down'
)}
><
img
src
=
{
upImg
}
alt
=
""
/>
<
/div>
}
<
div
className
=
{
pageStyle
.
delete
}
onClick
=
{()
=>
deleteThemeImg
(
index
,
sort
)}
><
Icon
type
=
"delete"
theme
=
"filled"
/><
/div
>
<
/div
>
<
/div
>
);
default
:
return
<
div
><
/div>
;
}
})
}
<
div
className
=
{
pageStyle
.
uploadflex
}
>
<
div
className
=
{
pageStyle
.
uploadimg
}
onClick
=
{()
=>
editorAddText
(
sort
)}
><
Icon
style
=
{{
marginRight
:
10
}}
type
=
"font-size"
/>
添加文字
<
/div
>
<
div
className
=
{
pageStyle
.
uploadimg
}
><
input
type
=
"file"
id
=
"uploadImg"
className
=
{
pageStyle
.
fileuploadinput
}
onChange
=
{
e
=>
editorUploadImg
(
e
,
sort
)}
accept
=
"image/*"
/><
Icon
style
=
{{
marginRight
:
10
}}
type
=
"picture"
/>
添加图片
<
/div
>
<
div
className
=
{
pageStyle
.
uploadimg
}
><
input
type
=
"file"
id
=
"uploadVoice"
className
=
{
pageStyle
.
fileuploadinput
}
onChange
=
{
e
=>
editorUploadVoice
(
e
,
sort
)}
accept
=
"audio/mp3"
/><
Icon
style
=
{{
marginRight
:
10
}}
type
=
"audio"
/>
添加录音
<
/div
>
<
div
className
=
{
pageStyle
.
uploadimg
}
><
input
type
=
"file"
id
=
"uploadVideo"
className
=
{
pageStyle
.
fileuploadinput
}
onChange
=
{
e
=>
editorUploadAudio
(
e
,
sort
)}
accept
=
"video/mp4,video/*"
/><
Icon
style
=
{{
marginRight
:
10
}}
type
=
"video-camera"
/>
添加视频
<
/div
>
<
/div
>
<
p
>
请上传视频小于
2
G
,支持
MP4
格式
<
/p
>
<
/div
>
);
})
}
<
/div
>
{
/* <div className={pageStyle.addPlate} onClick={editorAddPlate}>添加版块</div> */
}
<
Modal
visible
=
{
previewVisible
}
footer
=
{
null
}
onCancel
=
{
this
.
handleCancel
}
>
<
img
alt
=
"图片"
style
=
{{
width
:
'100%'
}}
src
=
{
imagify
(
previewImage
)}
/
>
<
/Modal
>
<
/div
>
);
}
}
ThemeEditor
.
propTypes
=
{
};
export
default
connect
()(
ThemeEditor
);
src/pages/newtheme/components/MarkeEditor.less
0 → 100644
View file @
f4b452b7
.textWrap {
width: 560px;
height: auto;
position: relative;
margin-bottom:10px;
:global {
.ant-input {
padding: 6px 15px 20px;
}
}
.sizeNumber {
position: absolute;
bottom: 8px;
right: 10px;
}
}
.editorwrap {
//width: 563px;
margin-top: 15px;
.videowrap {
width: 100%;
height: 200px;
position: relative;
margin-bottom: 10px;
.videoPoster {
width: 100%;
height: 100%;
}
}
.videowrap:hover {
cursor: pointer;
.videohide {
opacity: 1;
background-color: rgba(0,0,0,0.5);
transition: all .3s;
z-index: 1;
color: #fff;
}
}
.videohide {
position: absolute;
width: 100%;
height: 100%;
bottom: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
color: #fff;
z-index: -1;
a {
color: #fff;
}
}
.uploadimgbox {
display: flex;
align-items: flex-end;
margin-bottom: 18px;
flex-wrap: wrap;
.imgwrap {
width: 100%;
// height: 120px;
border-radius: 4px;
margin-right: 6px;
position: relative;
.uploadimgwrap {
width: 100%;
// height: 120px;
overflow: hidden;
}
}
.imgwrap:hover {
cursor: pointer;
.imghide {
opacity: 1;
background-color: rgba(0,0,0,0.5);
transition: all .3s;
z-index: 1;
color: #fff;
}
}
.imghide {
position: absolute;
width: 100%;
height: 100%;
bottom: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
color: #fff;
z-index: -1;
a {
color: #fff;
}
}
.teacheruploadimg {
object-fit: contain;
border-radius: 4px;
width: 100%;
}
.teacheruploadimg:nth-child(4n){
margin-right: 0;
}
}
}
.uploadflex {
display: flex;
align-items: center;
margin-top: 20px;
.uploadimg {
cursor: pointer;
width: 106px;
height: 32px;
line-height: 32px;
text-align: center;
border: 1px solid #D9D9D9;
border-radius: 4px;
color: rgba(0,0,0,0.65);
position: relative;
margin-right: 30px;
.fileuploadinput {
cursor: pointer;
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
opacity: 0;
left: 0;
top: 0;
}
}
}
.toolList {
position: absolute;
right: -30px;
top: 0;
width: 30px;
height: 90px;
display: flex;
flex-direction: column;
background-color: #D3D3D3;
opacity: 0;
border-radius: 2px;
z-index: -1;
div {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 14px;
cursor: pointer;
}
div>img {
width: 14px;
height: 14px;
}
.up:hover {
background-color: #1890FF;
}
.down,.nodown {
transform: rotate(180deg);
}
.nodown,.noup {
cursor: not-allowed;
}
.down:hover {
background-color: #1890FF;
}
.delete:hover {
background-color: #FF6060;
}
}
.textWrap:hover,.imgwrap:hover,.videowrap:hover {
.toolList {
opacity: 1;
z-index: 1;
}
}
.addPlate {
width: 214px;
height: 34px;
line-height: 34px;
text-align: center;
color: #65B8F4;
border: 1px solid #65B8F4;
border-radius: 17px;
margin: 40px auto 0;
cursor: pointer;
}
.plateWrap {
border:2px dashed rgba(208,208,208,1);
padding: 20px;
margin-bottom: 30px;
position: relative;
}
.plateDelete {
position: absolute;
right: -20px;
top: -25px;
font-size: 30px;
color: #FF6060;
cursor: pointer;
}
.progressWrap {
width: 300px;
height: 300px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f1f1f1;
border-radius: 15px;
z-index: 9999;
&>p {
font-size: 18px;
margin-top: 30px;
color: #1e8bff;
}
}
\ No newline at end of file
src/pages/newtheme/components/MarketingListModal.js
View file @
f4b452b7
...
...
@@ -4,7 +4,8 @@ import {
Form
,
Input
,
Modal
,
Checkbox
,
Checkbox
,
Pagination
,
Divider
,
}
from
'antd'
;
import
pageStyle
from
'./MarketingListModal.less'
;
import
{
pageIn
,
hasBtnPower
,
imagify
,
ossVideofy
}
from
'../../../utils/index'
;
...
...
@@ -34,14 +35,27 @@ class MarketingListModal extends React.Component {
const
{
createMarke
}
=
this
.
props
;
createMarke
();
}
changePagination
=
(
page
,
perPage
)
=>
{
const
{
changePagination
}
=
this
.
props
;
changePagination
({
page
,
perPage
});
}
editMarke
=
(
item
)
=>
{
const
{
editMarke
}
=
this
.
props
;
editMarke
(
item
);
}
deleteMarke
=
(
item
)
=>
{
const
{
deleteMarke
}
=
this
.
props
;
deleteMarke
(
item
);
}
render
()
{
const
{
visible
,
form
:
{
getFieldDecorator
,
getFieldValue
},
list
,
id
,
landingParams
,
landingTotal
,
}
=
this
.
props
;
console
.
log
(
list
);
return
(
<
Modal
visible
=
{
visible
}
...
...
@@ -58,7 +72,7 @@ class MarketingListModal extends React.Component {
<
div
className
=
{
pageStyle
.
container
}
>
<
div
className
=
{
pageStyle
.
list
}
>
{
list
.
length
>
0
&&
list
.
map
((
item
,
index
)
=>
{
list
&&
list
.
length
>
0
&&
list
.
map
((
item
,
index
)
=>
{
return
(
<
div
className
=
{
pageStyle
.
item
}
>
<
div
className
=
{
pageStyle
.
itemLeft
}
>
...
...
@@ -72,8 +86,11 @@ class MarketingListModal extends React.Component {
<
div
className
=
{
pageStyle
.
itemFlex
}
>
<
div
className
=
{
pageStyle
.
now
}
>
{
item
.
id
==
id
?
'当前使用'
:
''
}
<
/div
>
<
div
>
<
span
className
=
{
pageStyle
.
nowBtn
}
onClick
=
{()
=>
this
.
getID
(
item
)}
>
立即使用
<
/span
>
<
span
className
=
{
pageStyle
.
lookBtn
}
>
查看模板
<
/span
>
<
span
className
=
{
pageStyle
.
lookBtn
}
onClick
=
{()
=>
this
.
getID
(
item
)}
>
立即使用
<
/span
>
<
Divider
type
=
"vertical"
/>
<
span
className
=
{
pageStyle
.
lookBtn
}
onClick
=
{()
=>
this
.
editMarke
(
item
)}
>
编辑
<
/span
>
<
Divider
type
=
"vertical"
/>
<
span
className
=
{
pageStyle
.
lookBtn
}
onClick
=
{()
=>
this
.
deleteMarke
(
item
)}
>
删除
<
/span
>
<
/div
>
<
/div
>
<
/div
>
...
...
@@ -82,6 +99,13 @@ class MarketingListModal extends React.Component {
})
}
<
/div
>
<
div
className
=
{
pageStyle
.
page
}
>
<
Pagination
total
=
{
Number
(
landingTotal
)}
onChange
=
{
this
.
changePagination
}
pageSize
=
{
landingParams
.
perPage
}
/
>
<
/div
>
<
div
className
=
{
pageStyle
.
footer
}
onClick
=
{
this
.
createMarke
}
>
新建打卡营销页
<
/div
>
...
...
src/pages/newtheme/components/MarketingListModal.less
View file @
f4b452b7
...
...
@@ -2,7 +2,8 @@
.item {
display: flex;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid #e8e8e8;
padding: 15px 0;
.itemLeft {
width: 128px;
height: 100px;
...
...
@@ -32,7 +33,8 @@
font-size: 12px;
margin-bottom: 10px;
word-break: break-all;
color: #666666
color: #666666;
min-width: 36px;
}
.itemFlex {
display: flex;
...
...
@@ -42,26 +44,11 @@
color: #FEA917;
font-size: 12px;
}
.nowBtn {
font-size: 12px;
color: #1890ff;
border-radius: 2px;
display: inline-block;
width: 52px;
height: 20px;
text-align: center;
line-height: 20px;
margin-right: 20px;
cursor: pointer;
}
.lookBtn {
font-size: 12px;
color: #1890ff;
display: inline-block;
width: 52px;
height: 20px;
text-align: center;
line-height:
20
px;
line-height:
35
px;
cursor: pointer;
}
}
...
...
@@ -73,4 +60,10 @@
line-height: 45px;
color: #16B0FD;
cursor: pointer;
width: 150px;
margin: 0 auto;
}
.page {
text-align: right;
margin-top: 10px;
}
src/pages/newtheme/jobclock/index.js
View file @
f4b452b7
...
...
@@ -242,12 +242,114 @@ class JobClockForm extends React.Component {
addMarke
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'create
them
e/updateState'
,
type
:
'create
mark
e/updateState'
,
payload
:
{
addMarkeVisible
:
true
,
},
});
}
handleChangeMarke
=
({
page
,
perPage
})
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createtheme/queryLandingList'
,
payload
:
{
params
:
{
page
,
perPage
,
},
},
});
}
editMarke
=
(
item
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createmarke/editMarke'
,
payload
:
{
item
,
},
});
}
deleteMarke
=
(
item
)
=>
{
const
{
dispatch
}
=
this
.
props
;
Modal
.
confirm
({
title
:
'确认要删除这个营销页吗?'
,
okText
:
'确认'
,
cancelText
:
'取消'
,
okType
:
'danger'
,
centered
:
true
,
okButtonProps
:
{
type
:
'danger'
,
style
:
{
color
:
'#fff'
,
backgroundColor
:
'#ff4d4f'
,
borderColor
:
'#ff4d4f'
,
},
},
icon
:
<
Icon
type
=
"close-circle"
style
=
{{
color
:
'red'
}}
/>
,
onOk
:
()
=>
{
dispatch
({
type
:
'createmarke/deleteMarke'
,
payload
:
{
item
,
},
});
},
onCancel
:
()
=>
{
},
});
}
handleChangeRadio
=
(
e
)
=>
{
const
{
dispatch
}
=
this
.
props
;
if
(
Number
(
e
.
target
.
value
)
==
2
||
Number
(
e
.
target
.
value
)
==
4
)
{
dispatch
({
type
:
'createtheme/updateState'
,
payload
:
{
clockClassVisible
:
true
,
},
});
}
}
closeClassList
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createtheme/updateState'
,
payload
:
{
clockClassVisible
:
false
,
classes
:
[],
chooseClasses
:
[],
},
});
}
changeCourseClass
=
(
type
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createtheme/queryCourseClassList'
,
payload
:
{
params
:
{
extra
:
type
,
},
},
});
}
handleChooseClass
=
({
classes
,
chooseClasses
})
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createtheme/updateState'
,
payload
:
{
classes
,
chooseClasses
,
},
});
}
saveClasses
=
(
classes
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'createtheme/updateState'
,
payload
:
{
classes
,
},
});
}
render
()
{
const
{
isShow
,
textLength
}
=
this
.
state
;
const
{
...
...
@@ -262,6 +364,13 @@ class JobClockForm extends React.Component {
landingList
,
themeAdInfo
,
addMarkeVisible
,
landingParams
,
landingTotal
,
clockClassVisible
,
clockClassList
,
clockClassParams
,
classes
,
chooseClasses
,
}
=
this
.
props
;
const
formItemModalLineLayout
=
{
labelCol
:
{
...
...
@@ -406,17 +515,48 @@ class JobClockForm extends React.Component {
{
getFieldDecorator
(
'join_rule_type'
,
{
initialValue
:
''
,
})(
<
Radio
.
Group
>
<
Radio
.
Group
onChange
=
{
this
.
handleChangeRadio
}
>
<
Radio
style
=
{
radioStyle
}
value
=
"1"
><
div
className
=
{
pageStyle
.
radioText
}
>
任何人都可参与
&
nbsp
;
<
span
>
(
无通知
)
<
/span></
div
><
/Radio
>
<
Radio
style
=
{
radioStyle
}
value
=
"2"
><
div
className
=
{
pageStyle
.
radioText
}
>
任何人都可参与
&
nbsp
;
<
span
>
(
指定班级学生收到通知
)
<
/span></
div
><
/Radio
>
<
Radio
style
=
{
radioStyle
}
value
=
"3"
><
div
className
=
{
pageStyle
.
radioText
}
>
输入密码都可参与
&
nbsp
;
<
span
>
(
无通知
)
<
/span></
div
><
/Radio
>
<
Radio
style
=
{
radioStyle
}
value
=
"4"
><
div
className
=
{
pageStyle
.
radioText
}
>
指定班级学生参与
<
/div></
Radio
>
<
Radio
style
=
{
radioStyle
}
value
=
"3"
><
div
className
=
{
pageStyle
.
radioText
}
>
输入密码都可参与
&
nbsp
;
<
span
>
(
无通知
)
<
/span></
div
>
<
/Radio
>
<
/Radio.Group>
,
)}
{
Number
(
getFieldValue
(
'join_rule_type'
))
==
3
?
<
div
>
<
FormItem
{...
formItemModaltypeLayout
}
label
=
""
style
=
{{
marginBottom
:
0
}}
>
{
getFieldDecorator
(
'join_secret'
,
{
initialValue
:
''
,
rules
:
[
{
message
:
'请输入正整数'
,
pattern
:
new
RegExp
(
/^
[
0-9
]\d
*$/
,
'g'
),
},
],
})(
<
Input
style
=
{{
width
:
200
}}
maxLength
=
{
6
}
placeholder
=
"请设置密码"
/>
,
)}
<
/FormItem
>
<
/div
>
:
null
}
<
/FormItem
>
<
/div
>
<
/div
>
{
Number
(
getFieldValue
(
'join_rule_type'
))
==
2
?
<
ChooseClass
/>
:
null
}
{(
Number
(
getFieldValue
(
'join_rule_type'
))
==
2
||
Number
(
getFieldValue
(
'join_rule_type'
))
==
4
)
?
<
ChooseClass
visible
=
{
clockClassVisible
}
close
=
{
this
.
closeClassList
}
list
=
{
clockClassList
}
clockClassParams
=
{
clockClassParams
}
changeCourseClass
=
{
this
.
changeCourseClass
}
classes
=
{
classes
}
chooseClasses
=
{
chooseClasses
}
choose
=
{
this
.
handleChooseClass
}
saveClasses
=
{
this
.
saveClasses
}
/
>
:
null
}
<
div
className
=
{
pageStyle
.
commonwrap
}
>
<
div
className
=
{
pageStyle
.
commonleft
}
>
学生作业要求:
...
...
@@ -553,9 +693,14 @@ class JobClockForm extends React.Component {
visible
=
{
marketingVisible
}
list
=
{
landingList
}
id
=
{
themeAdInfo
.
id
}
landingTotal
=
{
landingTotal
}
landingParams
=
{
landingParams
}
close
=
{
this
.
closeMarketing
}
getID
=
{
this
.
getMarketingId
}
createMarke
=
{
this
.
addMarke
}
changePagination
=
{
this
.
handleChangeMarke
}
editMarke
=
{
this
.
editMarke
}
deleteMarke
=
{
this
.
deleteMarke
}
/
>
<
AddMarkeModal
visible
=
{
addMarkeVisible
}
...
...
@@ -585,8 +730,17 @@ function mapStateToProps(state) {
marketingVisible
,
landingList
,
themeAdInfo
,
addMarkeVisible
,
landingParams
,
landingTotal
,
clockClassVisible
,
clockClassList
,
clockClassParams
,
classes
,
chooseClasses
,
}
=
state
.
createtheme
;
const
{
addMarkeVisible
,
}
=
state
.
createmarke
;
return
{
themeAddLoading
,
classList
,
...
...
@@ -598,6 +752,13 @@ function mapStateToProps(state) {
landingList
,
themeAdInfo
,
addMarkeVisible
,
landingParams
,
landingTotal
,
clockClassVisible
,
clockClassList
,
clockClassParams
,
classes
,
chooseClasses
,
};
}
export
default
connect
(
mapStateToProps
)(
JobClock
);
...
...
src/services/createtheme.js
View file @
f4b452b7
...
...
@@ -117,10 +117,10 @@ export function UnlockStudentExport(params) {
data
,
});
}
export
function
getClassList
(
params
)
{
export
function
getC
ourseC
lassList
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
createtheme
.
classList
}
?
${
data
}
`
,
url
:
`
${
api
.
createtheme
.
class
Course
List
}
?
${
data
}
`
,
method
:
'GET'
,
data
,
});
...
...
@@ -133,4 +133,35 @@ export function getlandingList(params) {
data
,
});
}
export
function
addMarke
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
createtheme
.
landing_pages
}
`
,
method
:
'POST'
,
data
,
});
}
export
function
editMarke
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
createtheme
.
landing_pages
}
/
${
params
.
id
}
`
,
method
:
'PUT'
,
data
,
});
}
export
function
deleteMarke
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
createtheme
.
landing_pages
}
/
${
params
.
id
}
`
,
method
:
'DELETE'
,
data
,
});
}
export
function
markeDetail
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
createtheme
.
landing_pages
}
/
${
params
.
id
}
`
,
method
:
'GET'
,
data
,
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment