Selaa lähdekoodia

优化表格功能与样式,统一 API 调用方式

- 替换 `ueditor.all.min.js` 为未压缩版本,便于调试。
- 优化表格布局,调整列宽,新增分类列支持动态修改。
- 为操作按钮和状态切换添加 `q-tooltip` 提示信息。
- 重构拦截日志过滤逻辑,减少重复代码。
- 统一黑白名单 API 调用方式,改为请求体传递数据。
- 增加大量样式类,优化页面布局与交互效果。
- 增加响应式样式与网格布局,适配更多场景。
懒得勤快 1 kuukausi sitten
vanhempi
sitoutus
660caaead8

+ 1 - 1
front/public/index.html

@@ -12,7 +12,7 @@
     </script>
     <!-- 直接引入UEditor脚本 -->
     <script type="text/javascript" charset="utf-8" src="/UEditorPlus/ueditor.config.js"></script>
-    <script type="text/javascript" charset="utf-8" src="/UEditorPlus/ueditor.all.min.js"></script>
+    <script type="text/javascript" charset="utf-8" src="/UEditorPlus/ueditor.all.js"></script>
     <script type="text/javascript" charset="utf-8" src="/UEditorPlus/lang/zh-cn/zh-cn.js"></script>
   </head>
 

+ 34 - 28
front/src/views/advertisement/index.vue

@@ -48,8 +48,8 @@
       </div>
       <!-- 广告表格 -->
       <div v-else>
-        <vxe-table ref="tableRef" :data="advertisements" :loading="loading" border stripe :scroll-y="{ enabled: true }" :sort-config="{ remote: true }" @sort-change="onSortChange">
-          <vxe-column field="Id" title="ID" width="80" sortable />
+        <vxe-table ref="tableRef" :data="advertisements" :loading="loading" border stripe :scroll-y="{ enabled: true }" :sort-config="{ remote: true }">
+          <vxe-column field="Id" title="ID" width="80" />
           <vxe-column field="Title" title="标题" min-width="300">
             <template #default="{ row }">
               <q-btn flat dense color="primary" :label="row.Title" @click="showDetailDialog(row)" />
@@ -61,48 +61,60 @@
             </template>
           </vxe-column>
           <!-- 当前竞价列 -->
-          <vxe-column v-if="showColumns.includes('Price')" field="Price" title="当前竞价" width="100">
+          <vxe-column v-if="showColumns.includes('Price')" field="Price" title="当前竞价" width="80">
             <template #default="{ row }"> ¥{{ row.Price || 0 }} </template>
+          </vxe-column><!-- 分类列 -->
+          <vxe-column v-if="showColumns.includes('CategoryIds')" field="CategoryIds" title="分类" width="260" show-overflow>
+            <template #default="{ row }">
+              <q-select use-chips outlined :model-value="getCategoryIdsArray(row.CategoryIds)" @update:model-value="(val) => changeAdvertisementCategory(row, val)" :options="categoryOptions" option-value="value" option-label="label" multiple dense borderless map-options emit-value placeholder="选择分类" />
+            </template>
           </vxe-column>
           <!-- 当月曝光量列 -->
-          <vxe-column v-if="showColumns.includes('DisplayCount')" field="DisplayCount" title="当月曝光量" width="120" />
+          <vxe-column v-if="showColumns.includes('DisplayCount')" field="DisplayCount" title="当月曝光量" width="100" />
           <!-- 当月点击列 -->
-          <vxe-column v-if="showColumns.includes('ViewCount')" field="ViewCount" title="当月点击" width="100" />
+          <vxe-column v-if="showColumns.includes('ViewCount')" field="ViewCount" title="当月点击" width="80" />
           <!-- 日均点击列 -->
-          <vxe-column v-if="showColumns.includes('AverageViewCount')" field="AverageViewCount" title="日均点击" width="100">
+          <vxe-column v-if="showColumns.includes('AverageViewCount')" field="AverageViewCount" title="日均点击" width="80">
             <template #default="{ row }"> {{ row.AverageViewCount?.toFixed(2) }} </template>
           </vxe-column>
           <!-- 点击率列 -->
-          <vxe-column v-if="showColumns.includes('ClickRate')" field="ClickRate" title="点击率" width="100">
+          <vxe-column v-if="showColumns.includes('ClickRate')" field="ClickRate" title="点击率" width="70">
             <template #default="{ row }"> {{ row.ClickRate?.toFixed(2) }}% </template>
           </vxe-column>
-          <!-- 分类列 -->
-          <vxe-column v-if="showColumns.includes('CategoryIds')" field="CategoryIds" title="分类" width="260" show-overflow>
-            <template #default="{ row }">
-              <q-select use-chips outlined :model-value="getCategoryIdsArray(row.CategoryIds)" @update:model-value="(val) => changeAdvertisementCategory(row, val)" :options="categoryOptions" option-value="value" option-label="label" multiple dense borderless map-options emit-value placeholder="选择分类" />
-            </template>
-          </vxe-column>
           <!-- 创建时间列 -->
-          <vxe-column v-if="showColumns.includes('CreateTime')" field="CreateTime" title="创建时间" width="120">
+          <vxe-column v-if="showColumns.includes('CreateTime')" field="CreateTime" title="创建时间" width="100">
             <template #default="{ row }"> {{ formatDate(row.CreateTime) }} </template>
           </vxe-column>
           <!-- 修改时间列 -->
-          <vxe-column v-if="showColumns.includes('UpdateTime')" field="UpdateTime" title="修改时间" width="120">
+          <vxe-column v-if="showColumns.includes('UpdateTime')" field="UpdateTime" title="修改时间" width="100">
             <template #default="{ row }"> {{ formatDate(row.UpdateTime) }} </template>
           </vxe-column>
-          <vxe-column field="Status" title="状态" width="80">
+          <vxe-column field="Status" title="状态" width="50" fixed="right">
             <template #default="{ row }">
-              <q-toggle :model-value="row.Status === 1" @update:model-value="changeStatus(row)" color="positive" />
+              <q-toggle dense :model-value="row.Status === 1" @update:model-value="changeStatus(row)" color="positive">
+                <q-tooltip class="bg-green"> {{ row.Status === 1 ? '已上架' : '已下架' }} </q-tooltip>
+              </q-toggle>
             </template>
           </vxe-column>
           <vxe-column title="操作" width="160" fixed="right">
             <template #default="{ row }">
-              <q-btn dense flat size="sm" color="info" icon="visibility" @click="showDetailDialog(row)" />
-              <q-btn dense flat size="sm" color="primary" icon="edit" @click="editAdvertisement(row)" />
-              <q-btn dense flat size="sm" color="secondary" icon="content_copy" @click="copyAdvertisement(row)" />
-              <q-btn dense flat size="sm" color="warning" icon="schedule" @click="showDelayDialog(row)" />
-              <q-btn dense flat size="sm" color="accent" icon="insights" @click="showInsight(row)" />
+              <q-btn dense flat size="sm" color="info" icon="visibility" @click="showDetailDialog(row)">
+                <q-tooltip class="bg-info">查看详情</q-tooltip>
+              </q-btn>
+              <q-btn dense flat size="sm" color="primary" icon="edit" @click="editAdvertisement(row)">
+                <q-tooltip class="bg-primary">编辑广告</q-tooltip>
+              </q-btn>
+              <q-btn dense flat size="sm" color="secondary" icon="content_copy" @click="copyAdvertisement(row)">
+                <q-tooltip class="bg-secondary">复制广告</q-tooltip>
+              </q-btn>
+              <q-btn dense flat size="sm" color="warning" icon="schedule" @click="showDelayDialog(row)">
+                <q-tooltip class="bg-warning">延长广告投放时间</q-tooltip>
+              </q-btn>
+              <q-btn dense flat size="sm" color="accent" icon="insights" @click="showInsight(row)">
+                <q-tooltip class="bg-accent">查看广告统计数据</q-tooltip>
+              </q-btn>
               <q-btn dense flat size="sm" color="negative" icon="delete">
+                <q-tooltip class="bg-negative">删除广告</q-tooltip>
                 <q-popup-proxy transition-show="scale" transition-hide="scale">
                   <q-card>
                     <q-card-section class="row items-center">
@@ -835,12 +847,6 @@ const onPageSizeChange = () => {
   loadPageData()
 }
 
-// 表格排序处理
-const onSortChange = ({ field, order }: { field: string; order: string }) => {
-  // 处理表格排序逻辑
-  loadPageData()
-}
-
 // 保存显示列配置
 const saveShowColumns = () => {
   localStorage.setItem('advertisement-showColumns', showColumns.value.join(','))

+ 1 - 1
front/src/views/merge/list.vue

@@ -233,7 +233,7 @@ const blockMerge = async (row: any) => {
 
 // 加入黑名单
 const addToBlackList = async (ip: string) => {
-  const data = await api.post(`/system/AddToBlackList/${ip}`) as ApiResponse
+  const data = await api.post(`/system/AddToBlackList`, { ip }) as ApiResponse
   toast.success(data?.Message || '已加入黑名单', { autoClose: 2000, position: 'top-center' })
 }
 

+ 3 - 0
front/src/views/posts/list.vue

@@ -40,12 +40,14 @@
           <!-- 标题列 -->
           <vxe-column field="Title" title="标题" min-width="240" fixed="left">
             <template #default="{ row }">
+              <q-tooltip class="bg-grey" v-if="row.AverageViewCount < 1 && row.IsNsfw === false && row.LimitDesc === '无限制'">日均浏览量低于1</q-tooltip>
               <a :href="`/${row.Id}`" target="_blank" :class="{ 'text-grey text-bold': row.AverageViewCount < 1 && row.IsNsfw === false && row.LimitDesc === '无限制' }"> {{ row.Title }} </a>
             </template>
           </vxe-column>
           <!-- 作者列 -->
           <vxe-column field="Author" title="作者" width="120">
             <template #default="{ row }">
+              <q-tooltip class="bg-grey" v-if="row.AverageViewCount < 1 && row.IsNsfw === false && row.LimitDesc === '无限制'">日均浏览量低于1</q-tooltip>
               <a :href="`/author/${row.Author}`" target="_blank" :class="{ 'text-grey text-bold': row.AverageViewCount < 1 && row.IsNsfw === false && row.LimitDesc === '无限制' }">{{ row.Author }}</a>
             </template>
           </vxe-column>
@@ -65,6 +67,7 @@
           <!-- 阅读量列-->
           <vxe-column v-if="showColumns.includes('ViewCount')" field="ViewCount" title="阅读" min-width="70">
             <template #default="{ row }">
+              <q-tooltip class="bg-grey" v-if="row.AverageViewCount < 1">日均浏览量低于1</q-tooltip>
               <span :class="{ 'text-grey': row.AverageViewCount < 1 }"> {{ row.ViewCount }} </span>
             </template>
           </vxe-column>

+ 1 - 1
front/src/views/posts/post-pending.vue

@@ -222,7 +222,7 @@ const addToBlock = async (row: any) => {
 
 // 加入黑名单
 const addToBlackList = async (ip: string) => {
-  const data = await api.post(`/system/AddToBlackList/${ip}`) as ApiResponse
+  const data = await api.post(`/system/AddToBlackList`, { ip }) as ApiResponse
   toast.success(data?.Message || '已加入黑名单', { autoClose: 2000, position: 'top-center' })
 }
 

+ 1 - 1
front/src/views/search/index.vue

@@ -353,7 +353,7 @@ const onPageSizeChange = () => {
 
 // 加入黑名单
 const addToBlackList = async (ip: string) => {
-  const data = await api.post(`/system/AddToBlackList/${ip}`) as ApiResponse
+  const data = await api.post(`/system/AddToBlackList`, { ip }) as ApiResponse
   toast.success(data?.Message || '已加入黑名单', { autoClose: 2000, position: 'top-center' })
 }
 

+ 50 - 57
front/src/views/system/firewall.vue

@@ -94,7 +94,7 @@
       </div>
       <div class="q-mb-md">
         <div class="text-subtitle1 q-mb-sm">UA限制提示语:</div>
-        <div ref="userAgentBlockedMsgEditor" class="editor-container" style="height: 200px"></div>
+        <div ref="userAgentBlockedMsgEditor" class="editor-container" style="height: 400px"></div>
       </div>
     </q-card-section>
   </q-card>
@@ -104,47 +104,46 @@
       <div class="text-h6 q-mb-md">
         <q-icon name="security" class="q-mr-sm" /> 拦截日志 <q-chip v-if="interceptCount > 0" color="negative" text-color="white" :label="`累计拦截 ${interceptCount} 次`" class="q-ml-sm" />
       </div>
-      <!-- 操作按钮 -->
-      <div class="row q-gutter-md q-mb-md items-center">
-        <div class="col-auto">
-          <q-btn color="negative" icon="delete" label="清空日志" :loading="clearingLogs">
-            <q-popup-proxy transition-show="scale" transition-hide="scale">
-              <q-card>
-                <q-card-section class="row items-center">
-                  <q-icon name="warning" color="red" size="2rem" class="q-mr-sm" />
-                  <div>
-                    <div class="text-h6">确认清空拦截日志吗?</div>
-                    <div class="text-subtitle2">清空后将无法恢复!</div>
-                  </div>
-                </q-card-section>
-                <q-card-actions align="right">
-                  <q-btn color="negative" label="确认" v-close-popup @click="clearLogs" />
-                  <q-btn color="primary" label="取消" v-close-popup />
-                </q-card-actions>
-              </q-card>
-            </q-popup-proxy>
-          </q-btn>
-        </div>
-        <div class="col-auto">
-          <q-toggle v-model="distinctLogs" label="按IP去重" @update:model-value="toggleDistinct" />
-        </div>
-        <div class="col-auto">
-          <q-btn color="primary" icon="refresh" label="刷新" @click="loadInterceptLogs" :loading="loadingLogs" />
-        </div>
-        <div class="col-4 text-right">
-          <q-input autofocus v-model="searchTerm" dense outlined placeholder="搜索IP、请求URL、来源URL、UserAgent、备注" @keyup.enter="loadInterceptLogs" debounce="100">
-            <template #prepend>
-              <q-icon name="search" class="cursor-pointer" />
-            </template>
-            <template #append>
-              <q-icon name="close" class="cursor-pointer" v-if="searchTerm" @click="searchTerm = ''" />
-            </template>
-          </q-input>
-        </div>
-      </div>
       <!-- 拦截日志表格 -->
       <div v-if="logs.length === 0" class="text-center q-pa-md text-grey-6"> 暂无拦截日志 </div>
-      <div v-else>
+      <div v-else><!-- 操作按钮 -->
+        <div class="row q-gutter-md q-mb-md items-center">
+          <div class="col-auto">
+            <q-btn color="negative" icon="delete" label="清空日志" :loading="clearingLogs">
+              <q-popup-proxy transition-show="scale" transition-hide="scale">
+                <q-card>
+                  <q-card-section class="row items-center">
+                    <q-icon name="warning" color="red" size="2rem" class="q-mr-sm" />
+                    <div>
+                      <div class="text-h6">确认清空拦截日志吗?</div>
+                      <div class="text-subtitle2">清空后将无法恢复!</div>
+                    </div>
+                  </q-card-section>
+                  <q-card-actions align="right">
+                    <q-btn color="negative" label="确认" v-close-popup @click="clearLogs" />
+                    <q-btn color="primary" label="取消" v-close-popup />
+                  </q-card-actions>
+                </q-card>
+              </q-popup-proxy>
+            </q-btn>
+          </div>
+          <div class="col-auto">
+            <q-toggle v-model="distinctLogs" label="按IP去重" @update:model-value="toggleDistinct" />
+          </div>
+          <div class="col-auto">
+            <q-btn color="primary" icon="refresh" label="刷新" @click="loadInterceptLogs" :loading="loadingLogs" />
+          </div>
+          <div class="col-4 text-right">
+            <q-input autofocus v-model="searchTerm" dense outlined placeholder="搜索IP、请求URL、来源URL、UserAgent、备注" @keyup.enter="loadInterceptLogs" debounce="100">
+              <template #prepend>
+                <q-icon name="search" class="cursor-pointer" />
+              </template>
+              <template #append>
+                <q-icon name="close" class="cursor-pointer" v-if="searchTerm" @click="searchTerm = ''" />
+              </template>
+            </q-input>
+          </div>
+        </div>
         <!-- 表格 -->
         <vxe-table ref="tableRef" :data="paginatedLogs" stripe border :loading="loadingLogs">
           <vxe-column field="IP" title="IP地址" width="160" sortable>
@@ -236,7 +235,7 @@
                     </div>
                   </q-card-section>
                   <q-card-actions align="right">
-                    <q-btn flat label="确认" color="negative" v-close-popup @click="addToBlackList(row.IP)" />
+                    <q-btn flat label="确认" color="negative" v-close-popup @click="addToBlackList(row.Key)" />
                     <q-btn flat label="取消" color="primary" v-close-popup />
                   </q-card-actions>
                 </q-card>
@@ -437,9 +436,10 @@ const firewallEnabled = computed({
 
 // 分页计算属性
 const filteredLogs = computed(() => {
+  const filtered = logs.value.filter((x) => x.IP?.includes(searchTerm.value) || x.RequestUrl?.includes(searchTerm.value) || x.Referer?.includes(searchTerm.value) || x.UserAgent?.includes(searchTerm.value) || x.Remark?.includes(searchTerm.value));
   if (distinctLogs.value) {
     // 按IP去重
-    return logs.value.reduce((acc: InterceptLog[], current: InterceptLog) => {
+    return filtered.reduce((acc: InterceptLog[], current: InterceptLog) => {
       const exists = acc.find((item) => item.IP === current.IP);
       if (!exists) {
         acc.push(current);
@@ -447,14 +447,7 @@ const filteredLogs = computed(() => {
       return acc;
     }, []);
   }
-  return logs.value.filter(
-    (x) =>
-      x.IP?.includes(searchTerm.value) ||
-      x.RequestUrl?.includes(searchTerm.value) ||
-      x.Referer?.includes(searchTerm.value) ||
-      x.UserAgent?.includes(searchTerm.value) ||
-      x.Remark?.includes(searchTerm.value)
-  );
+  return filtered;
 });
 
 const totalPages = computed(() => {
@@ -605,7 +598,7 @@ const editIPBlackList = async () => {
 const editIPRangeBlackList = async () => {
   try {
     const response = (await api.get("/system/GetIPRangeBlackList")) as ApiResponse;
-    if (response?.Success && response.Data) {
+    if (response?.Success) {
       currentListType.value = "IP地址段黑名单";
       currentListTitle.value = "编辑IP地址段黑名单";
       currentIPList.value = response.Data;
@@ -639,14 +632,14 @@ const saveIPList = async () => {
   try {
     let endpoint = "";
     if (currentListType.value === "IP黑名单") {
-      endpoint = "/system/IpBlackList";
+      endpoint = "/system/SetIpBlackList";
     } else if (currentListType.value === "IP地址段黑名单") {
-      endpoint = "/system/IPRangeBlackList";
+      endpoint = "/system/SetIPRangeBlackList";
     } else if (currentListType.value === "IP白名单") {
-      endpoint = "/system/IpWhiteList";
+      endpoint = "/system/SetIpWhiteList";
     }
 
-    const response = (await api.post(endpoint, currentIPList.value)) as ApiResponse;
+    const response = (await api.post(endpoint, { content: currentIPList.value })) as ApiResponse;
     if (response?.Success) {
       toast.success("保存成功", { autoClose: 2000, position: "top-center" });
       closeIPListDialog();
@@ -674,7 +667,7 @@ const closeIPListDialog = () => {
 
 // 添加到白名单
 const addToWhiteList = async (ip: string) => {
-  const data = (await api.post(`/system/AddToWhiteList/${ip}`)) as ApiResponse;
+  const data = (await api.post(`/system/AddToWhiteList`, { ip })) as ApiResponse;
   toast.success(data?.Message || "已加入白名单", {
     autoClose: 2000,
     position: "top-center",
@@ -683,7 +676,7 @@ const addToWhiteList = async (ip: string) => {
 
 // 加入黑名单
 const addToBlackList = async (ip: string) => {
-  const data = (await api.post(`/system/AddToBlackList/${ip}`)) as ApiResponse;
+  const data = (await api.post(`/system/AddToBlackList`, { ip })) as ApiResponse;
   toast.success(data?.Message || "已加入黑名单", {
     autoClose: 2000,
     position: "top-center",

+ 1 - 1
front/src/views/system/sendbox.vue

@@ -307,7 +307,7 @@ const viewMailDetail = (mail: MailItem) => {
 
 // 加入黑名单
 const addToBlackList = async (ip: string) => {
-  const data = await api.post(`/system/AddToBlackList/${ip}`) as ApiResponse
+  const data = await api.post(`/system/AddToBlackList`, { ip }) as ApiResponse
   toast.success(data?.Message || '已加入黑名单', { autoClose: 2000, position: 'top-center' })
 }
 

+ 2 - 1
src/Masuit.MyBlogs.Core/Views/Post/Publish.cshtml

@@ -5,7 +5,8 @@
 <link href="~/Assets/fileupload/filestyle.css" rel="stylesheet" />
 <script src="~/scripts/xm-select.js"></script>
 <script src="~/UEditorPlus/ueditor.config.front.js"></script>
-<script src="https://apps.bdimg.com/libs/ueditor/1.4.3.1/ueditor.all.min.js"></script>
+<script type="text/javascript" charset="utf-8" src="~/UEditorPlus/ueditor.all.js"></script>
+<script type="text/javascript" charset="utf-8" src="~/UEditorPlus/lang/zh-cn/zh-cn.js"></script>
 <script src="~/Scripts/publish/publish.js"></script>
 <div class="container">
     <ol class="cd-breadcrumb triangle">

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Post/PushMerge.cshtml

@@ -65,7 +65,7 @@
 	</form>
 </div>
 <script src="~/UEditorPlus/ueditor.config.front.js"></script>
-<script src="~/UEditorPlus/ueditor.all.min.js"></script>
+<script src="~/UEditorPlus/ueditor.all.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/8.11.8/sweetalert2.min.js"></script>
 <script>
 	$(function () {

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Post/RepushMerge.cshtml

@@ -65,7 +65,7 @@
 	</form>
 </div>
 <script src="~/UEditorPlus/ueditor.config.front.js"></script>
-<script src="~/UEditorPlus/ueditor.all.min.js"></script>
+<script src="~/UEditorPlus/ueditor.all.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/8.11.8/sweetalert2.min.js"></script>
 <script>
 	$(function () {

+ 295 - 102
src/Masuit.MyBlogs.Core/wwwroot/Content/common/style.css

@@ -44,6 +44,7 @@ body {
 .min-height610 {
     min-height: 610px;
 }
+
 .footer {
     margin-top: 10px;
     padding: 8px;
@@ -55,15 +56,17 @@ body {
     transition: ease-in-out 1s;
     text-align: center;
 }
-.footer a {
-    color: wheat;
-    text-shadow: 1px 1px 1px #000;
-}
-.footer a:hover {
-    text-decoration: underline;
-    transform: rotate(360deg);
-    transition: ease-in-out .5s;
-}
+
+    .footer a {
+        color: wheat;
+        text-shadow: 1px 1px 1px #000;
+    }
+
+        .footer a:hover {
+            text-decoration: underline;
+            transform: rotate(360deg);
+            transition: ease-in-out .5s;
+        }
 
 /*#region 返回顶部火箭筒 */
 
@@ -109,183 +112,341 @@ body {
     z-index: 11;
 }
 
-
 /*#endregion */
-.box { margin: 0 10%; }
+.box {
+    margin: 0 10%;
+}
+
+.size14 {
+    font-size: 14px !important;
+}
+
+.size16 {
+    font-size: 16px !important;
+}
+
+.size18 {
+    font-size: 18px !important;
+}
+
+.size20 {
+    font-size: 20px !important;
+}
+
+.size24 {
+    font-size: 24px !important;
+}
 
-.size14 { font-size: 14px !important; }
+.size28 {
+    font-size: 28px !important;
+}
 
-.size16 { font-size: 16px !important; }
+.size32 {
+    font-size: 32px !important;
+}
 
-.size18 { font-size: 18px !important; }
+.size36 {
+    font-size: 36px !important;
+}
 
-.size20 { font-size: 20px !important; }
+.size48 {
+    font-size: 48px !important;
+}
 
-.size24 { font-size: 24px !important; }
-.size28 { font-size: 28px !important; }
-.size32 { font-size: 32px !important; }
-.size36 { font-size: 36px !important; }
+.ueditor {
+    border: none;
+}
 
-.size48 { font-size: 48px !important; }
+.margin-clear {
+    margin: 0 !important;
+}
 
-.ueditor { border: none; }
+.padding-clear {
+    padding: 0 !important;
+}
 
-.margin-clear { margin: 0 !important; }
+.margintop-clear {
+    margin-top: 0 !important;
+}
 
-.padding-clear { padding: 0 !important; }
+.paddingtop-clear {
+    padding-top: 0 !important;
+}
 
-.margintop-clear { margin-top: 0 !important; }
+.marginbot-clear {
+    margin-bottom: 0 !important;
+}
 
-.paddingtop-clear { padding-top: 0 !important; }
+.paddingbot-clear {
+    padding-bottom: 0 !important;
+}
 
-.marginbot-clear { margin-bottom: 0 !important; }
+.marginleft-clear {
+    margin-left: 0 !important;
+}
 
-.paddingbot-clear { padding-bottom: 0 !important; }
+.paddingleft-clear {
+    padding-left: 0 !important;
+}
 
-.marginleft-clear { margin-left: 0 !important; }
+.marginright-clear {
+    margin-right: 0 !important;
+}
 
-.paddingleft-clear { padding-left: 0 !important; }
+.paddingright-clear {
+    padding-right: 0 !important;
+}
 
-.marginright-clear { margin-right: 0 !important; }
+.padding-top40 {
+    padding-top: 40px !important;
+}
 
-.paddingright-clear { padding-right: 0 !important; }
+.padding-topt60 {
+    padding-top: 60px !important;
+}
 
-.padding-top40 { padding-top: 40px !important; }
+.padding-top70 {
+    padding-top: 70px !important;
+}
 
-.padding-topt60 { padding-top: 60px !important; }
+.padding-bot5 {
+    padding-bottom: 5px !important;
+}
 
-.padding-top70 { padding-top: 70px !important; }
+.padding-bot10 {
+    padding-bottom: 10px !important;
+}
 
-.padding-bot5 { padding-bottom: 5px !important; }
+.padding-bot15 {
+    padding-bottom: 15px !important;
+}
 
-.padding-bot10 { padding-bottom: 10px !important; }
+.padding-bot20 {
+    padding-bottom: 20px !important;
+}
 
-.padding-bot15 { padding-bottom: 15px !important; }
+.padding-bot30 {
+    padding-bottom: 30px !important;
+}
 
-.padding-bot20 { padding-bottom: 20px !important; }
+.padding-bot40 {
+    padding-bottom: 40px !important;
+}
 
-.padding-bot30 { padding-bottom: 30px !important; }
+.padding-bot50 {
+    padding-bottom: 50px !important;
+}
 
-.padding-bot40 { padding-bottom: 40px !important; }
+.padding-bot60 {
+    padding-bottom: 60px !important;
+}
 
-.padding-bot50 { padding-bottom: 50px !important; }
+.padding-bot70 {
+    padding-bottom: 70px !important;
+}
 
-.padding-bot60 { padding-bottom: 60px !important; }
+.margin-mintop10 {
+    margin-top: -10px !important;
+}
 
-.padding-bot70 { padding-bottom: 70px !important; }
+.margin-top10 {
+    margin-top: 10px !important;
+}
 
-.margin-mintop10 { margin-top: -10px !important; }
-.margin-top10 { margin-top: 10px !important; }
-.margin-bot10 { margin-bottom: 10px !important; }
+.margin-bot10 {
+    margin-bottom: 10px !important;
+}
 
-.margin-mintop20 { margin-top: -20px !important; }
+.margin-mintop20 {
+    margin-top: -20px !important;
+}
 
-.margin-mintop30 { margin-top: -30px !important; }
+.margin-mintop30 {
+    margin-top: -30px !important;
+}
 
-.margin-mintop60 { margin-top: -60px !important; }
+.margin-mintop60 {
+    margin-top: -60px !important;
+}
 
-.margin-mintop110 { margin-top: -110px !important; }
+.margin-mintop110 {
+    margin-top: -110px !important;
+}
 
-.margin-minbot10 { margin-bottom: -10px !important; }
+.margin-minbot10 {
+    margin-bottom: -10px !important;
+}
 
-.margin-minbot20 { margin-bottom: -20px !important; }
+.margin-minbot20 {
+    margin-bottom: -20px !important;
+}
 
-.margin-minbot30 { margin-bottom: -30px !important; }
+.margin-minbot30 {
+    margin-bottom: -30px !important;
+}
 
-.margin-minbot60 { margin-bottom: -60px !important; }
+.margin-minbot60 {
+    margin-bottom: -60px !important;
+}
 
 /*
 Custom left right spacer
 =========================== */
 
-.margin-left10 { margin-left: 10px !important; }
+.margin-left10 {
+    margin-left: 10px !important;
+}
 
-.margin-right10 { margin-right: 10px !important; }
+.margin-right10 {
+    margin-right: 10px !important;
+}
 
-.margin-left20 { margin-left: 20px !important; }
+.margin-left20 {
+    margin-left: 20px !important;
+}
 
-.margin-right20 { margin-right: 20px !important; }
+.margin-right20 {
+    margin-right: 20px !important;
+}
 
 /*
 Custom top spacer
 =========================== */
 
-.margintop5 { margin-top: 5px !important; }
+.margintop5 {
+    margin-top: 5px !important;
+}
 
-.margintop10 { margin-top: 10px !important; }
+.margintop10 {
+    margin-top: 10px !important;
+}
 
-.margintop15 { margin-top: 15px !important; }
+.margintop15 {
+    margin-top: 15px !important;
+}
 
-.margintop20 { margin-top: 20px !important; }
+.margintop20 {
+    margin-top: 20px !important;
+}
 
-.margintop25 { margin-top: 25px !important; }
+.margintop25 {
+    margin-top: 25px !important;
+}
 
-.margintop30 { margin-top: 30px !important; }
+.margintop30 {
+    margin-top: 30px !important;
+}
 
-.margintop35 { margin-top: 35px !important; }
+.margintop35 {
+    margin-top: 35px !important;
+}
 
-.margintop40 { margin-top: 40px !important; }
+.margintop40 {
+    margin-top: 40px !important;
+}
 
-.margintop45 { margin-top: 45px !important; }
+.margintop45 {
+    margin-top: 45px !important;
+}
 
-.margintop50 { margin-top: 50px !important; }
+.margintop50 {
+    margin-top: 50px !important;
+}
 
-.margintop55 { margin-top: 55px !important; }
+.margintop55 {
+    margin-top: 55px !important;
+}
 
-.margintop60 { margin-top: 60px !important; }
+.margintop60 {
+    margin-top: 60px !important;
+}
 
-.margintop80 { margin-top: 80px !important; }
+.margintop80 {
+    margin-top: 80px !important;
+}
 
 /*
 Custom bottom spacer
 =========================== */
 
-.marginbot5 { margin-bottom: 5px !important; }
-
-.marginbot10 { margin-bottom: 10px !important; }
+.marginbot5 {
+    margin-bottom: 5px !important;
+}
 
-.marginbot15 { margin-bottom: 15px !important; }
+.marginbot10 {
+    margin-bottom: 10px !important;
+}
 
-.marginbot20 { margin-bottom: 20px !important; }
+.marginbot15 {
+    margin-bottom: 15px !important;
+}
 
-.marginbot25 { margin-bottom: 25px !important; }
+.marginbot20 {
+    margin-bottom: 20px !important;
+}
 
-.marginbot30 { margin-bottom: 30px !important; }
+.marginbot25 {
+    margin-bottom: 25px !important;
+}
 
-.marginbot35 { margin-bottom: 35px !important; }
+.marginbot30 {
+    margin-bottom: 30px !important;
+}
 
-.marginbot40 { margin-bottom: 40px !important; }
+.marginbot35 {
+    margin-bottom: 35px !important;
+}
 
-.marginbot45 { margin-bottom: 45px !important; }
+.marginbot40 {
+    margin-bottom: 40px !important;
+}
 
+.marginbot45 {
+    margin-bottom: 45px !important;
+}
 
-.marginbot50 { margin-bottom: 50px !important; }
+.marginbot50 {
+    margin-bottom: 50px !important;
+}
 
-.marginbot55 { margin-bottom: 55px !important; }
+.marginbot55 {
+    margin-bottom: 55px !important;
+}
 
-.marginbot60 { margin-bottom: 60px !important; }
+.marginbot60 {
+    margin-bottom: 60px !important;
+}
 
-.marginbot80 { margin-bottom: 80px !important; }
+.marginbot80 {
+    margin-bottom: 80px !important;
+}
 
-.msg-list .panel .panel-body { padding: 2px; }
+.msg-list .panel .panel-body {
+    padding: 2px;
+}
 
 .msg-list article.panel {
     margin-top: 5px;
     margin-bottom: 0px;
     padding-bottom: 1px;
 }
+
     .msg-list article.panel article.panel {
         margin-left: 10px;
         /*margin-right: 15px;*/
     }
-.msg-list article.panel>.panel-heading {
-    padding: 5px 2px;
-}
-.msg-list article.panel>.panel-heading>span {
-    line-height: 18px;
-}
 
-.msg-list .cmvote { cursor: pointer; }
+    .msg-list article.panel > .panel-heading {
+        padding: 5px 2px;
+    }
+
+        .msg-list article.panel > .panel-heading > span {
+            line-height: 18px;
+        }
+
+.msg-list .cmvote {
+    cursor: pointer;
+}
 
 .news-item {
     padding: 4px 4px;
@@ -293,10 +454,18 @@ Custom bottom spacer
     border-bottom: 1px dotted #555;
 }
 
-.line-height28 { line-height: 28px; }
-.line-height24 { line-height: 24px; }
+.line-height28 {
+    line-height: 28px;
+}
+
+.line-height24 {
+    line-height: 24px;
+}
+
+.padding-10 {
+    padding: 10px;
+}
 
-.padding-10 { padding: 10px; }
 .notice {
     overflow: hidden;
     word-wrap: inherit;
@@ -306,26 +475,33 @@ Custom bottom spacer
     margin-bottom: 10px;
     padding: 5px;
 }
+
 .layui-layer-content img {
     max-height: 50vh;
     max-width: 100%;
 }
+
 .media {
     margin: 0;
 }
-.form-control,.panel, .list-group-item, .panel-footer {
+
+.form-control, .panel, .list-group-item, .panel-footer {
     background-color: transparent;
 }
+
 .page-header {
     margin-top: 0px;
 }
+
 .notices {
     min-height: 110px !important;
-    overflow:hidden !important;
+    overflow: hidden !important;
 }
+
 .text-white {
     color: white;
 }
+
 .text-focus {
     background-color: yellow;
     color: red;
@@ -340,6 +516,7 @@ Custom bottom spacer
 .inline {
     display: inline !important;
 }
+
 .highlight {
     background: yellow;
     color: red;
@@ -404,12 +581,14 @@ Custom bottom spacer
 .rainbow {
     background-image: linear-gradient(-45deg, rgba(255, 0, 0, .3), rgba(255, 255, 0, .3), rgba(0, 255, 255, .2), rgba(0, 0, 255, .15), rgba(255, 0, 255, .2), rgba(255, 0, 0, .3));
 }
+
 #reply-form {
     margin: 15px 0;
 }
-#reply-form .form-group {
-    margin-bottom: 0;
-}
+
+    #reply-form .form-group {
+        margin-bottom: 0;
+    }
 
 .close-bg {
     position: fixed;
@@ -418,17 +597,21 @@ Custom bottom spacer
     z-index: 10;
     display: none;
 }
+
 .text-red {
     font-size: 16px;
     color: #F44336 !important;
 }
+
 .text-bold {
     font-weight: bold;
 }
+
 .text-green {
     color: #009900;
     font-size: 16px;
 }
+
 .text-blue {
     font-size: 16px;
     color: #2196F3;
@@ -513,7 +696,8 @@ Custom bottom spacer
             color: #fff;
             background: #0099ff;
         }
-.container p,.container-fluid p {
+
+.container p, .container-fluid p {
     font-size: 16px;
     line-height: 22px;
     word-break: break-all;
@@ -522,15 +706,18 @@ Custom bottom spacer
 .mp-popup {
     max-height: 100vh !important;
 }
+
 @media (min-width: 2100px) {
     .container {
         width: 60vw;
     }
 }
+
 .mp-results.mp-bottomleft {
     top: unset !important;
     bottom: 0 !important;
 }
+
 .grid-30 {
     display: grid;
     grid-template-columns: repeat(30, 1fr);
@@ -540,6 +727,12 @@ Custom bottom spacer
 .span-col-27 {
     grid-column: span 27 / auto;
 }
+
 .span-col-28 {
     grid-column: span 28 / auto;
+}
+
+.syntaxhighlighter .code .container:before {
+    display: block;
+    content: "" !important;
 }