阶段更新
This commit is contained in:
@@ -0,0 +1,248 @@
|
|||||||
|
# Go (Beego) 多租户二级域名系统部署与配置指南
|
||||||
|
|
||||||
|
本指南详细介绍在 **不改变你目前宝塔部署路径** 的前提下,如何配置 `yunzer.com.cn` 和 `dh2.fun` 及其所有二级域名的 Nginx,让它们能够直接访问并使用部署在 `www.yunzer.cn` 的官网前端静态模板。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 第一步:宝塔物理部署路径规划(保持现状)
|
||||||
|
|
||||||
|
请确认或保持你目前在宝塔服务器上的物理目录部署,不需要进行任何文件移动:
|
||||||
|
|
||||||
|
1. **企业官网门户前台 (`frontend`)**
|
||||||
|
- 物理路径: `/www/wwwroot/www.yunzer.cn`
|
||||||
|
- 说明: 你的 `frontend` 编译打包文件平铺部署在此目录下(有 `index.html`)。
|
||||||
|
|
||||||
|
2. **平台运营管理后台 (`platform`)**
|
||||||
|
- 物理路径: `/www/wwwroot/platform.yunzer.cn`
|
||||||
|
|
||||||
|
3. **租户/商家管理后台 (`back`)**
|
||||||
|
- 物理路径: `/www/wwwroot/back.yunzer.cn`
|
||||||
|
|
||||||
|
4. **Go 后端 API 服务**
|
||||||
|
- 运行端口: 本地 `http://10.31.100.2:8081`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 第二步:修改 `yunzer.com.cn` 和 `dh2.fun` 的 Nginx 配置
|
||||||
|
|
||||||
|
为了绕过宝塔面板对 SSL 注释规则的保存验证限制,以下配置中**完全保留了宝塔原生生成的 `#SSL-START`、`#error_page 404/404.html;` 以及 `#SSL-END` 结构**。
|
||||||
|
|
||||||
|
请分别在宝塔面板对应站点的 **“配置文件”** 选项中,替换为以下内容:
|
||||||
|
|
||||||
|
### 1. `yunzer.com.cn` 的 Nginx 配置文件
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen 443 quic;
|
||||||
|
http2 on;
|
||||||
|
|
||||||
|
# 匹配主域名和所有租户的子域名
|
||||||
|
server_name yunzer.com.cn ~^(?<subdomain>.+)\.yunzer\.com\.cn$;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 核心修改:直接指向部署了 frontend 的静态资源路径
|
||||||
|
root /www/wwwroot/www.yunzer.cn;
|
||||||
|
|
||||||
|
#CERT-APPLY-CHECK--START
|
||||||
|
include /www/server/panel/vhost/nginx/well-known/yunzer.com.cn.conf;
|
||||||
|
#CERT-APPLY-CHECK--END
|
||||||
|
include /www/server/panel/vhost/nginx/extension/yunzer.com.cn/*.conf;
|
||||||
|
|
||||||
|
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
|
||||||
|
#error_page 404/404.html;
|
||||||
|
|
||||||
|
#HTTP_TO_HTTPS_START
|
||||||
|
set $isRedcert 1;
|
||||||
|
if ($server_port != 443) {
|
||||||
|
set $isRedcert 2;
|
||||||
|
}
|
||||||
|
if ( $uri ~ /\.well-known/ ) {
|
||||||
|
set $isRedcert 1;
|
||||||
|
}
|
||||||
|
if ($isRedcert != 1) {
|
||||||
|
rewrite ^(/.*)$ https://$host$1 permanent;
|
||||||
|
}
|
||||||
|
#HTTP_TO_HTTPS_END
|
||||||
|
|
||||||
|
ssl_certificate /www/server/panel/vhost/cert/yunzer.com.cn/fullchain.pem;
|
||||||
|
ssl_certificate_key /www/server/panel/vhost/cert/yunzer.com.cn/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_tickets on;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000";
|
||||||
|
add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';
|
||||||
|
error_page 497 https://$host$request_uri;
|
||||||
|
#SSL-END
|
||||||
|
|
||||||
|
#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
|
||||||
|
error_page 404 /404.html;
|
||||||
|
#ERROR-PAGE-END
|
||||||
|
|
||||||
|
# --- 安全与防盗链拦截 START ---
|
||||||
|
location ~* (\.user.ini|\.htaccess|\.htpasswd|\.env.*|\.project|\.bashrc|\.bash_profile|\.bash_logout|\.DS_Store|\.gitignore|\.gitattributes|LICENSE|README\.md|CLAUDE\.md|CHANGELOG\.md|CHANGELOG|CONTRIBUTING\.md|TODO\.md|FAQ\.md|composer\.json|composer\.lock|package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml|\.\w+~|\.swp|\.swo|\.bak(up)?|\.old|\.tmp|\.temp|\.log|\.sql(\.gz)?|docker-compose\.yml|docker\.env|Dockerfile|\.csproj|\.sln|Cargo\.toml|Cargo\.lock|go\.mod|go\.sum|phpunit\.xml|phpunit\.xml|pom\.xml|build\.gradl|pyproject\.toml|requirements\.txt|application(-\w+)?\.(ya?ml|properties))$ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* /(\.git|\.svn|\.bzr|\.vscode|\.claude|\.idea|\.ssh|\.github|\.npm|\.yarn|\.pnpm|\.cache|\.husky|\.turbo|\.next|\.nuxt|node_modules|runtime)/ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.well-known {
|
||||||
|
allow all;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
# --- 安全与防盗链拦截 END ---
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
|
||||||
|
expires 30d;
|
||||||
|
error_log /dev/null;
|
||||||
|
access_log /dev/null;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(js|css)$ {
|
||||||
|
expires 12h;
|
||||||
|
error_log /dev/null;
|
||||||
|
access_log /dev/null;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Go API 反向代理及 Vue 路由支持 ---
|
||||||
|
# 1. 代理 /api/ 请求到本地运行在 8081 端口的 Go 服务
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://10.31.100.2:8081/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. 代理文件上传目录到 Go 静态服务
|
||||||
|
location /uploads/ {
|
||||||
|
proxy_pass http://10.31.100.2:8081/uploads/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. 前端单页面应用 (Vue Router) History 模式重定向
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
access_log /www/wwwlogs/yunzer.com.cn.log;
|
||||||
|
error_log /www/wwwlogs/yunzer.com.cn.error.log;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `dh2.fun` 的 Nginx 配置文件
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen 443 quic;
|
||||||
|
http2 on;
|
||||||
|
|
||||||
|
# 匹配主域名和所有租户的子域名
|
||||||
|
server_name dh2.fun ~^(?<subdomain>.+)\.dh2\.fun$;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 核心修改:直接指向部署了 frontend 的静态资源路径
|
||||||
|
root /www/wwwroot/www.yunzer.cn;
|
||||||
|
|
||||||
|
#CERT-APPLY-CHECK--START
|
||||||
|
include /www/server/panel/vhost/nginx/well-known/dh2.fun.conf;
|
||||||
|
#CERT-APPLY-CHECK--END
|
||||||
|
include /www/server/panel/vhost/nginx/extension/dh2.fun/*.conf;
|
||||||
|
|
||||||
|
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
|
||||||
|
#error_page 404/404.html;
|
||||||
|
|
||||||
|
#HTTP_TO_HTTPS_START
|
||||||
|
set $isRedcert 1;
|
||||||
|
if ($server_port != 443) {
|
||||||
|
set $isRedcert 2;
|
||||||
|
}
|
||||||
|
if ( $uri ~ /\.well-known/ ) {
|
||||||
|
set $isRedcert 1;
|
||||||
|
}
|
||||||
|
if ($isRedcert != 1) {
|
||||||
|
rewrite ^(/.*)$ https://$host$1 permanent;
|
||||||
|
}
|
||||||
|
#HTTP_TO_HTTPS_END
|
||||||
|
|
||||||
|
ssl_certificate /www/server/panel/vhost/cert/dh2.fun/fullchain.pem;
|
||||||
|
ssl_certificate_key /www/server/panel/vhost/cert/dh2.fun/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_tickets on;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000";
|
||||||
|
add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';
|
||||||
|
error_page 497 https://$host$request_uri;
|
||||||
|
#SSL-END
|
||||||
|
|
||||||
|
#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
|
||||||
|
error_page 404 /404.html;
|
||||||
|
#ERROR-PAGE-END
|
||||||
|
|
||||||
|
# 禁止访问的敏感文件
|
||||||
|
location ~* (\.user.ini|\.htaccess|\.htpasswd|\.env.*|\.project|\.bashrc|\.bash_profile|\.bash_logout|\.DS_Store|\.gitignore|\.gitattributes|LICENSE|README\.md|CLAUDE\.md|CHANGELOG\.md|CHANGELOG|CONTRIBUTING\.md|TODO\.md|FAQ\.md|composer\.json|composer\.lock|package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml|\.\w+~|\.swp|\.swo|\.bak(up)?|\.old|\.tmp|\.temp|\.log|\.sql(\.gz)?|docker-compose\.yml|docker\.env|Dockerfile|\.csproj|\.sln|Cargo\.toml|Cargo\.lock|go\.mod|go\.sum|phpunit\.xml|phpunit\.xml|pom\.xml|build\.gradl|pyproject\.toml|requirements\.txt|application(-\w+)?\.(ya?ml|properties))$
|
||||||
|
{
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 禁止访问的敏感目录
|
||||||
|
location ~* /(\.git|\.svn|\.bzr|\.vscode|\.claude|\.idea|\.ssh|\.github|\.npm|\.yarn|\.pnpm|\.cache|\.husky|\.turbo|\.next|\.nuxt|node_modules|runtime)/ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
#一键申请SSL证书验证目录相关设置
|
||||||
|
location ~ \.well-known{
|
||||||
|
allow all;
|
||||||
|
}
|
||||||
|
|
||||||
|
#禁止在证书验证目录放入敏感文件
|
||||||
|
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||||
|
{
|
||||||
|
expires 30d;
|
||||||
|
error_log /dev/null;
|
||||||
|
access_log /dev/null;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ .*\.(js|css)?$
|
||||||
|
{
|
||||||
|
expires 12h;
|
||||||
|
error_log /dev/null;
|
||||||
|
access_log /dev/null;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Go API 反向代理及 Vue 路由支持 ---
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://10.31.100.2:8081/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /uploads/ {
|
||||||
|
proxy_pass http://10.31.100.2:8081/uploads/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -4,7 +4,7 @@ import { createComponentLoader } from '@/utils/pathResolver'
|
|||||||
export const constantRoute = [
|
export const constantRoute = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('@/views/theme/index.vue'),
|
component: () => import('@/views/home/index.vue'),
|
||||||
name: '首页',
|
name: '首页',
|
||||||
meta: {
|
meta: {
|
||||||
title: '首页',
|
title: '首页',
|
||||||
|
|||||||
@@ -0,0 +1,396 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/client/orm"
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IndexPortalController 前台门户及租户官网公共接口(无需登录)
|
||||||
|
type IndexPortalController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTidByHost 根据访问域名识别租户 ID,默认返回 1
|
||||||
|
func (c *IndexPortalController) getTidByHost() uint64 {
|
||||||
|
host := c.Ctx.Request.Host
|
||||||
|
parts := strings.Split(host, ":")
|
||||||
|
domain := parts[0]
|
||||||
|
|
||||||
|
if domain == "localhost" || domain == "127.0.0.1" {
|
||||||
|
return 1 // 开发环境默认租户
|
||||||
|
}
|
||||||
|
|
||||||
|
var td models.SystemTenantDomain
|
||||||
|
err := models.Orm.QueryTable(new(models.SystemTenantDomain)).
|
||||||
|
Filter("full_domain", domain).
|
||||||
|
Filter("status", 1).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&td)
|
||||||
|
if err == nil && td.Tid != nil {
|
||||||
|
return *td.Tid
|
||||||
|
}
|
||||||
|
return 1 // 默认租户
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeadMenu GET /index/headmenu
|
||||||
|
func (c *IndexPortalController) GetHeadMenu() {
|
||||||
|
// 前台的静态导航菜单结构
|
||||||
|
menuData := []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "首页",
|
||||||
|
"path": "/",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "解决方案",
|
||||||
|
"path": "/solutions",
|
||||||
|
"type": 2,
|
||||||
|
"children": []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"title": "行业解决方案",
|
||||||
|
"path": "/solutions/solution",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"title": "成功案例",
|
||||||
|
"path": "/solutions/successCases",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "新闻中心",
|
||||||
|
"path": "/newsCenter",
|
||||||
|
"type": 2,
|
||||||
|
"children": []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"title": "站点新闻",
|
||||||
|
"path": "/newsCenter/news",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"title": "站点公告",
|
||||||
|
"path": "/newsCenter/announcement",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"title": "技术中心",
|
||||||
|
"path": "/newsCenter/technologyCenter",
|
||||||
|
"type": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": menuData,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFooterData GET /index/footerdata
|
||||||
|
func (c *IndexPortalController) GetFooterData() {
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
|
||||||
|
// 准备默认的 Footer 配置数据
|
||||||
|
footerMap := map[string]string{
|
||||||
|
"sitename": "云泽官方网站",
|
||||||
|
"companyname": "云泽智能科技有限公司",
|
||||||
|
"copyright": "Copyright © 2026 Yunzer. All Rights Reserved.",
|
||||||
|
"icp": "苏ICP备2026000000号-1",
|
||||||
|
"description": "专业为企业建设数字化管理与官网门户的一站式 SaaS 服务平台",
|
||||||
|
"logo": "",
|
||||||
|
"logow": "",
|
||||||
|
"ico": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从租户站点设置表中查询最新的配置
|
||||||
|
var rows []models.TenantSiteSetting
|
||||||
|
_, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
Limit(1).
|
||||||
|
All(&rows)
|
||||||
|
if err == nil && len(rows) > 0 {
|
||||||
|
r := rows[0]
|
||||||
|
if r.Sitename != "" {
|
||||||
|
footerMap["sitename"] = r.Sitename
|
||||||
|
}
|
||||||
|
if r.Companyname != "" {
|
||||||
|
footerMap["companyname"] = r.Companyname
|
||||||
|
}
|
||||||
|
if r.Copyright != "" {
|
||||||
|
footerMap["copyright"] = r.Copyright
|
||||||
|
}
|
||||||
|
if r.Icp != "" {
|
||||||
|
footerMap["icp"] = r.Icp
|
||||||
|
}
|
||||||
|
if r.Description != "" {
|
||||||
|
footerMap["description"] = r.Description
|
||||||
|
}
|
||||||
|
footerMap["logo"] = r.Logo
|
||||||
|
footerMap["logow"] = r.Logow
|
||||||
|
footerMap["ico"] = r.Ico
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 Map 转换为前端需要的 [{label: "...", value: "..."}] 数组格式
|
||||||
|
var dataList []map[string]string
|
||||||
|
for k, v := range footerMap {
|
||||||
|
dataList = append(dataList, map[string]string{
|
||||||
|
"label": k,
|
||||||
|
"value": v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": dataList,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNewsCenterTop4 GET /index/newscentertop4
|
||||||
|
func (c *IndexPortalController) GetNewsCenterTop4() {
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
var articles []models.CmsArticle
|
||||||
|
_, err := models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("status", 1).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
OrderBy("-top", "-recommend", "-id").
|
||||||
|
Limit(4).
|
||||||
|
All(&articles)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "data": []interface{}{}}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": articles,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryCmsArticles 统一的分页查询文章辅助方法
|
||||||
|
func (c *IndexPortalController) queryCmsArticles(categoryName string, categoryID int64) {
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
page, _ := c.GetInt("page", 1)
|
||||||
|
limit, _ := c.GetInt("limit", 10)
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if limit < 1 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("status", 1).
|
||||||
|
Filter("delete_time__isnull", true)
|
||||||
|
|
||||||
|
// 如果有传具体的分类 ID
|
||||||
|
if categoryID > 0 {
|
||||||
|
qs = qs.Filter("cate_id", categoryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
total, _ := qs.Count()
|
||||||
|
|
||||||
|
var articles []models.CmsArticle
|
||||||
|
_, err := qs.OrderBy("-top", "-id").
|
||||||
|
Limit(limit, (page-1)*limit).
|
||||||
|
All(&articles)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"list": []interface{}{},
|
||||||
|
"total": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"list": articles,
|
||||||
|
"total": total,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyNews GET /index/companynews
|
||||||
|
func (c *IndexPortalController) GetCompanyNews() {
|
||||||
|
// 可根据实际需要分配固定的分类 ID
|
||||||
|
c.queryCmsArticles("companynews", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKingdeeNews GET /index/kingdeenews
|
||||||
|
func (c *IndexPortalController) GetKingdeeNews() {
|
||||||
|
c.queryCmsArticles("kingdeenews", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTechnologyCenter GET /index/technologyCenter
|
||||||
|
func (c *IndexPortalController) GetTechnologyCenter() {
|
||||||
|
categoryID, _ := c.GetInt64("category_id", 0)
|
||||||
|
c.queryCmsArticles("technologyCenter", categoryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTechnologyCategories GET /index/technologyCategories
|
||||||
|
func (c *IndexPortalController) GetTechnologyCategories() {
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
var categories []models.CmsArticleCategory
|
||||||
|
_, err := models.Orm.QueryTable(new(models.CmsArticleCategory)).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("status", 1).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
OrderBy("sort", "id").
|
||||||
|
All(&categories)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "data": []interface{}{}}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": categories,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyNewsDetail GET /index/companynews/detail/:id
|
||||||
|
func (c *IndexPortalController) GetCompanyNewsDetail() {
|
||||||
|
c.getArticleDetail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKingdeeNewsDetail GET /index/kingdeenews/detail/:id
|
||||||
|
func (c *IndexPortalController) GetKingdeeNewsDetail() {
|
||||||
|
c.getArticleDetail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getArticleDetail 查询文章详情的内部辅助方法
|
||||||
|
func (c *IndexPortalController) getArticleDetail() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
var article models.CmsArticle
|
||||||
|
err := models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&article)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "文章不存在"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": article,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLike POST /index/articleLikes/:id
|
||||||
|
func (c *IndexPortalController) AddLike() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Update(orm.Params{
|
||||||
|
"likes": orm.ColValue(orm.ColAdd, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "点赞成功"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceLike POST /index/articleUnlikes/:id
|
||||||
|
func (c *IndexPortalController) ReduceLike() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Filter("likes__gt", 0).
|
||||||
|
Update(orm.Params{
|
||||||
|
"likes": orm.ColValue(orm.ColMinus, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "已取消点赞"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddViews POST /index/views/:id
|
||||||
|
func (c *IndexPortalController) AddViews() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
tid := c.getTidByHost()
|
||||||
|
_ = models.EnsureCmsArticleTables()
|
||||||
|
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.CmsArticle)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("tid", tid).
|
||||||
|
Update(orm.Params{
|
||||||
|
"views": orm.ColValue(orm.ColAdd, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "阅读量+1"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOnePageByPath GET /index/onepage/:path
|
||||||
|
func (c *IndexPortalController) GetOnePageByPath() {
|
||||||
|
// 简单返回空数据,防止前端单页接口报错
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": nil,
|
||||||
|
}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
@@ -401,41 +401,168 @@ func batchAddPoolRows(c *beego.Controller, module string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var accounts []string
|
||||||
|
var tokens []string
|
||||||
|
seenAccs := make(map[string]bool)
|
||||||
|
seenTks := make(map[string]bool)
|
||||||
|
|
||||||
for _, row := range payload.Rows {
|
for _, row := range payload.Rows {
|
||||||
|
acc := strings.TrimSpace(row.Account)
|
||||||
|
tk := strings.TrimSpace(row.Token)
|
||||||
|
if acc != "" && !seenAccs[acc] {
|
||||||
|
seenAccs[acc] = true
|
||||||
|
accounts = append(accounts, acc)
|
||||||
|
}
|
||||||
|
if tk != "" && !seenTks[tk] {
|
||||||
|
seenTks[tk] = true
|
||||||
|
tokens = append(tokens, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
existingAccs := make(map[string]bool)
|
||||||
|
existingTks := make(map[string]bool)
|
||||||
|
|
||||||
|
if len(accounts) > 0 || len(tokens) > 0 {
|
||||||
|
condObj := orm.NewCondition()
|
||||||
|
subCond := orm.NewCondition()
|
||||||
|
if len(accounts) > 0 {
|
||||||
|
subCond = subCond.Or("account__in", accounts)
|
||||||
|
}
|
||||||
|
if len(tokens) > 0 {
|
||||||
|
subCond = subCond.Or("token__in", tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch module {
|
||||||
|
case "cursor":
|
||||||
|
var dbRows []models.PlatformAccountPoolCursor
|
||||||
|
cond := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("delete_time__isnull", true)
|
||||||
|
cond = cond.SetCond(condObj.AndCond(subCond))
|
||||||
|
if _, err := cond.All(&dbRows, "account", "token"); err == nil {
|
||||||
|
for _, r := range dbRows {
|
||||||
|
if r.Account != "" {
|
||||||
|
existingAccs[r.Account] = true
|
||||||
|
}
|
||||||
|
if r.Token != "" {
|
||||||
|
existingTks[r.Token] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "windsurf":
|
||||||
|
var dbRows []models.PlatformAccountPoolWindsurf
|
||||||
|
cond := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("delete_time__isnull", true)
|
||||||
|
cond = cond.SetCond(condObj.AndCond(subCond))
|
||||||
|
if _, err := cond.All(&dbRows, "account", "token"); err == nil {
|
||||||
|
for _, r := range dbRows {
|
||||||
|
if r.Account != "" {
|
||||||
|
existingAccs[r.Account] = true
|
||||||
|
}
|
||||||
|
if r.Token != "" {
|
||||||
|
existingTks[r.Token] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "krio":
|
||||||
|
var dbRows []models.PlatformAccountPoolKiro
|
||||||
|
cond := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("delete_time__isnull", true)
|
||||||
|
cond = cond.SetCond(condObj.AndCond(subCond))
|
||||||
|
if _, err := cond.All(&dbRows, "account", "token"); err == nil {
|
||||||
|
for _, r := range dbRows {
|
||||||
|
if r.Account != "" {
|
||||||
|
existingAccs[r.Account] = true
|
||||||
|
}
|
||||||
|
if r.Token != "" {
|
||||||
|
existingTks[r.Token] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "codex":
|
||||||
|
var dbRows []models.PlatformAccountPoolCodex
|
||||||
|
cond := models.Orm.QueryTable(new(models.PlatformAccountPoolCodex)).Filter("delete_time__isnull", true)
|
||||||
|
cond = cond.SetCond(condObj.AndCond(subCond))
|
||||||
|
if _, err := cond.All(&dbRows, "account", "token"); err == nil {
|
||||||
|
for _, r := range dbRows {
|
||||||
|
if r.Account != "" {
|
||||||
|
existingAccs[r.Account] = true
|
||||||
|
}
|
||||||
|
if r.Token != "" {
|
||||||
|
existingTks[r.Token] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertedAccs := make(map[string]bool)
|
||||||
|
insertedTks := make(map[string]bool)
|
||||||
|
|
||||||
|
var successCount int
|
||||||
|
var duplicateCount int
|
||||||
|
|
||||||
|
for _, row := range payload.Rows {
|
||||||
|
acc := strings.TrimSpace(row.Account)
|
||||||
|
tk := strings.TrimSpace(row.Token)
|
||||||
|
|
||||||
|
isDuplicate := false
|
||||||
|
if row.DataType == "account" {
|
||||||
|
if existingAccs[acc] || insertedAccs[acc] {
|
||||||
|
isDuplicate = true
|
||||||
|
}
|
||||||
|
} else if row.DataType == "tk" {
|
||||||
|
if existingTks[tk] || insertedTks[tk] {
|
||||||
|
isDuplicate = true
|
||||||
|
}
|
||||||
|
} else if row.DataType == "account_tk" {
|
||||||
|
if (acc != "" && (existingAccs[acc] || insertedAccs[acc])) || (tk != "" && (existingTks[tk] || insertedTks[tk])) {
|
||||||
|
isDuplicate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDuplicate {
|
||||||
|
duplicateCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if acc != "" {
|
||||||
|
insertedAccs[acc] = true
|
||||||
|
}
|
||||||
|
if tk != "" {
|
||||||
|
insertedTks[tk] = true
|
||||||
|
}
|
||||||
|
|
||||||
switch module {
|
switch module {
|
||||||
case "cursor":
|
case "cursor":
|
||||||
_, err = models.Orm.Insert(&models.PlatformAccountPoolCursor{
|
_, err = models.Orm.Insert(&models.PlatformAccountPoolCursor{
|
||||||
DataType: strings.TrimSpace(row.DataType),
|
DataType: strings.TrimSpace(row.DataType),
|
||||||
Account: strings.TrimSpace(row.Account),
|
Account: acc,
|
||||||
Password: strings.TrimSpace(row.Password),
|
Password: strings.TrimSpace(row.Password),
|
||||||
Token: strings.TrimSpace(row.Token),
|
Token: tk,
|
||||||
Remark: strings.TrimSpace(row.Remark),
|
Remark: strings.TrimSpace(row.Remark),
|
||||||
IsExtracted: 0,
|
IsExtracted: 0,
|
||||||
})
|
})
|
||||||
case "windsurf":
|
case "windsurf":
|
||||||
_, err = models.Orm.Insert(&models.PlatformAccountPoolWindsurf{
|
_, err = models.Orm.Insert(&models.PlatformAccountPoolWindsurf{
|
||||||
DataType: strings.TrimSpace(row.DataType),
|
DataType: strings.TrimSpace(row.DataType),
|
||||||
Account: strings.TrimSpace(row.Account),
|
Account: acc,
|
||||||
Password: strings.TrimSpace(row.Password),
|
Password: strings.TrimSpace(row.Password),
|
||||||
Token: strings.TrimSpace(row.Token),
|
Token: tk,
|
||||||
Remark: strings.TrimSpace(row.Remark),
|
Remark: strings.TrimSpace(row.Remark),
|
||||||
IsExtracted: 0,
|
IsExtracted: 0,
|
||||||
})
|
})
|
||||||
case "krio":
|
case "krio":
|
||||||
_, err = models.Orm.Insert(&models.PlatformAccountPoolKiro{
|
_, err = models.Orm.Insert(&models.PlatformAccountPoolKiro{
|
||||||
DataType: strings.TrimSpace(row.DataType),
|
DataType: strings.TrimSpace(row.DataType),
|
||||||
Account: strings.TrimSpace(row.Account),
|
Account: acc,
|
||||||
Password: strings.TrimSpace(row.Password),
|
Password: strings.TrimSpace(row.Password),
|
||||||
Token: strings.TrimSpace(row.Token),
|
Token: tk,
|
||||||
Remark: strings.TrimSpace(row.Remark),
|
Remark: strings.TrimSpace(row.Remark),
|
||||||
IsExtracted: 0,
|
IsExtracted: 0,
|
||||||
})
|
})
|
||||||
case "codex":
|
case "codex":
|
||||||
_, err = models.Orm.Insert(&models.PlatformAccountPoolCodex{
|
_, err = models.Orm.Insert(&models.PlatformAccountPoolCodex{
|
||||||
DataType: strings.TrimSpace(row.DataType),
|
DataType: strings.TrimSpace(row.DataType),
|
||||||
Account: strings.TrimSpace(row.Account),
|
Account: acc,
|
||||||
Password: strings.TrimSpace(row.Password),
|
Password: strings.TrimSpace(row.Password),
|
||||||
Token: strings.TrimSpace(row.Token),
|
Token: tk,
|
||||||
Remark: strings.TrimSpace(row.Remark),
|
Remark: strings.TrimSpace(row.Remark),
|
||||||
IsExtracted: 0,
|
IsExtracted: 0,
|
||||||
})
|
})
|
||||||
@@ -447,8 +574,16 @@ func batchAddPoolRows(c *beego.Controller, module string) {
|
|||||||
poolJSONErr(c, 500, 500, "批量添加失败: "+err.Error())
|
poolJSONErr(c, 500, 500, "批量添加失败: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
successCount++
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "批量添加成功",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"success_count": successCount,
|
||||||
|
"duplicate_count": duplicateCount,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量添加成功"}
|
|
||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
// Register 注册前端站点(index)路由。
|
import (
|
||||||
// 建议统一使用 /index/* 或根路径,根据后续设计补充。
|
"server/controllers"
|
||||||
func Register() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register 注册前端站点(index)路由。
|
||||||
|
func Register() {
|
||||||
|
beego.Router("/index/headmenu", &controllers.IndexPortalController{}, "get:GetHeadMenu")
|
||||||
|
beego.Router("/index/footerdata", &controllers.IndexPortalController{}, "get:GetFooterData")
|
||||||
|
beego.Router("/index/newscentertop4", &controllers.IndexPortalController{}, "get:GetNewsCenterTop4")
|
||||||
|
beego.Router("/index/companynews", &controllers.IndexPortalController{}, "get:GetCompanyNews")
|
||||||
|
beego.Router("/index/kingdeenews", &controllers.IndexPortalController{}, "get:GetKingdeeNews")
|
||||||
|
beego.Router("/index/technologyCenter", &controllers.IndexPortalController{}, "get:GetTechnologyCenter")
|
||||||
|
beego.Router("/index/technologyCategories", &controllers.IndexPortalController{}, "get:GetTechnologyCategories")
|
||||||
|
beego.Router("/index/companynews/detail/:id", &controllers.IndexPortalController{}, "get:GetCompanyNewsDetail")
|
||||||
|
beego.Router("/index/kingdeenews/detail/:id", &controllers.IndexPortalController{}, "get:GetKingdeeNewsDetail")
|
||||||
|
beego.Router("/index/articleLikes/:id", &controllers.IndexPortalController{}, "post:AddLike")
|
||||||
|
beego.Router("/index/articleUnlikes/:id", &controllers.IndexPortalController{}, "post:ReduceLike")
|
||||||
|
beego.Router("/index/views/:id", &controllers.IndexPortalController{}, "post:AddViews")
|
||||||
|
beego.Router("/index/onepage/:path", &controllers.IndexPortalController{}, "get:GetOnePageByPath")
|
||||||
|
}
|
||||||
|
|||||||
@@ -103,19 +103,24 @@ function openAddDialog(mode = "single") {
|
|||||||
async function saveRows(rows) {
|
async function saveRows(rows) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (rows.length === 1) {
|
if (rows.length === 1) {
|
||||||
await addAccountPool(moduleKey, rows[0]);
|
return await addAccountPool(moduleKey, rows[0]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await batchAddAccountPool(moduleKey, rows);
|
return await batchAddAccountPool(moduleKey, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEditSubmit(payload) {
|
async function handleEditSubmit(payload) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await saveRows(payload.rows || []);
|
const res = await saveRows(payload.rows || []);
|
||||||
ElMessage.success(
|
if (payload.mode === "batch") {
|
||||||
payload.mode === "batch" ? "批量添加成功" : "账号添加成功",
|
const successCount = res?.data?.success_count ?? 0;
|
||||||
);
|
const duplicateCount = res?.data?.duplicate_count ?? 0;
|
||||||
|
ElMessage.success(
|
||||||
|
`批量添加完成:成功 ${successCount} 条,重复跳过 ${duplicateCount} 条`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ElMessage.success("账号添加成功");
|
||||||
|
}
|
||||||
await fetchList();
|
await fetchList();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -165,19 +165,24 @@ function nowText() {
|
|||||||
async function saveRows(rows) {
|
async function saveRows(rows) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (rows.length === 1) {
|
if (rows.length === 1) {
|
||||||
await addAccountPool(moduleKey, rows[0]);
|
return await addAccountPool(moduleKey, rows[0]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await batchAddAccountPool(moduleKey, rows);
|
return await batchAddAccountPool(moduleKey, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEditSubmit(payload) {
|
async function handleEditSubmit(payload) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await saveRows(payload.rows || []);
|
const res = await saveRows(payload.rows || []);
|
||||||
ElMessage.success(
|
if (payload.mode === "batch") {
|
||||||
payload.mode === "batch" ? "批量添加成功" : "账号添加成功",
|
const successCount = res?.data?.success_count ?? 0;
|
||||||
);
|
const duplicateCount = res?.data?.duplicate_count ?? 0;
|
||||||
|
ElMessage.success(
|
||||||
|
`批量添加完成:成功 ${successCount} 条,重复跳过 ${duplicateCount} 条`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ElMessage.success("账号添加成功");
|
||||||
|
}
|
||||||
await fetchList();
|
await fetchList();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -103,19 +103,24 @@ function openAddDialog(mode = "single") {
|
|||||||
async function saveRows(rows) {
|
async function saveRows(rows) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (rows.length === 1) {
|
if (rows.length === 1) {
|
||||||
await addAccountPool(moduleKey, rows[0]);
|
return await addAccountPool(moduleKey, rows[0]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await batchAddAccountPool(moduleKey, rows);
|
return await batchAddAccountPool(moduleKey, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEditSubmit(payload) {
|
async function handleEditSubmit(payload) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await saveRows(payload.rows || []);
|
const res = await saveRows(payload.rows || []);
|
||||||
ElMessage.success(
|
if (payload.mode === "batch") {
|
||||||
payload.mode === "batch" ? "批量添加成功" : "账号添加成功",
|
const successCount = res?.data?.success_count ?? 0;
|
||||||
);
|
const duplicateCount = res?.data?.duplicate_count ?? 0;
|
||||||
|
ElMessage.success(
|
||||||
|
`批量添加完成:成功 ${successCount} 条,重复跳过 ${duplicateCount} 条`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ElMessage.success("账号添加成功");
|
||||||
|
}
|
||||||
await fetchList();
|
await fetchList();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -89,19 +89,24 @@ function openAddDialog(mode = "single") {
|
|||||||
async function saveRows(rows) {
|
async function saveRows(rows) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (rows.length === 1) {
|
if (rows.length === 1) {
|
||||||
await addAccountPool(moduleKey, rows[0]);
|
return await addAccountPool(moduleKey, rows[0]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await batchAddAccountPool(moduleKey, rows);
|
return await batchAddAccountPool(moduleKey, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEditSubmit(payload) {
|
async function handleEditSubmit(payload) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await saveRows(payload.rows || []);
|
const res = await saveRows(payload.rows || []);
|
||||||
ElMessage.success(
|
if (payload.mode === "batch") {
|
||||||
payload.mode === "batch" ? "批量添加成功" : "账号添加成功",
|
const successCount = res?.data?.success_count ?? 0;
|
||||||
);
|
const duplicateCount = res?.data?.duplicate_count ?? 0;
|
||||||
|
ElMessage.success(
|
||||||
|
`批量添加完成:成功 ${successCount} 条,重复跳过 ${duplicateCount} 条`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ElMessage.success("账号添加成功");
|
||||||
|
}
|
||||||
await fetchList();
|
await fetchList();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user