个人建站

This commit is contained in:
谢思 2023-08-28 10:46:11 +08:00
parent 6f3f2e9857
commit 99b2c03583
11 changed files with 115 additions and 59 deletions

View File

@ -79,6 +79,7 @@ function Index(props){
const { current_user,mygetHelmetapi , checkIfLogin } = props;
const { pathname } = props.location;
const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice;
const {id_card_verify, website_permission} = current_user;
const [ avatarVisible , setAvatarVisible ] = useState(false);
const [active, setActive] = useState(1);
@ -266,12 +267,12 @@ function Index(props){
)}
></Route>
{/* 创建站点 */}
<Route
{id_card_verify && website_permission && <Route
path="/settings/mysite/create"
render={(p) => (
<CreateSite {...props} {...p}/>
)}
></Route>
></Route>}
{/* 个人建站 */}
<Route
path="/settings/mysite"

View File

@ -111,4 +111,9 @@
border-color: rgba(230, 126, 34, 1);
color: rgba(230, 126, 34, 1);
}
&.error{
background-color: #fcbabe47;
border-color: #f60011;
color: #f60011;
}
}

View File

@ -5,6 +5,7 @@ import "../../users/Material/Index.scss";
import axios from "axios";
import { AlignCenter, AlignTop } from "../../Component/layout";
import { Link } from "react-router-dom";
import { getImageUrl } from "../../../common/UrlTool";
const { TextArea } = Input;
function CreateSite(props) {
@ -20,7 +21,7 @@ function CreateSite(props) {
}, []);
useEffect(()=>{
axios.get(`/site_pages/themes.json`, {params: {language_frame: language === 1 ? "jekyll" : language === 2 ? "hexo" : "hugo"}}).then(res=>{
axios.get(`/site_pages/themes.json`, {params: {language_frame: language}}).then(res=>{
if(res && res.status === 200){
setThemes(res.data.themes);
setTheme(res.data.themes[0])
@ -79,9 +80,11 @@ function CreateSite(props) {
wrapperCol={{ span: 16 }}
onSubmit={submit}
>
<div className="formTitle font-16 mb20 ml20">站点配置</div>
<Form.Item name="站点名称" label="站点名称">
{getFieldDecorator("site_name", {
rules: [{ required: true, message: "请输入站点名称" }],
rules: [{ required: true, message: "请输入站点名称" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(<Input placeholder="请输入站点名称" />)}
</Form.Item>
<Form.Item
@ -90,7 +93,8 @@ function CreateSite(props) {
className="Create-Form-biaoshi"
>
{getFieldDecorator("identifier", {
rules: [{ required: true, message: "请输入站点标识" }],
rules: [{ required: true, message: "请输入站点标识" },
{pattern: /^[a-zA-Z0-9]{2,100}$/, message: '长度2-100只能包含数字和字母'}],
})(
<Input
onChange={(e) => {
@ -99,7 +103,7 @@ function CreateSite(props) {
placeholder="请输入站点标识"
/>
)}
<span>
<span style={{wordBreak: 'break-all'}}>
http://{current_user.login}.kingchan.cn/{tag}
</span>
</Form.Item>
@ -115,15 +119,17 @@ function CreateSite(props) {
<Form.Item name="主题选择" label="主题选择">
<AlignTop>
{themes && themes.map(item=>{
const {imgage_url, name, clone_url} = item;
const {image, name, clone_url} = item;
return <div className="mr20 themeBox" onClick={()=>{setTheme(item)}}>
<i className={`iconfont icon-wancheng ${theme && theme.clone_url === clone_url ? 'active' : ''}`}></i>
<img src={imgage_url} alt="" width="125px"/>
<img src={getImageUrl(image)} alt="" width="125px"/>
<p>{name}</p>
</div>
})}
</AlignTop>
</Form.Item>
<div className="formTitle font-16 mb20 ml20">项目配置</div>
<div className="font-15 mb20 ml40">我们会为您生成一个项目来管理您的个人主页请配置您的项目信息</div>
<AlignCenter style={{marginLeft: '58px'}}>
<Form.Item name="拥有者" label="拥有者" style={{width: '260px'}} labelCol={{span: 6}}>
<Input
@ -131,30 +137,31 @@ function CreateSite(props) {
disabled
/>
</Form.Item>
{/* <span className="mb20 font-18">/</span> */}
<Form.Item
name="项目名称"
label="项目名称"
// 0.87
style={{flex: '0.83'}}
labelCol={{span: 4}}
>
{getFieldDecorator("name", {
rules: [{ required: true, message: "请输入项目名称" }],
{/* <span className="mb20 font-18">/</span> 0.87 */}
<Form.Item name="项目标识" label="项目标识" style={{flex: '0.83'}} labelCol={{span: 4}}>
{getFieldDecorator("repository_name", {
rules: [{ required: true, message: "请输入项目标识" },
{pattern: /^[a-zA-Z0-9][a-zA-Z0-9_.-]{2,100}[a-zA-Z0-9]$/, message: "长度2-100只能包含数字、字母、下划线、中划线、英文句号必须以数字和字母开头不能以下划线/中划线/英文句号开头和结尾"}],
})(
<Input placeholder="请输入项目名称"/>
<Input placeholder="请输入项目标识"/>
)}
</Form.Item>
</AlignCenter>
<Form.Item name="项目标识" label="项目标识">
{getFieldDecorator("repository_name", {
rules: [{ required: true, message: "请输入项目标识" }],
<Form.Item
name="项目名称"
label="项目名称"
>
{getFieldDecorator("name", {
rules: [{ required: true, message: "请输入项目名称" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(
<Input placeholder="请输入项目标识"/>
<Input placeholder="请输入项目名称"/>
)}
</Form.Item>
<Form.Item name="项目简介" label="项目简介">
{getFieldDecorator("description", {})(
{getFieldDecorator("description", {
rules: [{type: 'string', max: 200, message: "长度200"}]
})(
<TextArea rows={4} />
)}
</Form.Item>

View File

@ -25,6 +25,9 @@
padding: 15px 0;
border-bottom: 1px dashed #e0e6f5;
.siteName{
color: $primary-color;
color: #05101a;
&:active, &:hover{
color: $primary-color;
}
}
}

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, Fragment } from "react";
import { Button, Spin } from "antd";
import { Button, Pagination, Spin } from "antd";
import './index.scss';
import '../../users/Material/Index.scss';
import '../bot/exploit/index.scss'
@ -9,20 +9,22 @@ import { FlexAJ } from "../../Component/layout";
function MySiteList(props){
const {current_user, history} = props;
const {id_card_verify} = current_user;
const {id_card_verify, website_permission} = current_user;
const [total, setTotal] = useState(0);
const [pages, setPages] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
useEffect(()=>{
document.title='我的站点';
window.scrollTo(0,0);
}, [])
useEffect(()=>{
if(id_card_verify){
setLoading(true);
//
axios.get(`/site_pages.json`).then(res=>{
axios.get(`/site_pages.json`, {params: {limit: 15, page: page}}).then(res=>{
if(res && res.status === 200){
const {total_count, pages} = res.data;
setPages(pages);
@ -31,13 +33,15 @@ function MySiteList(props){
setLoading(false);
})
}
}, [id_card_verify])
}, [id_card_verify, page])
return <Spin spinning={loading}>
<FlexAJ className="mySites_head">
<span>我的站点</span>
{id_card_verify && <Button type="primary" onClick={()=>{history.push(`/settings/mysite/create`)}}>新建站点</Button>}
{id_card_verify && website_permission && <Button type="primary" onClick={()=>{history.push(`/settings/mysite/create`)}}>新建站点</Button>}
</FlexAJ>
{/* 站点权限被管理员关闭 */}
{id_card_verify && !website_permission && <div className="tipsBox font-15 mt20">您的站点权限被锁定请联系平台管理员</div>}
{/* 未通过实名认证 */}
{!id_card_verify && <Fragment>
<div className="tipsBox font-15 mt30">您尚未通过实名认证无法使用此服务如需使用请先进行实名认证</div>
@ -46,14 +50,17 @@ function MySiteList(props){
{/* 通过实名认证 */}
{id_card_verify && <Fragment>
{!loading && total === 0 ? <div style={{marginTop: '130px'}}><Nodata _html="暂无数据"/></div> : <div className="mt10">
{!loading && total === 0 ? <div className="mt30">
<div style={{marginBottom: '130px'}}>个人建站是一个免费的静态网页托管服务您可以使用 个人建站服务托管个人主页项目站点等静态网页</div>
<Nodata _html="暂无数据"/>
</div> : <div className="mt10">
{pages && pages.map(item=>{
const {owner, repo, url, last_build_at} = item
const {owner, repo, url, last_build_at, state} = item
return <FlexAJ>
<div className="siteBox">
<i className="iconfont icon-cangkuyuyanicon font-16 mr5 color-grey-6"></i>
<a className="font-16 siteName" onClick={()=>{last_build_at && window.open(url)}}>{item.site_name}</a>
<span className={`statusBot ml20 font-12 ${last_build_at ? "public" : "private"}`}>{last_build_at ? "已部署" : "未部署"}</span>
{last_build_at && state ? <a className={`font-16 siteName`} onClick={()=>{window.open(url)}}>{item.site_name}</a> : <span className={`font-16`}>{item.site_name}</span>}
<span className={`statusBot ml20 font-12 ${state ? last_build_at ? "public" : "private" : "error"}`}>{state ? last_build_at ? "已部署" : "未部署" : '被关闭'}</span>
<div className="color-grey-6 mt8">
<span className="">主题</span> {item.theme}
<span className="ml5 mr5 color-grey-ccc">|</span>
@ -63,6 +70,16 @@ function MySiteList(props){
<Button className="themeCorBorBut" onClick={()=>{history.push(`/${owner}/${repo}/service/pages`)}}>去仓库</Button>
</FlexAJ>
})}
<div className="mt50" style={{textAlign: 'center'}}>
<Pagination
simple
current = {page}
pageSize={15}
onChange={(page)=>{setPage(page)}}
total = {total}
hideOnSinglePage
></Pagination>
</div>
</div>}
</Fragment>}
</Spin>

View File

@ -24,6 +24,7 @@ const Pages = Loadable({
loading: Loading,
})
function ServerIndex(props){
const { projectDetail } = props;
return(
<div className="panels">
<Switch {...props}>
@ -38,11 +39,11 @@ function ServerIndex(props){
}
></Route>
{/* 个人建站服务 */}
<Route path="/:owner/:projectsId/service/pages"
{projectDetail && projectDetail.author.type === "User" && <Route path="/:owner/:projectsId/service/pages"
render={
() => (<Pages {...props}/>)
}
></Route>
></Route>}
<Route path="/:owner/:projectsId/service"
render={
() => (<List {...props}/>)

View File

@ -7,7 +7,7 @@ function Main(props){
const { owner , projectsId } = props.match.params;
const [ has_trace_user , setHas_trace_user ] = useState(false);
const { current_user , resetUserInfo, projectDetail, showNotification } = props;
const { current_user , resetUserInfo, projectDetail, showNotification, isDeveloper, isManager, isReporter } = props;
useEffect(()=>{
//
@ -15,6 +15,10 @@ function Main(props){
const { author, name} = projectDetail;
document.title = `服务-${author.name}/${name}`;
}
// tab 3397
if(projectDetail && !(isDeveloper || isManager || isReporter)){
props.history.push(`/${owner}/${projectsId}`);
}
}, [projectDetail])
useEffect(()=>{
@ -64,7 +68,7 @@ function Main(props){
<Link to={`/${owner}/${projectsId}/service/reposyncer`} className="btnhover">查看详情</Link>
</span>
</li> */}
<li>
{projectDetail && projectDetail.author.type === "User" && <li>
<span className="servername">
<img src={require('./img/logo.png')} alt=""/>
<a onClick={openDetail}>个人建站服务</a>
@ -73,7 +77,7 @@ function Main(props){
<span className="serverbtn">
<Link to={`/${owner}/${projectsId}/service/pages`} className="btnhover">查看详情</Link>
</span>
</li>
</li>}
</ul>
</div>
)

View File

@ -1,12 +1,12 @@
import React, { Fragment, useEffect, useState } from 'react';
import { Spin, message, Button, Form, Input, Select, Descriptions } from 'antd';
import { Spin, message, Button, Form, Input, Select, Descriptions, Alert } from 'antd';
import axios from 'axios';
import SelectBranch from '../../../forge/Branch/Select';
import './index.scss';
import { Link } from 'react-router-dom';
function Pages(props) {
const {current_user, history, form, projectDetail, defaultBranch} = props;
const {current_user, history, form, projectDetail, defaultBranch, isManager} = props;
const { owner , projectsId } = props.match.params;
const {id_card_verify} = current_user;
const { getFieldDecorator, validateFieldsAndScroll } = form;
@ -72,6 +72,8 @@ function Pages(props) {
}else{
message.error((res && res.data && res.data.message) || "部署失败,请稍后重试");
}
setReload(Math.random());
setIsBuild(false);
setBuilding(false);
})
}
@ -79,16 +81,16 @@ function Pages(props) {
return (
<div className='mb100'>
<div className="servertitle">
<span className="systitle">个人建站服务{!pageInfo && !loading && id_card_verify && ' - 新建站点'}</span>
<span className="systitle">个人建站服务{!pageInfo && !loading && id_card_verify && isManager && ' - 新建站点'}</span>
</div>
{/* 未通过实名认证 */}
{!id_card_verify && <Fragment>
<div className="tipsBox font-15 mt30">您尚未通过实名认证无法使用此服务如需使用请先进行实名认证</div>
<Button type="primary" className="mt20" onClick={()=>{history.push(`/settings/verification`)}}>前往验证</Button>
</Fragment>}
{id_card_verify && <Spin spinning={loading}>
{id_card_verify && isManager && <Spin spinning={loading}>
{/* 未开通 */}
{!pageInfo && <Form
{!pageInfo ? <Form
form={form}
name="register"
className="mt30"
@ -100,7 +102,8 @@ function Pages(props) {
>
<Form.Item name="站点名称" label="站点名称">
{getFieldDecorator("site_name", {
rules: [{ required: true, message: "请输入站点名称" }],
rules: [{ required: true, message: "请输入站点名称" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(<Input placeholder="请输入站点名称" />)}
</Form.Item>
<Form.Item
@ -109,7 +112,8 @@ function Pages(props) {
className="Create-Form-biaoshi"
>
{getFieldDecorator("identifier", {
rules: [{ required: true, message: "请输入站点标识" }],
rules: [{ required: true, message: "请输入站点标识" },
{pattern: /^[a-zA-Z0-9]{2,100}$/, message: '长度2-100只能包含数字和字母'}],
})(
<Input
onChange={(e) => {
@ -118,7 +122,7 @@ function Pages(props) {
placeholder="请输入站点标识"
/>
)}
<span>
<span style={{wordBreak: 'break-all'}}>
http://{current_user.login}.kingchan.cn/{tag}
</span>
</Form.Item>
@ -143,13 +147,12 @@ function Pages(props) {
取消
</Button>
</Form.Item>
</Form>}
{/* 已开通 */}
{pageInfo && <div className='mt30'>
</Form> : pageInfo.state ? <div className='mt30'>
{/* 已开通 */}
<Descriptions title="站点信息">
<Descriptions.Item label="站点名称"><a onClick={()=>{window.open(pageInfo.url)}} className='theme-btn'>{pageInfo.site_name}</a></Descriptions.Item>
<Descriptions.Item label="网站地址">{pageInfo.url}</Descriptions.Item>
<Descriptions.Item label="工具">{pageInfo.language_frame}</Descriptions.Item>
<Descriptions.Item label="站点名称"><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.site_name}</span></Descriptions.Item>
<Descriptions.Item label="网站地址" span={2}><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.url}</span></Descriptions.Item>
<Descriptions.Item label="站工具">{pageInfo.language_frame}</Descriptions.Item>
<Descriptions.Item label="建站时间">{pageInfo.created_at}</Descriptions.Item>
{pageInfo.last_build_at && <Descriptions.Item label="上次部署时间">{pageInfo.last_build_at}</Descriptions.Item>}
</Descriptions>
@ -175,12 +178,25 @@ function Pages(props) {
<Button type='primary' ghost onClick={()=>{setIsBuild(false)}} className='ml30'>取消</Button>
</div>
</div>}
</div> : <div className='mt30'>
{/* 站点权限被关闭 */}
<Alert type="error" message={pageInfo && pageInfo.state_description}></Alert>
</div>}
{result && <div className='mt30'>
<div className='mt20 ant-descriptions-title'>部署结果</div>
<div className='buildResult'>{result.map(item=> <p>{item}</p>)}</div>
</div>}
</Spin>}
{!isManager && <div className='mt30'>
{/* 非管理员权限 */}
{pageInfo && pageInfo.state && <Descriptions title="站点信息">
<Descriptions.Item label="站点名称"><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.site_name}</span></Descriptions.Item>
<Descriptions.Item label="网站地址" span={2}><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.url}</span></Descriptions.Item>
<Descriptions.Item label="建站工具">{pageInfo.language_frame}</Descriptions.Item>
<Descriptions.Item label="建站时间">{pageInfo.created_at}</Descriptions.Item>
{pageInfo.last_build_at && <Descriptions.Item label="上次部署时间">{pageInfo.last_build_at}</Descriptions.Item>}
</Descriptions>}
</div>}
</div>
)
}

View File

@ -5,4 +5,5 @@
}
.theme-btn{
color: $primary-color;
cursor: pointer;
}

View File

@ -35,7 +35,7 @@ function Index(props){
break;
case '/settings/verification':
setType('4');
documentTitle = "身份认证";
documentTitle = "实名认证";
break;
default:
setType('2');
@ -55,7 +55,7 @@ function Index(props){
<Menu.Item key="3" className="font-16" onClick={()=>{props.history.push('/settings/phone')}}>手机号管理</Menu.Item>
<Menu.Item key="0" className="font-16" onClick={()=>{props.history.push('/settings/emails')}}>邮箱管理</Menu.Item>
<Menu.Item key="1" className="font-16" onClick={()=>{props.history.push('/settings/password')}}>密码管理</Menu.Item>
<Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>身份认证</Menu.Item>
<Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>实名认证</Menu.Item>
<Menu.Item key="2" className="font-16" onClick={()=>{props.history.push('/settings/cancel')}}>账号注销</Menu.Item>
</Menu>}
</div>

View File

@ -67,15 +67,16 @@ export default Form.create()(
</div>
)}
{data.state === "已拒绝" && <div className="tipsBox mb20 errorTip">
您提交的身份信息未提供原因{data.description || "无,如有需要请联系平台管理员"}
您提交的身份信息未审核通过原因{data.description || "无,如有需要请联系平台管理员"}
</div>}
{data.state === "已通过" && <Alert className="mb20" type='success' message="您提交的身份信息已通过" />}
{data.state === "已通过" && <Alert className="mb20" type='success' message="您提交的身份信息已审核通过" />}
</Fragment>
)}
<Form layout={"inline"} className="verificationForm">
<Form.Item label="真实姓名" labelCol={{ span: 4 }}>
{getFieldDecorator("name", {
rules: [{ required: true, message: "请输入真实姓名" }],
rules: [{ required: true, message: "请输入真实姓名" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(
<Input
placeholder="请输入与身份证一致的真实姓名"
@ -83,7 +84,7 @@ export default Form.create()(
/>
)}
</Form.Item>
<Form.Item label="身份证号" labelCol={{ span: 4 }}>
<Form.Item label="身份证号" labelCol={{ span: 4 }}>
{getFieldDecorator("number", {
rules: [
{ required: true, message: "请输入大陆身份证号码" },