This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
<first-menu>
<menu-item href="/settings/user-ui" code="index">界面设置</menu-item>
<span class="disabled item">|</span>
<menu-item href="/settings/user-ui/posts" code="post">文章管理</menu-item>
<span class="disabled item">|</span>
<menu-item href="/settings/userNodes" target="_blank">用户节点 &nbsp;<i class="icon external small"></i></menu-item>
</first-menu>

View File

@@ -0,0 +1,15 @@
.theme-background-preview-box {
width: 1.6em;
height: 1.6em;
}
.color-spans {
position: absolute;
margin-top: 0.2em;
}
.color-spans .color-span {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.6em;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,YAAA;EACA,aAAA;;AAGD;EACC,kBAAA;EACA,iBAAA;;AAFD,YAGC;EACC,qBAAA;EACA,UAAA;EACA,WAAA;EACA,mBAAA","file":"index.css"}

View File

@@ -0,0 +1,221 @@
{$layout}
{$template "menu"}
<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="productName" v-model="config.productName" maxlength="100"/>
</td>
</tr>
<tr>
<td>用户系统名称 *</td>
<td>
<input type="text" name="userSystemName" v-model="config.userSystemName" maxlength="100"/>
</td>
</tr>
<tr>
<td>浏览器图标</td>
<td>
<div v-if="config.faviconFileId > 0">
<a :href="'/ui/image/' + config.faviconFileId" target="_blank"><img :src="'/ui/image/' + config.faviconFileId" style="width:32px;border:1px #ccc solid;"/></a>
</div>
<div v-else>
<span class="disabled">还没有上传。</span>
</div>
<div style="margin-top: 0.8em">
<input type="file" name="faviconFile" accept=".png"/>
</div>
<p class="comment">在浏览器标签栏显示的图标请使用PNG格式。</p>
</td>
</tr>
<tr>
<td>Logo图</td>
<td>
<div v-if="config.logoFileId > 0">
<a :href="'/ui/image/' + config.logoFileId" target="_blank"><img :src="'/ui/image/' + config.logoFileId" style="width:32px;border:1px #ccc solid; padding: 4px; background: #276AC6"/></a>
</div>
<div v-else>
<span class="disabled">还没有上传。</span>
</div>
<div style="margin-top: 0.8em">
<input type="file" name="logoFile" accept=".png"/>
</div>
<p class="comment">显示在系统控制面板界面上的图标请使用PNG格式将会被用于深色背景之上请使用浅色勾勒线条。</p>
</td>
</tr>
<tr>
<td class="color-border">显示版本号</td>
<td>
<checkbox name="showVersion" v-model="config.showVersion"></checkbox>
<p class="comment">选中后,表示在系统名称后面加入版本号。</p>
</td>
</tr>
<tr v-show="config.showVersion">
<td class="color-border">定制版本号</td>
<td>
<input type="text" name="version" v-model="config.version" maxlength="100"/>
<p class="comment">定制自己的版本号,留空表示使用系统自带的版本号。</p>
</td>
</tr>
<tr>
<td>页面背景颜色</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<div class="ui input left labeled">
<span class="ui label">#</span>
<input type="text" maxlength="7" style="width: 8em" v-model="config.theme.backgroundColor" @input="changeThemeBackgroundColor" placeholder="16进制色值" name="themeBackgroundColor"/>
</div>
</div>
<div class="ui field">
<div class="theme-background-preview-box" :style="'background-color: ' + ((config.theme.backgroundColor.length > 0) ? '#' + config.theme.backgroundColor : '')" title="背景颜色预览" ref="themeBackgroundPreviewBox"></div>
</div>
</div>
<p class="comment">整个页面框架的背景颜色,留空表示使用默认的背景颜色。可点击使用:
<span class="color-spans">
<a href="" v-for="color in themeBackgroundColors" @click.prevent="selectThemeBackgroundColor(color)" :title="'#' + color"><span class="color-span" :style="'background-color: #' + color">&nbsp;</span></a>
</span>
</p>
</td>
</tr>
<tr>
<td>显示页脚</td>
<td>
<checkbox name="showPageFooter" v-model="config.showPageFooter"></checkbox>
</td>
</tr>
<tr v-show="config.showPageFooter">
<td>自定义页脚HTML</td>
<td>
<textarea rows="3" name="pageFooterHTML" v-model="config.pageFooterHTML"></textarea>
<p class="comment">类似于<code-label>&lt;a class=&quot;item&quot;&gt;内容1&lt;/a&gt;&lt;a class=&quot;item&quot;&gt;内容2&lt;/a&gt;</code-label></p>
</td>
</tr>
<tr>
<td>显示财务相关功能</td>
<td>
<checkbox name="showFinance" v-model="config.showFinance"></checkbox>
<p class="comment">选中后,表示在菜单中显示财务管理功能。</p>
</td>
</tr>
<tr>
<td>注册设置</td>
<td>
<a href="/users/setting">修改用户注册相关设置 &raquo;</a>
</td>
</tr>
</table>
<h4>网站相关设置</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">自动检查CNAME</td>
<td>
<checkbox name="serverCheckCNAME" v-model="config.server.checkCNAME"></checkbox>
<p class="comment">选中后表示自动在网站列表中检查网站域名是否已正确解析到对应的CNAME。</p>
</td>
</tr>
</table>
<h4>数据看板设置</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">显示流量相关数据和图表</td>
<td><checkbox name="showTrafficCharts" v-model="config.showTrafficCharts"></checkbox></td>
</tr>
<tr>
<td>在流量图表中显示缓存相关信息</td>
<td>
<checkbox name="showCacheInfoInTrafficCharts" v-model="config.showCacheInfoInTrafficCharts"></checkbox>
<p class="comment">选中后,表示在流量图表中显示缓存流量、缓存命中率等信息。</p>
</td>
</tr>
<tr>
<td class="title">显示带宽相关数据和图表</td>
<td><checkbox name="showBandwidthCharts" v-model="config.showBandwidthCharts"></checkbox></td>
</tr>
<tr v-show="false">
<td class="title">带宽单位</td>
<td>
<select class="ui dropdown auto-width" name="bandwidthUnit" v-model="config.bandwidthUnit">
<option value="bit">比特</option>
<option value="byte">字节</option>
</select>
</td>
</tr>
</table>
<h4>门户页面</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">启用内置门户页面</td>
<td>
<checkbox name="portalIsOn" v-model="config.portal.isOn"></checkbox>
<p class="comment">启用内置门户页面后,访问用户系统首页会自动跳转到内置的门户页面(网址是:<code-label>/portal</code-label>)。</p>
</td>
</tr>
<tr v-show="config.portal.isOn">
<td>Logo图</td>
<td>
<div v-if="config.portal.logoFileId > 0">
<a :href="'/ui/image/' + config.portal.logoFileId" target="_blank"><img :src="'/ui/image/' + config.portal.logoFileId" style="width:32px;border:1px #ccc solid; padding: 4px;" alt=""/></a>
</div>
<div v-else>
<span class="disabled">还没有上传。</span>
</div>
<div style="margin-top: 0.8em">
<input type="file" name="portalLogoFile" accept=".png"/>
</div>
<p class="comment">显示在门户页面上的图标请使用PNG格式。</p>
</td>
</tr>
</table>
<h4>其他</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">时区</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<select class="ui dropdown" v-model="timeZoneGroupCode">
<option v-for="timeZoneGroup in timeZoneGroups" :value="timeZoneGroup.code">{{timeZoneGroup.name}}</option>
</select>
</div>
<div class="ui field">
<select class="ui dropdown" name="timeZone" v-model="config.timeZone">
<option v-for="timeZoneLocation in timeZoneLocations" :value="timeZoneLocation.name" v-if="timeZoneLocation.group == timeZoneGroupCode">{{timeZoneLocation.name}} ({{timeZoneLocation.offset}})</option>
</select>
</div>
</div>
<p class="comment">用户系统显示时间使用的时区。</p>
</td>
</tr>
<tr>
<td>使用的DNS解析库</td>
<td>
<select class="ui dropdown auto-width" name="dnsResolverType" v-model="config.dnsResolver.type">
<option value="default">默认</option>
<option value="cgo">CGO</option>
<option value="goNative">Go原生</option>
</select>
<p class="comment">用户系统使用的DNS解析库修改此项配置后需要重启用户系统进程才会生效通常不需要修改如要修改请在专家指导下进行。</p>
</td>
</tr>
<tr>
<td>自定义客户端IP报头<a id="client-ip-header-names"></a></td>
<td>
<input type="text" name="clientIPHeaderNames" v-model="config.clientIPHeaderNames"/>
<p class="comment">可以通过此报头获取客户端IP类似于<code-label>X-Forwarded-For X-Real-IP True-Client-IP Client-IP</code-label>&nbsp;<a href=""><span class="small" @click.prevent="addDefaultClientIPHeaderNames('X-Forwarded-For X-Real-IP True-Client-IP Client-IP')">[填入]</span></a>,用于使用反向代理访问用户系统的情形;如果有多个报头可以使用空格隔开。</p>
</td>
</tr>
</table>
<p class="comment">修改后在3分钟内生效。</p>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,59 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
// 时区
this.timeZoneGroupCode = "asia"
if (this.timeZoneLocation != null) {
this.timeZoneGroupCode = this.timeZoneLocation.group
}
let oldTimeZoneGroupCode = this.timeZoneGroupCode
let oldTimeZoneName = ""
if (this.timeZoneLocation != null) {
oldTimeZoneName = this.timeZoneLocation.name
}
this.$delay(function () {
this.$watch("timeZoneGroupCode", function (groupCode) {
if (groupCode == oldTimeZoneGroupCode && oldTimeZoneName.length > 0) {
this.config.timeZone = oldTimeZoneName
return
}
let firstLocation = null
this.timeZoneLocations.forEach(function (v) {
if (firstLocation != null) {
return
}
if (v.group == groupCode) {
firstLocation = v
}
})
if (firstLocation != null) {
this.config.timeZone = firstLocation.name
}
})
})
this.changeThemeBackgroundColor = function () {
let color = this.config.theme.backgroundColor
if (!color.startsWith("#")) {
color = "#" + color
}
this.$refs.themeBackgroundPreviewBox.style.cssText = "background-color: " + color
}
this.selectThemeBackgroundColor = function (color) {
this.config.theme.backgroundColor = color
this.changeThemeBackgroundColor()
}
this.addDefaultClientIPHeaderNames = function (headerNames) {
if (this.config.clientIPHeaderNames == null || this.config.clientIPHeaderNames.length == 0) {
this.config.clientIPHeaderNames = headerNames
} else {
this.config.clientIPHeaderNames += " " + headerNames
}
}
})

View File

@@ -0,0 +1,16 @@
.theme-background-preview-box {
width: 1.6em;
height: 1.6em;
}
.color-spans {
position: absolute;
margin-top: 0.2em;
.color-span {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.6em;
}
}

View File

@@ -0,0 +1,4 @@
<second-menu>
<menu-item href="/settings/user-ui/posts" :class="{active: secondMenuItem == 'index'}">文章列表</menu-item>
<menu-item href="/settings/user-ui/posts/create" :class="{active: secondMenuItem == 'create'}">创建文章</menu-item>
</second-menu>

View File

@@ -0,0 +1,4 @@
#editor {
height: 30em;
}
/*# sourceMappingURL=create.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["create.less"],"names":[],"mappings":"AAAA;EACC,YAAA","file":"create.css"}

View File

@@ -0,0 +1,61 @@
{$layout}
{$template "../menu"}
{$template "menu"}
{$var "header"}
<link href="/js/quill/quill.snow.css" rel="stylesheet">
<script src="/js/quill/quill.min.js"></script>
{$end}
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="body" v-model="body"/>
<table class="ui table definition selectable">
<tr>
<td>所属产品 *</td>
<td>
<select class="ui dropdown auto-width" name="productCode">
<option value="">[所属产品]</option>
<option v-for="product in products" :value="product.code">{{product.name}}</option>
</select>
</td>
</tr>
<tr>
<td class="title">文章分类 *</td>
<td>
<select class="ui dropdown auto-width" name="categoryId">
<option value="0">[选择分类]</option>
<option v-for="category in categories" :value="category.id">{{category.name}}</option>
</select>
</td>
</tr>
<tr>
<td class="title">文章标题 *</td>
<td>
<input type="text" name="subject" ref="focus"/>
</td>
</tr>
<tr>
<td>文章类型 *</td>
<td>
<select class="ui dropdown auto-width" name="type" v-model="type">
<option value="normal">常规</option>
<option value="url">URL</option>
</select>
</td>
</tr>
<tr v-show="type == 'url'">
<td>URL *</td>
<td>
<input type="text" name="url" maxlength="200"/>
</td>
</tr>
<tr v-show="type == 'normal'">
<td>内容 *</td>
<td>
<div id="editor"></div>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,33 @@
Tea.context(function () {
this.success = NotifySuccess("保存成功", "/settings/user-ui/posts")
this.type = "normal"
// reference: https://quilljs.com/docs/modules/toolbar/
this.body = ""
let that = this
setTimeout(function () {
let quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{'header': 1}, {'header': 2}],
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'indent': '-1'}, {'indent': '+1'}],
[{'direction': 'rtl'}],
[{'size': ['small', false, 'large', 'huge']}],
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}],
[{'font': []}],
[{'align': []}],
['link', 'image'],
['clean']
]
}
})
quill.on("text-change", function () {
that.body = quill.root.innerHTML
})
}, 100)
})

View File

@@ -0,0 +1,3 @@
#editor {
height: 30em;
}

View File

@@ -0,0 +1,46 @@
{$layout}
{$template "../menu"}
{$template "menu"}
<not-found-box v-if="posts.length == 0">暂时还没有文章。</not-found-box>
<div v-if="posts.length > 0">
<table class="ui table selectable celled">
<thead>
<tr>
<th>文章标题</th>
<th class="two wide">所属产品</th>
<th class="two wide">文章分类</th>
<th class="three wide">创建时间</th>
<th class="width6">状态</th>
<th class="three op">操作</th>
</tr>
</thead>
<tr v-for="post in posts">
<td> <a :href="Tea.url('.post', {postId: post.id})">{{post.subject}}</a>
<div v-if="post.type == 'url'">
<grey-label>URL</grey-label>
</div>
</td>
<td>{{post.productName}}</td>
<td>
<span v-if="post.category != null && post.category.id > 0">{{post.category.name}}</span>
<span v-else class="disabled">已删除</span>
</td>
<td>{{post.createdTime}}</td>
<td>
<span v-if="post.isPublished" class="green">已发布</span>
<span v-else>未发布</span>
</td>
<td>
<a :href="Tea.url('.post', {postId: post.id})">详情</a> &nbsp;
<a href="" @click.prevent="deletePost(post.id)">删除</a> &nbsp;
<a href="" @click.prevent="publishPost(post.id)" v-if="!post.isPublished">发布</a>
<span v-else class="disabled">发布</span>
</td>
</tr>
</table>
</div>
<page-box></page-box>

View File

@@ -0,0 +1,27 @@
Tea.context(function () {
this.deletePost = function (postId) {
let that = this
teaweb.confirm("确定删除此文章吗?", function () {
that.$post(".delete")
.params({
postId: postId
})
.success(function () {
teaweb.successRefresh("发布成功")
})
})
}
this.publishPost = function (postId) {
let that = this
teaweb.confirm("确定发布此文章吗?", function () {
that.$post(".publish")
.params({
postId: postId
})
.success(function () {
teaweb.successRefresh("发布成功")
})
})
}
})

View File

@@ -0,0 +1,44 @@
{$layout}
{$template "../menu"}
{$template "menu"}
<p class="comment" style="padding: 0"><a :href="Tea.url('.update', { postId: post.id })">[修改文章]</a> </p>
<table class="ui table definition selectable">
<tr>
<td>所属产品</td>
<td>{{post.productName}}</td>
</tr>
<tr>
<td class="title">文章分类</td>
<td>
<span v-if="post.category != null && post.category.id > 0">{{post.category.name}}</span>
<span v-else class="disabled">已删除</span>
</td>
</tr>
<tr>
<td class="title">文章标题</td>
<td>
{{post.subject}}
</td>
</tr>
<tr>
<td>文章类型</td>
<td>
<span v-if="post.type == 'url'">URL</span>
<span v-if="post.type == 'normal'">常规</span>
</td>
</tr>
<tr v-show="post.type == 'url'">
<td>URL</td>
<td>
{{post.url}}
</td>
</tr>
<tr v-show="post.type == 'normal'">
<td>内容</td>
<td>
<div v-html="post.body"></div>
</td>
</tr>
</table>

View File

@@ -0,0 +1,4 @@
#editor {
height: 30em;
}
/*# sourceMappingURL=update.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["update.less"],"names":[],"mappings":"AAAA;EACC,YAAA","file":"update.css"}

View File

@@ -0,0 +1,62 @@
{$layout}
{$template "../menu"}
{$template "menu"}
{$var "header"}
<link href="/js/quill/quill.snow.css" rel="stylesheet">
<script src="/js/quill/quill.min.js"></script>
{$end}
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="postId" :value="post.id"/>
<input type="hidden" name="body" v-model="body"/>
<table class="ui table definition selectable">
<tr>
<td>所属产品 *</td>
<td>
<select class="ui dropdown auto-width" name="productCode" v-model="post.productCode">
<option value="">[所属产品]</option>
<option v-for="product in products" :value="product.code">{{product.name}}</option>
</select>
</td>
</tr>
<tr>
<td class="title">文章分类 *</td>
<td>
<select class="ui dropdown auto-width" name="categoryId" v-model="post.categoryId">
<option value="0">[选择分类]</option>
<option v-for="category in categories" :value="category.id">{{category.name}}</option>
</select>
</td>
</tr>
<tr>
<td class="title">文章标题 *</td>
<td>
<input type="text" name="subject" ref="focus" v-model="post.subject"/>
</td>
</tr>
<tr>
<td>文章类型 *</td>
<td>
<select class="ui dropdown auto-width" name="type" v-model="post.type">
<option value="normal">常规</option>
<option value="url">URL</option>
</select>
</td>
</tr>
<tr v-show="post.type == 'url'">
<td>URL *</td>
<td>
<input type="text" name="url" maxlength="200" v-model="post.url"/>
</td>
</tr>
<tr v-show="post.type == 'normal'">
<td>内容 *</td>
<td>
<div id="editor" v-html="post.body"></div>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,33 @@
Tea.context(function () {
this.success = NotifySuccess("保存成功", Tea.url(".post", { postId: this.post.id }))
this.type = "normal"
// reference: https://quilljs.com/docs/modules/toolbar/
this.body = this.post.body
let that = this
setTimeout(function () {
let quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{'header': 1}, {'header': 2}],
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'indent': '-1'}, {'indent': '+1'}],
[{'direction': 'rtl'}],
[{'size': ['small', false, 'large', 'huge']}],
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}],
[{'font': []}],
[{'align': []}],
['link', 'image'],
['clean']
]
}
})
quill.on("text-change", function () {
that.body = quill.root.innerHTML
})
}, 100)
})

View File

@@ -0,0 +1,3 @@
#editor {
height: 30em;
}