Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<first-menu>
<menu-item href="/users">用户列表</menu-item>
<span class="item">|</span>
<menu-item :href="'/users/user?userId=' + user.id" code="index">{{user.fullname}}&nbsp; <span class="small">({{user.username}})</span></menu-item>
<menu-item :href="'/users/user/servers?userId=' + user.id" code="server" v-if="teaIsPlus">网站列表</menu-item>
<menu-item :href="'/users/update?userId=' + user.id" code="update">修改</menu-item>
<menu-item :href="'/users/features?userId=' + user.id" code="feature" v-if="teaIsPlus">功能</menu-item>
<menu-item :href="'/users/identity?userId=' + user.id" code="identity" v-if="teaIsPlus">实名认证<span v-if="user.hasNewIndividualIdentity || user.hasNewEnterpriseIdentity" class="red small">(待审核)</span><span v-if="user.identityTag != null && user.identityTag.length > 0" class="green">({{user.identityTag}})</span></menu-item>
<menu-item :href="'/users/accesskeys?userId=' + user.id" code="accessKey">API AccessKey({{user.countAccessKeys}})</menu-item>
</first-menu>

View File

@@ -0,0 +1,28 @@
{$layout "layout_popup"}
<h3>创建新AccessKey</h3>
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="userId" :value="userId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">AccessKey ID</td>
<td><span class="disabled">自动生成</span></td>
</tr>
<tr>
<td>AccessKey密钥</td>
<td><span class="disabled">自动生成</span></td>
</tr>
<tr>
<td class="title">备注 *</td>
<td>
<textarea rows="2" name="description" maxlength="100" ref="focus"></textarea>
<p class="comment">描述AccessKey的用途等。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,39 @@
{$layout}
{$template "../user_menu"}
<second-menu>
<menu-item @click.prevent="createAccessKey()">[创建AccessKey]</menu-item>
</second-menu>
<p class="comment" v-if="accessKeys.length == 0">暂时还没有AccessKey。</p>
<table class="ui table selectable" v-if="accessKeys.length > 0">
<thead>
<tr>
<th>AccessKey ID</th>
<th>AccessKey密钥</th>
<th>备注</th>
<th>最后访问</th>
<th>状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="accessKey in accessKeys">
<td :class="{disabled: !accessKey.isOn}">{{accessKey.uniqueId}}</td>
<td :class="{disabled: !accessKey.isOn}">{{accessKey.secret}}</td>
<td :class="{disabled: !accessKey.isOn}">{{accessKey.description}}</td>
<td :class="{disabled: !accessKey.isOn}">
<span v-if="accessKey.accessedTime.length > 0">{{accessKey.accessedTime}}</span>
<span v-else class="disabled">尚无访问</span>
</td>
<td>
<span v-if="accessKey.isOn" class="green">已启用</span>
<span v-else class="disabled">已禁用</span>
</td>
<td>
<a href="" v-if="accessKey.isOn" @click.prevent="updateAccessKeyIsOn(accessKey.id, false)">禁用</a>
<a href="" v-if="!accessKey.isOn" @click.prevent="updateAccessKeyIsOn(accessKey.id, true)">启用</a>
&nbsp; <a href="" @click.prevent="deleteAccessKey(accessKey.id)">删除</a>
</td>
</tr>
</table>

View File

@@ -0,0 +1,41 @@
Tea.context(function () {
this.createAccessKey = function () {
teaweb.popup("/users/accesskeys/createPopup?userId=" + this.user.id, {
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.updateAccessKeyIsOn = function (accessKeyId, isOn) {
let that = this
let message = ""
if (isOn) {
message = "确定要启用此AccessKey吗"
} else {
message = "确定要禁用此AccessKey吗"
}
teaweb.confirm(message, function () {
that.$post(".updateIsOn")
.params({
accessKeyId: accessKeyId,
isOn: isOn ? 1 : 0
})
.refresh()
})
}
this.deleteAccessKey = function (accessKeyId) {
let that = this
teaweb.confirm("确定要删除此AccessKey吗", function () {
that.$post(".delete")
.params({
accessKeyId: accessKeyId
})
.refresh()
})
}
})

View File

@@ -0,0 +1,88 @@
{$layout "layout_popup"}
<h3>创建用户</h3>
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" ref="focus"/>
<p class="comment">用户名只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>密码 *</td>
<td>
<input type="password" name="pass1" maxlength="100"/>
</td>
</tr>
<tr>
<td>确认密码 *</td>
<td>
<input type="password" name="pass2" maxlength="100"/>
</td>
</tr>
<tr>
<td>全名 *</td>
<td>
<input type="text" name="fullname" maxlength="100"/>
<p class="comment">用户姓名或者公司名称等等。</p>
</td>
</tr>
<tr>
<td>关联集群 *</td>
<td>
<cluster-selector></cluster-selector>
<p class="comment">用户发布的网站会自动部署到此集群。</p>
</td>
</tr>
<tr v-show="teaIsPlus">
<td>开通功能</td>
<td>
<radio name="featuresType" value="default" :v-value="'default'">默认功能</radio>
&nbsp; &nbsp;
<radio name="featuresType" :v-value="'all'">全部功能</radio>
<p class="comment">用户默认开通的功能可以在<a href="/users/setting" target="_blank">[这里]</a>设置。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>手机号</td>
<td>
<input type="text" name="mobile" maxlength="11"/>
</td>
</tr>
<tr>
<td>联系电话</td>
<td>
<input type="text" name="tel" maxlength="100"/>
</td>
</tr>
<tr>
<td>电子邮箱</td>
<td>
<input type="text" name="email" maxlength="100"/>
</td>
</tr>
<tr>
<td>OTP认证</td>
<td>
<checkbox name="otpOn">启用OTP</checkbox>
<p class="comment">启用OTP认证后在用户登录的时候需要同时填写OTP动态密码。</p>
</td>
</tr>
<tr>
<td>备注</td>
<td>
<textarea rows="3" name="remark"></textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,9 @@
.feature-boxes .feature-box {
margin-bottom: 1em;
width: 24em;
float: left;
}
.feature-boxes .feature-box:hover label {
font-weight: bold;
}
/*# sourceMappingURL=features.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["features.less"],"names":[],"mappings":"AAAA,cACC;EACC,kBAAA;EACA,WAAA;EACA,WAAA;;AAJF,cAOC,aAAY,MACX;EACC,iBAAA","file":"features.css"}

View File

@@ -0,0 +1,24 @@
{$layout}
{$template "user_menu"}
<div class="margin"></div>
<form class="ui form" data-tea-success="success" data-tea-action="$">
<csrf-token></csrf-token>
<input type="hidden" name="userId" :value="user.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">功能列表</td>
<td>
<div class="feature-boxes">
<div class="feature-box" v-for="feature in features">
<checkbox name="codes" :v-value="feature.code" v-model="feature.isChecked">{{feature.name}}</checkbox>
<p class="comment">{{feature.description}}</p>
</div>
</div>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,13 @@
.feature-boxes {
.feature-box {
margin-bottom: 1em;
width: 24em;
float: left;
}
.feature-box:hover {
label {
font-weight: bold;
}
}
}

View File

@@ -0,0 +1,18 @@
.idcard-bg {
width: 20em;
height: 13em;
border: 1px #ccc solid;
border-radius: 1em;
position: relative;
overflow: hidden;
}
.idcard-bg img {
width: 100%;
height: 100%;
}
.form .success,
.form .error,
.form .warning {
display: block !important;
}
/*# sourceMappingURL=enterprise.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["enterprise.less"],"names":[],"mappings":"AAAA;EACC,WAAA;EACA,YAAA;EACA,sBAAA;EACA,kBAAA;EACA,kBAAA;EACA,gBAAA;;AAND,UAQC;EACC,WAAA;EACA,YAAA;;AAIF,KACC;AADD,KACW;AADX,KACmB;EACjB,yBAAA","file":"enterprise.css"}

View File

@@ -0,0 +1,71 @@
{$layout}
{$template "../user_menu"}
<second-menu>
<menu-item :href="'/users/identity?userId=' + user.id">个人<span v-if="user.hasNewIndividualIdentity" class="red">(待审核)</span></menu-item>
<menu-item :href="'/users/identity/enterprise?userId=' + user.id" active="true">企业<span v-if="user.hasNewEnterpriseIdentity" class="red">(待审核)</span></menu-item>
</second-menu>
<div class="ui message warning" v-if="identity == null">暂时还没有实名认证信息。</div>
<div v-if="identity != null">
<div class="margin"></div>
<form class="ui form">
<table class="ui table selectable definition">
<tr>
<td class="title">企业全称</td>
<td>
{{identity.realName}}
</td>
</tr>
<tr>
<td>营业执照号码</td>
<td>
{{identity.number}}
</td>
</tr>
<tr>
<td>身份证 - 国徽面</td>
<td>
<div class="idcard-bg">
<a :href="'/files/file?fileId=' + identity.frontFileId" target="_blank"><img :src="'/files/file?fileId=' + identity.frontFileId" v-if="identity != null && identity.frontFileId > 0" alt=""/></a>
</div>
</td>
</tr>
<tr v-if="isRejecting">
<td>驳回理由 *</td>
<td>
<textarea v-model="rejectReason" placeholder="填写驳回原因,方便用户改正" rows="3" ref="rejectReasonBox"></textarea>
</td>
</tr>
</table>
<div v-if="identity != null">
<!-- 待提交 -->
<div v-if="identity.status == 'none'">
<div class="ui message warning">已上传,等待用户提交审核。</div>
</div>
<!-- 已提交 -->
<div v-if="identity.status == 'submitted'">
<div v-if="!isRejecting">
<a href="" class="ui button primary" @click.prevent="verify">审核通过</a> &nbsp; &nbsp; <a href="" class="ui button" @click.prevent="reject">驳回</a>
</div>
<div v-if="isRejecting">
<a href="" class="ui button primary" @click.prevent="rejectConfirm">确定驳回</a> &nbsp; &nbsp; <a href="" @click.prevent="cancelRejecting()">取消</a>
</div>
</div>
<!-- 已拒绝 -->
<div v-if="identity.status == 'rejected'">
<div class="ui message error">审核不通过,原因:{{identity.rejectReason}}。</div>
</div>
<!-- 已通过 -->
<div v-if="identity.status == 'verified'">
<div class="ui message success">实名认证审核通过。</div>
<button class="ui button" type="button" @click.prevent="resetStatus">重置状态</button>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,72 @@
Tea.context(function () {
this.isRejecting = false
this.rejectReason = ""
this.reject = function () {
this.isRejecting = true
this.$delay(function () {
this.$refs.rejectReasonBox.focus()
})
}
this.cancelRejecting = function () {
this.isRejecting = !this.isRejecting
}
this.rejectConfirm = function () {
if (this.identity == null) {
teaweb.warn("表单错误,请刷新后重试")
return
}
if (this.rejectReason.length == 0) {
let that = this
teaweb.warn("请输入驳回理由", function () {
that.$refs.rejectReasonBox.focus()
})
return
}
this.$post(".reject")
.params({
userId: this.user.id,
identityId: this.identity.id,
reason: this.rejectReason
})
.success(function () {
teaweb.success("驳回成功", function () {
teaweb.reload()
})
})
}
this.verify = function () {
let that = this
teaweb.confirm("确定要通过审核吗?", function () {
this.$post(".verify")
.params({
userId: that.user.id,
identityId: that.identity.id
})
.success(function () {
teaweb.success("操作成功", function () {
teaweb.reload()
})
})
})
}
this.resetStatus = function () {
let that = this
teaweb.confirm("确定要重置当前实名状态吗?", function () {
this.$post(".reset")
.params({
userId: that.user.id,
identityId: that.identity.id
})
.success(function () {
teaweb.success("操作成功", function () {
teaweb.reload()
})
})
})
}
})

View File

@@ -0,0 +1,19 @@
.idcard-bg {
width: 20em;
height: 13em;
border: 1px #ccc solid;
border-radius: 1em;
position: relative;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.form {
.success, .error, .warning {
display: block !important;
}
}

View File

@@ -0,0 +1,18 @@
.idcard-bg {
width: 20em;
height: 13em;
border: 1px #ccc solid;
border-radius: 1em;
position: relative;
overflow: hidden;
}
.idcard-bg img {
width: 100%;
height: 100%;
}
.form .success,
.form .error,
.form .warning {
display: block !important;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,WAAA;EACA,YAAA;EACA,sBAAA;EACA,kBAAA;EACA,kBAAA;EACA,gBAAA;;AAND,UAQC;EACC,WAAA;EACA,YAAA;;AAIF,KACC;AADD,KACW;AADX,KACmB;EACjB,yBAAA","file":"index.css"}

View File

@@ -0,0 +1,79 @@
{$layout}
{$template "../user_menu"}
<second-menu>
<menu-item :href="'/users/identity?userId=' + user.id" active="true">个人<span v-if="user.hasNewIndividualIdentity" class="red">(待审核)</span></menu-item>
<menu-item :href="'/users/identity/enterprise?userId=' + user.id">企业<span v-if="user.hasNewEnterpriseIdentity" class="red">(待审核)</span></menu-item>
</second-menu>
<div class="ui message warning" v-if="identity == null">暂时还没有实名认证信息。</div>
<div v-if="identity != null">
<div class="margin"></div>
<form class="ui form">
<table class="ui table selectable definition">
<tr>
<td class="title">真实姓名</td>
<td>
{{identity.realName}}
</td>
</tr>
<tr>
<td>身份证号</td>
<td>
{{identity.number}}
</td>
</tr>
<tr>
<td>身份证 - 照片面</td>
<td>
<div class="idcard-bg">
<a :href="'/files/file?fileId=' + identity.backFileId" target="_blank"><img :src="'/files/file?fileId=' + identity.backFileId" v-if="identity != null && identity.backFileId > 0" alt=""/></a>
</div>
</td>
</tr>
<tr>
<td>身份证 - 国徽面</td>
<td>
<div class="idcard-bg">
<a :href="'/files/file?fileId=' + identity.frontFileId" target="_blank"><img :src="'/files/file?fileId=' + identity.frontFileId" v-if="identity != null && identity.frontFileId > 0" alt=""/></a>
</div>
</td>
</tr>
<tr v-if="isRejecting">
<td>驳回理由 *</td>
<td>
<textarea v-model="rejectReason" placeholder="填写驳回原因,方便用户改正" rows="3" ref="rejectReasonBox"></textarea>
</td>
</tr>
</table>
<div v-if="identity != null">
<!-- 待提交 -->
<div v-if="identity.status == 'none'">
<div class="ui message warning">已上传,等待用户提交审核。</div>
</div>
<!-- 已提交 -->
<div v-if="identity.status == 'submitted'">
<div v-if="!isRejecting">
<a href="" class="ui button primary" @click.prevent="verify">审核通过</a> &nbsp; &nbsp; <a href="" class="ui button" @click.prevent="reject">驳回</a>
</div>
<div v-if="isRejecting">
<a href="" class="ui button primary" @click.prevent="rejectConfirm">确定驳回</a> &nbsp; &nbsp; <a href="" @click.prevent="cancelRejecting()">取消</a>
</div>
</div>
<!-- 已拒绝 -->
<div v-if="identity.status == 'rejected'">
<div class="ui message error">审核不通过,原因:{{identity.rejectReason}}。</div>
</div>
<!-- 已通过 -->
<div v-if="identity.status == 'verified'">
<div class="ui message success">实名认证审核通过。</div>
<button class="ui button" type="button" @click.prevent="resetStatus">重置状态</button>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,72 @@
Tea.context(function () {
this.isRejecting = false
this.rejectReason = ""
this.reject = function () {
this.isRejecting = true
this.$delay(function () {
this.$refs.rejectReasonBox.focus()
})
}
this.cancelRejecting = function () {
this.isRejecting = !this.isRejecting
}
this.rejectConfirm = function () {
if (this.identity == null) {
teaweb.warn("表单错误,请刷新后重试")
return
}
if (this.rejectReason.length == 0) {
let that = this
teaweb.warn("请输入驳回理由", function () {
that.$refs.rejectReasonBox.focus()
})
return
}
this.$post(".reject")
.params({
userId: this.user.id,
identityId: this.identity.id,
reason: this.rejectReason
})
.success(function () {
teaweb.success("驳回成功", function () {
teaweb.reload()
})
})
}
this.verify = function () {
let that = this
teaweb.confirm("确定要通过审核吗?", function () {
this.$post(".verify")
.params({
userId: that.user.id,
identityId: that.identity.id
})
.success(function () {
teaweb.success("操作成功", function () {
teaweb.reload()
})
})
})
}
this.resetStatus = function () {
let that = this
teaweb.confirm("确定要重置当前实名状态吗?", function () {
this.$post(".reset")
.params({
userId: that.user.id,
identityId: that.identity.id
})
.success(function () {
teaweb.success("操作成功", function () {
teaweb.reload()
})
})
})
}
})

View File

@@ -0,0 +1,19 @@
.idcard-bg {
width: 20em;
height: 13em;
border: 1px #ccc solid;
border-radius: 1em;
position: relative;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.form {
.success, .error, .warning {
display: block !important;
}
}

View File

@@ -0,0 +1,89 @@
{$layout}
<first-menu>
<menu-item href="/users" :active="!isVerifying">全部用户</menu-item>
<menu-item href="/users?verifying=1" :active="isVerifying" v-if="teaIsPlus">待审核<span v-if="countVerifyingUsers > 0" class="red">({{countVerifyingUsers}})</span></menu-item>
<span class="item disabled">|</span>
<menu-item @click.prevent="createUser">[创建用户]</menu-item>
</first-menu>
<warning-message v-if="!teaIsPlus">当前系统为免费版本,这里的平台用户仅作为登记用途,无法登录系统。</warning-message>
<div class="margin"></div>
<form class="ui form" action="/users" method="get">
<input type="hidden" name="verifying" :value="isVerifying ? 1 : 0"/>
<div class="ui fields inline">
<div class="ui field">
<input type="text" placeholder="用户名、全名、手机号..." name="keyword" v-model="keyword"/>
</div>
<div class="ui field" v-show="teaIsPlus">
<select class="ui dropdown" name="mobileIsVerified" v-model="mobileIsVerified">
<option value="-1">[手机号绑定状态]</option>
<option value="1">已绑定</option>
<option value="0">未绑定</option>
</select>
</div>
<div class="ui field">
<button type="submit" class="ui button">搜索</button>
&nbsp;
<a :href="'/users?verifying=' + (isVerifying ? 1 : 0)" v-if="keyword.length > 0 || mobileIsVerified != -1">[清除条件]</a>
</div>
</div>
</form>
<p class="comment" v-if="users.length == 0">暂时还没有用户。</p>
<table class="ui table selectable celled" v-if="users.length > 0">
<thead>
<tr>
<th>用户名</th>
<th>全名</th>
<th>关联集群</th>
<th>绑定手机号</th>
<th v-if="windowWidth > columnWidth1">联系手机号</th>
<th>注册时间</th>
<th class="center" style="width: 6em">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="user in users">
<td :class="{disabled:!user.isOn}">
<a :href="'/users/user?userId=' + user.id">
<keyword :v-word="keyword">{{user.username}}</keyword>
</a>
<div v-if="!user.isVerified">
<a :href="'/users/user?userId=' + user.id"><grey-label color="red"><i class="icon info circle"></i>信息未审核</grey-label></a>
</div>
<div v-if="user.isRejected">
<grey-label color="red"><i class="icon info circle"></i>信息已拒绝</grey-label>
</div>
<div v-if="user.identityIsSubmitted">
<a :href="'/users/identity?userId=' + user.id"><grey-label color="red"><i class="icon address card"></i>实名未审核</grey-label></a>
</div>
</td>
<td :class="{disabled:!user.isOn}">
<keyword :v-word="keyword">{{user.fullname}}</keyword>
</td>
<td>
<span v-if="user.cluster != null">{{user.cluster.name}} <link-icon :href="'/clusters/cluster?clusterId=' + user.cluster.id"></link-icon></span>
<span v-else class="disabled">-</span>
</td>
<td :class="{disabled:!user.isOn}">
<span v-if="user.verifiedMobile.length > 0">{{user.verifiedMobile}}</span>
<span v-else class="disabled">-</span>
</td>
<td :class="{disabled:!user.isOn}" v-if="windowWidth > columnWidth1">
<span v-if="user.mobile.length > 0"><keyword :v-word="keyword">{{user.mobile}}</keyword></span>
<span v-else class="disabled">-</span>
</td>
<td :class="{disabled:!user.isOn}">{{user.createdTime}}</td>
<td class="center">
<label-on :v-is-on="user.isOn"></label-on>
</td>
<td>
<a :href="'/users/user?userId=' + user.id">详情</a> &nbsp;
<a href="" @click.prevent="deleteUser(user.id)">删除</a>
</td>
</tr>
</table>
<div class="page" v-html="page"></div>

View File

@@ -0,0 +1,26 @@
Tea.context(function () {
this.windowWidth = window.innerWidth
this.columnWidth1 = 1000
this.createUser = function () {
teaweb.popup(Tea.url(".createPopup"), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.deleteUser = function (userId) {
let that = this
teaweb.confirm("确定要删除这个用户吗?", function () {
that.$post(".delete")
.params({
userId: userId
})
.refresh()
})
}
})

View File

@@ -0,0 +1,9 @@
<first-menu>
<menu-item href="/users/setting" code="index">注册设置</menu-item>
<menu-item href="/users/setting/server" code="server">服务设置</menu-item>
<menu-item href="/users/setting/email" code="email">邮件设置</menu-item>
<menu-item href="/users/setting/sms" code="sms">短信设置</menu-item>
<span class="item disabled">|</span>
<menu-item href="/finance/fee">计费设置 &nbsp; <i class="icon external small"></i></menu-item>
<menu-item href="/settings/user-ui">界面设置 &nbsp; <i class="icon external small"></i></menu-item>
</first-menu>

View File

@@ -0,0 +1,14 @@
{$layout}
{$template "menu"}
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<h4>激活邮件</h4>
<email-sender v-model="config.verifyEmail" name="verifyEmailJSON"></email-sender>
<h4>通知邮件</h4>
<email-sender v-model="config.notifyEmail" name="notifyEmailJSON"></email-sender>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,74 @@
{$layout "layout_popup"}
<h3>邮件发送测试</h3>
<form class="ui form" data-tea-action="$" data-tea-success="successSend" data-tea-before="before" data-tea-done="done">
<csrf-token></csrf-token>
<input type="hidden" name="configJSON" :value="JSON.stringify(config)"/>
<table class="ui table selectable definition">
<tr>
<td>SMTP地址 *</td>
<td>
<span v-if="config.smtpHost.length > 0">{{config.smtpHost}}</span>
<span v-else class="red">没有设置。</span>
</td>
</tr>
<tr>
<td>SMTP端口 *</td>
<td>
<span v-if="config.smtpPort > 0">{{config.smtpPort}}</span>
<span v-else class="red">没有设置。</span>
</td>
</tr>
<tr>
<td>用户名 *</td>
<td>
<span v-if="config.username.length > 0">{{config.username}}</span>
<span v-else class="red">没有设置。</span>
</td>
</tr>
<tr>
<td>密码 *</td>
<td>
<span v-if="config.password.length > 0"><span v-for="i in config.password.length">*</span></span>
<span v-else class="red">没有设置。</span>
</td>
</tr>
<tr>
<td>发件人Email *</td>
<td>
<span v-if="config.fromEmail.length > 0">{{config.fromEmail}}</span>
<span v-else class="red">没有设置。</span>
</td>
</tr>
<tr>
<td>发件人名称</td>
<td>
<span v-if="config.fromName.length > 0">{{config.fromName}}</span>
<span v-else class="disabled">使用默认。</span>
</td>
</tr>
<tr>
<td class="color-border">收件人Email *</td>
<td>
<input type="text" name="toEmail" placeholder="test@example.com" ref="focus"/>
</td>
</tr>
<tr>
<td class="color-border">测试标题 *</td>
<td>
<input type="text" maxlength="100" name="subject" value="这是一封测试邮件"/>
</td>
</tr>
<tr>
<td class="color-border">测试内容 *</td>
<td>
<textarea name="body" rows="3">&lt;p&gt;测试邮件内容 1&lt;/p&gt;
&lt;p&gt;测试邮件内容 2&lt;/p&gt;</textarea>
<p class="comment">通常支持HTML。</p>
</td>
</tr>
</table>
<submit-btn v-show="!isSending">发送测试邮件</submit-btn>
<button class="ui button disabled" type="button" v-show="isSending">发送中...</button>
</form>

View File

@@ -0,0 +1,17 @@
Tea.context(function () {
this.config = window.parent.TESTING_EMAIL_CONFIG
this.isSending = false
this.before = function () {
this.isSending = true
}
this.done = function () {
this.isSending = false
}
this.successSend = function () {
teaweb.success("发送成功")
}
})

View File

@@ -0,0 +1,9 @@
.feature-boxes .feature-box {
margin-bottom: 1em;
width: 24em;
float: left;
}
.feature-boxes .feature-box:hover label {
font-weight: bold;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,cACC;EACC,kBAAA;EACA,WAAA;EACA,WAAA;;AAJF,cAOC,aAAY,MACX;EACC,iBAAA","file":"index.css"}

View File

@@ -0,0 +1,260 @@
{$layout}
{$template "menu"}
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">允许注册</td>
<td>
<checkbox name="isOn" v-model="config.isOn"></checkbox>
<p class="comment">选中表示允许用户自行注册。</p>
</td>
</tr>
<tbody v-show="config.isOn">
<tr>
<td>必须使用复杂密码</td>
<td>
<checkbox name="complexPassword" v-model="config.complexPassword"></checkbox>
<p class="comment">选中表示用户注册时必须使用复杂密码即长度不能低于6位且必须包含大小写字母。</p>
</td>
</tr>
<tr>
<td>需要审核</td>
<td>
<checkbox name="requireVerification" v-model="config.requireVerification"></checkbox>
<p class="comment">选中后,表示用户注册后需要审核才能创建服务。</p>
</td>
</tr>
<tr>
<td>需要实名认证</td>
<td>
<checkbox name="requireIdentity" v-model="config.requireIdentity"></checkbox>
<p class="comment">选中后,表示用户注册后需要实名认证后才能创建服务。</p>
</td>
</tr>
</tbody>
</table>
<h4>登录设置</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">检查客户端区域</td>
<td>
<checkbox name="checkClientRegion" v-model="config.checkClientRegion"></checkbox>
<p class="comment">选中后,表示每次用户访问时都检查客户端所在地理区域是否和登录时一致,以提升安全性;如果当前系统下游有反向代理设置,请在<a href="/settings/user-ui#client-ip-header-names" target="_blank">[用户界面设置]</a>中设置“自定义客户端IP报头”选项。</p>
</td>
</tr>
</table>
<h4>电子邮箱相关</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">启用电子邮箱绑定功能</td>
<td>
<checkbox name="emailVerificationIsOn" v-model="config.emailVerification.isOn"></checkbox>
<p class="comment">选中后,电子邮箱需要激活之后可以使用邮箱登录、找回密码等。此功能需要事先设置 <a href="/users/setting/email" target="_blank">[激活邮件设置]</a></p>
</td>
</tr>
<tbody v-show="config.emailVerification.isOn">
<tr>
<td>提示用户未绑定</td>
<td>
<checkbox name="emailVerificationShowNotice" v-model="config.emailVerification.showNotice"></checkbox>
<p class="comment">选中后,将在页面上提示用户尚未绑定电子邮箱.</p>
</td>
</tr>
<tr>
<td>允许使用电子邮箱登录</td>
<td>
<checkbox name="emailVerificationCanLogin" v-model="config.emailVerification.canLogin"></checkbox>
<p class="comment">选中后,表示允许用户使用激活后的邮箱登录。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator v-model="emailVerificationMoreOptions"></more-options-indicator></td>
</tr>
<tr v-show="emailVerificationMoreOptions">
<td>激活邮件标题</td>
<td>
<input type="text" name="emailVerificationSubject" v-model="config.emailVerification.subject"/>
<p class="comment">其中<code-label>${product.name}</code-label>为当前设置的<a href="/settings/ui" target="_blank">产品名称</a></p>
</td>
</tr>
<tr v-show="emailVerificationMoreOptions">
<td>激活邮件内容</td>
<td>
<textarea name="emailVerificationBody" rows="8" v-model="config.emailVerification.body"></textarea>
<p class="comment">可以使用简单的HTML其中<code-label>${product.name}</code-label>为当前设置的<a href="/settings/ui" target="_blank">产品名称</a><code-label>${url.verify}</code-label>为生成的激活地址,<code-label>${url.home}</code-label>为用户平台主页地址,由<a href="/settings/userNodes" target="_blank">用户节点</a>访问地址组合而成。</p>
</td>
</tr>
</tbody>
</table>
<p class="comment" v-if="config.emailVerification.isOn">修改邮箱相关设置后,请记得自行注册新用户测试。</p>
<div class="margin"></div>
<div v-show="config.emailVerification.isOn">
<h4>通过邮箱找回密码</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">启用找回密码功能</td>
<td>
<checkbox name="emailResetPasswordIsOn" v-model="config.emailResetPassword.isOn"></checkbox>
<p class="comment">选中后,用户可以通过已绑定的电子邮箱找回密码;此功能需要同时开启电子邮箱绑定功能。</p>
</td>
</tr>
<tbody v-show="config.emailResetPassword.isOn">
<tr>
<td colspan="2"><more-options-indicator v-model="emailResetPasswordMoreOptions"></more-options-indicator></td>
</tr>
<tr v-show="emailResetPasswordMoreOptions">
<td>找回密码邮件标题</td>
<td>
<input type="text" name="emailResetPasswordSubject" v-model="config.emailResetPassword.subject" maxlength="100"/>
<p class="comment">其中<code-label>${product.name}</code-label>为当前设置的<a href="/settings/ui" target="_blank">产品名称</a></p>
</td>
</tr>
<tr v-show="emailResetPasswordMoreOptions">
<td>找回密码邮件内容</td>
<td>
<textarea name="emailResetPasswordBody" v-model="config.emailResetPassword.body" rows="5"></textarea>
<p class="comment">可以使用简单的HTML其中<code-label>${product.name}</code-label>为当前设置的<a href="/settings/ui" target="_blank">产品名称</a><code-label>${code}</code-label>为找回密码时用到的验证码,<code-label>${url.home}</code-label>为用户平台主页地址,由<a href="/settings/userNodes" target="_blank">用户节点</a>访问地址组合而成。</p>
</td>
</tr>
</tbody>
</table>
<div class="margin"></div>
</div>
<h4>手机号码相关</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">启用手机号码绑定功能</td>
<td>
<checkbox name="mobileVerificationIsOn" v-model="config.mobileVerification.isOn"></checkbox>
<p class="comment">选中后,手机号码需要激活之后可以使用手机号码、找回密码等。此功能需要事先设置 <a href="/users/setting/sms" target="_blank">[激活短信设置]</a></p>
</td>
</tr>
<tbody v-show="config.mobileVerification.isOn">
<tr>
<td>提示用户未绑定</td>
<td>
<checkbox name="mobileVerificationShowNotice" v-model="config.mobileVerification.showNotice"></checkbox>
<p class="comment">选中后,将在页面上提示用户尚未绑定手机号码.</p>
</td>
</tr>
<tr>
<td>允许使用手机号码登录</td>
<td>
<checkbox name="mobileVerificationCanLogin" v-model="config.mobileVerification.canLogin"></checkbox>
<p class="comment">选中后,表示允许用户使用激活后的手机号码登录。</p>
</td>
</tr>
<tr>
<td>强制绑定手机号</td>
<td>
<checkbox name="mobileVerificationForce" v-model="config.mobileVerification.force"></checkbox>
<p class="comment">选中后,表示强制用户必须绑定手机号后才能使用其他功能。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator v-model="mobileVerificationMoreOptions"></more-options-indicator></td>
</tr>
<tr v-show="mobileVerificationMoreOptions">
<td>激活短信内容</td>
<td>
<textarea name="mobileVerificationBody" rows="8" v-model="config.mobileVerification.body"></textarea>
<p class="comment">其中使用<code-label>${code}</code-label>表示验证码。</p>
</td>
</tr>
</tbody>
</table>
<p class="comment" v-if="config.mobileVerification.isOn">修改手机号码相关设置后,请记得自行注册新用户测试。</p>
<div class="margin"></div>
<h4>CDN服务</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">开通CDN服务</td>
<td>
<checkbox name="cdnIsOn" v-model="config.cdnIsOn"></checkbox>
<p class="comment">选中表示自动为用户开通CDN服务。 </p>
</td>
</tr>
<tbody v-show="config.cdnIsOn">
<tr>
<td>CDN集群分配 *</td>
<td>
<cluster-selector :v-cluster-id="config.clusterId"></cluster-selector>
<p class="comment">选择用户新创建服务自动分配的集群。</p>
</td>
</tr>
<tr>
<td>默认开通功能</td>
<td>
<!-- 已选中功能 -->
<div>
<div v-if="selectedFeatureNames().length > 0">
<span>{{selectedFeatureNames()}}</span>
</div>
<span v-else class="grey">暂时还没有开通任何功能。</span>
<div style="margin-top: 0.1em">
<a href="" @click.prevent="showFeatures">修改<i class="icon angle" :class="{up: featuresVisible, down: !featuresVisible}"></i></a>
</div>
</div>
<div class="feature-boxes" v-show="featuresVisible" style="margin-top: 1em">
<div class="feature-box" v-for="feature in features">
<checkbox name="features" :v-value="feature.code" v-model="feature.isChecked">{{feature.name}}</checkbox>
<p class="comment">{{feature.description}}</p>
</div>
</div>
</td>
</tr>
<tr>
<td>对已有用户功能操作</td>
<td>
<select class="ui dropdown auto-width" name="featureOp" v-model="featureOp">
<option value="overwrite">覆盖</option>
<option value="append">追加</option>
<option value="keep">保持</option>
</select>
<p class="comment" v-if="featureOp == 'overwrite'">覆盖用户已经有的功能设置。</p>
<p class="comment" v-if="featureOp == 'append'">只追加用户功能,不减少。</p>
<p class="comment" v-if="featureOp == 'keep'">保持原有用户功能不变,功能修改只对新用户生效。</p>
</td>
</tr>
</tbody>
</table>
<div v-show="adIsVisible">
<div class="margin"></div>
<h4>DDoS高防</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">开通DDoS高防管理</td>
<td><checkbox name="adIsOn" v-model="config.adIsOn"></checkbox>
<p class="comment">选中表示自动为用户开通DDoS高防IP使用服务。</p>
</td>
</tr>
</table>
<div class="margin"></div>
</div>
<div v-show="nsIsVisible">
<div class="margin"></div>
<h4>智能DNS服务</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">开通智能DNS服务</td>
<td><checkbox name="nsIsOn" v-model="config.nsIsOn"></checkbox>
<p class="comment">选中表示自动为用户开通智能DNS服务。</p>
</td>
</tr>
</table>
<p class="comment" v-show="config.nsIsOn"><a href="/ns/settings/user">智能DNS相关功能设置</a></p>
<div class="margin"></div>
</div>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,30 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.emailVerificationMoreOptions = false
this.emailResetPasswordMoreOptions = false
this.mobileVerificationMoreOptions = false
this.mobileResetPasswordMoreOptions = false
this.featureOp = "overwrite"
this.featuresVisible = false
this.showFeatures = function () {
this.featuresVisible = !this.featuresVisible
}
this.selectedFeatureNames = function () {
if (this.features == null) {
return ""
}
let names = []
this.features.forEach(function (v) {
if (v.isChecked) {
names.push(v.name)
}
})
return names.join(" / ")
}
})

View File

@@ -0,0 +1,13 @@
.feature-boxes {
.feature-box {
margin-bottom: 1em;
width: 24em;
float: left;
}
.feature-box:hover {
label {
font-weight: bold;
}
}
}

View File

@@ -0,0 +1,85 @@
{$layout}
{$template "menu"}
<div class="margin"></div>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td>必须使用套餐</td>
<td>
<checkbox name="requirePlan" v-model="config.requirePlan"></checkbox>
<p class="comment">选中后,用户在创建网站时,必须选择一个套餐。</p>
</td>
</tr>
<tr>
<td class="title">网站分组<optional-label></optional-label></td>
<td>
<select class="ui dropdown auto-width" name="groupId" v-model="config.groupId">
<option value="0">[选择分组]</option>
<option v-for="group in groups" :value="group.id">{{group.name}}</option>
</select>
<p class="comment">用户创建的新网站自动加入此分组。</p>
</td>
</tr>
<tr>
<td>启用统计</td>
<td>
<checkbox name="enableStat" v-model="config.enableStat"></checkbox>
<p class="comment">选中后,用户在创建网站后,自动启用统计。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>缓存清理任务限制</td>
<td>
<div class="ui fields">
<div class="ui field" style="width: 15em">
<div class="ui input right labeled">
<digit-input name="purgeMaxKeysPerTask" v-model="config.httpCacheTaskPurgeConfig.maxKeysPerTask"></digit-input>
<span class="ui label">个URL/每任务</span>
</div>
<p class="comment">每次能提交的最多URL数量默认{{defaultMaxCacheKeysPerTask}}。</p>
</div>
</div>
<div class="ui fields">
<div class="ui field" style="width: 16em">
<div class="ui input right labeled">
<digit-input name="purgeMaxKeysPerDay" v-model="config.httpCacheTaskPurgeConfig.maxKeysPerDay"></digit-input>
<span class="ui label">个URL/天/每用户</span>
</div>
<p class="comment">每天每个用户能提交的最多URL数量默认{{defaultMaxCacheKeysPerDay}}。</p>
</div>
</div>
</td>
</tr>
<tr>
<td>缓存预热任务限制</td>
<td>
<div class="ui fields">
<div class="ui field" style="width: 15em">
<div class="ui input right labeled">
<digit-input name="fetchMaxKeysPerTask" v-model="config.httpCacheTaskFetchConfig.maxKeysPerTask" style="width: 5em"></digit-input>
<span class="ui label">个URL/每任务</span>
</div>
<p class="comment">每次能提交的最多URL数量默认{{defaultMaxCacheKeysPerTask}}。</p>
</div>
</div>
<div class="ui fields">
<div class="ui field" style="width: 16em">
<div class="ui input right labeled">
<digit-input name="fetchMaxKeysPerDay" v-model="config.httpCacheTaskFetchConfig.maxKeysPerDay" style="width: 5em"></digit-input>
<span class="ui label">个URL/天/每用户</span>
</div>
<p class="comment">每天每个用户能提交的最多URL数量默认{{defaultMaxCacheKeysPerDay}}。</p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,14 @@
{$layout}
{$template "menu"}
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<h4>激活短信</h4>
<sms-sender v-model="config.verifySMS" name="verifySMSJSON"></sms-sender>
<h4>通知短信</h4>
<sms-sender v-model="config.notifySMS" name="notifySMSJSON"></sms-sender>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,31 @@
{$layout "layout_popup"}
<h3>短信发送测试</h3>
<form class="ui form" data-tea-action="$" data-tea-success="successSend" data-tea-before="before" data-tea-done="done">
<csrf-token></csrf-token>
<input type="hidden" name="configJSON" :value="JSON.stringify(config)"/>
<table class="ui table selectable definition">
<tr>
<td class="color-border">收信人手机号 *</td>
<td>
<input type="text" name="toMobile" placeholder="138xxxxxxx" ref="focus"/>
</td>
</tr>
<tr>
<td class="color-border">测试内容 *</td>
<td>
<textarea name="body" rows="3" maxlength="50">测试短信内容</textarea>
</td>
</tr>
<tr>
<td class="color-border">验证码</td>
<td>
<input type="text" name="code" maxlength="10" style="width: 10em"/>
<p class="comment">可选参数,仅验证类短信需要填写。</p>
</td>
</tr>
</table>
<submit-btn v-show="!isSending">发送测试短信</submit-btn>
<button class="ui button disabled" type="button" v-show="isSending">发送中...</button>
</form>

View File

@@ -0,0 +1,17 @@
Tea.context(function () {
this.config = window.parent.TESTING_SMS_CONFIG
this.isSending = false
this.before = function () {
this.isSending = true
}
this.done = function () {
this.isSending = false
}
this.successSend = function () {
teaweb.success("发送成功")
}
})

View File

@@ -0,0 +1,104 @@
{$layout}
{$template "user_menu"}
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="userId" :value="user.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" ref="focus" v-model="user.username"/>
<p class="comment">用户名只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>密码</td>
<td>
<div>
<a href="" @click.prevent="changePasswordEditing">修改密码<i class="icon angle" :class="{down:!passwordEditing, up:passwordEditing}"></i></a>
</div>
<div v-show="passwordEditing" style="margin-top: 0.6em">
<input type="password" name="pass1" maxlength="100"/>
<p class="comment">留空表示不修改。</p>
</div>
</td>
</tr>
<tr v-show="passwordEditing">
<td>确认密码</td>
<td>
<input type="password" name="pass2" maxlength="100"/>
</td>
</tr>
<tr>
<td>全名 *</td>
<td>
<input type="text" name="fullname" maxlength="100" v-model="user.fullname"/>
<p class="comment">用户姓名或者公司名称等等。</p>
</td>
</tr>
<tr>
<td>关联集群 *</td>
<td>
<cluster-selector :v-cluster-id="clusterId"></cluster-selector>
<p class="comment">用户发布的网站会自动部署到此集群,修改此选项会同步修改当前用户下的所有网站,但不影响和套餐绑定的服务。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>手机号</td>
<td>
<input type="text" name="mobile" maxlength="11" v-model="user.mobile"/>
</td>
</tr>
<tr>
<td>联系电话</td>
<td>
<input type="text" name="tel" maxlength="100" v-model="user.tel"/>
</td>
</tr>
<tr>
<td>电子邮箱</td>
<td>
<input type="text" name="email" maxlength="100" v-model="user.email"/>
</td>
</tr>
<tr>
<td>OTP认证</td>
<td>
<checkbox name="otpOn" v-model="user.otpLoginIsOn">启用OTP</checkbox>
<p class="comment">启用OTP认证后在用户登录的时候需要同时填写OTP动态密码。</p>
</td>
</tr>
<tr>
<td>备注</td>
<td>
<textarea rows="3" name="remark" v-model="user.remark"></textarea>
</td>
</tr>
<tr>
<td>带宽算法</td>
<td>
<select class="ui dropdown auto-width" name="bandwidthAlgo" v-model="user.bandwidthAlgo">
<option value="">[使用默认]</option>
<option value="secondly">峰值带宽</option>
<option value="avg">平均带宽</option>
</select>
<p class="comment" v-if="user.bandwidthAlgo == 'secondly'">按在计时时间段内5分钟最高带宽峰值计算比如5分钟内最高的某个时间点带宽为100Mbps那么就认为此时间段内的峰值带宽为100Mbps。</p>
<p class="comment" v-if="user.bandwidthAlgo == 'avg'">按在计时时间段内5分钟平均带宽计算即此时间段内的总流量除以时间段的秒数比如5分钟300秒内总流量600MiB那么带宽即为<code-label>600MiB * 8bit/300s = 16Mbps</code-label>;通常平均带宽算法要比峰值带宽要少很多。</p>
</td>
</tr>
<tr>
<td>启用当前用户</td>
<td>
<checkbox name="isOn" v-model="user.isOn"></checkbox>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,9 @@
Tea.context(function () {
this.success = NotifySuccess("保存成功", "/users/user?userId=" + this.user.id)
this.passwordEditing = false
this.changePasswordEditing = function () {
this.passwordEditing = !this.passwordEditing
}
})

View File

@@ -0,0 +1,117 @@
{$layout}
{$template "user_menu"}
<table class="ui table definition selectable">
<tr>
<td>状态</td>
<td>
<span v-if="!user.isVerified" class="red">
信息未审核 &nbsp; <a href="" @click.prevent="verify">[审核]</a>
</span>
<span v-else-if="user.isRejected" class="red">已拒绝
&nbsp; <a href="" @click.prevent="verify">[重新审核]</a>
</span>
<span v-else>
<label-on :v-is-on="user.isOn"></label-on>
</span>
<p class="comment" v-if="user.isVerified && user.isRejected && user.rejectReason.length > 0">拒绝原因:{{user.rejectReason}}</p>
</td>
</tr>
<tr>
<td class="title">用户名</td>
<td>
{{user.username}}
</td>
</tr>
<tr>
<td>全名</td>
<td>
{{user.fullname}}
</td>
</tr>
<tr>
<td>关联集群</td>
<td>
<span v-if="user.cluster != null">{{user.cluster.name}} <link-icon :href="'/clusters/cluster?clusterId=' + user.cluster.id"></link-icon></span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>手机号</td>
<td>
<span v-if="user.mobile.length > 0">{{user.mobile}}</span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>联系电话</td>
<td>
<span v-if="user.tel.length > 0">{{user.tel}}</span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>常用电子邮箱</td>
<td>
<span v-if="user.email.length > 0">{{user.email}}</span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>已绑定电子邮箱</td>
<td>
<span v-if="user.verifiedEmail.length > 0">{{user.verifiedEmail}}</span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>备注</td>
<td>
<span v-if="user.remark.length > 0">{{user.remark}}</span>
<span v-else class="disabled">没有设置。</span>
</td>
</tr>
<tr>
<td>带宽算法</td>
<td>
<span v-if="user.bandwidthAlgo.length > 0">
<span v-if="user.bandwidthAlgo == 'secondly'">峰值带宽</span>
<span v-if="user.bandwidthAlgo == 'avg'">平均带宽</span>
</span>
<span v-else class="disabled">使用默认。</span>
</td>
</tr>
<tr>
<td>注册IP</td>
<td>
<span v-if="user.registeredIP.length == 0" class="disabled">-</span>
<span v-else-if="user.registeredRegion.length == 0">{{user.registeredIP}}</span>
<span v-else>{{user.registeredIP}}<span class="grey small">{{user.registeredRegion}}</span></span>
</td>
</tr>
</table>
<h3>OTP认证</h3>
<table class="ui table definition selectable">
<tr>
<td class="title">状态</td>
<td>
<span v-if="otp != null && otp.isOn" class="green">已启用</span>
<span v-else class="disabled">未启用</span>
</td>
</tr>
<tr v-if="otp != null && otp.isOn">
<td colspan="2"><more-options-indicator>更多信息</more-options-indicator></td>
</tr>
<tr v-if="otp != null && otp.isOn && moreOptionsVisible">
<td>认证二维码</td>
<td>
<img alt="qrcode" :src="'./otpQrcode?userId=' + user.id"/>
<p class="comment"><a :href="'./otpQrcode?userId=' + user.id + '&download=true'">[下载]</a> &nbsp; 可以通过二维码快速添加OTP认证信息到认证App中。</p>
</td>
</tr>
<tr v-if="otp != null && otp.isOn && moreOptionsVisible">
<td>密钥</td>
<td>{{otp.params.secret}}</td>
</tr>
</table>

View File

@@ -0,0 +1,11 @@
Tea.context(function () {
this.verify = function () {
teaweb.popup(".verifyPopup?userId=" + this.user.id, {
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
})

View File

@@ -0,0 +1,35 @@
.ui.message .icon.remove {
position: absolute;
right: 1em;
top: 2.2em;
}
.ui.message .content {
padding-right: 1em;
}
.node-logs-box {
max-height: 14em;
overflow-y: auto;
}
.node-logs-box::-webkit-scrollbar {
width: 4px;
}
.server-name-td {
position: relative;
}
.server-name-td .icon.setting {
display: none;
position: absolute;
right: 1em;
top: 50%;
margin-top: -1em;
}
.server-name-td:hover .icon.setting {
display: inline;
}
.bandwidth-span var {
font-size: 0.8em;
}
.bandwidth-span var span {
font-size: 1.2em;
}
/*# sourceMappingURL=servers.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["servers.less"],"names":[],"mappings":"AAAA,GAAG,QACF,MAAK;EACJ,kBAAA;EACA,UAAA;EACA,UAAA;;AAJF,GAAG,QAOF;EACC,kBAAA;;AAIF;EACC,gBAAA;EACA,gBAAA;;AAGD,cAAc;EACb,UAAA;;AAGD;EACC,kBAAA;;AADD,eAGC,MAAK;EACJ,aAAA;EACA,kBAAA;EACA,UAAA;EACA,QAAA;EACA,gBAAA;;AAIF,eAAe,MACd,MAAK;EACJ,eAAA;;AAIF,eACC;EACC,gBAAA;;AAFF,eACC,IAGC;EACC,gBAAA","file":"servers.css"}

View File

@@ -0,0 +1,95 @@
{$layout}
{$template "../user_menu"}
<columns-grid>
<div class="ui column" v-if="uiConfig.showTrafficCharts">
<h4>本月总流量</h4>
<div class="value"><bytes-var :v-bytes="dashboard.monthlyTrafficBytes"></bytes-var></div>
</div>
<div class="ui column" v-if="uiConfig.showBandwidthCharts">
<h4>本月带宽峰值</h4>
<div class="value"><bits-var :v-bits="dashboard.monthlyPeekBandwidthBits"></bits-var></div>
</div>
<div class="ui column" v-if="uiConfig.showTrafficCharts">
<h4>今日流量</h4>
<div class="value"><bytes-var :v-bytes="dashboard.dailyTrafficBytes"></bytes-var></div>
</div>
<div class="ui column" v-if="uiConfig.showBandwidthCharts">
<h4>今日带宽峰值</h4>
<div class="value"><bits-var :v-bits="dashboard.dailyPeekBandwidthBits"></bits-var></div>
</div>
</columns-grid>
<h4>网站列表</h4>
<p class="comment" v-if="servers.length == 0">当前用户下暂时还没有网站。</p>
<table class="ui table celled selectable" v-if="servers.length > 0">
<thead>
<tr>
<th>网站名称</th>
<th>部署集群</th>
<th>域名</th>
<th>端口</th>
<th class="center" style="width: 8em">下行带宽<tip-icon content="最近5分钟峰值带宽每5分钟更新一次"></tip-icon><sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="two wide center">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="server in servers">
<td class="server-name-td"><a :href="'/servers/server?serverId=' + server.id"><keyword :v-word="keyword">{{server.name}}</keyword></a> &nbsp; <a :href="'/servers/server/settings?serverId=' + server.id" title="设置"><i class="icon setting grey"></i></a>
<div style="margin-top:0.4em">
<grey-label>{{server.serverTypeName}}</grey-label>
</div>
</td>
<td>{{server.cluster.name}}</td>
<td>
<span v-if="server.firstServerName.length > 0">
<keyword :v-word="keyword">{{server.firstServerName}}</keyword>
<span v-if="server.countServerNames > 1">等{{server.countServerNames}}个域名 <popup-icon :href="'/servers/serverNamesPopup?serverId=' + server.id" height="20em"></popup-icon></span>
</span>
<span v-else class="disabled">-</span>
<!-- 审核中 -->
<div v-if="server.isAuditing" style="margin-top: 0.5em">
<a class="ui label basic tiny red" title="点击跳到审核页面" :href="'/servers/server/settings/serverNames?serverId=' + server.id">审核中<span class="small grey" v-if="server.auditingTime.length > 0">{{server.auditingTime}}</span> &nbsp;<i class="icon long arrow right alternate"></i></a>
</div>
<!-- 审核失败 -->
<div v-if="!server.auditingIsOk" style="margin-top: 0.5em">
<a class="ui label basic tiny red" title="点击跳到审核页面" :href="'/servers/server/settings/serverNames?serverId=' + server.id">审核不通过 &nbsp;<i class="icon long arrow right alternate"></i></a>
</div>
</td>
<td>
<span v-if="server.ports.length == 0">-</span>
<div v-for="port in server.ports">
<tiny-basic-label><keyword :v-word="keyword">{{port.portRange}}</keyword><span class="small">{{port.protocol}}</span></tiny-basic-label>
</div>
</td>
<td class="center">
<span v-if="server.bandwidthBits > 0" class="bandwidth-span"><bits-var :v-bits="server.bandwidthBits"></bits-var></span>
<span class="disabled" v-else>-</span>
</td>
<td class="center">
<div v-if="!checkDNS">
<label-on :v-is-on="server.isOn"></label-on>
</div>
<div v-else>
<span v-if="!server.isOn" class="grey">停用中</span>
<span v-else-if="server.status.isOk" class="green">正常</span>
<span v-else-if="server.status.message.length == 0">检查中</span>
<span v-else class="red">{{server.status.message}}
<tip-icon :content="server.status.todo"></tip-icon>
</span>
</div>
</td>
<td>
<a :href="'/servers/server?serverId=' + server.id">详情</a> &nbsp;
<a :href="'/servers/server/settings?serverId=' + server.id">设置</a>
</td>
</tr>
</table>
<page-box></page-box>

View File

@@ -0,0 +1,48 @@
.ui.message {
.icon.remove {
position: absolute;
right: 1em;
top: 2.2em;
}
.content {
padding-right: 1em;
}
}
.node-logs-box {
max-height: 14em;
overflow-y: auto;
}
.node-logs-box::-webkit-scrollbar {
width: 4px;
}
.server-name-td {
position: relative;
.icon.setting {
display: none;
position: absolute;
right: 1em;
top: 50%;
margin-top: -1em;
}
}
.server-name-td:hover {
.icon.setting {
display: inline;
}
}
.bandwidth-span {
var {
font-size: 0.8em;
span {
font-size: 1.2em;
}
}
}

View File

@@ -0,0 +1,31 @@
{$layout "layout_popup"}
<h3>审核</h3>
<form class="ui form" data-tea-success="success" data-tea-action="$">
<csrf-token></csrf-token>
<input type="hidden" name="userId" :value="userId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">审核结果</td>
<td>
<select class="ui dropdown auto-width" name="result" v-model="result">
<option value="pass">通过</option>
<option value="reject">拒绝</option>
<option value="delete">拒绝并删除</option>
</select>
<p class="comment" v-if="result == 'pass'">通过后,用户可正常创建服务。</p>
<p class="comment" v-if="result == 'reject'">拒绝后,用户不可创建服务。</p>
<p class="comment" v-if="result == 'delete'">将删除当前用户信息。</p>
</td>
</tr>
<tr v-if="result == 'reject' || result == 'delete'">
<td>拒绝原因</td>
<td>
<textarea rows="2" name="rejectReason"></textarea>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.result = "pass"
})